diff --git a/.gitattributes b/.gitattributes index 86883b05..e6da31fa 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,34 @@ # Preserve concurrent additions to the shared agent instructions. AGENTS.md merge=union +# Preserve every authored byte occurrence hashed into provider-generator identity. +Cargo.lock -text +Cargo.toml -text +rust-toolchain.toml -text +crates/echo-edict-canonical/Cargo.toml -text +crates/echo-edict-canonical/src/lib.rs -text +crates/echo-wesley-gen/Cargo.toml -text +crates/echo-wesley-gen/src/lib.rs -text +crates/echo-wesley-gen/src/provider_*.rs -text +crates/echo-wesley-gen/src/bin/echo-edict-provider-*.rs -text +schemas/edict-provider/contracts/v1/** -text +schemas/edict-provider/echo-provider-semantics-v1.json -text +schemas/edict-provider/generation-settings-v1.json -text + +# Preserve the exact audited component owners before package assembly. +schemas/edict-provider/components/v1/** -text +schemas/edict-provider/components/v1/**/*.wasm binary + # Preserve exact checked provider corpus bytes across checkout configurations. schemas/edict-provider/generated/v1/** -text schemas/edict-provider/generated/v1/**/*.cbor binary + +# Preserve the exact digest-locked provider distribution across checkouts. +schemas/edict-provider/package/v1/** -text +schemas/edict-provider/package/v1/**/*.cbor binary +schemas/edict-provider/package/v1/**/*.wasm binary + +# Preserve package-local provider source/artifact carriers byte-for-byte. +crates/echo-wesley-gen/assets/v1/** -text +crates/echo-wesley-gen/assets/v1/**/*.cbor binary +crates/echo-wesley-gen/assets/v1/**/*.wasm binary diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5da49394..91090aac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -177,6 +177,10 @@ jobs: . - name: cargo test (workspace sans warp-core) run: cargo test --workspace --exclude warp-core + - name: checked Edict provider crate assets are current and packaged + run: cargo +1.90.0 run --locked -p echo-wesley-gen --bin echo-edict-provider-assets -- --check-package-list + - name: checked Edict provider package is current + run: cargo +1.90.0 run --locked -p echo-wesley-gen --bin echo-edict-provider-package -- --check - name: package publishable Edict canonical leaf run: cargo package -p echo-edict-canonical --locked diff --git a/CHANGELOG.md b/CHANGELOG.md index 03dae4a8..b7d97488 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,42 @@ ### Added +- `echo-wesley-gen` now purely assembles and digest-admits the first complete + Echo Edict provider distribution from the verified 22-file generated corpus + and explicit lowerer/verifier bytes. The derived provider manifest carries ten + exact routes and 24 schema bindings—nine invocation domains, the generated + artifact profile, and 14 generated-resource domains—but never inventories + itself. A versioned canonical-CBOR package root binds those semantics plus raw hashes of + all 24 non-manifest members, while the exact 25-file inventory, deterministic + JSON rendering, mixed raw/domain-framed digest laws, packaged Wesley + provenance/review, component bounds, and an external expected provider pin + all fail closed through structured errors. This is package-occurrence + authentication only; Edict schema/component preflight and Echo runtime + installation remain separate authority crossings. A dedicated publisher now + checks that all 22 generated members exactly reproduce the current checked + provider corpus introduced by #652 before writing the two components and derived manifest as a + self-contained 25-file distribution. Its capability-oriented filesystem + boundary refuses invalid expected inventories before resolving the root, + bounds actual-tree enumeration and expected-byte reads, never opens an + unexpected regular file, and makes `--check` report sorted drift without + creating, deleting, or rewriting package material. +- The checked provider package now passes an isolated Edict-native readiness + boundary pinned to Edict merge `c75c3f55`. The exact manifest constructs its + immutable 24-domain schema registry, all five canonical primaries and 14 + generated resources satisfy their owning CDDL roots, resource references are + bound field-by-field to independently recomputed domain-framed digests, both + components pass frozen-WIT preflight, and both request kinds produce opaque + validation proofs. Schema-valid byte replacement, digest mutation, semantic + field swaps, authority-source disagreement, malformed schemas/components, + and invalid requests all fail before guest execution. This proves package + readiness only, never Echo installation, execution, or runtime authority. +- `echo-wesley-gen` now carries a fixed 35-file package-local source and provider + asset boundary, preserving original logical source labels while making its + `.crate` archive independent of workspace-parent files. An explicit sync tool + distinguishes authoritative generated/component owners from their checked + package corroboration, supports staged regeneration without circularity, and + checks exact Cargo archive selection. The extracted archive compiles when its + still-unpublished Echo dependencies are supplied through local patches. - Echo now provides the exact `edict:target-provider/lowerer@1.0.0` Component Model implementation for the first checked provider closure. The pure lowerer accepts only explicit digest-bound Core, target-profile, @@ -50,8 +86,8 @@ predicate, input-constraint, require-failure, and Core-value shapes before separating malformed artifacts from well-formed unsupported semantics, and one admitted diagnostic-ABI identity now binds both the target profile and - every emitted report. The 188,736-byte checked component has SHA-256 - `e13eda6e02d5a46d2aecdec0546d53a7bf66f2580f8d5ec06e5d76710716a27b` + every emitted report. The 189,515-byte checked component has SHA-256 + `11fcaf291ffacac62800ab3fc198e29a23a591bcd30ca98828fa10a8ad1f1952` and reproduces byte-for-byte across independently provisioned designated `linux/amd64` builders. The isolated pinned Edict host preflights the exact request artifacts and declared output schema, invokes that checked component, @@ -64,7 +100,11 @@ canonical CBOR and domain-framed digest contracts as a publishable `0.1.0` leaf. `echo-wesley-gen` retains its existing compatibility surface through a re-export, while executable provider components use the same codec without - depending on generator or Wesley APIs. + depending on generator or Wesley APIs. Its decoder now applies a 65,536-node + host materialization ceiling, charging map keys and values separately. A + matching cumulative reservation budget rejects both oversized direct + containers and nested declarations that attempt to reuse the same capacity + allowance before reserving their storage. - `echo-wesley-gen` now checks in the first exact 22-file Edict provider artifact corpus: five canonical-CBOR primaries, fourteen canonical-CBOR resources, the self-contained CDDL, Wesley provenance JSON, and diff --git a/crates/echo-edict-canonical/README.md b/crates/echo-edict-canonical/README.md index 5e663009..9fcc652e 100644 --- a/crates/echo-edict-canonical/README.md +++ b/crates/echo-edict-canonical/README.md @@ -15,7 +15,12 @@ definite-length byte and UTF-8 text strings, arrays, and maps. Encoding sorts maps by canonical key bytes and rejects duplicate canonical keys. Decoding rejects indefinite-length, tagged, floating-point, simple, non-minimal, trailing, malformed, duplicate-key, and noncanonical inputs. Both directions -enforce Edict's exact 128-container nesting boundary. +enforce Edict's exact 128-container nesting boundary. Decoding additionally +caps one materialized value tree at 65,536 nodes, charging map keys and values +separately. A matching cumulative reservation budget is charged before every +container capacity allocation, so nested declarations cannot reuse the same +budget to amplify host allocation. Those ceilings are Echo host resource +bounds, not new Edict byte-identity claims. Failures expose stable `CanonicalValueErrorKind` values rather than Rust debug spellings. The implementation is deterministic and pure: it performs no diff --git a/crates/echo-edict-canonical/src/lib.rs b/crates/echo-edict-canonical/src/lib.rs index 81cac630..53815795 100644 --- a/crates/echo-edict-canonical/src/lib.rs +++ b/crates/echo-edict-canonical/src/lib.rs @@ -26,6 +26,14 @@ pub const EDICT_DIGEST_FRAME_V1: &str = "edict.digest/v1"; /// is accepted; one additional container is rejected. pub const MAX_CANONICAL_NESTING_DEPTH_V1: usize = 128; +/// Maximum number of value nodes materialized by one canonical decode. +/// +/// The root counts as one node. Array members, map keys, and map values each +/// count separately. This is an Echo host resource bound rather than part of +/// the Edict canonical byte identity; it prevents compact collection headers +/// from amplifying into unbounded host allocations before admission. +pub const MAX_CANONICAL_DECODE_NODES_V1: usize = 65_536; + /// Stable failure categories for Edict canonical values and byte streams. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum CanonicalValueErrorKind { @@ -360,11 +368,22 @@ where struct Decoder<'a> { bytes: &'a [u8], position: usize, + remaining_nodes: usize, + remaining_reserved_nodes: usize, } impl<'a> Decoder<'a> { const fn new(bytes: &'a [u8]) -> Self { - Self { bytes, position: 0 } + Self::with_node_budget(bytes, MAX_CANONICAL_DECODE_NODES_V1) + } + + const fn with_node_budget(bytes: &'a [u8], node_budget: usize) -> Self { + Self { + bytes, + position: 0, + remaining_nodes: node_budget, + remaining_reserved_nodes: node_budget, + } } const fn remaining(&self) -> usize { @@ -372,6 +391,7 @@ impl<'a> Decoder<'a> { } fn value(&mut self, depth: usize) -> Result { + self.charge_nodes(1)?; check_depth(depth)?; let initial = self.byte()?; let major = initial >> 5; @@ -401,6 +421,8 @@ impl<'a> Decoder<'a> { 4 => { check_container_depth(depth)?; let length = self.length(additional)?; + self.ensure_nodes_available(length)?; + self.reserve_nodes(length)?; let mut values = Vec::with_capacity(length); for _ in 0..length { values.push(self.value(depth + 1)?); @@ -410,6 +432,9 @@ impl<'a> Decoder<'a> { 5 => { check_container_depth(depth)?; let length = self.length(additional)?; + let child_nodes = length.checked_mul(2).ok_or_else(node_budget_error)?; + self.ensure_nodes_available(child_nodes)?; + self.reserve_nodes(child_nodes)?; let mut entries = Vec::with_capacity(length); let mut encoded_keys = BTreeSet::new(); for _ in 0..length { @@ -469,6 +494,29 @@ impl<'a> Decoder<'a> { checked_collection_length::(declared, remaining) } + fn ensure_nodes_available(&self, required: usize) -> Result<(), CanonicalValueError> { + if required > self.remaining_nodes { + return Err(node_budget_error()); + } + Ok(()) + } + + fn charge_nodes(&mut self, count: usize) -> Result<(), CanonicalValueError> { + self.remaining_nodes = self + .remaining_nodes + .checked_sub(count) + .ok_or_else(node_budget_error)?; + Ok(()) + } + + fn reserve_nodes(&mut self, count: usize) -> Result<(), CanonicalValueError> { + self.remaining_reserved_nodes = self + .remaining_reserved_nodes + .checked_sub(count) + .ok_or_else(node_budget_error)?; + Ok(()) + } + fn byte(&mut self) -> Result { let Some(value) = self.bytes.get(self.position).copied() else { return Err(CanonicalValueError::new( @@ -504,9 +552,16 @@ impl<'a> Decoder<'a> { } } +fn node_budget_error() -> CanonicalValueError { + CanonicalValueError::new( + CanonicalValueErrorKind::UnsupportedValue, + "canonical CBOR decoded node budget exceeded", + ) +} + #[cfg(test)] mod tests { - use super::{checked_collection_length, CanonicalValueError, CanonicalValueErrorKind}; + use super::{checked_collection_length, CanonicalValueError, CanonicalValueErrorKind, Decoder}; #[test] fn declared_length_bounds_precede_host_width_conversion() { @@ -518,4 +573,22 @@ mod tests { )) ); } + + #[test] + fn nested_container_reservations_are_cumulatively_bounded() { + let bytes = [0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0xf6]; + let mut decoder = Decoder::with_node_budget(&bytes, 8); + + assert_eq!( + decoder.value(0), + Err(CanonicalValueError::new( + CanonicalValueErrorKind::UnsupportedValue, + "canonical CBOR decoded node budget exceeded" + )) + ); + assert_eq!( + decoder.position, 2, + "the second container must be refused before its capacity is reserved" + ); + } } diff --git a/crates/echo-edict-canonical/tests/canonical_contract.rs b/crates/echo-edict-canonical/tests/canonical_contract.rs index 1e095f0e..a3ff2cbb 100644 --- a/crates/echo-edict-canonical/tests/canonical_contract.rs +++ b/crates/echo-edict-canonical/tests/canonical_contract.rs @@ -5,7 +5,8 @@ use echo_edict_canonical::{ decode_canonical_cbor_v1, digest_canonical_value_v1, encode_canonical_cbor_v1, - CanonicalValueErrorKind, CanonicalValueV1, MAX_CANONICAL_NESTING_DEPTH_V1, + CanonicalValueErrorKind, CanonicalValueV1, MAX_CANONICAL_DECODE_NODES_V1, + MAX_CANONICAL_NESTING_DEPTH_V1, }; fn text(value: &str) -> CanonicalValueV1 { @@ -267,3 +268,39 @@ fn unsupported_cbor_and_digest_domains_fail_closed() { digest_canonical_value_v1("test.second/v1", &value).expect("second digest computes") ); } + +#[test] +fn decoded_node_budget_rejects_compact_container_amplification_before_reserve() { + let node_limit = u32::try_from(MAX_CANONICAL_DECODE_NODES_V1) + .expect("canonical decode-node limit fits the CBOR u32 length form"); + + let mut array = vec![0x9a]; + array.extend_from_slice(&node_limit.to_be_bytes()); + array.resize(array.len() + MAX_CANONICAL_DECODE_NODES_V1, 0xf6); + let array_error = decode_canonical_cbor_v1(&array) + .expect_err("an array that exhausts the child-node budget rejects before reserve"); + assert_eq!( + array_error.kind(), + CanonicalValueErrorKind::UnsupportedValue + ); + assert_eq!( + array_error.detail(), + "canonical CBOR decoded node budget exceeded" + ); + + let map_entries = node_limit / 2; + let mut map = vec![0xb9]; + map.extend_from_slice( + &u16::try_from(map_entries) + .expect("half the canonical decode-node limit fits the CBOR u16 length form") + .to_be_bytes(), + ); + map.resize(map.len() + MAX_CANONICAL_DECODE_NODES_V1, 0xf6); + let map_error = decode_canonical_cbor_v1(&map) + .expect_err("a map that exhausts the key/value-node budget rejects before reserve"); + assert_eq!(map_error.kind(), CanonicalValueErrorKind::UnsupportedValue); + assert_eq!( + map_error.detail(), + "canonical CBOR decoded node budget exceeded" + ); +} diff --git a/crates/echo-edict-provider-verifier/README.md b/crates/echo-edict-provider-verifier/README.md index 5a58ccd0..301bf0df 100644 --- a/crates/echo-edict-provider-verifier/README.md +++ b/crates/echo-edict-provider-verifier/README.md @@ -21,9 +21,9 @@ target profile's diagnostic ABI and every emitted report consume one shared admitted identity. The `wasm32` guest adapter vendors Edict's exact frozen `edict:target-provider/verifier@1.0.0` WIT world and performs only exhaustive -transport-to-model conversion. Its reproducibly built 188,736-byte checked +transport-to-model conversion. Its reproducibly built 189,515-byte checked component has SHA-256 -`e13eda6e02d5a46d2aecdec0546d53a7bf66f2580f8d5ec06e5d76710716a27b`. +`11fcaf291ffacac62800ab3fc198e29a23a591bcd30ca98828fa10a8ad1f1952`. Component identity and admitted host replay remain separate propositions: the pinned Edict host preflights the request artifacts and declared output schema, invokes the checked component, then admits and manifests each returned accepted diff --git a/crates/echo-wesley-gen/README.md b/crates/echo-wesley-gen/README.md index bdb45b60..f7d3286e 100644 --- a/crates/echo-wesley-gen/README.md +++ b/crates/echo-wesley-gen/README.md @@ -112,6 +112,56 @@ semantic/settings/contract inputs already bound by Wesley and every generated output, preventing a circular generator identity. This is source/dependency-lock identity, not an executable-build or supply-chain attestation. +`provider_package` purely assembles the 22 generated files and two explicit +provider components into one 24-member, non-self-referential package closure, +then derives the separate `edict.provider-manifest/v1` JSON file. The Echo-owned +package root is a domain-framed canonical-CBOR value over the exact manifest +routes, 24 schema bindings (nine invocation domains, the generated artifact +profile, and 14 generated-resource domains), and raw SHA-256 of every +non-manifest member. +Canonical-CBOR artifact routes retain their Edict domain-framed identities; +CDDL, Wesley evidence JSON, components, and every physical member retain raw +exact-byte identities. Digest admission requires exact deterministic manifest +JSON, one shared semantic-source/generator reference proven against the packaged +Wesley provenance and review, the exact sorted 25-file inventory, and an +external caller pin. The resulting proof authenticates package occurrence only. +Edict schema-registry construction and component contract preflight remain +separate required crossings before guest execution, and Echo runtime admission +remains later still. + +`echo-edict-provider-package` is the explicit publication boundary for that +distribution. Before any filesystem action it proves that the package's 22 +`generated/` members are the current checked provider corpus introduced by #652, +then writes the two +checked components and derived manifest through the same no-follow, +unexpected-entry-refusing filesystem boundary used by the artifact corpus. The +checked 25-file result lives under `schemas/edict-provider/package/v1/`. +`--check` reports sorted missing, changed, or unexpected package members and +never repairs or creates them. The shared boundary validates a strictly sorted, +unique expected inventory before resolving the root, caps that inventory at 256 +files and 64 MiB, caps an actual scan at 1,024 entries, and never opens or reads +an unexpected regular file. + +`echo-edict-provider-assets` maintains the exact 35-file package-local carrier +tree under `assets/v1/`. The physical carrier names are packaging locations, +not replacement source identities: generator provenance continues to name the +original repository-relative authored paths. Read-only mode requires every +carrier to match its fixed owner, requires generated artifacts and components +to match their checked package copies, and can prove that `cargo package --list` +selects exactly the complete carrier inventory. Explicit `--write` mode copies +authoritative owners without requiring the temporarily stale package copy, +allowing the honest staged sequence artifact generation, carrier sync, package +generation, then final carrier corroboration. It never discovers a preferred +owner or normalizes authored bytes. + +The isolated `tests/edict-provider-host-v1` gate pins Edict revision +`c75c3f550d049485ba00eae0dc272c6dd6aca11f` and consumes the exact checked +package. It constructs the native 24-domain schema registry, validates all 19 +canonical package members under their owning roots, binds every lawpack and +target-profile resource field to exact packaged bytes, prepares both frozen-WIT +components, and validates both request kinds without invoking guest code. That +is pre-execution package readiness, not Echo installation or runtime authority. + The dedicated `echo-edict-provider-artifacts` binary is the explicit filesystem boundary. The caller-supplied `--out` path is resolved once under ambient filesystem authority; its final corpus-root entry is opened or created without @@ -151,6 +201,14 @@ cargo +1.90.0 run --locked -p echo-wesley-gen \ # Report checked-corpus drift without rewriting anything cargo +1.90.0 run --locked -p echo-wesley-gen \ --bin echo-edict-provider-artifacts -- --check + +# Publish the self-contained digest-locked provider package +cargo +1.90.0 run --locked -p echo-wesley-gen \ + --bin echo-edict-provider-package -- + +# Report package drift without rewriting anything +cargo +1.90.0 run --locked -p echo-wesley-gen \ + --bin echo-edict-provider-package -- --check ``` ## Notes diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/contracts/v1/edict-provider-contracts.cddl b/crates/echo-wesley-gen/assets/v1/edict-provider/contracts/v1/edict-provider-contracts.cddl new file mode 100644 index 00000000..7544122d --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/contracts/v1/edict-provider-contracts.cddl @@ -0,0 +1,793 @@ +; SPDX-License-Identifier: Apache-2.0 +; edict-provider-contracts.cddl +; Generated from Edict-owned ABI fragments. DO NOT EDIT. + +; --- edict-common.cddl --- +; edict-common.cddl +; Shared CDDL types for the Edict ABIs, defined ONCE here so they cannot drift +; (EDICT-ABI-NODUP-001). Two groups: +; - resource-ref, sha256-digest, effect-failure-body, effect-kind, authority-class, +; core-type-ref: assembled with edict-target-profile.cddl and +; edict-lawpack.cddl by the build; those schemas do not redefine them. +; - operation-profile, optic-template, aperture-requirement and their refs: +; consumed by the Core/optic layer (edict-core.cddl) and referenced by the +; language/target-profile specs. +; Authoritative byte encoding: edict.canonical-cbor/v1. + +; A normative subcomponent referenced by identity plus digest. Manifests never +; embed their own self-digest in their preimage (EDICT-CORE-SELFHASH-001). +resource-ref = { id: tstr, digest: sha256-digest } + +; Digests are authoritative as typed byte values, never hex strings. Review JSON +; renders this as "sha256:<64 lowercase hex>" (EDICT-DIGEST-WIRE-001). +sha256-digest = [ algorithm: "sha256", bytes: bstr .size 32 ] + +; A named low-level failure an effect can raise. The source obstruction map +; binds it (by coordinate) and constructs a typed domain obstruction from its +; payload (EDICT-ABI-FAILURE-NAMED-001). +; An effect's `effectFailures` list MUST have unique `coordinate`s: since the +; obstruction map is keyed by coordinate, two failures sharing a coordinate (even +; with different authorityClass/payloadType) make exhaustive mapping and binder +; typing ambiguous and are rejected (EDICT-ABI-FAILURE-UNIQUE-001). +; +; Effects carry their failures as a map `{ failure-ident => effect-failure-body }` +; (see the target/lawpack effect schemas). The failure coordinate is the map +; KEY, so it is not repeated in the body and cannot disagree with the key. +effect-failure-body = { + authorityClass: authority-class, + payloadType: core-type-ref, ; typed, bounded (may be empty record) +} + +; A failure coordinate must be a bare Edict `ident` (letter/underscore then +; letters/digits/underscores) AND must not be a reserved keyword (e.g. `else`, +; `basis`, `where`, `require`, `for`, `if`). The source obstruction-map LHS only +; accepts a non-keyword `ident`, so a hyphen/dot/keyword coordinate would be +; ABI-valid yet impossible to map exhaustively in source. The regex captures the +; lexical shape; keyword exclusion is an additional validation rule +; (EDICT-ABI-FAILURE-IDENT-001). +failure-ident = tstr .regexp "[A-Za-z_][A-Za-z0-9_]*" + +effect-kind = "read" / "create" / "ensure" / "replace" / "delete" / + "append" / "reduce" / "semantic.emit" / "custom" + +authority-class = "domainMappable" / "participantOwned" / "integrityFault" / + "resourceFault" / "internalFault" + +core-type-ref = tstr ; canonical Core type coordinate + +; An operation profile supplies the optic template a Core intent resolves its +; opticKind/boundaryKind/supportPolicy/lossDisposition from. Target profiles and +; lawpacks publish these as a map `{ coordinate => operation-profile }`, so the +; coordinate is the KEY, not a value field (EDICT-OPTIC-TEMPLATE-OWNER-001, +; EDICT-ABI-OPPROFILE-UNIQUE-001). +operation-profile = { + opticTemplate: optic-template, + effectPredicate: tstr, ; coordinate of the operation-mode predicate +} + +optic-template = { + opticKind: "revelation" / "affectReintegration", + boundaryKind: "projection" / "affect", + supportPolicy: tstr, ; canonical support-policy coordinate + lossDisposition: tstr, ; canonical loss-disposition coordinate + ? basisTemplate: tstr, ; optional digest-locked basis template coord + ; the aperture requirement this template supplies. Required when the template + ; is the source of a Core optic's apertureRequirement (i.e. the intent has no + ; source `footprint <= ...`), since apertureRequirement is mandatory in Core + ; (EDICT-OPTIC-APERTURE-REF-001). + ? apertureRequirement: aperture-requirement, +} + +; apertureRequirement is a typed reference, never a free-form string. A review +; rendering may show its coordinate (EDICT-OPTIC-APERTURE-REF-001). +aperture-requirement = footprint-ceiling-ref / abstract-footprint-obligation-ref +footprint-ceiling-ref = { kind: "footprintCeiling", ref: tstr } +abstract-footprint-obligation-ref = { kind: "abstractFootprintObligation", ref: tstr } + +; --- edict-core.cddl --- +; edict-core.cddl +; Normative schema for the Edict Core v1 semantic model. +; +; Scope boundary: this file defines Core meaning and schema shape only. It does +; not define a canonical encoder, Core module hash fields, hash fixtures, target +; lowering, admission bundles, or target-owned IR. + +core-module = { + apiVersion: "edict.core/v1", + coordinate: tstr, + imports: [* core-import], + types: { * tstr => core-type }, + intents: { + tstr => core-intent }, + requiredCoreCapabilities: [* tstr], +} + +core-import = { + kind: "lawpack" / "target" / "core", + ref: resource-ref, + ? alias: tstr, +} + +; --- types --------------------------------------------------------------- + +core-type = core-scalar-type / core-record-type / core-variant-type / + core-option-type / core-list-type / core-map-type / + core-capability-ref-type + +core-scalar-type = core-bool-type / core-int-type / core-string-type / + core-bytes-type / core-unit-type + +core-bool-type = { kind: "Bool" } +core-unit-type = { kind: "Unit" } +core-int-type = { + kind: "I64" / "U64" / "I32" / "U32" / "I16" / "U16" / "I8" / "U8", +} +core-string-type = { + kind: "String", + max: uint, + canonical: "unicode-scalar-nfc" / "raw-utf8", +} +core-bytes-type = { + kind: "Bytes", + max: uint, +} +core-record-type = { + kind: "Record", + fields: { * tstr => core-type-ref }, +} +core-variant-type = { + kind: "Variant", + cases: { + tstr => variant-case-body }, +} +variant-case-body = { + ? payload: core-type-ref, +} +core-option-type = { + kind: "Option", + item: core-type-ref, +} +core-list-type = { + kind: "List", + item: core-type-ref, + max: uint, +} +core-map-type = { + kind: "Map", + key: core-type-ref, + value: core-type-ref, + max: uint, +} +core-capability-ref-type = { + kind: "CapabilityRef", + item: core-type-ref, +} + +; core-type-ref is defined in edict-common.cddl and assembled with this schema. + +; --- references and values ---------------------------------------------- + +; Local identity is alpha-stable. `id` is the compiler-owned local coordinate; +; `alphaName` is the normalized human/debug name. Source binder spelling is not +; identity. +local-ref = { + id: tstr, + alphaName: tstr, + type: core-type-ref, +} + +core-value = core-null-value / core-bool-value / core-int-value / + core-string-value / core-bytes-value / core-record-value / + core-variant-value / core-list-value / core-map-value / + core-capability-value + +core-null-value = { kind: "null" } +core-bool-value = { kind: "bool", value: bool } +core-int-value = { kind: "int", width: tstr, value: int } +core-string-value = { kind: "string", value: tstr } +core-bytes-value = { kind: "bytes", value: bstr } +core-record-value = { kind: "record", fields: { * tstr => core-value } } +core-variant-value = { + kind: "variant", + type: core-type-ref, + case: tstr, + ? payload: core-value, +} +core-list-value = { kind: "list", values: [* core-value] } +core-map-value = { kind: "map", entries: [* [key: core-value, value: core-value]] } +core-capability-value = { + kind: "capability", + receipt: sha256-digest, +} + +; Edict-authored pure helpers use a pure Core function body. The body can bind +; pure expressions and return an expression, but it cannot contain Core effect, +; guard, branch, loop, match-node, or proof-obligation nodes. +core-fn-body = { + params: [* local-ref], + body: core-pure-block, +} + +core-pure-block = { + locals: [* local-ref], + bindings: [* pure-let-node], + result: core-expr, +} + +pure-let-node = { + kind: "let", + binding: local-ref, + value: core-expr, +} + +; --- expressions and predicates ----------------------------------------- + +core-expr = local-expr / const-expr / record-expr / field-expr / + variant-expr / match-expr / call-expr / list-expr / map-expr / + if-expr + +local-expr = { kind: "local", ref: local-ref } +const-expr = { kind: "const", value: core-value } +record-expr = { kind: "record", fields: { * tstr => core-expr } } +field-expr = { kind: "field", base: core-expr, field: tstr } +variant-expr = { + kind: "variant", + type: core-type-ref, + case: tstr, + ? payload: core-expr, +} +match-expr = { + kind: "match", + scrutinee: core-expr, + arms: [+ match-arm], +} +match-arm = { + case: tstr, + ? binder: local-ref, + body: core-expr, +} +call-expr = { + kind: "call", + callee: tstr, + typeArgs: [* core-type-ref], + args: [* core-expr], +} +list-expr = { kind: "list", values: [* core-expr] } +map-expr = { kind: "map", entries: [* [key: core-expr, value: core-expr]] } +if-expr = { + kind: "if", + predicate: core-predicate, + then: core-expr, + else: core-expr, +} + +core-predicate = true-predicate / false-predicate / not-predicate / + all-predicate / any-predicate / compare-predicate / + call-predicate / obstruction-predicate + +true-predicate = { kind: "true" } +false-predicate = { kind: "false" } +not-predicate = { kind: "not", value: core-predicate } +all-predicate = { kind: "all", values: [+ core-predicate] } +any-predicate = { kind: "any", values: [+ core-predicate] } +compare-predicate = { + kind: "compare", + op: "==" / "!=" / "<" / "<=" / ">" / ">=", + left: core-expr, + right: core-expr, +} +call-predicate = { + kind: "call", + predicate: tstr, + args: [* core-expr], +} +obstruction-predicate = { + kind: "obstruction", + coordinate: failure-ident, + payload: core-expr, +} + +input-constraint = { + coordinate: tstr, + source: "where" / "compiler", + predicate: core-predicate, +} + +; --- intents, blocks, and nodes ----------------------------------------- + +core-intent = { + input: core-type-ref, + output: core-type-ref, + requiredOperationProfile: tstr, + inputConstraints: [* input-constraint], + coreEvaluationBudget: core-budget, + body: core-block, + ? optic: core-optic, +} + +core-budget = { + maxSteps: uint, + maxAllocatedBytes: uint, + maxOutputBytes: uint, +} + +core-optic = { + opticKind: "revelation" / "affectReintegration", + boundaryKind: "projection" / "affect", + apertureRequirement: aperture-requirement, + supportPolicy: tstr, + lossDisposition: tstr, +} + +core-block = { + locals: [* local-ref], + nodes: [* core-node], + result: core-expr, +} + +core-node = let-node / require-node / effect-node / guard-node / branch-node / + for-node / match-node / proof-obligation-node + +let-node = { + kind: "let", + binding: local-ref, + value: core-expr, +} +require-node = { + kind: "require", + predicate: core-predicate, + onFailure: require-failure-arm, +} +require-failure-arm = terminal-require-failure / + continue-obstructed-require-failure +terminal-require-failure = { + kind: "terminal", + reason: obstruction-reason, +} +continue-obstructed-require-failure = { + kind: "continueObstructed", + reason: obstruction-reason, +} +obstruction-reason = { + reasonKind: tstr, + payload: { * tstr => core-expr }, +} +effect-node = { + kind: "effect", + binding: local-ref, + effect: tstr, + input: core-expr, + obstructionMap: { * failure-ident => obstruction-arm }, +} +obstruction-arm = { + binder: local-ref, + value: core-expr, +} +guard-node = { + kind: "guard", + predicate: core-predicate, + obstruction: core-expr, +} +branch-node = { + kind: "branch", + predicate: core-predicate, + then: core-block, + else: core-block, +} +for-node = { + kind: "for", + binder: local-ref, + iter: core-expr, + bound: core-bound, + body: core-block, +} +match-node = { + kind: "match", + scrutinee: core-expr, + arms: [+ match-block-arm], +} +match-block-arm = { + case: tstr, + ? binder: local-ref, + body: core-block, +} +proof-obligation-node = { + kind: "proof", + coordinate: tstr, + predicate: core-predicate, +} + +core-bound = literal-bound / coordinate-bound +literal-bound = { kind: "literal", value: uint } +coordinate-bound = { kind: "coordinate", ref: tstr } + +; Shared resource-ref, sha256-digest, failure-ident, aperture-requirement, and +; core-type-ref are defined once in edict-common.cddl. + +; --- edict-lawpack.cddl --- +; edict-lawpack.cddl +; Canonical schema for the Edict lawpack manifest and export surface. +; Authoritative byte encoding: edict.canonical-cbor/v1. JSON in the prose specs +; is a review rendering generated from this schema (EDICT-ABI-NODUP-001). + +lawpack-manifest = { + apiVersion: "edict.lawpack/v1", + id: tstr, + version: tstr, + acceptedCoreAbi: [+ tstr], + dependencies: [* lawpack-dep], ; acyclic, digest-locked (EDICT-LAWPACK-DAG-001) + exports: resource-ref, + ? targetAdapters: [+ target-adapter], ; required only if any runtime effect exists + ? helperComponent: executable-component, ; executable helpers carry their own sandbox+fuel + verifier: verifier, ; classified: declarative or executable + compatibility: resource-ref, + conformanceFixtureCorpus: resource-ref, +} + +; A verifier is either a declarative ruleset (no runtime) or an executable +; component. An executable verifier MUST carry its own sandbox and fuel model, +; so the schema enforces that no executable component is left unbounded +; (EDICT-ABI-VERIFIER-BOUND-001). +verifier = declarative-verifier / executable-verifier +declarative-verifier = { class: "declarative", ruleset: resource-ref } +executable-verifier = { + class: "executable", + component: resource-ref, + sandbox: resource-ref, + fuelModel: resource-ref, +} + +; Any executable component is bounded by its own sandbox + fuel model. +executable-component = { + component: resource-ref, + sandbox: resource-ref, + fuelModel: resource-ref, +} + +lawpack-dep = { id: tstr, version: tstr, digest: sha256-digest } + +; Adapter selection keys SOLELY off the digest-locked `acceptedTargetProfile` +; (its `id` is the profile id; its `digest` pins the exact profile/version). There +; are no independent display strings that could disagree with the lock, so a +; resolver cannot bind an adapter to one target while the lock proves another +; (EDICT-LAWPACK-ADAPTER-TARGETIR-001). +target-adapter = { + acceptedTargetProfile: resource-ref, ; digest-locked, authoritative selector + acceptedTargetIr: resource-ref, ; digest-locked + adapter: resource-ref, +} + +; Shared types resource-ref and sha256-digest are defined once in +; edict-common.cddl and assembled with this schema (EDICT-ABI-NODUP-001). + +; --- export surface ------------------------------------------------------ + +lawpack-exports = { + types: [* exported-type], + constants: [* exported-constant], + pureFunctions: [* pure-function], + effects: [* semantic-effect], + obstructions: [* obstruction-def], + ; keyed by operation-profile coordinate → uniqueness enforced + ; (EDICT-ABI-OPPROFILE-UNIQUE-001) + ; operation-profile records this lawpack exports (optic templates that + ; `implements`/`profile` clauses resolve against). operation-profile is + ; defined in edict-common.cddl (EDICT-ABI-OPPROFILE-SLOT-001). + operationProfiles: { * tstr => operation-profile }, ; keyed by coordinate +} + +exported-type = { coordinate: tstr, definition: core-type-ref } +exported-constant = { coordinate: tstr, type: core-type-ref, value: any } + +; A pure helper is a discriminated union by `source`, so the schema itself +; guarantees an implementation exists (EDICT-LAWPACK-PURE-IMPL-001): +; - "edict": authored in Edict/Core; the Core body is carried inline (hashed +; with the export surface). The schema requires the `body` field. +; - "component": implemented outside Edict; carries no inline body and instead +; carries its own digest-locked `implementation` (sandbox + fuel). It does +; not depend on the optional manifest-level helperComponent. +pure-function = edict-pure-function / component-pure-function + +pure-function-common = ( + coordinate: tstr, + typeParameters: [* tstr], + parameterTypes: [* core-type-ref], ; all bounded + returnType: core-type-ref, ; bounded + costTemplate: tstr, + determinismClass: "total" / "total-with-typed-diagnostic", +) + +edict-pure-function = { + pure-function-common, + source: "edict", + body: core-fn-body, ; inline, hash-significant +} + +component-pure-function = { + pure-function-common, + source: "component", + ; the digest-locked component implementing this helper. Required at the schema + ; level so a component helper can never validate without a hash-bound, + ; sandbox+fuel-described implementation (EDICT-LAWPACK-PURE-IMPL-001). + implementation: executable-component, +} + +; core-fn-body is defined by edict-core.cddl and assembled with this lawpack +; schema. It is a pure Core function body, not an effect-capable core-block. + +semantic-effect = { + coordinate: tstr, + typeParameters: [* tstr], + inputType: core-type-ref, ; bounded + outputType: core-type-ref, ; bounded + executionClass: "proofOnly" / "runtime", ; orthogonal to writeClass + effectKindHint: effect-kind, + footprintObligation: tstr, + costObligation: tstr, + effectFailures: { * failure-ident => effect-failure-body }, ; keyed by coordinate; unique + guardSupport: bool, +} + +obstruction-def = { + coordinate: tstr, + authorityClass: authority-class, + payloadSchema: core-type-ref, ; typed, bounded (may be empty record) +} + +; effect-failure-body, effect-kind, authority-class, and core-type-ref are defined +; once in edict-common.cddl and assembled with this schema (EDICT-ABI-NODUP-001). + +; --- edict-target-profile.cddl --- +; edict-target-profile.cddl +; Canonical schema for the Edict target profile manifest. +; Authoritative byte encoding: edict.canonical-cbor/v1 +; (see SPEC_continuum-contract-bundle-v1.md). JSON in the prose specs is a +; review rendering generated from this schema; this CDDL is the single source +; of truth (EDICT-ABI-NODUP-001). + +target-profile-manifest = { + apiVersion: "edict.target-profile/v1", + id: tstr, ; e.g. "echo.dpo" + version: tstr, ; e.g. "1" + acceptedCoreAbi: [+ tstr], ; e.g. ["edict.core/v1"] + + intrinsics: resource-ref, + intrinsicNamespace: tstr, + ; publishes this profile's operation-profile records (optic templates that + ; `profile`/`implements` clauses resolve against). References an + ; operation-profiles-document (EDICT-ABI-OPPROFILE-SLOT-001). + operationProfiles: resource-ref, + footprintAlgebra: resource-ref, + costAlgebra: resource-ref, + targetIr: resource-ref, + obstructionTaxonomy: resource-ref, + verifier: resource-ref, + lowerer: resource-ref, + sandbox: resource-ref, + fuelModel: resource-ref, + + ; fields the language spec requires of every profile + bundleProfile: resource-ref, + generatedArtifactProfiles: [* resource-ref], + canonicalEncodingRules: resource-ref, + ; Reserved/deferred: will list accepted lawpack-adapter ABI ids once that + ; byte-level ABI (edict.lawpack-adapter/v1) is specified. The schema enforces + ; emptiness now — the type is the empty array, so non-empty values are + ; rejected, not merely discouraged (EDICT-ABI-LAWPACK-ADAPTER-DEFER-001). + ? acceptedLawpackAdapterAbi: [], + diagnosticAbi: resource-ref, + + ; application doctrine + applicationModel: "atomic", + readConsistency: "application-snapshot" / tstr, + guardEvaluation: "precommit-atomic" / tstr, + obstructionRollback: "no-visible-effects" / tstr, + multiTarget: bool, + ; whether the target can evaluate precommit postcondition (`guarantee`) checks + ; inside the atomic application unit (EDICT-TARGET-POSTCOND-001) + postconditionSupport: bool, + + deterministicExecution: resource-ref, + conformanceFixtureCorpus: resource-ref, +} + +; Shared types resource-ref and sha256-digest are defined once in +; edict-common.cddl and assembled with this schema by the build +; (EDICT-ABI-NODUP-001). They are not redefined here. + +; --- intrinsic signature ------------------------------------------------- +; The artifact referenced by the manifest's `intrinsics` resource-ref is the +; intrinsic-signature corpus document below. Its layout is fixed so two +; independent profiles validate/hash the corpus identically +; (EDICT-ABI-INTRINSICS-DOC-001). + +; intrinsics is a MAP keyed by coordinate, so the schema itself enforces +; coordinate uniqueness. A provider receives the resolved corpus as a +; digest-bound semantic input and resolves coordinates within that artifact. +; Each map key MUST equal its record's `coordinate` field +; (EDICT-ABI-INTRINSIC-UNIQUE-001). +intrinsics-document = { + apiVersion: "edict.target-profile.intrinsics/v1", + intrinsics: { * tstr => intrinsic }, +} + +; The artifact referenced by the manifest's `operationProfiles` resource-ref. +; operation-profile / optic-template are defined in edict-common.cddl. Keyed by +; coordinate so resolution can't pick between two same-coordinate profiles +; (EDICT-ABI-OPPROFILE-SLOT-001, EDICT-ABI-OPPROFILE-UNIQUE-001). +operation-profiles-document = { + apiVersion: "edict.target-profile.operation-profiles/v1", + profiles: { * tstr => operation-profile }, +} + +; A typed pre-lowering question that can be proposed by Watson or an agent and +; checked by the compiler. It is canonical-CBOR encoded under +; `edict.lowering-requirements/v1`; the compiler checks this artifact, not the +; prose that produced it. +lowering-requirements = { + apiVersion: "edict.lowering-requirements/v1", + operationProfile: tstr, + semanticEffects: [* semantic-effect-requirement], + requiredWriteClasses: [* write-class], + guardKinds: [* guard-kind], + atomicity: atomicity-requirement, + postconditionSupport: bool, + obstructionCoordinates: [* tstr], + footprintObligations: [* tstr], + costObligations: [* tstr], + opticContract: tstr, +} + +semantic-effect-requirement = { + coordinate: tstr, + writeClass: write-class, + guardKinds: [* guard-kind], + obstructionCoordinates: [* tstr], + footprintObligations: [* tstr], + costObligations: [* tstr], +} + +write-class = "none" / "read" / "create" / "ensure" / "append" / + "replace" / "delete" / tstr +guard-kind = "precommit-atomic" / tstr +atomicity-requirement = "atomic" / tstr + +; A genuine union: pure constructors carry no effect kind or failures; effect +; intrinsics must (EDICT-TARGET-INTRINSIC-CLASS-001). The schema enforces this, +; not a comment. + +; The intrinsic's coordinate is the intrinsics map KEY, not a value field, so the +; key and coordinate can never disagree (EDICT-ABI-INTRINSIC-UNIQUE-001). +intrinsic = pure-intrinsic / effect-intrinsic + +pure-intrinsic = { + intrinsicClass: "pure", + typeParameters: [* tstr], + argumentTypes: [* core-type-ref], + returnType: core-type-ref, + guardSupport: false, + footprintTemplate: tstr, + costTemplate: tstr, + writeClass: "none", +} + +effect-intrinsic = { + intrinsicClass: "effect", + typeParameters: [* tstr], + argumentTypes: [* core-type-ref], + returnType: core-type-ref, + effectKind: effect-kind, + ; map keyed by failure coordinate (failure-ident); the failure coordinate is + ; the key, not a value field, so uniqueness is structural + ; (EDICT-ABI-FAILURE-UNIQUE-001). + effectFailures: { * failure-ident => effect-failure-body }, + guardSupport: bool, + footprintTemplate: tstr, + costTemplate: tstr, + writeClass: "read" / "create" / "ensure" / "append" / "replace" / + "delete" / "custom", + canParticipateInAtomicGuard: bool, +} + +; effect-failure-body, effect-kind, authority-class, and core-type-ref are defined +; once in edict-common.cddl and assembled with this schema (EDICT-ABI-NODUP-001). + +; --- edict-authority-facts.cddl --- +; edict-authority-facts.cddl +; Canonical schema for the first compiler-context authority-facts document. +; Authoritative byte encoding: edict.canonical-cbor/v1. +; +; This schema is assembled with edict-common.cddl so source.digest uses the +; shared sha256-digest typed value. JSON is a review/input rendering: its +; `sha256:<64 hex>` source digest is projected to [`sha256`, 32 raw bytes] on +; the wire, and its fact arrays project to the coordinate-keyed maps below. + +authority-facts = { + apiVersion: "edict.authority-facts/v1", + source: authority-fact-source, + operationProfiles: { * tstr => authority-operation-profile-fact }, + effectWriteClasses: { * tstr => authority-write-class }, + budgets: { * tstr => authority-budget-fact }, +} + +authority-fact-source = { + kind: "lawpack" / "targetProfile", + coordinate: tstr, + digest: sha256-digest, +} + +; The map key is the source operation-profile coordinate. It is not repeated in +; the value, so a key and embedded coordinate cannot disagree. Allowed write +; classes are a canonical map-set: the class is the unique key and null is the +; unit marker. Canonical CBOR fixes key order without a second ordering rule. +authority-operation-profile-fact = { + core: tstr, + allowedWriteClasses: { * authority-write-class => null }, +} + +; The effectWriteClasses map key is the semantic effect coordinate. The budgets +; map key is the source budget coordinate. Canonical CBOR map-key uniqueness +; makes duplicate fact coordinates structurally unrepresentable. +authority-budget-fact = { + maxSteps: uint, + maxAllocatedBytes: uint, + maxOutputBytes: uint, +} + +; AuthorityFactsDocument v1 intentionally accepts only the write classes the +; current compiler model can consume. `custom` is the sole v1 custom spelling; +; arbitrary target-profile extension strings do not enter this compiler path. +authority-write-class = "none" / "read" / "create" / "ensure" / "append" / + "replace" / "delete" / "custom" + +; --- edict-target-ir.cddl --- +; SPDX-License-Identifier: Apache-2.0 +; edict-target-ir.cddl +; Canonical schema for the Edict-owned Target IR artifact envelope. +; Authoritative byte encoding: edict.canonical-cbor/v1. +; +; This schema is assembled with edict-common.cddl and edict-core.cddl. It +; deliberately reuses Core expressions, predicates, budgets, local references, +; obstruction reasons, and obstruction arms so the schema matches the value +; emitted by the canonical Target IR encoder rather than restating those types. +; It describes the structural shape of valid lowering-produced artifacts. The +; lowering and encoder contracts separately enforce semantic identifier rules +; and canonical ordering/deduplication for set-like values. + +; Target IR encoding rejects an empty target-profile coordinate before bytes +; exist, so this root tightens the shared structural resource-ref accordingly. +target-ir-resource-ref = { + id: tstr .regexp "(?s).+", + digest: sha256-digest, +} + +target-ir-artifact = { + kind: "targetIrArtifact", + domain: tstr, + targetProfile: target-ir-resource-ref, + sourceCoreCoordinate: tstr, + intents: { * tstr => target-ir-intent }, +} + +target-ir-intent = { + operationProfile: tstr, + inputConstraints: [* input-constraint], + coreEvaluationBudget: core-budget, + requirements: [* target-ir-requirement], + steps: [* target-ir-step], + result: core-expr, +} + +target-ir-requirement = { + id: tstr, + predicate: core-predicate, + onFailure: require-failure-arm, +} + +target-ir-step = { + id: tstr, + binding: local-ref, + effect: tstr, + targetIntrinsic: tstr, + input: core-expr, + obstructionFailures: [* failure-ident], + obstructionArms: { * failure-ident => obstruction-arm }, +} diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/contracts/v1/manifest.json b/crates/echo-wesley-gen/assets/v1/edict-provider/contracts/v1/manifest.json new file mode 100644 index 00000000..fe91f629 --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/contracts/v1/manifest.json @@ -0,0 +1,80 @@ +{ + "apiVersion": "edict.provider-contract-pack/v1", + "coordinate": "edict.provider-contract-pack.cddl@1", + "license": "Apache-2.0", + "schema": { + "bytesHex": "3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d70726f76696465722d636f6e7472616374732e6364646c0a3b2047656e6572617465642066726f6d2045646963742d6f776e65642041424920667261676d656e74732e20444f204e4f5420454449542e0a0a3b202d2d2d2065646963742d636f6d6d6f6e2e6364646c202d2d2d0a3b2065646963742d636f6d6d6f6e2e6364646c0a3b20536861726564204344444c20747970657320666f722074686520456469637420414249732c20646566696e6564204f4e4345206865726520736f20746865792063616e6e6f742064726966740a3b202845444943542d4142492d4e4f4455502d303031292e2054776f2067726f7570733a0a3b2020202d207265736f757263652d7265662c207368613235362d6469676573742c206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c0a3b2020202020636f72652d747970652d7265663a20617373656d626c656420776974682065646963742d7461726765742d70726f66696c652e6364646c20616e640a3b202020202065646963742d6c61777061636b2e6364646c20627920746865206275696c643b2074686f736520736368656d617320646f206e6f74207265646566696e65207468656d2e0a3b2020202d206f7065726174696f6e2d70726f66696c652c206f707469632d74656d706c6174652c2061706572747572652d726571756972656d656e7420616e6420746865697220726566733a0a3b2020202020636f6e73756d65642062792074686520436f72652f6f70746963206c61796572202865646963742d636f72652e6364646c2920616e64207265666572656e636564206279207468650a3b20202020206c616e67756167652f7461726765742d70726f66696c652073706563732e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a0a3b2041206e6f726d617469766520737562636f6d706f6e656e74207265666572656e636564206279206964656e7469747920706c7573206469676573742e204d616e696665737473206e657665720a3b20656d626564207468656972206f776e2073656c662d64696765737420696e20746865697220707265696d616765202845444943542d434f52452d53454c46484153482d303031292e0a7265736f757263652d726566203d207b2069643a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20446967657374732061726520617574686f726974617469766520617320747970656420627974652076616c7565732c206e657665722068657820737472696e67732e20526576696577204a534f4e0a3b2072656e64657273207468697320617320227368613235363a3c3634206c6f77657263617365206865783e22202845444943542d4449474553542d574952452d303031292e0a7368613235362d646967657374203d205b20616c676f726974686d3a2022736861323536222c2062797465733a2062737472202e73697a65203332205d0a0a3b2041206e616d6564206c6f772d6c6576656c206661696c75726520616e206566666563742063616e2072616973652e2054686520736f75726365206f62737472756374696f6e206d61700a3b2062696e64732069742028627920636f6f7264696e6174652920616e6420636f6e73747275637473206120747970656420646f6d61696e206f62737472756374696f6e2066726f6d206974730a3b207061796c6f6164202845444943542d4142492d4641494c5552452d4e414d45442d303031292e0a3b20416e20656666656374277320606566666563744661696c7572657360206c697374204d555354206861766520756e697175652060636f6f7264696e61746560733a2073696e6365207468650a3b206f62737472756374696f6e206d6170206973206b6579656420627920636f6f7264696e6174652c2074776f206661696c757265732073686172696e67206120636f6f7264696e61746520286576656e0a3b207769746820646966666572656e7420617574686f72697479436c6173732f7061796c6f61645479706529206d616b652065786861757374697665206d617070696e6720616e642062696e6465720a3b20747970696e6720616d626967756f757320616e64206172652072656a6563746564202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a3b0a3b2045666665637473206361727279207468656972206661696c757265732061732061206d617020607b206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d600a3b202873656520746865207461726765742f6c61777061636b2065666665637420736368656d6173292e20546865206661696c75726520636f6f7264696e61746520697320746865206d61700a3b204b45592c20736f206974206973206e6f7420726570656174656420696e2074686520626f647920616e642063616e6e6f74206469736167726565207769746820746865206b65792e0a6566666563742d6661696c7572652d626f6479203d207b0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164547970653a20636f72652d747970652d7265662c202020202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b2041206661696c75726520636f6f7264696e617465206d7573742062652061206261726520456469637420606964656e746020286c65747465722f756e64657273636f7265207468656e0a3b206c6574746572732f6469676974732f756e64657273636f7265732920414e44206d757374206e6f742062652061207265736572766564206b6579776f72642028652e672e2060656c7365602c0a3b20606261736973602c20607768657265602c206072657175697265602c2060666f72602c2060696660292e2054686520736f75726365206f62737472756374696f6e2d6d6170204c4853206f6e6c790a3b20616363657074732061206e6f6e2d6b6579776f726420606964656e74602c20736f20612068797068656e2f646f742f6b6579776f726420636f6f7264696e61746520776f756c642062650a3b204142492d76616c69642079657420696d706f737369626c6520746f206d617020657868617573746976656c7920696e20736f757263652e20546865207265676578206361707475726573207468650a3b206c65786963616c2073686170653b206b6579776f7264206578636c7573696f6e20697320616e206164646974696f6e616c2076616c69646174696f6e2072756c650a3b202845444943542d4142492d4641494c5552452d4944454e542d303031292e0a6661696c7572652d6964656e74203d2074737472202e72656765787020225b412d5a612d7a5f5d5b412d5a612d7a302d395f5d2a220a0a6566666563742d6b696e64203d20227265616422202f202263726561746522202f2022656e7375726522202f20227265706c61636522202f202264656c65746522202f0a202020202020202020202020202022617070656e6422202f202272656475636522202f202273656d616e7469632e656d697422202f2022637573746f6d220a0a617574686f726974792d636c617373203d2022646f6d61696e4d61707061626c6522202f20227061727469636970616e744f776e656422202f2022696e746567726974794661756c7422202f0a202020202020202020202020202020202020227265736f757263654661756c7422202f2022696e7465726e616c4661756c74220a0a636f72652d747970652d726566203d20747374722020203b2063616e6f6e6963616c20436f7265207479706520636f6f7264696e6174650a0a3b20416e206f7065726174696f6e2070726f66696c6520737570706c69657320746865206f707469632074656d706c617465206120436f726520696e74656e74207265736f6c766573206974730a3b206f707469634b696e642f626f756e646172794b696e642f737570706f7274506f6c6963792f6c6f7373446973706f736974696f6e2066726f6d2e205461726765742070726f66696c657320616e640a3b206c61777061636b73207075626c6973682074686573652061732061206d617020607b20636f6f7264696e617465203d3e206f7065726174696f6e2d70726f66696c65207d602c20736f207468650a3b20636f6f7264696e61746520697320746865204b45592c206e6f7420612076616c7565206669656c64202845444943542d4f505449432d54454d504c4154452d4f574e45522d3030312c0a3b2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65203d207b0a20206f7074696354656d706c6174653a206f707469632d74656d706c6174652c0a20206566666563745072656469636174653a20747374722c20202020202020202020203b20636f6f7264696e617465206f6620746865206f7065726174696f6e2d6d6f6465207072656469636174650a7d0a0a6f707469632d74656d706c617465203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a2020737570706f7274506f6c6963793a20747374722c202020202020202020202020203b2063616e6f6e6963616c20737570706f72742d706f6c69637920636f6f7264696e6174650a20206c6f7373446973706f736974696f6e3a20747374722c20202020202020202020203b2063616e6f6e6963616c206c6f73732d646973706f736974696f6e20636f6f7264696e6174650a20203f20626173697354656d706c6174653a20747374722c20202020202020202020203b206f7074696f6e616c206469676573742d6c6f636b65642062617369732074656d706c61746520636f6f72640a20203b2074686520617065727475726520726571756972656d656e7420746869732074656d706c61746520737570706c6965732e205265717569726564207768656e207468652074656d706c6174650a20203b2069732074686520736f75726365206f66206120436f7265206f707469632773206170657274757265526571756972656d656e742028692e652e2074686520696e74656e7420686173206e6f0a20203b20736f757263652060666f6f747072696e74203c3d202e2e2e60292c2073696e6365206170657274757265526571756972656d656e74206973206d616e6461746f727920696e20436f72650a20203b202845444943542d4f505449432d41504552545552452d5245462d303031292e0a20203f206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a7d0a0a3b206170657274757265526571756972656d656e742069732061207479706564207265666572656e63652c206e65766572206120667265652d666f726d20737472696e672e2041207265766965770a3b2072656e646572696e67206d61792073686f772069747320636f6f7264696e617465202845444943542d4f505449432d41504552545552452d5245462d303031292e0a61706572747572652d726571756972656d656e74203d20666f6f747072696e742d6365696c696e672d726566202f2061627374726163742d666f6f747072696e742d6f626c69676174696f6e2d7265660a666f6f747072696e742d6365696c696e672d726566203d207b206b696e643a2022666f6f747072696e744365696c696e67222c207265663a2074737472207d0a61627374726163742d666f6f747072696e742d6f626c69676174696f6e2d726566203d207b206b696e643a20226162737472616374466f6f747072696e744f626c69676174696f6e222c207265663a2074737472207d0a0a3b202d2d2d2065646963742d636f72652e6364646c202d2d2d0a3b2065646963742d636f72652e6364646c0a3b204e6f726d617469766520736368656d6120666f722074686520456469637420436f72652076312073656d616e746963206d6f64656c2e0a3b0a3b2053636f706520626f756e646172793a20746869732066696c6520646566696e657320436f7265206d65616e696e6720616e6420736368656d61207368617065206f6e6c792e20497420646f65730a3b206e6f7420646566696e6520612063616e6f6e6963616c20656e636f6465722c20436f7265206d6f64756c652068617368206669656c64732c20686173682066697874757265732c207461726765740a3b206c6f776572696e672c2061646d697373696f6e2062756e646c65732c206f72207461726765742d6f776e65642049522e0a0a636f72652d6d6f64756c65203d207b0a202061706956657273696f6e3a202265646963742e636f72652f7631222c0a2020636f6f7264696e6174653a20747374722c0a2020696d706f7274733a205b2a20636f72652d696d706f72745d2c0a202074797065733a207b202a2074737472203d3e20636f72652d74797065207d2c0a2020696e74656e74733a207b202b2074737472203d3e20636f72652d696e74656e74207d2c0a20207265717569726564436f72654361706162696c69746965733a205b2a20747374725d2c0a7d0a0a636f72652d696d706f7274203d207b0a20206b696e643a20226c61777061636b22202f202274617267657422202f2022636f7265222c0a20207265663a207265736f757263652d7265662c0a20203f20616c6961733a20747374722c0a7d0a0a3b202d2d2d207479706573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d74797065203d20636f72652d7363616c61722d74797065202f20636f72652d7265636f72642d74797065202f20636f72652d76617269616e742d74797065202f0a202020202020202020202020636f72652d6f7074696f6e2d74797065202f20636f72652d6c6973742d74797065202f20636f72652d6d61702d74797065202f0a202020202020202020202020636f72652d6361706162696c6974792d7265662d747970650a0a636f72652d7363616c61722d74797065203d20636f72652d626f6f6c2d74797065202f20636f72652d696e742d74797065202f20636f72652d737472696e672d74797065202f0a20202020202020202020202020202020202020636f72652d62797465732d74797065202f20636f72652d756e69742d747970650a0a636f72652d626f6f6c2d74797065203d207b206b696e643a2022426f6f6c22207d0a636f72652d756e69742d74797065203d207b206b696e643a2022556e697422207d0a636f72652d696e742d74797065203d207b0a20206b696e643a202249363422202f202255363422202f202249333222202f202255333222202f202249313622202f202255313622202f2022493822202f20225538222c0a7d0a636f72652d737472696e672d74797065203d207b0a20206b696e643a2022537472696e67222c0a20206d61783a2075696e742c0a202063616e6f6e6963616c3a2022756e69636f64652d7363616c61722d6e666322202f20227261772d75746638222c0a7d0a636f72652d62797465732d74797065203d207b0a20206b696e643a20224279746573222c0a20206d61783a2075696e742c0a7d0a636f72652d7265636f72642d74797065203d207b0a20206b696e643a20225265636f7264222c0a20206669656c64733a207b202a2074737472203d3e20636f72652d747970652d726566207d2c0a7d0a636f72652d76617269616e742d74797065203d207b0a20206b696e643a202256617269616e74222c0a202063617365733a207b202b2074737472203d3e2076617269616e742d636173652d626f6479207d2c0a7d0a76617269616e742d636173652d626f6479203d207b0a20203f207061796c6f61643a20636f72652d747970652d7265662c0a7d0a636f72652d6f7074696f6e2d74797065203d207b0a20206b696e643a20224f7074696f6e222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a636f72652d6c6973742d74797065203d207b0a20206b696e643a20224c697374222c0a20206974656d3a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6d61702d74797065203d207b0a20206b696e643a20224d6170222c0a20206b65793a20636f72652d747970652d7265662c0a202076616c75653a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6361706162696c6974792d7265662d74797065203d207b0a20206b696e643a20224361706162696c697479526566222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a0a3b20636f72652d747970652d72656620697320646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d612e0a0a3b202d2d2d207265666572656e63657320616e642076616c756573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a3b204c6f63616c206964656e7469747920697320616c7068612d737461626c652e20606964602069732074686520636f6d70696c65722d6f776e6564206c6f63616c20636f6f7264696e6174653b0a3b2060616c7068614e616d656020697320746865206e6f726d616c697a65642068756d616e2f6465627567206e616d652e20536f757263652062696e646572207370656c6c696e67206973206e6f740a3b206964656e746974792e0a6c6f63616c2d726566203d207b0a202069643a20747374722c0a2020616c7068614e616d653a20747374722c0a2020747970653a20636f72652d747970652d7265662c0a7d0a0a636f72652d76616c7565203d20636f72652d6e756c6c2d76616c7565202f20636f72652d626f6f6c2d76616c7565202f20636f72652d696e742d76616c7565202f0a20202020202020202020202020636f72652d737472696e672d76616c7565202f20636f72652d62797465732d76616c7565202f20636f72652d7265636f72642d76616c7565202f0a20202020202020202020202020636f72652d76617269616e742d76616c7565202f20636f72652d6c6973742d76616c7565202f20636f72652d6d61702d76616c7565202f0a20202020202020202020202020636f72652d6361706162696c6974792d76616c75650a0a636f72652d6e756c6c2d76616c7565203d207b206b696e643a20226e756c6c22207d0a636f72652d626f6f6c2d76616c7565203d207b206b696e643a2022626f6f6c222c2076616c75653a20626f6f6c207d0a636f72652d696e742d76616c7565203d207b206b696e643a2022696e74222c2077696474683a20747374722c2076616c75653a20696e74207d0a636f72652d737472696e672d76616c7565203d207b206b696e643a2022737472696e67222c2076616c75653a2074737472207d0a636f72652d62797465732d76616c7565203d207b206b696e643a20226279746573222c2076616c75653a2062737472207d0a636f72652d7265636f72642d76616c7565203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d76616c7565207d207d0a636f72652d76617269616e742d76616c7565203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d76616c75652c0a7d0a636f72652d6c6973742d76616c7565203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d76616c75655d207d0a636f72652d6d61702d76616c7565203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d76616c75652c2076616c75653a20636f72652d76616c75655d5d207d0a636f72652d6361706162696c6974792d76616c7565203d207b0a20206b696e643a20226361706162696c697479222c0a2020726563656970743a207368613235362d6469676573742c0a7d0a0a3b2045646963742d617574686f72656420707572652068656c70657273207573652061207075726520436f72652066756e6374696f6e20626f64792e2054686520626f64792063616e2062696e640a3b20707572652065787072657373696f6e7320616e642072657475726e20616e2065787072657373696f6e2c206275742069742063616e6e6f7420636f6e7461696e20436f7265206566666563742c0a3b2067756172642c206272616e63682c206c6f6f702c206d617463682d6e6f64652c206f722070726f6f662d6f626c69676174696f6e206e6f6465732e0a636f72652d666e2d626f6479203d207b0a2020706172616d733a205b2a206c6f63616c2d7265665d2c0a2020626f64793a20636f72652d707572652d626c6f636b2c0a7d0a0a636f72652d707572652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a202062696e64696e67733a205b2a20707572652d6c65742d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a707572652d6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a0a3b202d2d2d2065787072657373696f6e7320616e642070726564696361746573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d65787072203d206c6f63616c2d65787072202f20636f6e73742d65787072202f207265636f72642d65787072202f206669656c642d65787072202f0a20202020202020202020202076617269616e742d65787072202f206d617463682d65787072202f2063616c6c2d65787072202f206c6973742d65787072202f206d61702d65787072202f0a20202020202020202020202069662d657870720a0a6c6f63616c2d65787072203d207b206b696e643a20226c6f63616c222c207265663a206c6f63616c2d726566207d0a636f6e73742d65787072203d207b206b696e643a2022636f6e7374222c2076616c75653a20636f72652d76616c7565207d0a7265636f72642d65787072203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d65787072207d207d0a6669656c642d65787072203d207b206b696e643a20226669656c64222c20626173653a20636f72652d657870722c206669656c643a2074737472207d0a76617269616e742d65787072203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d657870722c0a7d0a6d617463682d65787072203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d61726d5d2c0a7d0a6d617463682d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d657870722c0a7d0a63616c6c2d65787072203d207b0a20206b696e643a202263616c6c222c0a202063616c6c65653a20747374722c0a202074797065417267733a205b2a20636f72652d747970652d7265665d2c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6c6973742d65787072203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d657870725d207d0a6d61702d65787072203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d657870722c2076616c75653a20636f72652d657870725d5d207d0a69662d65787072203d207b0a20206b696e643a20226966222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d657870722c0a2020656c73653a20636f72652d657870722c0a7d0a0a636f72652d707265646963617465203d20747275652d707265646963617465202f2066616c73652d707265646963617465202f206e6f742d707265646963617465202f0a2020202020202020202020202020202020616c6c2d707265646963617465202f20616e792d707265646963617465202f20636f6d706172652d707265646963617465202f0a202020202020202020202020202020202063616c6c2d707265646963617465202f206f62737472756374696f6e2d7072656469636174650a0a747275652d707265646963617465203d207b206b696e643a20227472756522207d0a66616c73652d707265646963617465203d207b206b696e643a202266616c736522207d0a6e6f742d707265646963617465203d207b206b696e643a20226e6f74222c2076616c75653a20636f72652d707265646963617465207d0a616c6c2d707265646963617465203d207b206b696e643a2022616c6c222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a616e792d707265646963617465203d207b206b696e643a2022616e79222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a636f6d706172652d707265646963617465203d207b0a20206b696e643a2022636f6d70617265222c0a20206f703a20223d3d22202f2022213d22202f20223c22202f20223c3d22202f20223e22202f20223e3d222c0a20206c6566743a20636f72652d657870722c0a202072696768743a20636f72652d657870722c0a7d0a63616c6c2d707265646963617465203d207b0a20206b696e643a202263616c6c222c0a20207072656469636174653a20747374722c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6f62737472756374696f6e2d707265646963617465203d207b0a20206b696e643a20226f62737472756374696f6e222c0a2020636f6f7264696e6174653a206661696c7572652d6964656e742c0a20207061796c6f61643a20636f72652d657870722c0a7d0a0a696e7075742d636f6e73747261696e74203d207b0a2020636f6f7264696e6174653a20747374722c0a2020736f757263653a2022776865726522202f2022636f6d70696c6572222c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a3b202d2d2d20696e74656e74732c20626c6f636b732c20616e64206e6f646573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d696e74656e74203d207b0a2020696e7075743a20636f72652d747970652d7265662c0a20206f75747075743a20636f72652d747970652d7265662c0a202072657175697265644f7065726174696f6e50726f66696c653a20747374722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020626f64793a20636f72652d626c6f636b2c0a20203f206f707469633a20636f72652d6f707469632c0a7d0a0a636f72652d627564676574203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a636f72652d6f70746963203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a20206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a2020737570706f7274506f6c6963793a20747374722c0a20206c6f7373446973706f736974696f6e3a20747374722c0a7d0a0a636f72652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a20206e6f6465733a205b2a20636f72652d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a636f72652d6e6f6465203d206c65742d6e6f6465202f20726571756972652d6e6f6465202f206566666563742d6e6f6465202f2067756172642d6e6f6465202f206272616e63682d6e6f6465202f0a202020202020202020202020666f722d6e6f6465202f206d617463682d6e6f6465202f2070726f6f662d6f626c69676174696f6e2d6e6f64650a0a6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a726571756972652d6e6f6465203d207b0a20206b696e643a202272657175697265222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a726571756972652d6661696c7572652d61726d203d207465726d696e616c2d726571756972652d6661696c757265202f0a20202020202020202020202020202020202020202020636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c7572650a7465726d696e616c2d726571756972652d6661696c757265203d207b0a20206b696e643a20227465726d696e616c222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c757265203d207b0a20206b696e643a2022636f6e74696e75654f627374727563746564222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a6f62737472756374696f6e2d726561736f6e203d207b0a2020726561736f6e4b696e643a20747374722c0a20207061796c6f61643a207b202a2074737472203d3e20636f72652d65787072207d2c0a7d0a6566666563742d6e6f6465203d207b0a20206b696e643a2022656666656374222c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4d61703a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a6f62737472756374696f6e2d61726d203d207b0a202062696e6465723a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a67756172642d6e6f6465203d207b0a20206b696e643a20226775617264222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f62737472756374696f6e3a20636f72652d657870722c0a7d0a6272616e63682d6e6f6465203d207b0a20206b696e643a20226272616e6368222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d626c6f636b2c0a2020656c73653a20636f72652d626c6f636b2c0a7d0a666f722d6e6f6465203d207b0a20206b696e643a2022666f72222c0a202062696e6465723a206c6f63616c2d7265662c0a2020697465723a20636f72652d657870722c0a2020626f756e643a20636f72652d626f756e642c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a6d617463682d6e6f6465203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d626c6f636b2d61726d5d2c0a7d0a6d617463682d626c6f636b2d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a70726f6f662d6f626c69676174696f6e2d6e6f6465203d207b0a20206b696e643a202270726f6f66222c0a2020636f6f7264696e6174653a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a636f72652d626f756e64203d206c69746572616c2d626f756e64202f20636f6f7264696e6174652d626f756e640a6c69746572616c2d626f756e64203d207b206b696e643a20226c69746572616c222c2076616c75653a2075696e74207d0a636f6f7264696e6174652d626f756e64203d207b206b696e643a2022636f6f7264696e617465222c207265663a2074737472207d0a0a3b20536861726564207265736f757263652d7265662c207368613235362d6469676573742c206661696c7572652d6964656e742c2061706572747572652d726571756972656d656e742c20616e640a3b20636f72652d747970652d7265662061726520646566696e6564206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c2e0a0a3b202d2d2d2065646963742d6c61777061636b2e6364646c202d2d2d0a3b2065646963742d6c61777061636b2e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374206c61777061636b206d616e696665737420616e64206578706f727420737572666163652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e204a534f4e20696e207468652070726f73652073706563730a3b2069732061207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a6c61777061636b2d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e6c61777061636b2f7631222c0a202069643a20747374722c0a202076657273696f6e3a20747374722c0a20206163636570746564436f72654162693a205b2b20747374725d2c0a2020646570656e64656e636965733a205b2a206c61777061636b2d6465705d2c202020202020202020203b20616379636c69632c206469676573742d6c6f636b6564202845444943542d4c41575041434b2d4441472d303031290a20206578706f7274733a207265736f757263652d7265662c0a20203f2074617267657441646170746572733a205b2b207461726765742d616461707465725d2c2020203b207265717569726564206f6e6c7920696620616e792072756e74696d6520656666656374206578697374730a20203f2068656c706572436f6d706f6e656e743a2065786563757461626c652d636f6d706f6e656e742c203b2065786563757461626c652068656c70657273206361727279207468656972206f776e2073616e64626f782b6675656c0a202076657269666965723a2076657269666965722c202020202020202020202020202020202020202020203b20636c61737369666965643a206465636c61726174697665206f722065786563757461626c650a2020636f6d7061746962696c6974793a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b2041207665726966696572206973206569746865722061206465636c617261746976652072756c6573657420286e6f2072756e74696d6529206f7220616e2065786563757461626c650a3b20636f6d706f6e656e742e20416e2065786563757461626c65207665726966696572204d55535420636172727920697473206f776e2073616e64626f7820616e64206675656c206d6f64656c2c0a3b20736f2074686520736368656d6120656e666f726365732074686174206e6f2065786563757461626c6520636f6d706f6e656e74206973206c65667420756e626f756e6465640a3b202845444943542d4142492d56455249464945522d424f554e442d303031292e0a7665726966696572203d206465636c617261746976652d7665726966696572202f2065786563757461626c652d76657269666965720a6465636c617261746976652d7665726966696572203d207b20636c6173733a20226465636c61726174697665222c2072756c657365743a207265736f757263652d726566207d0a65786563757461626c652d7665726966696572203d207b0a2020636c6173733a202265786563757461626c65222c0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a3b20416e792065786563757461626c6520636f6d706f6e656e7420697320626f756e64656420627920697473206f776e2073616e64626f78202b206675656c206d6f64656c2e0a65786563757461626c652d636f6d706f6e656e74203d207b0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a6c61777061636b2d646570203d207b2069643a20747374722c2076657273696f6e3a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20416461707465722073656c656374696f6e206b65797320534f4c454c59206f666620746865206469676573742d6c6f636b65642060616363657074656454617267657450726f66696c65600a3b20286974732060696460206973207468652070726f66696c652069643b206974732060646967657374602070696e73207468652065786163742070726f66696c652f76657273696f6e292e2054686572650a3b20617265206e6f20696e646570656e64656e7420646973706c617920737472696e6773207468617420636f756c64206469736167726565207769746820746865206c6f636b2c20736f20610a3b207265736f6c7665722063616e6e6f742062696e6420616e206164617074657220746f206f6e6520746172676574207768696c6520746865206c6f636b2070726f76657320616e6f746865720a3b202845444943542d4c41575041434b2d414441505445522d54415247455449522d303031292e0a7461726765742d61646170746572203d207b0a2020616363657074656454617267657450726f66696c653a207265736f757263652d7265662c202020203b206469676573742d6c6f636b65642c20617574686f72697461746976652073656c6563746f720a2020616363657074656454617267657449723a207265736f757263652d7265662c2020202020202020203b206469676573742d6c6f636b65640a2020616461707465723a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d206578706f72742073757266616365202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a6c61777061636b2d6578706f727473203d207b0a202074797065733a205b2a206578706f727465642d747970655d2c0a2020636f6e7374616e74733a205b2a206578706f727465642d636f6e7374616e745d2c0a20207075726546756e6374696f6e733a205b2a20707572652d66756e6374696f6e5d2c0a2020656666656374733a205b2a2073656d616e7469632d6566666563745d2c0a20206f62737472756374696f6e733a205b2a206f62737472756374696f6e2d6465665d2c0a20203b206b65796564206279206f7065726174696f6e2d70726f66696c6520636f6f7264696e61746520e2869220756e697175656e65737320656e666f726365640a20203b202845444943542d4142492d4f5050524f46494c452d554e495155452d303031290a20203b206f7065726174696f6e2d70726f66696c65207265636f7264732074686973206c61777061636b206578706f72747320286f707469632074656d706c6174657320746861740a20203b2060696d706c656d656e7473602f6070726f66696c656020636c6175736573207265736f6c766520616761696e7374292e206f7065726174696f6e2d70726f66696c652069730a20203b20646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c20203b206b6579656420627920636f6f7264696e6174650a7d0a0a6578706f727465642d7479706520202020203d207b20636f6f7264696e6174653a20747374722c20646566696e6974696f6e3a20636f72652d747970652d726566207d0a6578706f727465642d636f6e7374616e74203d207b20636f6f7264696e6174653a20747374722c20747970653a20636f72652d747970652d7265662c2076616c75653a20616e79207d0a0a3b204120707572652068656c7065722069732061206469736372696d696e6174656420756e696f6e2062792060736f75726365602c20736f2074686520736368656d6120697473656c660a3b2067756172616e7465657320616e20696d706c656d656e746174696f6e20657869737473202845444943542d4c41575041434b2d505552452d494d504c2d303031293a0a3b2020202d20226564696374223a20617574686f72656420696e2045646963742f436f72653b2074686520436f726520626f6479206973206361727269656420696e6c696e6520286861736865640a3b20202020207769746820746865206578706f72742073757266616365292e2054686520736368656d61207265717569726573207468652060626f647960206669656c642e0a3b2020202d2022636f6d706f6e656e74223a20696d706c656d656e746564206f7574736964652045646963743b2063617272696573206e6f20696e6c696e6520626f647920616e6420696e73746561640a3b20202020206361727269657320697473206f776e206469676573742d6c6f636b65642060696d706c656d656e746174696f6e60202873616e64626f78202b206675656c292e20497420646f65730a3b20202020206e6f7420646570656e64206f6e20746865206f7074696f6e616c206d616e69666573742d6c6576656c2068656c706572436f6d706f6e656e742e0a707572652d66756e6374696f6e203d2065646963742d707572652d66756e6374696f6e202f20636f6d706f6e656e742d707572652d66756e6374696f6e0a0a707572652d66756e6374696f6e2d636f6d6d6f6e203d20280a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020706172616d6574657254797065733a205b2a20636f72652d747970652d7265665d2c2020202020203b20616c6c20626f756e6465640a202072657475726e547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020636f737454656d706c6174653a20747374722c0a202064657465726d696e69736d436c6173733a2022746f74616c22202f2022746f74616c2d776974682d74797065642d646961676e6f73746963222c0a290a0a65646963742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a20226564696374222c0a2020626f64793a20636f72652d666e2d626f64792c2020202020202020202020202020202020202020203b20696e6c696e652c20686173682d7369676e69666963616e740a7d0a0a636f6d706f6e656e742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a2022636f6d706f6e656e74222c0a20203b20746865206469676573742d6c6f636b656420636f6d706f6e656e7420696d706c656d656e74696e6720746869732068656c7065722e2052657175697265642061742074686520736368656d610a20203b206c6576656c20736f206120636f6d706f6e656e742068656c7065722063616e206e657665722076616c696461746520776974686f7574206120686173682d626f756e642c0a20203b2073616e64626f782b6675656c2d64657363726962656420696d706c656d656e746174696f6e202845444943542d4c41575041434b2d505552452d494d504c2d303031292e0a2020696d706c656d656e746174696f6e3a2065786563757461626c652d636f6d706f6e656e742c0a7d0a0a3b20636f72652d666e2d626f647920697320646566696e65642062792065646963742d636f72652e6364646c20616e6420617373656d626c656420776974682074686973206c61777061636b0a3b20736368656d612e2049742069732061207075726520436f72652066756e6374696f6e20626f64792c206e6f7420616e206566666563742d63617061626c6520636f72652d626c6f636b2e0a0a73656d616e7469632d656666656374203d207b0a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020696e707574547970653a20636f72652d747970652d7265662c2020202020202020202020202020203b20626f756e6465640a20206f7574707574547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020657865637574696f6e436c6173733a202270726f6f664f6e6c7922202f202272756e74696d65222c2020203b206f7274686f676f6e616c20746f207772697465436c6173730a20206566666563744b696e6448696e743a206566666563742d6b696e642c0a2020666f6f747072696e744f626c69676174696f6e3a20747374722c0a2020636f73744f626c69676174696f6e3a20747374722c0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c20203b206b6579656420627920636f6f7264696e6174653b20756e697175650a20206775617264537570706f72743a20626f6f6c2c0a7d0a0a6f62737472756374696f6e2d646566203d207b0a2020636f6f7264696e6174653a20747374722c0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164536368656d613a20636f72652d747970652d7265662c20202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d7461726765742d70726f66696c652e6364646c202d2d2d0a3b2065646963742d7461726765742d70726f66696c652e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374207461726765742070726f66696c65206d616e69666573742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76310a3b202873656520535045435f636f6e74696e75756d2d636f6e74726163742d62756e646c652d76312e6d64292e204a534f4e20696e207468652070726f736520737065637320697320610a3b207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d613b2074686973204344444c206973207468652073696e676c6520736f757263650a3b206f66207472757468202845444943542d4142492d4e4f4455502d303031292e0a0a7461726765742d70726f66696c652d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652f7631222c0a202069643a20747374722c202020202020202020202020202020202020202020202020203b20652e672e20226563686f2e64706f220a202076657273696f6e3a20747374722c20202020202020202020202020202020202020203b20652e672e202231220a20206163636570746564436f72654162693a205b2b20747374725d2c20202020202020203b20652e672e205b2265646963742e636f72652f7631225d0a0a2020696e7472696e736963733a207265736f757263652d7265662c0a2020696e7472696e7369634e616d6573706163653a20747374722c0a20203b207075626c697368657320746869732070726f66696c652773206f7065726174696f6e2d70726f66696c65207265636f72647320286f707469632074656d706c6174657320746861740a20203b206070726f66696c65602f60696d706c656d656e74736020636c6175736573207265736f6c766520616761696e7374292e205265666572656e63657320616e0a20203b206f7065726174696f6e2d70726f66696c65732d646f63756d656e74202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207265736f757263652d7265662c0a2020666f6f747072696e74416c67656272613a207265736f757263652d7265662c0a2020636f7374416c67656272613a207265736f757263652d7265662c0a202074617267657449723a207265736f757263652d7265662c0a20206f62737472756374696f6e5461786f6e6f6d793a207265736f757263652d7265662c0a202076657269666965723a207265736f757263652d7265662c0a20206c6f77657265723a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a0a20203b206669656c647320746865206c616e67756167652073706563207265717569726573206f662065766572792070726f66696c650a202062756e646c6550726f66696c653a207265736f757263652d7265662c0a202067656e657261746564417274696661637450726f66696c65733a205b2a207265736f757263652d7265665d2c0a202063616e6f6e6963616c456e636f64696e6752756c65733a207265736f757263652d7265662c0a20203b2052657365727665642f64656665727265643a2077696c6c206c697374206163636570746564206c61777061636b2d616461707465722041424920696473206f6e636520746861740a20203b20627974652d6c6576656c20414249202865646963742e6c61777061636b2d616461707465722f763129206973207370656369666965642e2054686520736368656d6120656e666f726365730a20203b20656d7074696e657373206e6f7720e280942074686520747970652069732074686520656d7074792061727261792c20736f206e6f6e2d656d7074792076616c756573206172650a20203b2072656a65637465642c206e6f74206d6572656c7920646973636f757261676564202845444943542d4142492d4c41575041434b2d414441505445522d44454645522d303031292e0a20203f2061636365707465644c61777061636b416461707465724162693a205b5d2c0a2020646961676e6f737469634162693a207265736f757263652d7265662c0a0a20203b206170706c69636174696f6e20646f637472696e650a20206170706c69636174696f6e4d6f64656c3a202261746f6d6963222c0a202072656164436f6e73697374656e63793a20226170706c69636174696f6e2d736e617073686f7422202f20747374722c0a202067756172644576616c756174696f6e3a2022707265636f6d6d69742d61746f6d696322202f20747374722c0a20206f62737472756374696f6e526f6c6c6261636b3a20226e6f2d76697369626c652d6566666563747322202f20747374722c0a20206d756c74695461726765743a20626f6f6c2c0a20203b207768657468657220746865207461726765742063616e206576616c7561746520707265636f6d6d697420706f7374636f6e646974696f6e20286067756172616e746565602920636865636b730a20203b20696e73696465207468652061746f6d6963206170706c69636174696f6e20756e6974202845444943542d5441524745542d504f5354434f4e442d303031290a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a0a202064657465726d696e6973746963457865637574696f6e3a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d6120627920746865206275696c640a3b202845444943542d4142492d4e4f4455502d303031292e205468657920617265206e6f74207265646566696e656420686572652e0a0a3b202d2d2d20696e7472696e736963207369676e6174757265202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e696665737427732060696e7472696e7369637360207265736f757263652d726566206973207468650a3b20696e7472696e7369632d7369676e617475726520636f7270757320646f63756d656e742062656c6f772e20497473206c61796f757420697320666978656420736f2074776f0a3b20696e646570656e64656e742070726f66696c65732076616c69646174652f686173682074686520636f72707573206964656e746963616c6c790a3b202845444943542d4142492d494e5452494e534943532d444f432d303031292e0a0a3b20696e7472696e736963732069732061204d4150206b6579656420627920636f6f7264696e6174652c20736f2074686520736368656d6120697473656c6620656e666f726365730a3b20636f6f7264696e61746520756e697175656e6573732e20412070726f766964657220726563656976657320746865207265736f6c76656420636f7270757320617320610a3b206469676573742d626f756e642073656d616e74696320696e70757420616e64207265736f6c76657320636f6f7264696e617465732077697468696e20746861742061727469666163742e0a3b2045616368206d6170206b6579204d55535420657175616c20697473207265636f726427732060636f6f7264696e61746560206669656c640a3b202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e696e7472696e736963732f7631222c0a2020696e7472696e736963733a207b202a2074737472203d3e20696e7472696e736963207d2c0a7d0a0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e6966657374277320606f7065726174696f6e50726f66696c657360207265736f757263652d7265662e0a3b206f7065726174696f6e2d70726f66696c65202f206f707469632d74656d706c6174652061726520646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c2e204b657965642062790a3b20636f6f7264696e61746520736f207265736f6c7574696f6e2063616e2774207069636b206265747765656e2074776f2073616d652d636f6f7264696e6174652070726f66696c65730a3b202845444943542d4142492d4f5050524f46494c452d534c4f542d3030312c2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e6f7065726174696f6e2d70726f66696c65732f7631222c0a202070726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c0a7d0a0a3b2041207479706564207072652d6c6f776572696e67207175657374696f6e20746861742063616e2062652070726f706f73656420627920576174736f6e206f7220616e206167656e7420616e640a3b20636865636b65642062792074686520636f6d70696c65722e2049742069732063616e6f6e6963616c2d43424f5220656e636f64656420756e6465720a3b206065646963742e6c6f776572696e672d726571756972656d656e74732f7631603b2074686520636f6d70696c657220636865636b7320746869732061727469666163742c206e6f74207468650a3b2070726f736520746861742070726f64756365642069742e0a6c6f776572696e672d726571756972656d656e7473203d207b0a202061706956657273696f6e3a202265646963742e6c6f776572696e672d726571756972656d656e74732f7631222c0a20206f7065726174696f6e50726f66696c653a20747374722c0a202073656d616e746963456666656374733a205b2a2073656d616e7469632d6566666563742d726571756972656d656e745d2c0a202072657175697265645772697465436c61737365733a205b2a2077726974652d636c6173735d2c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a202061746f6d69636974793a2061746f6d69636974792d726571756972656d656e742c0a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a20206f70746963436f6e74726163743a20747374722c0a7d0a0a73656d616e7469632d6566666563742d726571756972656d656e74203d207b0a2020636f6f7264696e6174653a20747374722c0a20207772697465436c6173733a2077726974652d636c6173732c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a7d0a0a77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a2020202020202020202020202020227265706c61636522202f202264656c65746522202f20747374720a67756172642d6b696e64203d2022707265636f6d6d69742d61746f6d696322202f20747374720a61746f6d69636974792d726571756972656d656e74203d202261746f6d696322202f20747374720a0a3b20412067656e75696e6520756e696f6e3a207075726520636f6e7374727563746f7273206361727279206e6f20656666656374206b696e64206f72206661696c757265733b206566666563740a3b20696e7472696e73696373206d757374202845444943542d5441524745542d494e5452494e5349432d434c4153532d303031292e2054686520736368656d6120656e666f7263657320746869732c0a3b206e6f74206120636f6d6d656e742e0a0a3b2054686520696e7472696e736963277320636f6f7264696e6174652069732074686520696e7472696e73696373206d6170204b45592c206e6f7420612076616c7565206669656c642c20736f207468650a3b206b657920616e6420636f6f7264696e6174652063616e206e65766572206469736167726565202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963203d20707572652d696e7472696e736963202f206566666563742d696e7472696e7369630a0a707572652d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a202270757265222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206775617264537570706f72743a2066616c73652c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20226e6f6e65222c0a7d0a0a6566666563742d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a2022656666656374222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206566666563744b696e643a206566666563742d6b696e642c0a20203b206d6170206b65796564206279206661696c75726520636f6f7264696e61746520286661696c7572652d6964656e74293b20746865206661696c75726520636f6f7264696e6174652069730a20203b20746865206b65792c206e6f7420612076616c7565206669656c642c20736f20756e697175656e657373206973207374727563747572616c0a20203b202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c0a20206775617264537570706f72743a20626f6f6c2c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f20227265706c61636522202f0a20202020202020202020202020202264656c65746522202f2022637573746f6d222c0a202063616e5061727469636970617465496e41746f6d696347756172643a20626f6f6c2c0a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d617574686f726974792d66616374732e6364646c202d2d2d0a3b2065646963742d617574686f726974792d66616374732e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f722074686520666972737420636f6d70696c65722d636f6e7465787420617574686f726974792d666163747320646f63756d656e742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20736f20736f757263652e6469676573742075736573207468650a3b20736861726564207368613235362d6469676573742074797065642076616c75652e204a534f4e2069732061207265766965772f696e7075742072656e646572696e673a206974730a3b20607368613235363a3c3634206865783e6020736f75726365206469676573742069732070726f6a656374656420746f205b60736861323536602c203332207261772062797465735d206f6e0a3b2074686520776972652c20616e64206974732066616374206172726179732070726f6a65637420746f2074686520636f6f7264696e6174652d6b65796564206d6170732062656c6f772e0a0a617574686f726974792d6661637473203d207b0a202061706956657273696f6e3a202265646963742e617574686f726974792d66616374732f7631222c0a2020736f757263653a20617574686f726974792d666163742d736f757263652c0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e20617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374207d2c0a20206566666563745772697465436c61737365733a207b202a2074737472203d3e20617574686f726974792d77726974652d636c617373207d2c0a2020627564676574733a207b202a2074737472203d3e20617574686f726974792d6275646765742d66616374207d2c0a7d0a0a617574686f726974792d666163742d736f75726365203d207b0a20206b696e643a20226c61777061636b22202f202274617267657450726f66696c65222c0a2020636f6f7264696e6174653a20747374722c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a3b20546865206d6170206b65792069732074686520736f75726365206f7065726174696f6e2d70726f66696c6520636f6f7264696e6174652e204974206973206e6f7420726570656174656420696e0a3b207468652076616c75652c20736f2061206b657920616e6420656d62656464656420636f6f7264696e6174652063616e6e6f742064697361677265652e20416c6c6f7765642077726974650a3b20636c61737365732061726520612063616e6f6e6963616c206d61702d7365743a2074686520636c6173732069732074686520756e69717565206b657920616e64206e756c6c206973207468650a3b20756e6974206d61726b65722e2043616e6f6e6963616c2043424f52206669786573206b6579206f7264657220776974686f75742061207365636f6e64206f72646572696e672072756c652e0a617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374203d207b0a2020636f72653a20747374722c0a2020616c6c6f7765645772697465436c61737365733a207b202a20617574686f726974792d77726974652d636c617373203d3e206e756c6c207d2c0a7d0a0a3b20546865206566666563745772697465436c6173736573206d6170206b6579206973207468652073656d616e7469632065666665637420636f6f7264696e6174652e2054686520627564676574730a3b206d6170206b65792069732074686520736f757263652062756467657420636f6f7264696e6174652e2043616e6f6e6963616c2043424f52206d61702d6b657920756e697175656e6573730a3b206d616b6573206475706c6963617465206661637420636f6f7264696e61746573207374727563747572616c6c7920756e726570726573656e7461626c652e0a617574686f726974792d6275646765742d66616374203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a3b20417574686f726974794661637473446f63756d656e7420763120696e74656e74696f6e616c6c792061636365707473206f6e6c792074686520777269746520636c6173736573207468650a3b2063757272656e7420636f6d70696c6572206d6f64656c2063616e20636f6e73756d652e2060637573746f6d602069732074686520736f6c6520763120637573746f6d207370656c6c696e673b0a3b20617262697472617279207461726765742d70726f66696c6520657874656e73696f6e20737472696e677320646f206e6f7420656e746572207468697320636f6d70696c657220706174682e0a617574686f726974792d77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a202020202020202020202020202020202020202020202020227265706c61636522202f202264656c65746522202f2022637573746f6d220a0a3b202d2d2d2065646963742d7461726765742d69722e6364646c202d2d2d0a3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d7461726765742d69722e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f72207468652045646963742d6f776e65642054617267657420495220617274696661637420656e76656c6f70652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20616e642065646963742d636f72652e6364646c2e2049740a3b2064656c696265726174656c792072657573657320436f72652065787072657373696f6e732c20707265646963617465732c20627564676574732c206c6f63616c207265666572656e6365732c0a3b206f62737472756374696f6e20726561736f6e732c20616e64206f62737472756374696f6e2061726d7320736f2074686520736368656d61206d617463686573207468652076616c75650a3b20656d6974746564206279207468652063616e6f6e6963616c2054617267657420495220656e636f64657220726174686572207468616e20726573746174696e672074686f73652074797065732e0a3b2049742064657363726962657320746865207374727563747572616c207368617065206f662076616c6964206c6f776572696e672d70726f6475636564206172746966616374732e205468650a3b206c6f776572696e6720616e6420656e636f64657220636f6e7472616374732073657061726174656c7920656e666f7263652073656d616e746963206964656e7469666965722072756c65730a3b20616e642063616e6f6e6963616c206f72646572696e672f64656475706c69636174696f6e20666f72207365742d6c696b652076616c7565732e0a0a3b2054617267657420495220656e636f64696e672072656a6563747320616e20656d707479207461726765742d70726f66696c6520636f6f7264696e617465206265666f72652062797465730a3b2065786973742c20736f207468697320726f6f74207469676874656e732074686520736861726564207374727563747572616c207265736f757263652d726566206163636f7264696e676c792e0a7461726765742d69722d7265736f757263652d726566203d207b0a202069643a2074737472202e7265676578702022283f73292e2b222c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a7461726765742d69722d6172746966616374203d207b0a20206b696e643a202274617267657449724172746966616374222c0a2020646f6d61696e3a20747374722c0a202074617267657450726f66696c653a207461726765742d69722d7265736f757263652d7265662c0a2020736f75726365436f7265436f6f7264696e6174653a20747374722c0a2020696e74656e74733a207b202a2074737472203d3e207461726765742d69722d696e74656e74207d2c0a7d0a0a7461726765742d69722d696e74656e74203d207b0a20206f7065726174696f6e50726f66696c653a20747374722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020726571756972656d656e74733a205b2a207461726765742d69722d726571756972656d656e745d2c0a202073746570733a205b2a207461726765742d69722d737465705d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a7461726765742d69722d726571756972656d656e74203d207b0a202069643a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a0a7461726765742d69722d73746570203d207b0a202069643a20747374722c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020746172676574496e7472696e7369633a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4661696c757265733a205b2a206661696c7572652d6964656e745d2c0a20206f62737472756374696f6e41726d733a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a", + "rawSha256": "92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74" + }, + "contracts": [ + {"contract": "authority-facts", "rootRule": "authority-facts"}, + {"contract": "core-module", "rootRule": "core-module"}, + {"contract": "lawpack-exports", "rootRule": "lawpack-exports"}, + {"contract": "lawpack-manifest", "rootRule": "lawpack-manifest"}, + {"contract": "lowering-requirements", "rootRule": "lowering-requirements"}, + {"contract": "target-ir-artifact", "rootRule": "target-ir-artifact"}, + {"contract": "target-profile-intrinsics", "rootRule": "intrinsics-document"}, + {"contract": "target-profile-manifest", "rootRule": "target-profile-manifest"}, + {"contract": "target-profile-operation-profiles", "rootRule": "operation-profiles-document"} + ], + "domains": [ + {"domain": "edict.authority-facts/v1", "rootRule": "authority-facts"}, + {"domain": "edict.core.module/v1", "rootRule": "core-module"}, + {"domain": "edict.lawpack/v1", "rootRule": "lawpack-manifest"}, + {"domain": "edict.lowering-requirements/v1", "rootRule": "lowering-requirements"}, + {"domain": "edict.target-ir.artifact/v1", "rootRule": "target-ir-artifact"}, + {"domain": "edict.target-profile/v1", "rootRule": "target-profile-manifest"} + ], + "resources": [ + { + "coordinate": "edict.canonical-cbor/v1", + "canonicalBytesHex": "a368636f6e7472616374a96a76616c75654d6f64656c8765617272617964626f6f6c65627974657367696e7465676572636d6170646e756c6c64746578746b6469676573744672616d656f65646963742e6469676573742f76316b6d61704f72646572696e67781b63616e6f6e6963616c2d656e636f6465642d6b65792d62797465736c74657874456e636f64696e67657574662d386d747261696c696e6742797465736672656a6563746f696e7465676572456e636f64696e676873686f727465737470636f6e7461696e65724c656e6774687368646566696e697465706475706c69636174654d61704b6579736672656a656374736d6178696d756d4e657374696e67446570746818806a61706956657273696f6e782965646963742e7461726765742d70726f66696c652e636f6e74726163742d7265736f757263652f76316a636f6f7264696e6174657765646963742e63616e6f6e6963616c2d63626f722f7631", + "rawSha256": "8306e4f08c1e4e7d29ab22bcf55c324312712aac3eeeb675857ced57c3e48bdc", + "domainFramedDigest": "sha256:d1ea6d3de2a9762a438cbf4fac1d5ae2f357a4b27d13e0347e94ea655bf40f9d", + "provenance": { + "repository": "https://github.com/flyingrobots/edict", + "sourcePath": "fixtures/target-profile/contract-resources/canonical-cbor.cbor" + } + }, + { + "coordinate": "edict.determinism/v1", + "canonicalBytesHex": "a368636f6e7472616374aa6c616d6269656e74436c6f636bf46e616d6269656e744e6574776f726bf46e6f75747075744964656e74697479781e686f73742d636f6d70757465642d61667465722d76616c69646174696f6e6e72656769737472794c6f6f6b7570f471616d6269656e7446696c6573797374656df471616d6269656e7452616e646f6d6e657373f472616d6269656e74456e7669726f6e6d656e74f4756d757461626c654e616d655265736f6c7574696f6ef477667265736853746f7265506572496e766f636174696f6ef5782263616e6f6e6963616c526573756c74496e646570656e64656e744f664c696d697473f56a61706956657273696f6e782965646963742e7461726765742d70726f66696c652e636f6e74726163742d7265736f757263652f76316a636f6f7264696e6174657465646963742e64657465726d696e69736d2f7631", + "rawSha256": "84073f5c1734b625e16799048e28458d43e0a10befdae56a79d906f5e37ef76a", + "domainFramedDigest": "sha256:af4e6c774d5ea82db30e680be2ba7abc5ba04c6aead146139de32d8c5bb4981e", + "provenance": { + "repository": "https://github.com/flyingrobots/edict", + "sourcePath": "fixtures/target-profile/contract-resources/determinism.cbor" + } + }, + { + "coordinate": "edict.diagnostics/v1", + "canonicalBytesHex": "a368636f6e7472616374a7666669656c64738464636f6465687365766572697479676d65737361676566726570616972686f72646572696e67781c636f64652d73657665726974792d6d6573736167652d726570616972697265636f7264416269782f65646963743a7461726765742d70726f76696465722f70726f746f636f6c40312e302e3023646961676e6f737469636a7472756e636174696f6e69666f7262696464656e6d73657665726974794f7264657283656572726f72677761726e696e6764696e666f6e6c697374436f756e74426f756e64f57261676772656761746542797465426f756e64f56a61706956657273696f6e782965646963742e7461726765742d70726f66696c652e636f6e74726163742d7265736f757263652f76316a636f6f7264696e6174657465646963742e646961676e6f73746963732f7631", + "rawSha256": "e465a28f1170fe478db5ff65d96a1fdbfbbcb95d327e6965021c252437991e4b", + "domainFramedDigest": "sha256:28fd72a98223153982ca084c29dbb1b2d430623967ab3b6db9d7fee668e614b9", + "provenance": { + "repository": "https://github.com/flyingrobots/edict", + "sourcePath": "fixtures/target-profile/contract-resources/diagnostics.cbor" + } + }, + { + "coordinate": "edict.fuel/v1", + "canonicalBytesHex": "a368636f6e7472616374a6687265717569726564f56a6163636f756e74696e67781864657465726d696e69737469632d67756573742d776f726b6a627564676574556e6974646675656c6a65786861757374696f6e72686f73742d6f776e65642d6661696c7572656d706572496e766f636174696f6ef57165706f6368496e74657272757074696f6e736e6f742d612d7265706c61792d6275646765746a61706956657273696f6e782965646963742e7461726765742d70726f66696c652e636f6e74726163742d7265736f757263652f76316a636f6f7264696e6174656d65646963742e6675656c2f7631", + "rawSha256": "c712c2d831cc8e731bdc1dfb8ea536f4630f38f0b0e0c6448b2df57176d3d0bd", + "domainFramedDigest": "sha256:006c6ebc01a3c5d36d50bd390b69d47e42378be61074e3b5c96ecb9f5ee53207", + "provenance": { + "repository": "https://github.com/flyingrobots/edict", + "sourcePath": "fixtures/target-profile/contract-resources/fuel.cbor" + } + }, + { + "coordinate": "edict.wasm-component/v1", + "canonicalBytesHex": "a368636f6e7472616374a7647761736969666f7262696464656e6e70726f746f636f6c496d706f727469747970652d6f6e6c796f63616c6c61626c65496d706f72747369666f7262696464656e70636f6e74726163744964656e746974796565786163747070726f7669646572436f6e7472616374781b65646963743a7461726765742d70726f766964657240312e302e3077667265736853746f7265506572496e766f636174696f6ef5781b636f6d706f6e656e74446967657374566572696669636174696f6e6d6265666f72652d6465636f64656a61706956657273696f6e782965646963742e7461726765742d70726f66696c652e636f6e74726163742d7265736f757263652f76316a636f6f7264696e6174657765646963742e7761736d2d636f6d706f6e656e742f7631", + "rawSha256": "cd09d702db1d10be825e72effe35627bec56963cb3d78601b69478c58787e34d", + "domainFramedDigest": "sha256:095b4dd18f1a6a7276533f758665be319c5f476e7bdc70cc56d30b6b3e9f0a80", + "provenance": { + "repository": "https://github.com/flyingrobots/edict", + "sourcePath": "fixtures/target-profile/contract-resources/wasm-component.cbor" + } + } + ] +} diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/echo-provider-semantics-v1.json b/crates/echo-wesley-gen/assets/v1/edict-provider/echo-provider-semantics-v1.json new file mode 100644 index 00000000..6b52ada5 --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/echo-provider-semantics-v1.json @@ -0,0 +1,703 @@ +{ + "apiVersion": "echo.edict-provider-semantics/v1", + "coordinate": "echo.semantic-schema@1", + "authoritySources": [ + { + "coordinate": "echo.provider-semantic-declaration@1", + "kind": "echoSemanticDeclaration", + "artifact": "schemas/edict-provider/echo-provider-semantics-v1.json" + }, + { + "coordinate": "echo.provider-target-metadata@1", + "kind": "targetMetadata", + "artifact": "schemas/edict-provider/echo-provider-semantics-v1.json" + } + ], + "types": [ + { + "identity": { + "coordinate": "a.b@1.Input", + "domain": "echo.edict-provider/value/v1", + "authority": "echo.provider-semantic-declaration@1" + }, + "shape": { + "kind": "record", + "fields": [ + { + "name": "id", + "type": "a.b@1.Id" + } + ] + } + }, + { + "identity": { + "coordinate": "a.b@1.Receipt", + "domain": "echo.edict-provider/value/v1", + "authority": "echo.provider-semantic-declaration@1" + }, + "shape": { + "kind": "record", + "fields": [ + { + "name": "id", + "type": "a.b@1.Id" + } + ] + } + }, + { + "identity": { + "coordinate": "a.b@1.Output", + "domain": "echo.edict-provider/value/v1", + "authority": "echo.provider-semantic-declaration@1" + }, + "shape": { + "kind": "record", + "fields": [ + { + "name": "id", + "type": "a.b@1.Id" + } + ] + } + }, + { + "identity": { + "coordinate": "a.b@1.Id", + "domain": "echo.edict-provider/value/v1", + "authority": "echo.provider-semantic-declaration@1" + }, + "shape": { + "kind": "coreStringAlias", + "maxScalarValues": 16, + "canonical": "raw-utf8" + } + }, + { + "identity": { + "coordinate": "domain.WriteRejected.Payload", + "domain": "echo.edict-provider/value/v1", + "authority": "echo.provider-semantic-declaration@1" + }, + "shape": { + "kind": "record", + "fields": [] + } + }, + { + "identity": { + "coordinate": "target.replace.rejected", + "domain": "echo.edict-provider/value/v1", + "authority": "echo.provider-semantic-declaration@1" + }, + "shape": { + "kind": "record", + "fields": [] + } + } + ], + "writeClasses": [ + { + "identity": { + "coordinate": "replace", + "domain": "echo.edict-provider/write-class/v1", + "authority": "echo.provider-target-metadata@1" + } + } + ], + "obstructions": [ + { + "identity": { + "coordinate": "domain.WriteRejected", + "domain": "echo.edict-provider/obstruction/v1", + "authority": "echo.provider-semantic-declaration@1" + }, + "authorityClass": "domainMappable", + "payloadSchema": "domain.WriteRejected.Payload" + } + ], + "effects": [ + { + "identity": { + "coordinate": "target.replace", + "domain": "echo.edict-provider/semantic-effect/v1", + "authority": "echo.provider-semantic-declaration@1" + }, + "parameterTypes": ["a.b@1.Id"], + "resultType": "a.b@1.Receipt", + "executionClass": "runtime", + "effectKindHint": "replace", + "guardKinds": ["precommit-atomic"], + "failures": [ + { + "key": "rejected", + "domain": "echo.edict-provider/effect-failure/v1", + "authority": "echo.provider-target-metadata@1", + "authorityClass": "domainMappable", + "payloadType": "target.replace.rejected" + } + ], + "footprintObligation": "target.replace.footprint", + "costObligation": "target.replace.cost", + "guardSupport": true + } + ], + "profiles": [ + { + "identity": { + "coordinate": "continuum.profile.write/v1", + "domain": "echo.edict-provider/operation-profile/v1", + "authority": "echo.provider-target-metadata@1" + }, + "sourceNames": ["p.effectful"], + "allowedWriteClasses": ["replace"], + "guardKinds": ["precommit-atomic"], + "atomicity": "atomic", + "postconditionSupport": true, + "opticTemplate": { + "opticKind": "affectReintegration", + "boundaryKind": "affect", + "supportPolicy": "continuum.support.carry-or-obstruct/v1", + "lossDisposition": "continuum.support.reject-on-loss/v1", + "apertureRequirement": { + "kind": "abstractFootprintObligation", + "ref": "target.replace.footprint" + } + }, + "effectPredicate": "echo.dpo.operation-mode.replace-only/v1", + "opticContract": "replace-point" + } + ], + "budgets": [ + { + "identity": { + "coordinate": "p.tiny", + "domain": "echo.edict-provider/core-budget/v1", + "authority": "echo.provider-semantic-declaration@1" + }, + "maxSteps": 8, + "maxAllocatedBytes": 1024, + "maxOutputBytes": 256 + } + ], + "capabilities": [ + { + "identity": { + "coordinate": "echo.dpo@1.replace", + "domain": "echo.edict-provider/target-intrinsic/v1", + "authority": "echo.provider-target-metadata@1" + }, + "effect": "target.replace", + "effectKind": "replace", + "writeClass": "replace", + "guardSupport": true, + "footprintTemplate": "target.replace.footprint", + "costTemplate": "target.replace.cost", + "semanticDischarge": { + "effectKindHint": "replace", + "footprintObligation": "target.replace.footprint", + "costObligation": "target.replace.cost" + }, + "canParticipateInAtomicGuard": true, + "targetProfile": "echo.dpo@1", + "targetIrDomain": "echo.span-ir/v1" + } + ], + "directAdapters": [], + "operations": [ + { + "identity": { + "coordinate": "a.b@1.t", + "domain": "echo.edict-provider/operation/v1", + "authority": "echo.provider-semantic-declaration@1" + }, + "inputType": "a.b@1.Input", + "outputType": "a.b@1.Output", + "effect": "target.replace", + "profile": "continuum.profile.write/v1", + "budget": "p.tiny", + "obstructionMappings": [ + { + "failure": "rejected", + "obstruction": "domain.WriteRejected" + } + ], + "implementation": { + "kind": "native", + "capability": "echo.dpo@1.replace" + } + } + ], + "artifactResources": [ + { + "role": "resource.canonical-encoding", + "coordinate": "edict.canonical-cbor/v1", + "schemaContract": "edict.canonical-cbor/v1", + "provision": "external" + }, + { + "role": "resource.conformance-corpus", + "coordinate": "echo.dpo.fixtures/v1", + "schemaContract": "echo.edict-provider.conformance-corpus/v1", + "provision": "generated" + }, + { + "role": "resource.diagnostic-abi", + "coordinate": "edict.diagnostics/v1", + "schemaContract": "edict.diagnostics/v1", + "provision": "external" + }, + { + "role": "resource.deterministic-execution", + "coordinate": "edict.determinism/v1", + "schemaContract": "edict.determinism/v1", + "provision": "external" + }, + { + "role": "resource.fuel-model", + "coordinate": "edict.fuel/v1", + "schemaContract": "edict.fuel/v1", + "provision": "external" + }, + { + "role": "resource.lawpack-compatibility", + "coordinate": "echo.dpo-lawpack.compatibility@1", + "schemaContract": "echo.edict-provider.lawpack-compatibility/v1", + "provision": "generated" + }, + { + "role": "resource.lawpack-exports", + "coordinate": "echo.dpo-lawpack.exports@1", + "schemaContract": "edict.lawpack/v1#lawpack-exports", + "provision": "generated" + }, + { + "role": "resource.lawpack-target-adapter", + "coordinate": "echo.dpo-lawpack.adapter.echo-dpo@1", + "schemaContract": "echo.edict-provider.lawpack-target-adapter/v1", + "provision": "generated" + }, + { + "role": "resource.lawpack-verifier", + "coordinate": "echo.dpo-lawpack.verifier@1", + "schemaContract": "echo.edict-provider.lawpack-verifier/v1", + "provision": "generated" + }, + { + "role": "resource.sandbox", + "coordinate": "edict.wasm-component/v1", + "schemaContract": "edict.wasm-component/v1", + "provision": "external" + }, + { + "role": "resource.target-bundle-profile", + "coordinate": "echo.dpo.bundle/v1", + "schemaContract": "echo.dpo.bundle/v1", + "provision": "generated" + }, + { + "role": "resource.target-cost-algebra", + "coordinate": "echo.dpo.cost/v1", + "schemaContract": "echo.dpo.cost/v1", + "provision": "generated" + }, + { + "role": "resource.target-footprint-algebra", + "coordinate": "echo.dpo.footprint/v1", + "schemaContract": "echo.dpo.footprint/v1", + "provision": "generated" + }, + { + "role": "resource.target-intrinsics", + "coordinate": "echo.dpo.intrinsics/v1", + "schemaContract": "edict.target-profile.intrinsics/v1", + "provision": "generated" + }, + { + "role": "resource.target-ir", + "coordinate": "echo.span-ir/v1", + "schemaContract": "echo.span-ir/v1", + "provision": "generated" + }, + { + "role": "resource.target-lowerer-contract", + "coordinate": "echo.dpo.lowerer/v1", + "schemaContract": "echo.dpo.lowerer/v1", + "provision": "generated" + }, + { + "role": "resource.target-obstruction-taxonomy", + "coordinate": "echo.dpo.obstructions/v1", + "schemaContract": "echo.dpo.obstructions/v1", + "provision": "generated" + }, + { + "role": "resource.target-operation-profiles", + "coordinate": "echo.dpo.operation-profiles/v1", + "schemaContract": "edict.target-profile.operation-profiles/v1", + "provision": "generated" + }, + { + "role": "resource.target-verifier-contract", + "coordinate": "echo.dpo.verifier/v1", + "schemaContract": "echo.dpo.verifier/v1", + "provision": "generated" + } + ], + "lawpackProjection": { + "authority": "echo.provider-semantic-declaration@1", + "artifactRole": "lawpack.echo-dpo", + "id": "echo.dpo-lawpack", + "version": "1", + "acceptedCoreAbis": ["edict.core/v1"], + "dependencies": [], + "exportsResource": "resource.lawpack-exports", + "targetAdapters": [ + { + "acceptedTargetProfileRole": "target-profile.echo-dpo", + "acceptedTargetIrResource": "resource.target-ir", + "adapterResource": "resource.lawpack-target-adapter", + "effects": ["target.replace"] + } + ], + "verifier": { + "class": "declarative", + "rulesetResource": "resource.lawpack-verifier" + }, + "compatibilityResource": "resource.lawpack-compatibility", + "conformanceFixtureCorpusResource": "resource.conformance-corpus" + }, + "targetProfileProjection": { + "authority": "echo.provider-target-metadata@1", + "artifactRole": "target-profile.echo-dpo", + "id": "echo.dpo", + "version": "1", + "acceptedCoreAbis": ["edict.core/v1"], + "intrinsicNamespace": "echo.dpo@1", + "intrinsicsResource": "resource.target-intrinsics", + "operationProfilesResource": "resource.target-operation-profiles", + "footprintAlgebraResource": "resource.target-footprint-algebra", + "costAlgebraResource": "resource.target-cost-algebra", + "targetIrResource": "resource.target-ir", + "obstructionTaxonomyResource": "resource.target-obstruction-taxonomy", + "verifierResource": "resource.target-verifier-contract", + "lowererResource": "resource.target-lowerer-contract", + "sandboxResource": "resource.sandbox", + "fuelModelResource": "resource.fuel-model", + "bundleProfileResource": "resource.target-bundle-profile", + "generatedArtifactProfileRoles": [ + "generated-artifact-profile.echo-dpo-registration" + ], + "canonicalEncodingRulesResource": "resource.canonical-encoding", + "acceptedLawpackAdapterAbis": [], + "diagnosticAbiResource": "resource.diagnostic-abi", + "applicationModel": "atomic", + "readConsistency": "application-snapshot", + "guardEvaluation": "precommit-atomic", + "obstructionRollback": "no-visible-effects", + "multiTarget": false, + "postconditionSupport": true, + "deterministicExecutionResource": "resource.deterministic-execution", + "conformanceFixtureCorpusResource": "resource.conformance-corpus" + }, + "generatedArtifacts": [ + { + "role": "authority-facts.echo-dpo", + "kind": "authorityFacts", + "coordinate": "echo.dpo-authority-facts@1", + "schemaContract": "edict.authority-facts/v1", + "contractOwner": "flyingrobots/edict#157", + "authorityFactSource": { + "kind": "targetProfile", + "coordinate": "echo.dpo@1" + } + }, + { + "role": "authority-facts.echo-lawpack", + "kind": "authorityFacts", + "coordinate": "echo.dpo-lawpack-authority-facts@1", + "schemaContract": "edict.authority-facts/v1", + "contractOwner": "flyingrobots/edict#157", + "authorityFactSource": { + "kind": "lawpack", + "coordinate": "echo.dpo-lawpack@1" + } + }, + { + "role": "generated-artifact-profile.echo-dpo-registration", + "kind": "generatedArtifactProfile", + "coordinate": "echo.dpo.registration/v1", + "schemaContract": "echo.generated-artifact-profile/v1" + }, + { + "role": "lawpack.echo-dpo", + "kind": "lawpack", + "coordinate": "echo.dpo-lawpack@1", + "schemaContract": "edict.lawpack/v1" + }, + { + "role": "provenance.provider-generation", + "kind": "generationProvenance", + "coordinate": "echo.edict-provider-generation-provenance@1", + "schemaContract": "wesley:GenerationProvenanceManifestV1", + "contractOwner": "flyingrobots/wesley#728" + }, + { + "role": "review.provider-generation", + "kind": "reviewArtifact", + "coordinate": "echo.edict-provider-generation-review@1", + "schemaContract": "wesley:GenerationReviewV1", + "contractOwner": "flyingrobots/wesley#728" + }, + { + "role": "schema.echo-provider-artifacts", + "kind": "artifactSchema", + "coordinate": "echo.provider-artifacts.cddl@1", + "schemaContract": "selfContainedCddlV1" + }, + { + "role": "target-profile.echo-dpo", + "kind": "targetProfile", + "coordinate": "echo.dpo@1", + "schemaContract": "edict.target-profile/v1" + } + ], + "packageManifest": { + "role": "provider-manifest.echo", + "coordinate": "echo.edict-provider-manifest@1", + "schemaContract": "edict.provider-manifest/v1", + "providerAbi": "edict:target-provider@1.0.0", + "providerCoordinate": "echo.edict-provider@1", + "components": [ + { + "role": "lowerer.echo-dpo", + "kind": "lowerer", + "coordinate": "echo.dpo.lowerer/component@1", + "contract": "edict:target-provider/lowerer@1.0.0" + }, + { + "role": "verifier.echo-dpo", + "kind": "verifier", + "coordinate": "echo.dpo.verifier/component@1", + "contract": "edict:target-provider/verifier@1.0.0" + } + ] + }, + "invocationInputs": [ + { + "role": "authority-facts.echo-dpo", + "kind": "authorityFacts", + "domain": "edict.authority-facts/v1", + "schemaRole": "schema.echo-provider-artifacts" + }, + { + "role": "authority-facts.echo-lawpack", + "kind": "authorityFacts", + "domain": "edict.authority-facts/v1", + "schemaRole": "schema.echo-provider-artifacts" + }, + { + "role": "core.echo-provider", + "kind": "core", + "domain": "edict.core.module/v1", + "schemaRole": "schema.echo-provider-artifacts" + }, + { + "role": "lawpack.echo-dpo", + "kind": "lawpack", + "domain": "edict.lawpack/v1", + "schemaRole": "schema.echo-provider-artifacts" + }, + { + "role": "lowerability.echo-dpo", + "kind": "lowerabilityFacts", + "domain": "edict.lowering-requirements/v1", + "schemaRole": "schema.echo-provider-artifacts" + }, + { + "role": "target-ir.echo-dpo", + "kind": "targetIr", + "domain": "edict.target-ir.artifact/v1", + "schemaRole": "schema.echo-provider-artifacts" + }, + { + "role": "target-profile.echo-dpo", + "kind": "targetProfile", + "domain": "edict.target-profile/v1", + "schemaRole": "schema.echo-provider-artifacts" + } + ], + "invocationOutputs": [ + { + "role": "generated.echo-dpo", + "kind": "generatedArtifact", + "domain": "echo.generated-artifact/v1", + "schemaRole": "schema.echo-provider-artifacts" + }, + { + "role": "review.echo-dpo", + "kind": "reviewPayload", + "domain": "echo.review-payload/v1", + "schemaRole": "schema.echo-provider-artifacts" + }, + { + "role": "target-ir.echo-dpo", + "kind": "targetIr", + "domain": "edict.target-ir.artifact/v1", + "schemaRole": "schema.echo-provider-artifacts" + }, + { + "role": "verifier-report.echo-dpo", + "kind": "verifierReport", + "domain": "echo.verifier-report/v1", + "schemaRole": "schema.echo-provider-artifacts" + } + ], + "schemaBindings": [ + { + "domain": "echo.dpo-lawpack.adapter.echo-dpo@1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-provider-lawpack-target-adapter" + }, + { + "domain": "echo.dpo-lawpack.compatibility@1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-provider-lawpack-compatibility" + }, + { + "domain": "echo.dpo-lawpack.exports@1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "lawpack-exports" + }, + { + "domain": "echo.dpo-lawpack.verifier@1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-provider-lawpack-verifier" + }, + { + "domain": "echo.dpo.bundle/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-bundle" + }, + { + "domain": "echo.dpo.cost/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-cost" + }, + { + "domain": "echo.dpo.fixtures/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-provider-conformance-corpus" + }, + { + "domain": "echo.dpo.footprint/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-footprint" + }, + { + "domain": "echo.dpo.intrinsics/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "intrinsics-document" + }, + { + "domain": "echo.dpo.lowerer/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-lowerer" + }, + { + "domain": "echo.dpo.obstructions/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-obstructions" + }, + { + "domain": "echo.dpo.operation-profiles/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "operation-profiles-document" + }, + { + "domain": "echo.dpo.verifier/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-verifier" + }, + { + "domain": "echo.generated-artifact-profile/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "generated-artifact-profile" + }, + { + "domain": "echo.generated-artifact/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "generated-artifact" + }, + { + "domain": "echo.review-payload/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "review-payload" + }, + { + "domain": "echo.span-ir/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-span-ir" + }, + { + "domain": "echo.verifier-report/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "verifier-report" + }, + { + "domain": "edict.authority-facts/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "authority-facts" + }, + { + "domain": "edict.core.module/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "core-module" + }, + { + "domain": "edict.lawpack/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "lawpack-manifest" + }, + { + "domain": "edict.lowering-requirements/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "lowering-requirements" + }, + { + "domain": "edict.target-ir.artifact/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "target-ir-artifact" + }, + { + "domain": "edict.target-profile/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "target-profile-manifest" + } + ] +} diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/generation-settings-v1.json b/crates/echo-wesley-gen/assets/v1/edict-provider/generation-settings-v1.json new file mode 100644 index 00000000..ab8cc166 --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/generation-settings-v1.json @@ -0,0 +1,7 @@ +{ + "apiVersion": "echo.edict-provider-generation-settings/v1", + "shapeSource": "none", + "canonicalArtifactEncoding": "edict.canonical-cbor/v1", + "contractPack": "edict.provider-contract-pack.cddl@1", + "generatorAbi": "wesley.extension-generator/v1" +} diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/components/lowerer.echo-dpo.component.wasm b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/components/lowerer.echo-dpo.component.wasm new file mode 100644 index 00000000..c98bfb79 Binary files /dev/null and b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/components/lowerer.echo-dpo.component.wasm differ diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/components/verifier.echo-dpo.component.wasm b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/components/verifier.echo-dpo.component.wasm new file mode 100644 index 00000000..65128439 Binary files /dev/null and b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/components/verifier.echo-dpo.component.wasm differ diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/evidence/provenance.provider-generation.json b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/evidence/provenance.provider-generation.json new file mode 100644 index 00000000..8d25de7f --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/evidence/provenance.provider-generation.json @@ -0,0 +1 @@ +{"apiVersion":"wesley.generation-provenance-manifest/v1","contractVersions":{"generatorAbi":"wesley.extension-generator/v1","inputSchema":"wesley.extension-generation-input/v1","provenanceSchema":"wesley.generation-provenance-manifest/v1"},"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:d17b03810ecc53f288aa1de457a5ba295c537c4f64046f8e3777b8f98ff3fc86"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:3911de5075d3709a3ba40419e4b67f1226961f3520ba9dfbbad78278c9bb0e96"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:df62a4ff2b56f9553c80cf400728cab3717f5f442c4c2fc415d2c89c21c41dad"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:8cc385a3f287ad6ea522766d7b4e92bc5164226586eef6b3f2ac6c5253370dd3"},{"coordinate":"echo.dpo@1","digest":"sha256:95626e5be6e6b2c1c8aa1858277f1c67487ab6724b08408eb3c0054adce6b1eb"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:19901cc33bea0699334af3cb4f9889f752652e35b81a2a9c91e7e31a52b803af"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e","version":"0.1.0"},"settingsDigest":"sha256:0f708e76898de6fdb8186352e81d0f5c445adf94bb6c7de9204952d9fe913d4a","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/evidence/review.provider-generation.json b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/evidence/review.provider-generation.json new file mode 100644 index 00000000..3c71a269 --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/evidence/review.provider-generation.json @@ -0,0 +1 @@ +{"apiVersion":"wesley.generation-review/v1","authoritative":false,"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:d17b03810ecc53f288aa1de457a5ba295c537c4f64046f8e3777b8f98ff3fc86"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:3911de5075d3709a3ba40419e4b67f1226961f3520ba9dfbbad78278c9bb0e96"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:df62a4ff2b56f9553c80cf400728cab3717f5f442c4c2fc415d2c89c21c41dad"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:8cc385a3f287ad6ea522766d7b4e92bc5164226586eef6b3f2ac6c5253370dd3"},{"coordinate":"echo.dpo@1","digest":"sha256:95626e5be6e6b2c1c8aa1858277f1c67487ab6724b08408eb3c0054adce6b1eb"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:19901cc33bea0699334af3cb4f9889f752652e35b81a2a9c91e7e31a52b803af"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e","version":"0.1.0"},"projectionRoles":["authority-facts.echo-dpo","authority-facts.echo-lawpack","generated-artifact-profile.echo-dpo-registration","lawpack.echo-dpo","schema.echo-provider-artifacts","target-profile.echo-dpo"],"provenanceManifestDigest":"sha256:b9c6d845e151f455bc7d0d6e7d209e7fbe92e3fc592145227cbb8e9b3e59c7b2","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/authority-facts.echo-dpo.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/authority-facts.echo-dpo.cbor new file mode 100644 index 00000000..807c2186 --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/authority-facts.echo-dpo.cbor @@ -0,0 +1 @@ +fsourcedkindmtargetProfilefdigestfsha256X VbZe/T~U2-jcoordinatejecho.dpo@1gbudgetsjapiVersionxedict.authority-facts/v1qoperationProfileskp.effectfuldcorexcontinuum.profile.write/v1sallowedWriteClassesgreplacereffectWriteClassesntarget.replacegreplace \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/authority-facts.echo-lawpack.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/authority-facts.echo-lawpack.cbor new file mode 100644 index 00000000..a3a7b681 Binary files /dev/null and b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/authority-facts.echo-lawpack.cbor differ diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/generated-artifact-profile.echo-dpo-registration.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/generated-artifact-profile.echo-dpo-registration.cbor new file mode 100644 index 00000000..bb7a2d6e --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/generated-artifact-profile.echo-dpo-registration.cbor @@ -0,0 +1 @@ +etypesha.b@1.IddkindocoreStringAliasicanonicalhraw-utf8omaxScalarValueska.b@1.Inputdkindfrecordffieldsdnamebiddtypeha.b@1.Idla.b@1.Outputdkindfrecordffieldsdnamebiddtypeha.b@1.Idma.b@1.Receiptdkindfrecordffieldsdnamebiddtypeha.b@1.Idwtarget.replace.rejecteddkindfrecordffieldsxdomain.WriteRejected.PayloaddkindfrecordffieldsjapiVersionx"echo.generated-artifact-profile/v1joperationsga.b@1.tfbudgetfp.tinyfeffectntarget.replaceiinputTypeka.b@1.InputjoutputTypela.b@1.OutputmopticContractmreplace-pointnimplementationdkindfnativejcoordinaterecho.dpo@1.replaceninvocationKindhmutationpoperationProfilexcontinuum.profile.write/v1sobstructionMappingshrejectedtdomain.WriteRejectedmtargetProfilejecho.dpo@1 \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/lawpack.echo-dpo.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/lawpack.echo-dpo.cbor new file mode 100644 index 00000000..354071e7 Binary files /dev/null and b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/lawpack.echo-dpo.cbor differ diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/schema.echo-provider-artifacts.cddl b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/schema.echo-provider-artifacts.cddl new file mode 100644 index 00000000..900e2fc2 --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/schema.echo-provider-artifacts.cddl @@ -0,0 +1,969 @@ +; SPDX-License-Identifier: Apache-2.0 +; edict-provider-contracts.cddl +; Generated from Edict-owned ABI fragments. DO NOT EDIT. + +; --- edict-common.cddl --- +; edict-common.cddl +; Shared CDDL types for the Edict ABIs, defined ONCE here so they cannot drift +; (EDICT-ABI-NODUP-001). Two groups: +; - resource-ref, sha256-digest, effect-failure-body, effect-kind, authority-class, +; core-type-ref: assembled with edict-target-profile.cddl and +; edict-lawpack.cddl by the build; those schemas do not redefine them. +; - operation-profile, optic-template, aperture-requirement and their refs: +; consumed by the Core/optic layer (edict-core.cddl) and referenced by the +; language/target-profile specs. +; Authoritative byte encoding: edict.canonical-cbor/v1. + +; A normative subcomponent referenced by identity plus digest. Manifests never +; embed their own self-digest in their preimage (EDICT-CORE-SELFHASH-001). +resource-ref = { id: tstr, digest: sha256-digest } + +; Digests are authoritative as typed byte values, never hex strings. Review JSON +; renders this as "sha256:<64 lowercase hex>" (EDICT-DIGEST-WIRE-001). +sha256-digest = [ algorithm: "sha256", bytes: bstr .size 32 ] + +; A named low-level failure an effect can raise. The source obstruction map +; binds it (by coordinate) and constructs a typed domain obstruction from its +; payload (EDICT-ABI-FAILURE-NAMED-001). +; An effect's `effectFailures` list MUST have unique `coordinate`s: since the +; obstruction map is keyed by coordinate, two failures sharing a coordinate (even +; with different authorityClass/payloadType) make exhaustive mapping and binder +; typing ambiguous and are rejected (EDICT-ABI-FAILURE-UNIQUE-001). +; +; Effects carry their failures as a map `{ failure-ident => effect-failure-body }` +; (see the target/lawpack effect schemas). The failure coordinate is the map +; KEY, so it is not repeated in the body and cannot disagree with the key. +effect-failure-body = { + authorityClass: authority-class, + payloadType: core-type-ref, ; typed, bounded (may be empty record) +} + +; A failure coordinate must be a bare Edict `ident` (letter/underscore then +; letters/digits/underscores) AND must not be a reserved keyword (e.g. `else`, +; `basis`, `where`, `require`, `for`, `if`). The source obstruction-map LHS only +; accepts a non-keyword `ident`, so a hyphen/dot/keyword coordinate would be +; ABI-valid yet impossible to map exhaustively in source. The regex captures the +; lexical shape; keyword exclusion is an additional validation rule +; (EDICT-ABI-FAILURE-IDENT-001). +failure-ident = tstr .regexp "[A-Za-z_][A-Za-z0-9_]*" + +effect-kind = "read" / "create" / "ensure" / "replace" / "delete" / + "append" / "reduce" / "semantic.emit" / "custom" + +authority-class = "domainMappable" / "participantOwned" / "integrityFault" / + "resourceFault" / "internalFault" + +core-type-ref = tstr ; canonical Core type coordinate + +; An operation profile supplies the optic template a Core intent resolves its +; opticKind/boundaryKind/supportPolicy/lossDisposition from. Target profiles and +; lawpacks publish these as a map `{ coordinate => operation-profile }`, so the +; coordinate is the KEY, not a value field (EDICT-OPTIC-TEMPLATE-OWNER-001, +; EDICT-ABI-OPPROFILE-UNIQUE-001). +operation-profile = { + opticTemplate: optic-template, + effectPredicate: tstr, ; coordinate of the operation-mode predicate +} + +optic-template = { + opticKind: "revelation" / "affectReintegration", + boundaryKind: "projection" / "affect", + supportPolicy: tstr, ; canonical support-policy coordinate + lossDisposition: tstr, ; canonical loss-disposition coordinate + ? basisTemplate: tstr, ; optional digest-locked basis template coord + ; the aperture requirement this template supplies. Required when the template + ; is the source of a Core optic's apertureRequirement (i.e. the intent has no + ; source `footprint <= ...`), since apertureRequirement is mandatory in Core + ; (EDICT-OPTIC-APERTURE-REF-001). + ? apertureRequirement: aperture-requirement, +} + +; apertureRequirement is a typed reference, never a free-form string. A review +; rendering may show its coordinate (EDICT-OPTIC-APERTURE-REF-001). +aperture-requirement = footprint-ceiling-ref / abstract-footprint-obligation-ref +footprint-ceiling-ref = { kind: "footprintCeiling", ref: tstr } +abstract-footprint-obligation-ref = { kind: "abstractFootprintObligation", ref: tstr } + +; --- edict-core.cddl --- +; edict-core.cddl +; Normative schema for the Edict Core v1 semantic model. +; +; Scope boundary: this file defines Core meaning and schema shape only. It does +; not define a canonical encoder, Core module hash fields, hash fixtures, target +; lowering, admission bundles, or target-owned IR. + +core-module = { + apiVersion: "edict.core/v1", + coordinate: tstr, + imports: [* core-import], + types: { * tstr => core-type }, + intents: { + tstr => core-intent }, + requiredCoreCapabilities: [* tstr], +} + +core-import = { + kind: "lawpack" / "target" / "core", + ref: resource-ref, + ? alias: tstr, +} + +; --- types --------------------------------------------------------------- + +core-type = core-scalar-type / core-record-type / core-variant-type / + core-option-type / core-list-type / core-map-type / + core-capability-ref-type + +core-scalar-type = core-bool-type / core-int-type / core-string-type / + core-bytes-type / core-unit-type + +core-bool-type = { kind: "Bool" } +core-unit-type = { kind: "Unit" } +core-int-type = { + kind: "I64" / "U64" / "I32" / "U32" / "I16" / "U16" / "I8" / "U8", +} +core-string-type = { + kind: "String", + max: uint, + canonical: "unicode-scalar-nfc" / "raw-utf8", +} +core-bytes-type = { + kind: "Bytes", + max: uint, +} +core-record-type = { + kind: "Record", + fields: { * tstr => core-type-ref }, +} +core-variant-type = { + kind: "Variant", + cases: { + tstr => variant-case-body }, +} +variant-case-body = { + ? payload: core-type-ref, +} +core-option-type = { + kind: "Option", + item: core-type-ref, +} +core-list-type = { + kind: "List", + item: core-type-ref, + max: uint, +} +core-map-type = { + kind: "Map", + key: core-type-ref, + value: core-type-ref, + max: uint, +} +core-capability-ref-type = { + kind: "CapabilityRef", + item: core-type-ref, +} + +; core-type-ref is defined in edict-common.cddl and assembled with this schema. + +; --- references and values ---------------------------------------------- + +; Local identity is alpha-stable. `id` is the compiler-owned local coordinate; +; `alphaName` is the normalized human/debug name. Source binder spelling is not +; identity. +local-ref = { + id: tstr, + alphaName: tstr, + type: core-type-ref, +} + +core-value = core-null-value / core-bool-value / core-int-value / + core-string-value / core-bytes-value / core-record-value / + core-variant-value / core-list-value / core-map-value / + core-capability-value + +core-null-value = { kind: "null" } +core-bool-value = { kind: "bool", value: bool } +core-int-value = { kind: "int", width: tstr, value: int } +core-string-value = { kind: "string", value: tstr } +core-bytes-value = { kind: "bytes", value: bstr } +core-record-value = { kind: "record", fields: { * tstr => core-value } } +core-variant-value = { + kind: "variant", + type: core-type-ref, + case: tstr, + ? payload: core-value, +} +core-list-value = { kind: "list", values: [* core-value] } +core-map-value = { kind: "map", entries: [* [key: core-value, value: core-value]] } +core-capability-value = { + kind: "capability", + receipt: sha256-digest, +} + +; Edict-authored pure helpers use a pure Core function body. The body can bind +; pure expressions and return an expression, but it cannot contain Core effect, +; guard, branch, loop, match-node, or proof-obligation nodes. +core-fn-body = { + params: [* local-ref], + body: core-pure-block, +} + +core-pure-block = { + locals: [* local-ref], + bindings: [* pure-let-node], + result: core-expr, +} + +pure-let-node = { + kind: "let", + binding: local-ref, + value: core-expr, +} + +; --- expressions and predicates ----------------------------------------- + +core-expr = local-expr / const-expr / record-expr / field-expr / + variant-expr / match-expr / call-expr / list-expr / map-expr / + if-expr + +local-expr = { kind: "local", ref: local-ref } +const-expr = { kind: "const", value: core-value } +record-expr = { kind: "record", fields: { * tstr => core-expr } } +field-expr = { kind: "field", base: core-expr, field: tstr } +variant-expr = { + kind: "variant", + type: core-type-ref, + case: tstr, + ? payload: core-expr, +} +match-expr = { + kind: "match", + scrutinee: core-expr, + arms: [+ match-arm], +} +match-arm = { + case: tstr, + ? binder: local-ref, + body: core-expr, +} +call-expr = { + kind: "call", + callee: tstr, + typeArgs: [* core-type-ref], + args: [* core-expr], +} +list-expr = { kind: "list", values: [* core-expr] } +map-expr = { kind: "map", entries: [* [key: core-expr, value: core-expr]] } +if-expr = { + kind: "if", + predicate: core-predicate, + then: core-expr, + else: core-expr, +} + +core-predicate = true-predicate / false-predicate / not-predicate / + all-predicate / any-predicate / compare-predicate / + call-predicate / obstruction-predicate + +true-predicate = { kind: "true" } +false-predicate = { kind: "false" } +not-predicate = { kind: "not", value: core-predicate } +all-predicate = { kind: "all", values: [+ core-predicate] } +any-predicate = { kind: "any", values: [+ core-predicate] } +compare-predicate = { + kind: "compare", + op: "==" / "!=" / "<" / "<=" / ">" / ">=", + left: core-expr, + right: core-expr, +} +call-predicate = { + kind: "call", + predicate: tstr, + args: [* core-expr], +} +obstruction-predicate = { + kind: "obstruction", + coordinate: failure-ident, + payload: core-expr, +} + +input-constraint = { + coordinate: tstr, + source: "where" / "compiler", + predicate: core-predicate, +} + +; --- intents, blocks, and nodes ----------------------------------------- + +core-intent = { + input: core-type-ref, + output: core-type-ref, + requiredOperationProfile: tstr, + inputConstraints: [* input-constraint], + coreEvaluationBudget: core-budget, + body: core-block, + ? optic: core-optic, +} + +core-budget = { + maxSteps: uint, + maxAllocatedBytes: uint, + maxOutputBytes: uint, +} + +core-optic = { + opticKind: "revelation" / "affectReintegration", + boundaryKind: "projection" / "affect", + apertureRequirement: aperture-requirement, + supportPolicy: tstr, + lossDisposition: tstr, +} + +core-block = { + locals: [* local-ref], + nodes: [* core-node], + result: core-expr, +} + +core-node = let-node / require-node / effect-node / guard-node / branch-node / + for-node / match-node / proof-obligation-node + +let-node = { + kind: "let", + binding: local-ref, + value: core-expr, +} +require-node = { + kind: "require", + predicate: core-predicate, + onFailure: require-failure-arm, +} +require-failure-arm = terminal-require-failure / + continue-obstructed-require-failure +terminal-require-failure = { + kind: "terminal", + reason: obstruction-reason, +} +continue-obstructed-require-failure = { + kind: "continueObstructed", + reason: obstruction-reason, +} +obstruction-reason = { + reasonKind: tstr, + payload: { * tstr => core-expr }, +} +effect-node = { + kind: "effect", + binding: local-ref, + effect: tstr, + input: core-expr, + obstructionMap: { * failure-ident => obstruction-arm }, +} +obstruction-arm = { + binder: local-ref, + value: core-expr, +} +guard-node = { + kind: "guard", + predicate: core-predicate, + obstruction: core-expr, +} +branch-node = { + kind: "branch", + predicate: core-predicate, + then: core-block, + else: core-block, +} +for-node = { + kind: "for", + binder: local-ref, + iter: core-expr, + bound: core-bound, + body: core-block, +} +match-node = { + kind: "match", + scrutinee: core-expr, + arms: [+ match-block-arm], +} +match-block-arm = { + case: tstr, + ? binder: local-ref, + body: core-block, +} +proof-obligation-node = { + kind: "proof", + coordinate: tstr, + predicate: core-predicate, +} + +core-bound = literal-bound / coordinate-bound +literal-bound = { kind: "literal", value: uint } +coordinate-bound = { kind: "coordinate", ref: tstr } + +; Shared resource-ref, sha256-digest, failure-ident, aperture-requirement, and +; core-type-ref are defined once in edict-common.cddl. + +; --- edict-lawpack.cddl --- +; edict-lawpack.cddl +; Canonical schema for the Edict lawpack manifest and export surface. +; Authoritative byte encoding: edict.canonical-cbor/v1. JSON in the prose specs +; is a review rendering generated from this schema (EDICT-ABI-NODUP-001). + +lawpack-manifest = { + apiVersion: "edict.lawpack/v1", + id: tstr, + version: tstr, + acceptedCoreAbi: [+ tstr], + dependencies: [* lawpack-dep], ; acyclic, digest-locked (EDICT-LAWPACK-DAG-001) + exports: resource-ref, + ? targetAdapters: [+ target-adapter], ; required only if any runtime effect exists + ? helperComponent: executable-component, ; executable helpers carry their own sandbox+fuel + verifier: verifier, ; classified: declarative or executable + compatibility: resource-ref, + conformanceFixtureCorpus: resource-ref, +} + +; A verifier is either a declarative ruleset (no runtime) or an executable +; component. An executable verifier MUST carry its own sandbox and fuel model, +; so the schema enforces that no executable component is left unbounded +; (EDICT-ABI-VERIFIER-BOUND-001). +verifier = declarative-verifier / executable-verifier +declarative-verifier = { class: "declarative", ruleset: resource-ref } +executable-verifier = { + class: "executable", + component: resource-ref, + sandbox: resource-ref, + fuelModel: resource-ref, +} + +; Any executable component is bounded by its own sandbox + fuel model. +executable-component = { + component: resource-ref, + sandbox: resource-ref, + fuelModel: resource-ref, +} + +lawpack-dep = { id: tstr, version: tstr, digest: sha256-digest } + +; Adapter selection keys SOLELY off the digest-locked `acceptedTargetProfile` +; (its `id` is the profile id; its `digest` pins the exact profile/version). There +; are no independent display strings that could disagree with the lock, so a +; resolver cannot bind an adapter to one target while the lock proves another +; (EDICT-LAWPACK-ADAPTER-TARGETIR-001). +target-adapter = { + acceptedTargetProfile: resource-ref, ; digest-locked, authoritative selector + acceptedTargetIr: resource-ref, ; digest-locked + adapter: resource-ref, +} + +; Shared types resource-ref and sha256-digest are defined once in +; edict-common.cddl and assembled with this schema (EDICT-ABI-NODUP-001). + +; --- export surface ------------------------------------------------------ + +lawpack-exports = { + types: [* exported-type], + constants: [* exported-constant], + pureFunctions: [* pure-function], + effects: [* semantic-effect], + obstructions: [* obstruction-def], + ; keyed by operation-profile coordinate → uniqueness enforced + ; (EDICT-ABI-OPPROFILE-UNIQUE-001) + ; operation-profile records this lawpack exports (optic templates that + ; `implements`/`profile` clauses resolve against). operation-profile is + ; defined in edict-common.cddl (EDICT-ABI-OPPROFILE-SLOT-001). + operationProfiles: { * tstr => operation-profile }, ; keyed by coordinate +} + +exported-type = { coordinate: tstr, definition: core-type-ref } +exported-constant = { coordinate: tstr, type: core-type-ref, value: any } + +; A pure helper is a discriminated union by `source`, so the schema itself +; guarantees an implementation exists (EDICT-LAWPACK-PURE-IMPL-001): +; - "edict": authored in Edict/Core; the Core body is carried inline (hashed +; with the export surface). The schema requires the `body` field. +; - "component": implemented outside Edict; carries no inline body and instead +; carries its own digest-locked `implementation` (sandbox + fuel). It does +; not depend on the optional manifest-level helperComponent. +pure-function = edict-pure-function / component-pure-function + +pure-function-common = ( + coordinate: tstr, + typeParameters: [* tstr], + parameterTypes: [* core-type-ref], ; all bounded + returnType: core-type-ref, ; bounded + costTemplate: tstr, + determinismClass: "total" / "total-with-typed-diagnostic", +) + +edict-pure-function = { + pure-function-common, + source: "edict", + body: core-fn-body, ; inline, hash-significant +} + +component-pure-function = { + pure-function-common, + source: "component", + ; the digest-locked component implementing this helper. Required at the schema + ; level so a component helper can never validate without a hash-bound, + ; sandbox+fuel-described implementation (EDICT-LAWPACK-PURE-IMPL-001). + implementation: executable-component, +} + +; core-fn-body is defined by edict-core.cddl and assembled with this lawpack +; schema. It is a pure Core function body, not an effect-capable core-block. + +semantic-effect = { + coordinate: tstr, + typeParameters: [* tstr], + inputType: core-type-ref, ; bounded + outputType: core-type-ref, ; bounded + executionClass: "proofOnly" / "runtime", ; orthogonal to writeClass + effectKindHint: effect-kind, + footprintObligation: tstr, + costObligation: tstr, + effectFailures: { * failure-ident => effect-failure-body }, ; keyed by coordinate; unique + guardSupport: bool, +} + +obstruction-def = { + coordinate: tstr, + authorityClass: authority-class, + payloadSchema: core-type-ref, ; typed, bounded (may be empty record) +} + +; effect-failure-body, effect-kind, authority-class, and core-type-ref are defined +; once in edict-common.cddl and assembled with this schema (EDICT-ABI-NODUP-001). + +; --- edict-target-profile.cddl --- +; edict-target-profile.cddl +; Canonical schema for the Edict target profile manifest. +; Authoritative byte encoding: edict.canonical-cbor/v1 +; (see SPEC_continuum-contract-bundle-v1.md). JSON in the prose specs is a +; review rendering generated from this schema; this CDDL is the single source +; of truth (EDICT-ABI-NODUP-001). + +target-profile-manifest = { + apiVersion: "edict.target-profile/v1", + id: tstr, ; e.g. "echo.dpo" + version: tstr, ; e.g. "1" + acceptedCoreAbi: [+ tstr], ; e.g. ["edict.core/v1"] + + intrinsics: resource-ref, + intrinsicNamespace: tstr, + ; publishes this profile's operation-profile records (optic templates that + ; `profile`/`implements` clauses resolve against). References an + ; operation-profiles-document (EDICT-ABI-OPPROFILE-SLOT-001). + operationProfiles: resource-ref, + footprintAlgebra: resource-ref, + costAlgebra: resource-ref, + targetIr: resource-ref, + obstructionTaxonomy: resource-ref, + verifier: resource-ref, + lowerer: resource-ref, + sandbox: resource-ref, + fuelModel: resource-ref, + + ; fields the language spec requires of every profile + bundleProfile: resource-ref, + generatedArtifactProfiles: [* resource-ref], + canonicalEncodingRules: resource-ref, + ; Reserved/deferred: will list accepted lawpack-adapter ABI ids once that + ; byte-level ABI (edict.lawpack-adapter/v1) is specified. The schema enforces + ; emptiness now — the type is the empty array, so non-empty values are + ; rejected, not merely discouraged (EDICT-ABI-LAWPACK-ADAPTER-DEFER-001). + ? acceptedLawpackAdapterAbi: [], + diagnosticAbi: resource-ref, + + ; application doctrine + applicationModel: "atomic", + readConsistency: "application-snapshot" / tstr, + guardEvaluation: "precommit-atomic" / tstr, + obstructionRollback: "no-visible-effects" / tstr, + multiTarget: bool, + ; whether the target can evaluate precommit postcondition (`guarantee`) checks + ; inside the atomic application unit (EDICT-TARGET-POSTCOND-001) + postconditionSupport: bool, + + deterministicExecution: resource-ref, + conformanceFixtureCorpus: resource-ref, +} + +; Shared types resource-ref and sha256-digest are defined once in +; edict-common.cddl and assembled with this schema by the build +; (EDICT-ABI-NODUP-001). They are not redefined here. + +; --- intrinsic signature ------------------------------------------------- +; The artifact referenced by the manifest's `intrinsics` resource-ref is the +; intrinsic-signature corpus document below. Its layout is fixed so two +; independent profiles validate/hash the corpus identically +; (EDICT-ABI-INTRINSICS-DOC-001). + +; intrinsics is a MAP keyed by coordinate, so the schema itself enforces +; coordinate uniqueness. A provider receives the resolved corpus as a +; digest-bound semantic input and resolves coordinates within that artifact. +; Each map key MUST equal its record's `coordinate` field +; (EDICT-ABI-INTRINSIC-UNIQUE-001). +intrinsics-document = { + apiVersion: "edict.target-profile.intrinsics/v1", + intrinsics: { * tstr => intrinsic }, +} + +; The artifact referenced by the manifest's `operationProfiles` resource-ref. +; operation-profile / optic-template are defined in edict-common.cddl. Keyed by +; coordinate so resolution can't pick between two same-coordinate profiles +; (EDICT-ABI-OPPROFILE-SLOT-001, EDICT-ABI-OPPROFILE-UNIQUE-001). +operation-profiles-document = { + apiVersion: "edict.target-profile.operation-profiles/v1", + profiles: { * tstr => operation-profile }, +} + +; A typed pre-lowering question that can be proposed by Watson or an agent and +; checked by the compiler. It is canonical-CBOR encoded under +; `edict.lowering-requirements/v1`; the compiler checks this artifact, not the +; prose that produced it. +lowering-requirements = { + apiVersion: "edict.lowering-requirements/v1", + operationProfile: tstr, + semanticEffects: [* semantic-effect-requirement], + requiredWriteClasses: [* write-class], + guardKinds: [* guard-kind], + atomicity: atomicity-requirement, + postconditionSupport: bool, + obstructionCoordinates: [* tstr], + footprintObligations: [* tstr], + costObligations: [* tstr], + opticContract: tstr, +} + +semantic-effect-requirement = { + coordinate: tstr, + writeClass: write-class, + guardKinds: [* guard-kind], + obstructionCoordinates: [* tstr], + footprintObligations: [* tstr], + costObligations: [* tstr], +} + +write-class = "none" / "read" / "create" / "ensure" / "append" / + "replace" / "delete" / tstr +guard-kind = "precommit-atomic" / tstr +atomicity-requirement = "atomic" / tstr + +; A genuine union: pure constructors carry no effect kind or failures; effect +; intrinsics must (EDICT-TARGET-INTRINSIC-CLASS-001). The schema enforces this, +; not a comment. + +; The intrinsic's coordinate is the intrinsics map KEY, not a value field, so the +; key and coordinate can never disagree (EDICT-ABI-INTRINSIC-UNIQUE-001). +intrinsic = pure-intrinsic / effect-intrinsic + +pure-intrinsic = { + intrinsicClass: "pure", + typeParameters: [* tstr], + argumentTypes: [* core-type-ref], + returnType: core-type-ref, + guardSupport: false, + footprintTemplate: tstr, + costTemplate: tstr, + writeClass: "none", +} + +effect-intrinsic = { + intrinsicClass: "effect", + typeParameters: [* tstr], + argumentTypes: [* core-type-ref], + returnType: core-type-ref, + effectKind: effect-kind, + ; map keyed by failure coordinate (failure-ident); the failure coordinate is + ; the key, not a value field, so uniqueness is structural + ; (EDICT-ABI-FAILURE-UNIQUE-001). + effectFailures: { * failure-ident => effect-failure-body }, + guardSupport: bool, + footprintTemplate: tstr, + costTemplate: tstr, + writeClass: "read" / "create" / "ensure" / "append" / "replace" / + "delete" / "custom", + canParticipateInAtomicGuard: bool, +} + +; effect-failure-body, effect-kind, authority-class, and core-type-ref are defined +; once in edict-common.cddl and assembled with this schema (EDICT-ABI-NODUP-001). + +; --- edict-authority-facts.cddl --- +; edict-authority-facts.cddl +; Canonical schema for the first compiler-context authority-facts document. +; Authoritative byte encoding: edict.canonical-cbor/v1. +; +; This schema is assembled with edict-common.cddl so source.digest uses the +; shared sha256-digest typed value. JSON is a review/input rendering: its +; `sha256:<64 hex>` source digest is projected to [`sha256`, 32 raw bytes] on +; the wire, and its fact arrays project to the coordinate-keyed maps below. + +authority-facts = { + apiVersion: "edict.authority-facts/v1", + source: authority-fact-source, + operationProfiles: { * tstr => authority-operation-profile-fact }, + effectWriteClasses: { * tstr => authority-write-class }, + budgets: { * tstr => authority-budget-fact }, +} + +authority-fact-source = { + kind: "lawpack" / "targetProfile", + coordinate: tstr, + digest: sha256-digest, +} + +; The map key is the source operation-profile coordinate. It is not repeated in +; the value, so a key and embedded coordinate cannot disagree. Allowed write +; classes are a canonical map-set: the class is the unique key and null is the +; unit marker. Canonical CBOR fixes key order without a second ordering rule. +authority-operation-profile-fact = { + core: tstr, + allowedWriteClasses: { * authority-write-class => null }, +} + +; The effectWriteClasses map key is the semantic effect coordinate. The budgets +; map key is the source budget coordinate. Canonical CBOR map-key uniqueness +; makes duplicate fact coordinates structurally unrepresentable. +authority-budget-fact = { + maxSteps: uint, + maxAllocatedBytes: uint, + maxOutputBytes: uint, +} + +; AuthorityFactsDocument v1 intentionally accepts only the write classes the +; current compiler model can consume. `custom` is the sole v1 custom spelling; +; arbitrary target-profile extension strings do not enter this compiler path. +authority-write-class = "none" / "read" / "create" / "ensure" / "append" / + "replace" / "delete" / "custom" + +; --- edict-target-ir.cddl --- +; SPDX-License-Identifier: Apache-2.0 +; edict-target-ir.cddl +; Canonical schema for the Edict-owned Target IR artifact envelope. +; Authoritative byte encoding: edict.canonical-cbor/v1. +; +; This schema is assembled with edict-common.cddl and edict-core.cddl. It +; deliberately reuses Core expressions, predicates, budgets, local references, +; obstruction reasons, and obstruction arms so the schema matches the value +; emitted by the canonical Target IR encoder rather than restating those types. +; It describes the structural shape of valid lowering-produced artifacts. The +; lowering and encoder contracts separately enforce semantic identifier rules +; and canonical ordering/deduplication for set-like values. + +; Target IR encoding rejects an empty target-profile coordinate before bytes +; exist, so this root tightens the shared structural resource-ref accordingly. +target-ir-resource-ref = { + id: tstr .regexp "(?s).+", + digest: sha256-digest, +} + +target-ir-artifact = { + kind: "targetIrArtifact", + domain: tstr, + targetProfile: target-ir-resource-ref, + sourceCoreCoordinate: tstr, + intents: { * tstr => target-ir-intent }, +} + +target-ir-intent = { + operationProfile: tstr, + inputConstraints: [* input-constraint], + coreEvaluationBudget: core-budget, + requirements: [* target-ir-requirement], + steps: [* target-ir-step], + result: core-expr, +} + +target-ir-requirement = { + id: tstr, + predicate: core-predicate, + onFailure: require-failure-arm, +} + +target-ir-step = { + id: tstr, + binding: local-ref, + effect: tstr, + targetIntrinsic: tstr, + input: core-expr, + obstructionFailures: [* failure-ident], + obstructionArms: { * failure-ident => obstruction-arm }, +} + + +; --- Echo provider-generated declarative contracts ----------------------- +; These roots validate provider semantic descriptions. They confer no Echo +; runtime authority and contain no package, component, or installation state. + +generated-artifact-profile = { + apiVersion: "echo.generated-artifact-profile/v1", + targetProfile: tstr, + types: { * tstr => echo-generated-type }, + operations: { * tstr => generated-operation }, +} + +echo-generated-type = echo-generated-string-alias / echo-generated-record +echo-generated-string-alias = { + kind: "coreStringAlias", + maxScalarValues: uint, + canonical: "raw-utf8", +} +echo-generated-record = { + kind: "record", + fields: [* echo-generated-record-field], +} +echo-generated-record-field = { name: tstr, type: tstr } + +generated-operation = { + inputType: tstr, + outputType: tstr, + effect: tstr, + operationProfile: tstr, + opticContract: tstr, + budget: tstr, + invocationKind: "mutation" / "observer", + implementation: { kind: "native" / "directAdapter", coordinate: tstr }, + obstructionMappings: { * tstr => tstr }, +} + +echo-provider-conformance-corpus = { + apiVersion: "echo.edict-provider.conformance-corpus/v1", + class: "declarative", + operations: { * tstr => null }, + capabilities: { * tstr => null }, + semanticEffects: { * tstr => null }, + cases: [], +} + +echo-provider-lawpack-compatibility = { + apiVersion: "echo.edict-provider.lawpack-compatibility/v1", + class: "declarative", + acceptedCoreAbi: { * tstr => null }, + acceptedTargetProfiles: { * tstr => null }, + semanticEffects: { * tstr => null }, +} + +echo-provider-lawpack-target-adapter = { + apiVersion: "echo.edict-provider.lawpack-target-adapter/v1", + class: "declarative", + targetProfile: tstr, + targetIrDomain: tstr, + effectImplementations: { * tstr => echo-effect-implementation }, +} + +echo-effect-implementation = echo-native-effect-implementation / echo-direct-effect-implementation +echo-native-effect-implementation = { + kind: "native", + capability: tstr, + writeClass: tstr, +} +echo-direct-effect-implementation = { + kind: "directAdapter", + adapter: tstr, + capability: tstr, + writeClass: tstr, +} + +echo-provider-lawpack-verifier = { + apiVersion: "echo.edict-provider.lawpack-verifier/v1", + class: "declarative", + operationObstructions: { * tstr => echo-operation-obstructions }, +} +echo-operation-obstructions = { + effect: tstr, + failureMappings: { * failure-ident => tstr }, +} + +echo-dpo-bundle = { + apiVersion: "echo.dpo.bundle/v1", + class: "declarative", + applicationModel: tstr, + readConsistency: tstr, + operationProfiles: { * tstr => null }, +} + +echo-dpo-cost = { + apiVersion: "echo.dpo.cost/v1", + class: "declarative", + capabilities: { * tstr => echo-cost-capability }, +} +echo-cost-capability = { + effect: tstr, + costTemplate: tstr, + semanticObligation: tstr, +} + +echo-dpo-footprint = { + apiVersion: "echo.dpo.footprint/v1", + class: "declarative", + capabilities: { * tstr => echo-footprint-capability }, +} +echo-footprint-capability = { + effect: tstr, + footprintTemplate: tstr, + semanticObligation: tstr, + writeClass: tstr, +} + +echo-span-ir = { + apiVersion: "echo.span-ir/v1", + class: "declarative", + domain: tstr, + targetProfile: tstr, + capabilities: { * tstr => null }, +} + +echo-dpo-lowerer = { + apiVersion: "echo.dpo.lowerer/v1", + class: "declarative", + acceptedCoreAbi: { * tstr => null }, + outputDomain: tstr, + targetProfile: tstr, + effectImplementations: { * tstr => echo-effect-implementation }, + opticContracts: { * tstr => tstr }, +} + +echo-dpo-obstructions = { + apiVersion: "echo.dpo.obstructions/v1", + class: "declarative", + effectFailures: { * tstr => { authorityClass: authority-class, payloadType: tstr } }, + domainObstructions: { * tstr => { authorityClass: authority-class, payloadSchema: tstr } }, +} + +echo-dpo-verifier = { + apiVersion: "echo.dpo.verifier/v1", + class: "declarative", + targetProfile: tstr, + targetIrDomain: tstr, + capabilities: { * tstr => null }, + operationProfiles: { * tstr => null }, + opticContracts: { * tstr => tstr }, +} + +echo-nonempty-tstr = tstr .regexp "(?s).+" + +generated-artifact = { + apiVersion: "echo.generated-artifact/v1", + profile: resource-ref, + operation: echo-nonempty-tstr, + mediaType: echo-nonempty-tstr, + bytes: bstr, +} + +review-payload = { + apiVersion: "echo.review-payload/v1", + authoritative: false, + subject: resource-ref, + mediaType: echo-nonempty-tstr, + bytes: bstr, +} + +verifier-report = { + apiVersion: "echo.verifier-report/v1", + targetIr: resource-ref, + outcome: "accepted" / "rejected", + diagnosticAbi: resource-ref, + diagnosticBytes: bstr, +} diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/target-profile.echo-dpo.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/target-profile.echo-dpo.cbor new file mode 100644 index 00000000..b3ca7861 Binary files /dev/null and b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/target-profile.echo-dpo.cbor differ diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.conformance-corpus.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.conformance-corpus.cbor new file mode 100644 index 00000000..4464f53c --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.conformance-corpus.cbor @@ -0,0 +1 @@ +ecaseseclasskdeclarativejapiVersionx)echo.edict-provider.conformance-corpus/v1joperationsga.b@1.tlcapabilitiesrecho.dpo@1.replaceosemanticEffectsntarget.replace \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.lawpack-compatibility.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.lawpack-compatibility.cbor new file mode 100644 index 00000000..a703b6fc --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.lawpack-compatibility.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersionx,echo.edict-provider.lawpack-compatibility/v1oacceptedCoreAbimedict.core/v1osemanticEffectsntarget.replacevacceptedTargetProfilesjecho.dpo@1 \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.lawpack-exports.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.lawpack-exports.cbor new file mode 100644 index 00000000..a0887cb0 --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.lawpack-exports.cbor @@ -0,0 +1 @@ +etypesjcoordinateha.b@1.Idjdefinitionx!StringgeffectsiinputTypeha.b@1.Idjcoordinatentarget.replacejoutputTypema.b@1.ReceiptlguardSupportncostObligationstarget.replace.costneffectFailureshrejectedkpayloadTypewtarget.replace.rejectednauthorityClassndomainMappableneffectKindHintgreplacenexecutionClassgruntimentypeParameterssfootprintObligationxtarget.replace.footprinticonstantslobstructionsjcoordinatetdomain.WriteRejectedmpayloadSchemaxdomain.WriteRejected.PayloadnauthorityClassndomainMappablempureFunctionsqoperationProfiles \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.lawpack-target-adapter.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.lawpack-target-adapter.cbor new file mode 100644 index 00000000..5a57d65b --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.lawpack-target-adapter.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersionx-echo.edict-provider.lawpack-target-adapter/v1mtargetProfilejecho.dpo@1ntargetIrDomainoecho.span-ir/v1ueffectImplementationsntarget.replacedkindfnativejcapabilityrecho.dpo@1.replacejwriteClassgreplace \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.lawpack-verifier.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.lawpack-verifier.cbor new file mode 100644 index 00000000..a6d10bd7 --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.lawpack-verifier.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersionx'echo.edict-provider.lawpack-verifier/v1uoperationObstructionsga.b@1.tfeffectntarget.replaceofailureMappingshrejectedtdomain.WriteRejected \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-bundle-profile.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-bundle-profile.cbor new file mode 100644 index 00000000..4c13093f --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-bundle-profile.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersionrecho.dpo.bundle/v1oreadConsistencytapplication-snapshotpapplicationModelfatomicqoperationProfilesxcontinuum.profile.write/v1 \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-cost-algebra.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-cost-algebra.cbor new file mode 100644 index 00000000..2242fed3 --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-cost-algebra.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersionpecho.dpo.cost/v1lcapabilitiesrecho.dpo@1.replacefeffectntarget.replacelcostTemplatestarget.replace.costrsemanticObligationstarget.replace.cost \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-footprint-algebra.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-footprint-algebra.cbor new file mode 100644 index 00000000..3118bbd0 --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-footprint-algebra.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersionuecho.dpo.footprint/v1lcapabilitiesrecho.dpo@1.replacefeffectntarget.replacejwriteClassgreplaceqfootprintTemplatextarget.replace.footprintrsemanticObligationxtarget.replace.footprint \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-intrinsics.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-intrinsics.cbor new file mode 100644 index 00000000..3250617a --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-intrinsics.cbor @@ -0,0 +1 @@ +japiVersionx"edict.target-profile.intrinsics/v1jintrinsicsrecho.dpo@1.replacejeffectKindgreplacejreturnTypema.b@1.ReceiptjwriteClassgreplacelcostTemplatestarget.replace.costlguardSupportmargumentTypesha.b@1.IdneffectFailureshrejectedkpayloadTypewtarget.replace.rejectednauthorityClassndomainMappablenintrinsicClassfeffectntypeParametersqfootprintTemplatextarget.replace.footprintxcanParticipateInAtomicGuard \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-ir.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-ir.cbor new file mode 100644 index 00000000..47cd6d6a --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-ir.cbor @@ -0,0 +1 @@ +eclasskdeclarativefdomainoecho.span-ir/v1japiVersionoecho.span-ir/v1lcapabilitiesrecho.dpo@1.replacemtargetProfilejecho.dpo@1 \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-lowerer-contract.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-lowerer-contract.cbor new file mode 100644 index 00000000..102f1d90 --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-lowerer-contract.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersionsecho.dpo.lowerer/v1loutputDomainoecho.span-ir/v1mtargetProfilejecho.dpo@1nopticContractsxcontinuum.profile.write/v1mreplace-pointoacceptedCoreAbimedict.core/v1ueffectImplementationsntarget.replacedkindfnativejcapabilityrecho.dpo@1.replacejwriteClassgreplace \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-obstruction-taxonomy.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-obstruction-taxonomy.cbor new file mode 100644 index 00000000..a15d8a30 --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-obstruction-taxonomy.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersionxecho.dpo.obstructions/v1neffectFailureswtarget.replace.rejectedkpayloadTypewtarget.replace.rejectednauthorityClassndomainMappablerdomainObstructionstdomain.WriteRejectedmpayloadSchemaxdomain.WriteRejected.PayloadnauthorityClassndomainMappable \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-operation-profiles.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-operation-profiles.cbor new file mode 100644 index 00000000..f4445c6a --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-operation-profiles.cbor @@ -0,0 +1 @@ +hprofilesxcontinuum.profile.write/v1mopticTemplateiopticKindsaffectReintegrationlboundaryKindfaffectmsupportPolicyx&continuum.support.carry-or-obstruct/v1olossDispositionx#continuum.support.reject-on-loss/v1sapertureRequirementcrefxtarget.replace.footprintdkindxabstractFootprintObligationoeffectPredicatex'echo.dpo.operation-mode.replace-only/v1japiVersionx*edict.target-profile.operation-profiles/v1 \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-verifier-contract.cbor b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-verifier-contract.cbor new file mode 100644 index 00000000..6fd4f838 --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/resources/resource.target-verifier-contract.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersiontecho.dpo.verifier/v1lcapabilitiesrecho.dpo@1.replacemtargetProfilejecho.dpo@1nopticContractsxcontinuum.profile.write/v1mreplace-pointntargetIrDomainoecho.span-ir/v1qoperationProfilesxcontinuum.profile.write/v1 \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/provider-manifest.echo.json b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/provider-manifest.echo.json new file mode 100644 index 00000000..b95686ea --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/provider-manifest.echo.json @@ -0,0 +1,338 @@ +{ + "apiVersion": "edict.provider-manifest/v1", + "providerAbi": "edict:target-provider@1.0.0", + "provider": { + "coordinate": "echo.edict-provider@1", + "digest": "sha256:38dd0532d4353f6260e8aaeafb149457b42f8ad94bb107ecbb74bf02f0051a92" + }, + "artifacts": [ + { + "role": "authority-facts.echo-dpo", + "artifactKind": "authorityFacts", + "resource": { + "coordinate": "echo.dpo-authority-facts@1", + "digest": "sha256:5c30871b115e82415980714281e250c2fda281afb4a4fe16c2917f93e86182a4" + }, + "source": { + "kind": "generated", + "semanticSource": { + "coordinate": "echo.semantic-schema@1", + "digest": "sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335" + }, + "generator": { + "coordinate": "echo-wesley-gen.provider-artifact-generator@1", + "digest": "sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e" + } + } + }, + { + "role": "authority-facts.echo-lawpack", + "artifactKind": "authorityFacts", + "resource": { + "coordinate": "echo.dpo-lawpack-authority-facts@1", + "digest": "sha256:37591478584a88d3dc08a620873d803a2d6fcf5dc70767aae3587e5862ee99e9" + }, + "source": { + "kind": "generated", + "semanticSource": { + "coordinate": "echo.semantic-schema@1", + "digest": "sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335" + }, + "generator": { + "coordinate": "echo-wesley-gen.provider-artifact-generator@1", + "digest": "sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e" + } + } + }, + { + "role": "generated-artifact-profile.echo-dpo-registration", + "artifactKind": "generatedArtifactProfile", + "resource": { + "coordinate": "echo.dpo.registration/v1", + "digest": "sha256:3377304d8634681821cd958427e0b8baccc37b7b08bfb342d988a08571eb83ab" + }, + "source": { + "kind": "generated", + "semanticSource": { + "coordinate": "echo.semantic-schema@1", + "digest": "sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335" + }, + "generator": { + "coordinate": "echo-wesley-gen.provider-artifact-generator@1", + "digest": "sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e" + } + } + }, + { + "role": "lawpack.echo-dpo", + "artifactKind": "lawpack", + "resource": { + "coordinate": "echo.dpo-lawpack@1", + "digest": "sha256:47006be4c2b569f13759078c560fe508e158b429cd9d4c5e47d9a7ea86b983b2" + }, + "source": { + "kind": "generated", + "semanticSource": { + "coordinate": "echo.semantic-schema@1", + "digest": "sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335" + }, + "generator": { + "coordinate": "echo-wesley-gen.provider-artifact-generator@1", + "digest": "sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e" + } + } + }, + { + "role": "lowerer.echo-dpo", + "artifactKind": "lowerer", + "resource": { + "coordinate": "echo.dpo.lowerer/component@1", + "digest": "sha256:d58a7838df9d7e65c72063aafad91df97cb21404be556c93e864234f3f846326" + }, + "source": { + "kind": "component", + "component": { + "coordinate": "echo.dpo.lowerer/component@1", + "digest": "sha256:d58a7838df9d7e65c72063aafad91df97cb21404be556c93e864234f3f846326" + } + } + }, + { + "role": "provenance.provider-generation", + "artifactKind": "generationProvenance", + "resource": { + "coordinate": "echo.edict-provider-generation-provenance@1", + "digest": "sha256:5cb085e1a35d01363a629b6933e7e88e96b38a2d841848d68320e9fb0fba8580" + }, + "source": { + "kind": "generated", + "semanticSource": { + "coordinate": "echo.semantic-schema@1", + "digest": "sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335" + }, + "generator": { + "coordinate": "echo-wesley-gen.provider-artifact-generator@1", + "digest": "sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e" + } + } + }, + { + "role": "review.provider-generation", + "artifactKind": "reviewArtifact", + "resource": { + "coordinate": "echo.edict-provider-generation-review@1", + "digest": "sha256:b9b5c7715089ba24c6ce15d9f8674a7ab2ed95225ff8127952eb4d3065d32f6c" + }, + "source": { + "kind": "generated", + "semanticSource": { + "coordinate": "echo.semantic-schema@1", + "digest": "sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335" + }, + "generator": { + "coordinate": "echo-wesley-gen.provider-artifact-generator@1", + "digest": "sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e" + } + } + }, + { + "role": "schema.echo-provider-artifacts", + "artifactKind": "artifactSchema", + "resource": { + "coordinate": "echo.provider-artifacts.cddl@1", + "digest": "sha256:19901cc33bea0699334af3cb4f9889f752652e35b81a2a9c91e7e31a52b803af" + }, + "source": { + "kind": "generated", + "semanticSource": { + "coordinate": "echo.semantic-schema@1", + "digest": "sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335" + }, + "generator": { + "coordinate": "echo-wesley-gen.provider-artifact-generator@1", + "digest": "sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e" + } + } + }, + { + "role": "target-profile.echo-dpo", + "artifactKind": "targetProfile", + "resource": { + "coordinate": "echo.dpo@1", + "digest": "sha256:f41df38156625a05c1ee8bce652ffddf04e71b54fe027eeab9d255d0d8322db0" + }, + "source": { + "kind": "generated", + "semanticSource": { + "coordinate": "echo.semantic-schema@1", + "digest": "sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335" + }, + "generator": { + "coordinate": "echo-wesley-gen.provider-artifact-generator@1", + "digest": "sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e" + } + } + }, + { + "role": "verifier.echo-dpo", + "artifactKind": "verifier", + "resource": { + "coordinate": "echo.dpo.verifier/component@1", + "digest": "sha256:11fcaf291ffacac62800ab3fc198e29a23a591bcd30ca98828fa10a8ad1f1952" + }, + "source": { + "kind": "component", + "component": { + "coordinate": "echo.dpo.verifier/component@1", + "digest": "sha256:11fcaf291ffacac62800ab3fc198e29a23a591bcd30ca98828fa10a8ad1f1952" + } + } + } + ], + "schemaBindings": [ + { + "domain": "echo.dpo-lawpack.adapter.echo-dpo@1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-provider-lawpack-target-adapter" + }, + { + "domain": "echo.dpo-lawpack.compatibility@1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-provider-lawpack-compatibility" + }, + { + "domain": "echo.dpo-lawpack.exports@1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "lawpack-exports" + }, + { + "domain": "echo.dpo-lawpack.verifier@1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-provider-lawpack-verifier" + }, + { + "domain": "echo.dpo.bundle/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-bundle" + }, + { + "domain": "echo.dpo.cost/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-cost" + }, + { + "domain": "echo.dpo.fixtures/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-provider-conformance-corpus" + }, + { + "domain": "echo.dpo.footprint/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-footprint" + }, + { + "domain": "echo.dpo.intrinsics/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "intrinsics-document" + }, + { + "domain": "echo.dpo.lowerer/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-lowerer" + }, + { + "domain": "echo.dpo.obstructions/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-obstructions" + }, + { + "domain": "echo.dpo.operation-profiles/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "operation-profiles-document" + }, + { + "domain": "echo.dpo.verifier/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-verifier" + }, + { + "domain": "echo.generated-artifact-profile/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "generated-artifact-profile" + }, + { + "domain": "echo.generated-artifact/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "generated-artifact" + }, + { + "domain": "echo.review-payload/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "review-payload" + }, + { + "domain": "echo.span-ir/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-span-ir" + }, + { + "domain": "echo.verifier-report/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "verifier-report" + }, + { + "domain": "edict.authority-facts/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "authority-facts" + }, + { + "domain": "edict.core.module/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "core-module" + }, + { + "domain": "edict.lawpack/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "lawpack-manifest" + }, + { + "domain": "edict.lowering-requirements/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "lowering-requirements" + }, + { + "domain": "edict.target-ir.artifact/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "target-ir-artifact" + }, + { + "domain": "edict.target-profile/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "target-profile-manifest" + } + ] +} diff --git a/crates/echo-wesley-gen/assets/v1/repository/Cargo.lock.source b/crates/echo-wesley-gen/assets/v1/repository/Cargo.lock.source new file mode 100644 index 00000000..87729c4c --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/repository/Cargo.lock.source @@ -0,0 +1,2785 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "ambient-authority" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" + +[[package]] +name = "apollo-parser" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "947e21ff51879f8a40d7519dfe619268de2afba4042a8a43878276de3cb910f0" +dependencies = [ + "memchr", + "rowan", + "thiserror 2.0.17", +] + +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "assert_cmd" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c5bcfa8749ac45dd12cb11055aeeb6b27a3895560d60d71e3c23bf979e60514" +dependencies = [ + "anstyle", + "bstr", + "libc", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "blake3" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "bytemuck" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cap-fs-ext" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78e5a3368ae89b7cb68186411452b4b9fac8b41be9c19bf3f47c2d2c8e36e6b" +dependencies = [ + "cap-primitives", + "cap-std", + "io-lifetimes 3.0.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "cap-primitives" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdadbd7c002d3a484b35243669abdae85a0ebaded5a61117169dc3400f9a7ff0" +dependencies = [ + "ambient-authority", + "fs-set-times", + "io-extras", + "io-lifetimes 3.0.1", + "ipnet", + "maybe-owned", + "rustix", + "rustix-linux-procfs", + "windows-sys 0.61.2", + "winx", +] + +[[package]] +name = "cap-std" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7281235d6e96d3544ca18bba9049be92f4190f8d923e3caef1b5f66cfa752608" +dependencies = [ + "cap-primitives", + "io-extras", + "io-lifetimes 3.0.1", + "rustix", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cddl-cat" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0def7310489015a41757b6ae8a0126ad1d91c4a3f77089f862eea7c000638825" +dependencies = [ + "base64", + "ciborium", + "escape8259", + "float-ord", + "hex", + "nom", + "regex", + "serde", + "strum_macros", + "thiserror 1.0.69", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "clap_lex" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" + +[[package]] +name = "clap_mangen" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ea63a92086df93893164221ad4f24142086d535b3a0957b9b9bea2dc86301" +dependencies = [ + "clap", + "roff", +] + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "comfy-table" +version = "7.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958c5d6ecf1f214b4c2bbbbf6ab9523a864bd136dcf71a7e8904799acfe1ad47" +dependencies = [ + "crossterm", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "countme" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +dependencies = [ + "bitflags", + "crossterm_winapi", + "document-features", + "parking_lot", + "rustix", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "echo-cas" +version = "0.1.0" +dependencies = [ + "blake3", + "thiserror 2.0.17", +] + +[[package]] +name = "echo-dind-harness" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "echo-dind-tests", + "echo-wasm-abi", + "hex", + "serde", + "serde_json", + "warp-core", +] + +[[package]] +name = "echo-dind-tests" +version = "0.1.0" +dependencies = [ + "bytes", + "echo-dry-tests", + "echo-wasm-abi", + "warp-core", +] + +[[package]] +name = "echo-dry-tests" +version = "0.1.0" +dependencies = [ + "blake3", + "bytes", + "echo-graph", + "warp-core", +] + +[[package]] +name = "echo-edict-canonical" +version = "0.1.0" +dependencies = [ + "hex", + "sha2", +] + +[[package]] +name = "echo-edict-provider-lowerer" +version = "0.1.0" +dependencies = [ + "echo-edict-canonical", + "hex", + "sha2", + "wit-bindgen 0.58.0", +] + +[[package]] +name = "echo-edict-provider-verifier" +version = "0.1.0" +dependencies = [ + "echo-edict-canonical", + "hex", + "sha2", + "wit-bindgen 0.58.0", +] + +[[package]] +name = "echo-file-aperture" +version = "0.1.0" +dependencies = [ + "blake3", + "thiserror 2.0.17", +] + +[[package]] +name = "echo-graph" +version = "0.1.0" +dependencies = [ + "anyhow", + "blake3", + "ciborium", + "serde", + "warp-core", +] + +[[package]] +name = "echo-registry-api" +version = "0.1.0" + +[[package]] +name = "echo-runtime-schema" +version = "0.1.0" +dependencies = [ + "ciborium", + "serde", +] + +[[package]] +name = "echo-scene-codec" +version = "0.1.0" +dependencies = [ + "echo-scene-port", + "hex", + "minicbor", + "rand 0.8.6", +] + +[[package]] +name = "echo-scene-port" +version = "0.1.0" +dependencies = [ + "hex", + "rand 0.8.6", + "serde_json", + "thiserror 2.0.17", +] + +[[package]] +name = "echo-trace" +version = "0.1.0" + +[[package]] +name = "echo-wasm-abi" +version = "0.1.0" +dependencies = [ + "blake3", + "ciborium", + "half", + "proptest", + "serde", + "serde-value", + "serde_json", + "thiserror 2.0.17", +] + +[[package]] +name = "echo-wesley-gen" +version = "0.1.0" +dependencies = [ + "anyhow", + "blake3", + "cap-fs-ext", + "cap-std", + "cddl-cat", + "ciborium", + "clap", + "echo-edict-canonical", + "hex", + "prettyplease", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "syn 2.0.111", + "warp-core", + "wesley-core", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "escape8259" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6" + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" + +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "float-ord" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "fs-set-times" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e7099f6313ecacbe1256e8ff9d617b75d1bcb16a6fddef94866d225a01a14a" +dependencies = [ + "io-lifetimes 2.0.4", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "foldhash", + "serde", + "serde_core", +] + +[[package]] +name = "hashlink" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824e001ac4f3012dd16a264bec811403a67ca9deb6c102fc5049b32c4574b35f" +dependencies = [ + "hashbrown 0.16.1", +] + +[[package]] +name = "hdrhistogram" +version = "7.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +dependencies = [ + "byteorder", + "num-traits", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "io-extras" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20fd6de4ccfcc187e38bc21cfa543cb5a302cb86a8b114eb7f0bf0dc9f8ac00f" +dependencies = [ + "io-lifetimes 3.0.1", + "windows-sys 0.60.2", +] + +[[package]] +name = "io-lifetimes" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" + +[[package]] +name = "io-lifetimes" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0fb0570afe1fed943c5c3d4102d5358592d8625fda6a0007fdbe65a92fba96" + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "maybe-owned" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "minicbor" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0452a60c1863c1f50b5f77cd295e8d2786849f35883f0b9e18e7e6e1b5691b0" +dependencies = [ + "minicbor-derive", +] + +[[package]] +name = "minicbor-derive" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd2209fff77f705b00c737016a48e73733d7fbccb8b007194db148f03561fb70" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "ninelives" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eb329d63297228258d6747b37dcaaf6d9daa83ea7473b6b5c248ce87e93219d" +dependencies = [ + "async-trait", + "futures", + "rand 0.9.3", + "tokio", + "tower 0.5.3", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "num-conv" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pin-project" +version = "1.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf0d9e68100b3a7989b4901972f265cd542e560a3a8a724e1e20322f4d06ce9" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a990e22f43e84855daf260dded30524ef4a9021cc7541c26540500a50b624389" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "predicates" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe" +dependencies = [ + "anstyle", + "difflib", + "float-cmp", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144" + +[[package]] +name = "predicates-tree" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.111", +] + +[[package]] +name = "proc-macro2" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "num-traits", + "rand 0.9.3", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "pulldown-cmark" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83c41efbf8f90ac44de7f3a868f0867851d261b56291732d0cbf7cceaaeb55a6" +dependencies = [ + "bitflags", + "memchr", + "unicase", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.3", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "roff" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3" + +[[package]] +name = "rowan" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "417a3a9f582e349834051b8a10c8d71ca88da4211e4093528e36b9845f6b5f21" +dependencies = [ + "countme", + "hashbrown 0.14.5", + "rustc-hash 1.1.0", + "text-size", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustix-linux-procfs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc84bf7e9aa16c4f2c758f27412dc9841341e16aa682d9c7ac308fe3ee12056" +dependencies = [ + "once_cell", + "rustix", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "serde_json" +version = "1.0.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da" +dependencies = [ + "indexmap", + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum_macros" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[package]] +name = "tempfile" +version = "3.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + +[[package]] +name = "text-size" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "hdrhistogram", + "indexmap", + "pin-project-lite", + "slab", + "sync_wrapper", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "warp-benches" +version = "0.1.0" +dependencies = [ + "blake3", + "bytes", + "criterion", + "echo-dry-tests", + "rayon", + "rustc-hash 2.1.1", + "warp-core", +] + +[[package]] +name = "warp-cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "assert_cmd", + "bytes", + "clap", + "comfy-table", + "echo-registry-api", + "echo-wasm-abi", + "hex", + "predicates", + "regex", + "serde", + "serde_json", + "tempfile", + "warp-core", +] + +[[package]] +name = "warp-core" +version = "0.1.1" +dependencies = [ + "blake3", + "bytemuck", + "bytes", + "echo-cas", + "echo-dry-tests", + "echo-registry-api", + "echo-runtime-schema", + "echo-wasm-abi", + "hex", + "proptest", + "rustc-hash 2.1.1", + "thiserror 1.0.69", + "warp-math", +] + +[[package]] +name = "warp-geom" +version = "0.1.0" +dependencies = [ + "warp-math", +] + +[[package]] +name = "warp-math" +version = "0.1.0" +dependencies = [ + "echo-wasm-abi", + "libm", + "serde", + "serde-value", +] + +[[package]] +name = "warp-wasm" +version = "0.1.0" +dependencies = [ + "blake3", + "console_error_panic_hook", + "echo-registry-api", + "echo-wasm-abi", + "js-sys", + "serde", + "serde-value", + "warp-core", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen 0.46.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.111", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.251.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a879a421bd17c528b74721b2abf4c62e8f1d1889c2ba8c3c50d02deaf2ce395" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.251.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f998ccc6e012f7b86865eb2a106c8a0422017a1a88977ce01a69f2244be2e57" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.251.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "437970b35b1a85cfde9c74b2398352d8d653f3bd8e3a3db0c063ea8f5b4b36ff" +dependencies = [ + "bitflags", + "hashbrown 0.17.1", + "indexmap", + "semver", + "serde", +] + +[[package]] +name = "web-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wesley-core" +version = "0.3.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca44ae03bac1966eaa6d7660621160f10d3e6979bac26f5929a25a3829c1f4" +dependencies = [ + "apollo-parser", + "async-trait", + "chrono", + "hex", + "indexmap", + "ninelives", + "serde", + "serde_json", + "sha2", + "thiserror 1.0.69", + "tokio", + "tower 0.4.13", + "yaml-rust2", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winx" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d" +dependencies = [ + "bitflags", + "windows-sys 0.59.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "wit-bindgen" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a43552cfa071f246cfd99e5dbb23710dfe7336b3259e09339818483359470749" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4738d1c9a78e97bc7f664bfafd5d8e67d7bb26faa5c41e6d628e8bbdad3ec351" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1130ce1f531bc9f9a75922244aa773bf5e2117fda1ef4a86b9f98d6b8135eb46" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap", + "prettyplease", + "syn 2.0.111", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07296369e4d598e7e79b64eef66f724d83324ea671bcf677d78fc5cf92604ae5" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.111", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.251.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a5e60173c413659c689f0581b0cf5d1a2404077568f9ffdce748a9eb2fc913" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.251.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e960732e824fab95099971a09e638979347c94ca48568d3c854c945729196947" +dependencies = [ + "anyhow", + "hashbrown 0.17.1", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "xtask" +version = "0.1.0" +dependencies = [ + "anyhow", + "blake3", + "bytes", + "clap", + "clap_mangen", + "hex", + "pulldown-cmark", + "same-file", + "serde", + "serde_json", + "sha2", + "time", + "warp-cli", + "warp-core", + "wasm-encoder", + "wasmparser", + "wit-component", + "wit-parser", +] + +[[package]] +name = "yaml-rust2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631a50d867fafb7093e709d75aaee9e0e0d5deb934021fcea25ac2fe09edc51e" +dependencies = [ + "arraydeque", + "hashlink", +] + +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "zmij" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4a4e8e9dc5c62d159f04fcdbe07f4c3fb710415aab4754bf11505501e3251d" diff --git a/crates/echo-wesley-gen/assets/v1/repository/Cargo.toml.source b/crates/echo-wesley-gen/assets/v1/repository/Cargo.toml.source new file mode 100644 index 00000000..a763c35c --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/repository/Cargo.toml.source @@ -0,0 +1,118 @@ +# SPDX-License-Identifier: Apache-2.0 +# © James Ross Ω FLYING•ROBOTS +[workspace] +members = [ + "crates/echo-runtime-schema", + "crates/warp-math", + "crates/warp-core", + + "crates/warp-wasm", + "crates/warp-cli", + "crates/warp-geom", + "crates/warp-benches", + "crates/echo-file-aperture", + "crates/echo-graph", + "crates/echo-dind-harness", + "crates/echo-dind-tests", + "crates/echo-wasm-abi", + "crates/echo-registry-api", + "crates/echo-edict-canonical", + "crates/echo-edict-provider-lowerer", + "crates/echo-edict-provider-verifier", + "crates/echo-wesley-gen", + "crates/echo-dry-tests", + "crates/echo-cas", + "crates/echo-scene-port", + "crates/echo-scene-codec", + "xtask", + "crates/echo-trace", +] +resolver = "2" + +[workspace.package] +license = "Apache-2.0" +repository = "https://github.com/flyingrobots/echo" +rust-version = "1.90.0" + +[workspace.dependencies] +echo-cas = { version = "0.1.0", path = "crates/echo-cas" } +echo-dind-tests = { version = "0.1.0", path = "crates/echo-dind-tests" } +echo-dry-tests = { version = "0.1.0", path = "crates/echo-dry-tests" } +echo-file-aperture = { version = "0.1.0", path = "crates/echo-file-aperture" } +echo-graph = { version = "0.1.0", path = "crates/echo-graph" } +echo-runtime-schema = { version = "0.1.0", path = "crates/echo-runtime-schema", default-features = false } +echo-registry-api = { version = "0.1.0", path = "crates/echo-registry-api" } +echo-edict-canonical = { version = "0.1.0", path = "crates/echo-edict-canonical" } +echo-scene-codec = { version = "0.1.0", path = "crates/echo-scene-codec" } +echo-scene-port = { version = "0.1.0", path = "crates/echo-scene-port" } +echo-wasm-abi = { version = "0.1.0", path = "crates/echo-wasm-abi" } +warp-math = { version = "0.1.0", path = "crates/warp-math" } +warp-core = { version = "0.1.1", path = "crates/warp-core" } + +# ── Workspace-wide lint policy ────────────────────────────────────── +# Maximum strictness. Crates opt in via `[lints] workspace = true`. +# Per-crate overrides go in that crate's `[lints]` table. +[workspace.lints.rust] +missing_docs = "deny" +rust_2018_idioms = "deny" +unused_must_use = "deny" +# deny (not forbid) so WASM/FFI crates can #![allow(unsafe_code)] locally +unsafe_code = "deny" + +[workspace.lints.clippy] +# Lint groups — all promoted to deny +all = { level = "deny", priority = -1 } +pedantic = { level = "deny", priority = -1 } +nursery = { level = "deny", priority = -1 } +cargo = { level = "deny", priority = -1 } +# Anti-pattern lints — explicit deny +unwrap_used = "deny" +expect_used = "deny" +panic = "deny" +todo = "deny" +unimplemented = "deny" +dbg_macro = "deny" +print_stdout = "deny" +print_stderr = "deny" +# Pedantic overrides — allow where too noisy for this codebase +must_use_candidate = "allow" +return_self_not_must_use = "allow" +unreadable_literal = "allow" +missing_const_for_fn = "allow" +suboptimal_flops = "allow" +redundant_pub_crate = "allow" +many_single_char_names = "allow" +module_name_repetitions = "allow" +use_self = "allow" +# Nursery overrides — too noisy or false-positive-prone +cognitive_complexity = "allow" +option_if_let_else = "allow" +significant_drop_tightening = "allow" +# Pedantic overrides — low signal-to-noise for this codebase +doc_markdown = "allow" +too_many_lines = "allow" +struct_excessive_bools = "allow" +too_long_first_doc_paragraph = "allow" +missing_errors_doc = "allow" +missing_panics_doc = "allow" +similar_names = "allow" +trivially_copy_pass_by_ref = "allow" +needless_collect = "allow" +manual_let_else = "allow" +needless_pass_by_value = "allow" +# cargo group overrides +multiple_crate_versions = "allow" + +[profile.release] +opt-level = "s" +lto = true +codegen-units = 1 +strip = true + +[profile.bench] +# Derive from release but optimize for throughput and faster builds +opt-level = 3 +lto = false +codegen-units = 16 +debug = true +strip = false diff --git a/crates/echo-wesley-gen/assets/v1/repository/crates/echo-edict-canonical/Cargo.toml.source b/crates/echo-wesley-gen/assets/v1/repository/crates/echo-edict-canonical/Cargo.toml.source new file mode 100644 index 00000000..922f345e --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/repository/crates/echo-edict-canonical/Cargo.toml.source @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: Apache-2.0 +# © James Ross Ω FLYING•ROBOTS +[package] +name = "echo-edict-canonical" +version = "0.1.0" +edition = "2021" +rust-version = "1.90.0" +description = "Pure Echo implementation of Edict canonical CBOR and artifact digest framing." +license = "Apache-2.0" +repository = "https://github.com/flyingrobots/echo" +readme = "README.md" +keywords = ["echo", "edict", "cbor", "canonical", "deterministic"] +categories = ["encoding", "development-tools"] + +[dependencies] +hex = "0.4" +sha2 = "0.10" + +[lints] +workspace = true diff --git a/crates/echo-wesley-gen/assets/v1/repository/crates/echo-edict-canonical/src/lib.rs.source b/crates/echo-wesley-gen/assets/v1/repository/crates/echo-edict-canonical/src/lib.rs.source new file mode 100644 index 00000000..53815795 --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/repository/crates/echo-edict-canonical/src/lib.rs.source @@ -0,0 +1,594 @@ +// SPDX-License-Identifier: Apache-2.0 +// © James Ross Ω FLYING•ROBOTS +//! Exact `edict.canonical-cbor/v1` values, bytes, and artifact digests. +//! +//! This crate is a pure compatibility boundary for the Edict-owned provider +//! contract. It deliberately does not reuse Echo's WASM ABI codec: that codec +//! admits a different value model. The implementation accepts only the +//! definite-length Edict v1 subset, enforces the published nesting bound, and +//! validates decoded bytes by exact canonical re-encoding. + +use std::collections::BTreeSet; +use std::fmt; +use std::str; + +use sha2::{Digest, Sha256}; + +/// Coordinate of the canonical encoding profile implemented by this module. +pub const EDICT_CANONICAL_CBOR_V1: &str = "edict.canonical-cbor/v1"; + +/// Domain marker at the head of every Edict v1 artifact digest frame. +pub const EDICT_DIGEST_FRAME_V1: &str = "edict.digest/v1"; + +/// Maximum child nesting depth accepted by the Edict v1 encoder and decoder. +/// +/// The root value is at depth zero. A scalar wrapped in exactly 128 containers +/// is accepted; one additional container is rejected. +pub const MAX_CANONICAL_NESTING_DEPTH_V1: usize = 128; + +/// Maximum number of value nodes materialized by one canonical decode. +/// +/// The root counts as one node. Array members, map keys, and map values each +/// count separately. This is an Echo host resource bound rather than part of +/// the Edict canonical byte identity; it prevents compact collection headers +/// from amplifying into unbounded host allocations before admission. +pub const MAX_CANONICAL_DECODE_NODES_V1: usize = 65_536; + +/// Stable failure categories for Edict canonical values and byte streams. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum CanonicalValueErrorKind { + /// A value or collection length is outside the supported value model. + UnsupportedValue, + /// An integer is outside the CBOR major-zero or major-one `u64` range. + InvalidInteger, + /// The byte stream ended before one complete value was decoded. + UnexpectedEof, + /// Bytes remained after one complete canonical value. + TrailingData, + /// The byte stream used an unsupported CBOR major or additional-info form. + UnsupportedCbor, + /// The decoded value did not re-encode to the exact supplied bytes. + NonCanonical, + /// The value exceeded the published 128-level nesting bound. + NestingLimitExceeded, + /// A map contained two keys with identical canonical encodings. + DuplicateMapKey, + /// A CBOR text string was not valid UTF-8. + InvalidUtf8, +} + +impl CanonicalValueErrorKind { + const fn label(self) -> &'static str { + match self { + Self::UnsupportedValue => "unsupported-value", + Self::InvalidInteger => "invalid-integer", + Self::UnexpectedEof => "unexpected-eof", + Self::TrailingData => "trailing-data", + Self::UnsupportedCbor => "unsupported-cbor", + Self::NonCanonical => "noncanonical", + Self::NestingLimitExceeded => "nesting-limit-exceeded", + Self::DuplicateMapKey => "duplicate-map-key", + Self::InvalidUtf8 => "invalid-utf8", + } + } +} + +/// Structured canonical-value failure with a stable kind and diagnostic detail. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct CanonicalValueError { + kind: CanonicalValueErrorKind, + detail: String, +} + +impl CanonicalValueError { + /// Returns the stable machine-readable failure category. + #[must_use] + pub const fn kind(&self) -> CanonicalValueErrorKind { + self.kind + } + + /// Returns deterministic diagnostic detail for the failed boundary. + #[must_use] + pub fn detail(&self) -> &str { + &self.detail + } + + fn new(kind: CanonicalValueErrorKind, detail: impl Into) -> Self { + Self { + kind, + detail: detail.into(), + } + } +} + +impl fmt::Display for CanonicalValueError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "{}: {}", self.kind.label(), self.detail) + } +} + +impl std::error::Error for CanonicalValueError {} + +/// Value tree admitted by `edict.canonical-cbor/v1`. +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum CanonicalValueV1 { + /// CBOR null. + Null, + /// CBOR false or true. + Bool(bool), + /// CBOR major-zero or major-one integer stored in a convenient host type. + Integer(i128), + /// Definite-length byte string. + Bytes(Vec), + /// Definite-length UTF-8 text string, without Unicode normalization. + Text(String), + /// Definite-length ordered array. + Array(Vec), + /// Definite-length map sorted during encoding by canonical key bytes. + Map(Vec<(CanonicalValueV1, CanonicalValueV1)>), +} + +/// Encodes one value using the exact `edict.canonical-cbor/v1` byte contract. +/// +/// # Errors +/// +/// Returns a stable failure when an integer or collection cannot be represented, +/// a map repeats a canonical key, or the value exceeds the nesting bound. +pub fn encode_canonical_cbor_v1(value: &CanonicalValueV1) -> Result, CanonicalValueError> { + let mut output = Vec::new(); + encode_value(value, &mut output, 0)?; + Ok(output) +} + +/// Decodes and authenticates one `edict.canonical-cbor/v1` value. +/// +/// Decoding is followed by exact canonical re-encoding. This rejects +/// non-minimal integers and lengths, unsorted maps, and any other supported +/// value whose supplied bytes differ from the unique Edict encoding. +/// Successful decoding authenticates the encoding only: callers must still +/// validate the value against its owning CDDL root, and Echo must separately +/// admit any runtime artifact or authority. +/// +/// # Errors +/// +/// Returns a stable failure for malformed, unsupported, trailing, +/// noncanonical, duplicate-key, invalid-UTF-8, or over-nested input. +pub fn decode_canonical_cbor_v1(bytes: &[u8]) -> Result { + let mut decoder = Decoder::new(bytes); + let value = decoder.value(0)?; + if decoder.remaining() != 0 { + return Err(CanonicalValueError::new( + CanonicalValueErrorKind::TrailingData, + "canonical CBOR stream contains bytes after the first value", + )); + } + if encode_canonical_cbor_v1(&value)? != bytes { + return Err(CanonicalValueError::new( + CanonicalValueErrorKind::NonCanonical, + "decoded value does not re-encode to the supplied bytes", + )); + } + Ok(value) +} + +/// Computes an Edict v1 domain-framed SHA-256 digest for a canonical value. +/// +/// The returned review rendering is `sha256:<64 lowercase hex>`. The preimage +/// is canonical CBOR for `["edict.digest/v1", domain, value]`. Each tuple member +/// is encoded at its own root so digest framing does not consume the artifact's +/// 128-level nesting budget. +/// A digest binds bytes and a caller-selected domain; it does not prove the +/// owning CDDL root or grant Echo runtime authority. +/// +/// # Errors +/// +/// Returns a stable failure for an empty domain or an unencodable value. +pub fn digest_canonical_value_v1( + domain: &str, + value: &CanonicalValueV1, +) -> Result { + if domain.is_empty() { + return Err(CanonicalValueError::new( + CanonicalValueErrorKind::UnsupportedValue, + "canonical artifact digest domain is empty", + )); + } + + let mut preimage = vec![0x83]; + preimage.extend(encode_canonical_cbor_v1(&CanonicalValueV1::Text( + EDICT_DIGEST_FRAME_V1.to_owned(), + ))?); + preimage.extend(encode_canonical_cbor_v1(&CanonicalValueV1::Text( + domain.to_owned(), + ))?); + preimage.extend(encode_canonical_cbor_v1(value)?); + + Ok(format!("sha256:{}", hex::encode(Sha256::digest(preimage)))) +} + +fn encode_value( + value: &CanonicalValueV1, + output: &mut Vec, + depth: usize, +) -> Result<(), CanonicalValueError> { + check_depth(depth)?; + match value { + CanonicalValueV1::Null => output.push(0xf6), + CanonicalValueV1::Bool(false) => output.push(0xf4), + CanonicalValueV1::Bool(true) => output.push(0xf5), + CanonicalValueV1::Integer(value) => encode_integer(*value, output)?, + CanonicalValueV1::Bytes(bytes) => { + encode_type_value(2, usize_to_u64(bytes.len())?, output); + output.extend_from_slice(bytes); + } + CanonicalValueV1::Text(text) => { + encode_type_value(3, usize_to_u64(text.len())?, output); + output.extend_from_slice(text.as_bytes()); + } + CanonicalValueV1::Array(values) => { + check_container_depth(depth)?; + encode_type_value(4, usize_to_u64(values.len())?, output); + for value in values { + encode_value(value, output, depth + 1)?; + } + } + CanonicalValueV1::Map(entries) => { + check_container_depth(depth)?; + let mut encoded_entries = Vec::with_capacity(entries.len()); + let mut encoded_keys = BTreeSet::new(); + for (key, value) in entries { + let mut key_bytes = Vec::new(); + encode_value(key, &mut key_bytes, depth + 1)?; + if !encoded_keys.insert(key_bytes.clone()) { + return Err(CanonicalValueError::new( + CanonicalValueErrorKind::DuplicateMapKey, + "canonical CBOR map contains duplicate keys", + )); + } + encoded_entries.push((key_bytes, value)); + } + encoded_entries.sort_by(|(left, _), (right, _)| left.cmp(right)); + encode_type_value(5, usize_to_u64(encoded_entries.len())?, output); + for (key_bytes, value) in encoded_entries { + output.extend_from_slice(&key_bytes); + encode_value(value, output, depth + 1)?; + } + } + } + Ok(()) +} + +fn check_depth(depth: usize) -> Result<(), CanonicalValueError> { + if depth > MAX_CANONICAL_NESTING_DEPTH_V1 { + return Err(CanonicalValueError::new( + CanonicalValueErrorKind::NestingLimitExceeded, + format!( + "canonical value nesting exceeds maximum depth {MAX_CANONICAL_NESTING_DEPTH_V1}" + ), + )); + } + Ok(()) +} + +fn check_container_depth(depth: usize) -> Result<(), CanonicalValueError> { + if depth >= MAX_CANONICAL_NESTING_DEPTH_V1 { + return Err(CanonicalValueError::new( + CanonicalValueErrorKind::NestingLimitExceeded, + format!( + "canonical container nesting exceeds maximum depth {MAX_CANONICAL_NESTING_DEPTH_V1}" + ), + )); + } + Ok(()) +} + +fn encode_integer(value: i128, output: &mut Vec) -> Result<(), CanonicalValueError> { + if value >= 0 { + let value = u64::try_from(value).map_err(|_| { + CanonicalValueError::new( + CanonicalValueErrorKind::InvalidInteger, + "positive integer exceeds the canonical CBOR uint range", + ) + })?; + encode_type_value(0, value, output); + return Ok(()); + } + + let magnitude = (-1i128).checked_sub(value).ok_or_else(|| { + CanonicalValueError::new( + CanonicalValueErrorKind::InvalidInteger, + "negative integer cannot be converted to the canonical CBOR range", + ) + })?; + let magnitude = u64::try_from(magnitude).map_err(|_| { + CanonicalValueError::new( + CanonicalValueErrorKind::InvalidInteger, + "negative integer exceeds the canonical CBOR negative range", + ) + })?; + encode_type_value(1, magnitude, output); + Ok(()) +} + +fn encode_type_value(major: u8, value: u64, output: &mut Vec) { + let prefix = major << 5; + let bytes = value.to_be_bytes(); + match value { + 0..=23 => output.push(prefix | bytes[7]), + 24..=0xff => { + output.push(prefix | 0x18); + output.push(bytes[7]); + } + 0x100..=0xffff => { + output.push(prefix | 0x19); + output.extend_from_slice(&bytes[6..]); + } + 0x1_0000..=0xffff_ffff => { + output.push(prefix | 0x1a); + output.extend_from_slice(&bytes[4..]); + } + _ => { + output.push(prefix | 0x1b); + output.extend_from_slice(&bytes); + } + } +} + +fn usize_to_u64(value: usize) -> Result { + u64::try_from(value).map_err(|_| { + CanonicalValueError::new( + CanonicalValueErrorKind::UnsupportedValue, + "canonical collection length does not fit the CBOR uint range", + ) + }) +} + +fn checked_collection_length( + declared: u64, + remaining: u64, +) -> Result +where + HostLength: TryFrom, +{ + if declared > remaining { + return Err(CanonicalValueError::new( + CanonicalValueErrorKind::UnexpectedEof, + "canonical CBOR declared length exceeds the remaining bytes", + )); + } + + HostLength::try_from(declared).map_err(|_| { + CanonicalValueError::new( + CanonicalValueErrorKind::UnsupportedCbor, + "canonical CBOR collection length does not fit usize", + ) + }) +} + +struct Decoder<'a> { + bytes: &'a [u8], + position: usize, + remaining_nodes: usize, + remaining_reserved_nodes: usize, +} + +impl<'a> Decoder<'a> { + const fn new(bytes: &'a [u8]) -> Self { + Self::with_node_budget(bytes, MAX_CANONICAL_DECODE_NODES_V1) + } + + const fn with_node_budget(bytes: &'a [u8], node_budget: usize) -> Self { + Self { + bytes, + position: 0, + remaining_nodes: node_budget, + remaining_reserved_nodes: node_budget, + } + } + + const fn remaining(&self) -> usize { + self.bytes.len() - self.position + } + + fn value(&mut self, depth: usize) -> Result { + self.charge_nodes(1)?; + check_depth(depth)?; + let initial = self.byte()?; + let major = initial >> 5; + let additional = initial & 0x1f; + match major { + 0 => Ok(CanonicalValueV1::Integer(i128::from( + self.argument(additional)?, + ))), + 1 => Ok(CanonicalValueV1::Integer( + -1 - i128::from(self.argument(additional)?), + )), + 2 => { + let length = self.length(additional)?; + Ok(CanonicalValueV1::Bytes(self.take(length)?.to_vec())) + } + 3 => { + let length = self.length(additional)?; + let bytes = self.take(length)?; + let text = str::from_utf8(bytes).map_err(|_| { + CanonicalValueError::new( + CanonicalValueErrorKind::InvalidUtf8, + "canonical CBOR text string is not valid UTF-8", + ) + })?; + Ok(CanonicalValueV1::Text(text.to_owned())) + } + 4 => { + check_container_depth(depth)?; + let length = self.length(additional)?; + self.ensure_nodes_available(length)?; + self.reserve_nodes(length)?; + let mut values = Vec::with_capacity(length); + for _ in 0..length { + values.push(self.value(depth + 1)?); + } + Ok(CanonicalValueV1::Array(values)) + } + 5 => { + check_container_depth(depth)?; + let length = self.length(additional)?; + let child_nodes = length.checked_mul(2).ok_or_else(node_budget_error)?; + self.ensure_nodes_available(child_nodes)?; + self.reserve_nodes(child_nodes)?; + let mut entries = Vec::with_capacity(length); + let mut encoded_keys = BTreeSet::new(); + for _ in 0..length { + let key = self.value(depth + 1)?; + let mut key_bytes = Vec::new(); + encode_value(&key, &mut key_bytes, depth + 1)?; + if !encoded_keys.insert(key_bytes) { + return Err(CanonicalValueError::new( + CanonicalValueErrorKind::DuplicateMapKey, + "canonical CBOR map contains duplicate keys", + )); + } + let value = self.value(depth + 1)?; + entries.push((key, value)); + } + Ok(CanonicalValueV1::Map(entries)) + } + 7 => match additional { + 20 => Ok(CanonicalValueV1::Bool(false)), + 21 => Ok(CanonicalValueV1::Bool(true)), + 22 => Ok(CanonicalValueV1::Null), + _ => Err(CanonicalValueError::new( + CanonicalValueErrorKind::UnsupportedCbor, + "canonical CBOR simple value is unsupported", + )), + }, + _ => Err(CanonicalValueError::new( + CanonicalValueErrorKind::UnsupportedCbor, + "canonical CBOR major type is unsupported", + )), + } + } + + fn argument(&mut self, additional: u8) -> Result { + match additional { + 0..=23 => Ok(u64::from(additional)), + 24 => Ok(u64::from(self.byte()?)), + 25 => Ok(u64::from(u16::from_be_bytes(self.take_array::<2>()?))), + 26 => Ok(u64::from(u32::from_be_bytes(self.take_array::<4>()?))), + 27 => Ok(u64::from_be_bytes(self.take_array::<8>()?)), + _ => Err(CanonicalValueError::new( + CanonicalValueErrorKind::UnsupportedCbor, + "indefinite or reserved canonical CBOR length is unsupported", + )), + } + } + + fn length(&mut self, additional: u8) -> Result { + let declared = self.argument(additional)?; + let remaining = u64::try_from(self.remaining()).map_err(|_| { + CanonicalValueError::new( + CanonicalValueErrorKind::UnsupportedCbor, + "remaining canonical CBOR input does not fit the CBOR uint range", + ) + })?; + + checked_collection_length::(declared, remaining) + } + + fn ensure_nodes_available(&self, required: usize) -> Result<(), CanonicalValueError> { + if required > self.remaining_nodes { + return Err(node_budget_error()); + } + Ok(()) + } + + fn charge_nodes(&mut self, count: usize) -> Result<(), CanonicalValueError> { + self.remaining_nodes = self + .remaining_nodes + .checked_sub(count) + .ok_or_else(node_budget_error)?; + Ok(()) + } + + fn reserve_nodes(&mut self, count: usize) -> Result<(), CanonicalValueError> { + self.remaining_reserved_nodes = self + .remaining_reserved_nodes + .checked_sub(count) + .ok_or_else(node_budget_error)?; + Ok(()) + } + + fn byte(&mut self) -> Result { + let Some(value) = self.bytes.get(self.position).copied() else { + return Err(CanonicalValueError::new( + CanonicalValueErrorKind::UnexpectedEof, + "canonical CBOR expected another byte", + )); + }; + self.position += 1; + Ok(value) + } + + fn take(&mut self, length: usize) -> Result<&'a [u8], CanonicalValueError> { + let end = self.position.checked_add(length).ok_or_else(|| { + CanonicalValueError::new( + CanonicalValueErrorKind::UnexpectedEof, + "canonical CBOR length overflowed the input position", + ) + })?; + let Some(bytes) = self.bytes.get(self.position..end) else { + return Err(CanonicalValueError::new( + CanonicalValueErrorKind::UnexpectedEof, + "canonical CBOR value extends past the input", + )); + }; + self.position = end; + Ok(bytes) + } + + fn take_array(&mut self) -> Result<[u8; LENGTH], CanonicalValueError> { + let mut output = [0u8; LENGTH]; + output.copy_from_slice(self.take(LENGTH)?); + Ok(output) + } +} + +fn node_budget_error() -> CanonicalValueError { + CanonicalValueError::new( + CanonicalValueErrorKind::UnsupportedValue, + "canonical CBOR decoded node budget exceeded", + ) +} + +#[cfg(test)] +mod tests { + use super::{checked_collection_length, CanonicalValueError, CanonicalValueErrorKind, Decoder}; + + #[test] + fn declared_length_bounds_precede_host_width_conversion() { + assert_eq!( + checked_collection_length::(u64::MAX, u64::from(u32::MAX)), + Err(CanonicalValueError::new( + CanonicalValueErrorKind::UnexpectedEof, + "canonical CBOR declared length exceeds the remaining bytes" + )) + ); + } + + #[test] + fn nested_container_reservations_are_cumulatively_bounded() { + let bytes = [0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0xf6]; + let mut decoder = Decoder::with_node_budget(&bytes, 8); + + assert_eq!( + decoder.value(0), + Err(CanonicalValueError::new( + CanonicalValueErrorKind::UnsupportedValue, + "canonical CBOR decoded node budget exceeded" + )) + ); + assert_eq!( + decoder.position, 2, + "the second container must be refused before its capacity is reserved" + ); + } +} diff --git a/crates/echo-wesley-gen/assets/v1/repository/crates/echo-wesley-gen/Cargo.toml.source b/crates/echo-wesley-gen/assets/v1/repository/crates/echo-wesley-gen/Cargo.toml.source new file mode 100644 index 00000000..a72cd6dd --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/repository/crates/echo-wesley-gen/Cargo.toml.source @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: Apache-2.0 +# © James Ross Ω FLYING•ROBOTS +[package] +name = "echo-wesley-gen" +version = "0.1.0" +default-run = "echo-wesley-gen" +edition = "2021" +rust-version = "1.90.0" +description = "Code generator for Echo: consumes Wesley IR (JSON) and emits Rust artifacts via syn/quote." +license = "Apache-2.0" +repository = "https://github.com/flyingrobots/echo" +readme = "README.md" +keywords = ["warp", "echo", "codegen", "wesley", "wasm"] +categories = ["development-tools", "command-line-utilities", "encoding", "wasm"] + +[dependencies] +anyhow = "1.0" +blake3 = "1.5" +cap-fs-ext = "=4.0.2" +cap-std = "=4.0.2" +cddl-cat = { version = "0.7.1", default-features = false, features = ["ciborium"] } +ciborium = "0.2.2" +clap = { version = "4.4", features = ["derive"] } +echo-edict-canonical = { workspace = true } +hex = "0.4" +proc-macro2 = "1.0" +quote = "1.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +sha2 = "0.10" +syn = { version = "2.0", features = ["full", "extra-traits"] } +prettyplease = "0.2" +warp-core = { workspace = true } +wesley-core = "=0.3.0-alpha.1" + +[lints] +workspace = true diff --git a/crates/echo-wesley-gen/assets/v1/repository/rust-toolchain.toml.source b/crates/echo-wesley-gen/assets/v1/repository/rust-toolchain.toml.source new file mode 100644 index 00000000..b7348846 --- /dev/null +++ b/crates/echo-wesley-gen/assets/v1/repository/rust-toolchain.toml.source @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 +# © James Ross Ω FLYING•ROBOTS +[toolchain] +channel = "1.90.0" +components = ["rustfmt", "clippy"] diff --git a/crates/echo-wesley-gen/src/bin/echo-edict-provider-artifacts.rs b/crates/echo-wesley-gen/src/bin/echo-edict-provider-artifacts.rs index 1bfd0a80..6fe9964e 100644 --- a/crates/echo-wesley-gen/src/bin/echo-edict-provider-artifacts.rs +++ b/crates/echo-wesley-gen/src/bin/echo-edict-provider-artifacts.rs @@ -3,43 +3,28 @@ #![allow(clippy::print_stdout, clippy::print_stderr)] //! Generates or checks Echo's deterministic Edict provider artifact corpus. -use anyhow::{anyhow, bail, Context, Result}; -use cap_fs_ext::{FollowSymlinks, OpenOptionsFollowExt, OpenOptionsMaybeDirExt}; -use cap_std::{ - ambient_authority, - fs::{Dir, File, OpenOptions}, -}; +use anyhow::{bail, Result}; use clap::Parser; use echo_wesley_gen::provider_artifacts::generate_provider_primary_artifacts_v1; use echo_wesley_gen::provider_contract_pack::admit_provider_contract_pack_v1; use echo_wesley_gen::provider_corpus::{ checked_provider_generator_source_bundle_v1, diff_provider_artifact_corpus_v1, - render_provider_artifact_corpus_v1, ProviderArtifactCorpusFileV1, + render_provider_artifact_corpus_v1, }; +use echo_wesley_gen::provider_corpus_fs::{read_actual_corpus, write_corpus}; use echo_wesley_gen::provider_generation::build_provider_generation_input_v1; use echo_wesley_gen::provider_provenance::generate_provider_generation_provenance_v1; use echo_wesley_gen::provider_review::generate_provider_generation_review_v1; -use std::collections::{BTreeMap, BTreeSet}; -use std::ffi::{OsStr, OsString}; -use std::io::{Read, Write}; -use std::path::{Path, PathBuf}; -use std::sync::atomic::{AtomicU64, Ordering}; +use std::path::PathBuf; const SOURCE: &[u8] = - include_bytes!("../../../../schemas/edict-provider/echo-provider-semantics-v1.json"); + include_bytes!("../../assets/v1/edict-provider/echo-provider-semantics-v1.json"); const SETTINGS: &[u8] = - include_bytes!("../../../../schemas/edict-provider/generation-settings-v1.json"); + include_bytes!("../../assets/v1/edict-provider/generation-settings-v1.json"); const CONTRACT_CDDL: &[u8] = - include_bytes!("../../../../schemas/edict-provider/contracts/v1/edict-provider-contracts.cddl"); + include_bytes!("../../assets/v1/edict-provider/contracts/v1/edict-provider-contracts.cddl"); const CONTRACT_MANIFEST: &[u8] = - include_bytes!("../../../../schemas/edict-provider/contracts/v1/manifest.json"); -const NON_REGULAR_ENTRY_BYTES: &[u8] = b"echo.provider-artifact-corpus.non-regular/v1"; -const OVERSIZED_ENTRY_BYTES: &[u8] = b"echo.provider-artifact-corpus.oversized/v1"; -const MAX_ACTUAL_CORPUS_FILE_BYTES: usize = 32 * 1024 * 1024; -const MAX_ACTUAL_CORPUS_FILE_BYTES_U64: u64 = 32 * 1024 * 1024; -const MAX_TEMP_FILE_ATTEMPTS: usize = 1_024; - -static NEXT_TEMP_FILE: AtomicU64 = AtomicU64::new(0); + include_bytes!("../../assets/v1/edict-provider/contracts/v1/manifest.json"); #[derive(Parser)] #[command( @@ -93,534 +78,3 @@ fn main() -> Result<()> { ); Ok(()) } - -fn read_actual_corpus( - root: &Path, - expected: &[ProviderArtifactCorpusFileV1], -) -> Result> { - let Some(root_directory) = open_corpus_root(root, false)? else { - return Ok(Vec::new()); - }; - - read_actual_corpus_from_directory(&root_directory, expected) -} - -fn read_actual_corpus_from_directory( - root: &Dir, - expected: &[ProviderArtifactCorpusFileV1], -) -> Result> { - let expected_directories = expected_directories(expected); - let mut actual = Vec::new(); - read_actual_directory(root, "", &expected_directories, &mut actual)?; - Ok(actual) -} - -fn expected_directories(expected: &[ProviderArtifactCorpusFileV1]) -> BTreeSet { - let mut directories = BTreeSet::new(); - for file in expected { - let components = file.relative_path().split('/').collect::>(); - for end in 1..components.len() { - directories.insert(components[..end].join("/")); - } - } - directories -} - -fn read_actual_directory( - directory: &Dir, - relative_directory: &str, - expected_directories: &BTreeSet, - actual: &mut Vec, -) -> Result<()> { - let mut entries = directory - .entries() - .with_context(|| format!("failed to read corpus directory {relative_directory}"))? - .collect::>>() - .with_context(|| format!("failed to enumerate corpus directory {relative_directory}"))?; - entries.sort_by_key(cap_std::fs::DirEntry::file_name); - - for entry in entries { - let raw_file_name = entry.file_name(); - let diagnostic_path = Path::new(relative_directory).join(&raw_file_name); - let file_name = utf8_file_name(raw_file_name.clone(), &diagnostic_path)?; - let relative_path = if relative_directory.is_empty() { - file_name - } else { - format!("{relative_directory}/{file_name}") - }; - let file_type = entry - .file_type() - .with_context(|| format!("failed to inspect corpus entry {relative_path}"))?; - - if file_type.is_file() { - let file = open_file_nofollow(directory, &raw_file_name) - .with_context(|| format!("failed to open corpus file {relative_path}"))?; - let metadata = file - .metadata() - .with_context(|| format!("failed to inspect corpus file {relative_path}"))?; - if !metadata.is_file() { - actual.push(ProviderArtifactCorpusFileV1::new( - relative_path, - NON_REGULAR_ENTRY_BYTES, - )?); - } else if metadata.len() > MAX_ACTUAL_CORPUS_FILE_BYTES_U64 { - actual.push(ProviderArtifactCorpusFileV1::new( - relative_path, - OVERSIZED_ENTRY_BYTES, - )?); - } else { - let mut bytes = Vec::new(); - file.take(MAX_ACTUAL_CORPUS_FILE_BYTES_U64 + 1) - .read_to_end(&mut bytes) - .with_context(|| format!("failed to read corpus file {relative_path}"))?; - let exact_bytes = if bytes.len() > MAX_ACTUAL_CORPUS_FILE_BYTES { - OVERSIZED_ENTRY_BYTES - } else { - &bytes - }; - actual.push(ProviderArtifactCorpusFileV1::new( - relative_path, - exact_bytes, - )?); - } - } else if file_type.is_dir() && expected_directories.contains(&relative_path) { - let child = open_dir_nofollow(directory, &raw_file_name) - .with_context(|| format!("failed to open corpus directory {relative_path}"))?; - read_actual_directory(&child, &relative_path, expected_directories, actual)?; - } else { - actual.push(ProviderArtifactCorpusFileV1::new( - relative_path, - NON_REGULAR_ENTRY_BYTES, - )?); - } - } - Ok(()) -} - -fn utf8_file_name(file_name: OsString, path: &Path) -> Result { - file_name.into_string().map_err(|name| { - anyhow!( - "corpus entry name is not UTF-8 at {}: {}", - path.display(), - name.to_string_lossy() - ) - }) -} - -fn open_corpus_root(root: &Path, create: bool) -> Result> { - let absolute = std::path::absolute(root) - .with_context(|| format!("failed to resolve corpus root {}", root.display()))?; - let Some(name) = absolute.file_name() else { - return Dir::open_ambient_dir(&absolute, ambient_authority()) - .map(Some) - .with_context(|| format!("failed to open corpus root {}", absolute.display())); - }; - let parent_path = absolute.parent().ok_or_else(|| { - anyhow!( - "corpus root has no parent directory: {}", - absolute.display() - ) - })?; - - if create { - std::fs::create_dir_all(parent_path).with_context(|| { - format!( - "failed to create corpus root parent {}", - parent_path.display() - ) - })?; - } - let parent = match Dir::open_ambient_dir(parent_path, ambient_authority()) { - Ok(parent) => parent, - Err(error) if !create && error.kind() == std::io::ErrorKind::NotFound => return Ok(None), - Err(error) => { - return Err(error).with_context(|| { - format!( - "failed to open corpus root parent {}", - parent_path.display() - ) - }); - } - }; - - if create { - ensure_corpus_directory(&parent, name, &absolute).map(Some) - } else { - open_existing_corpus_directory(&parent, name, &absolute) - } -} - -fn open_existing_corpus_directory( - parent: &Dir, - name: &OsStr, - display_path: &Path, -) -> Result> { - match parent.symlink_metadata(name) { - Ok(metadata) if metadata.file_type().is_symlink() => { - bail!( - "refusing to traverse corpus directory symlink {}", - display_path.display() - ); - } - Ok(metadata) if !metadata.is_dir() => { - bail!( - "corpus directory path is not a directory: {}", - display_path.display() - ); - } - Ok(_) => open_dir_nofollow(parent, name) - .map(Some) - .with_context(|| format!("failed to open corpus directory {}", display_path.display())), - Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None), - Err(error) => Err(error).with_context(|| { - format!( - "failed to inspect corpus directory {}", - display_path.display() - ) - }), - } -} - -fn ensure_corpus_directory(parent: &Dir, name: &OsStr, display_path: &Path) -> Result { - if let Some(directory) = open_existing_corpus_directory(parent, name, display_path)? { - return Ok(directory); - } - - match parent.create_dir(name) { - Ok(()) => {} - Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => {} - Err(error) => { - return Err(error).with_context(|| { - format!( - "failed to create corpus directory {}", - display_path.display() - ) - }); - } - } - open_existing_corpus_directory(parent, name, display_path)?.ok_or_else(|| { - anyhow!( - "created corpus directory disappeared before it could be opened: {}", - display_path.display() - ) - }) -} - -fn open_dir_nofollow(parent: &Dir, name: &OsStr) -> std::io::Result { - let mut options = OpenOptions::new(); - options.read(true); - options.follow(FollowSymlinks::No); - options.maybe_dir(true); - let file = parent.open_with(name, &options)?; - if !file.metadata()?.is_dir() { - return Err(std::io::Error::new( - std::io::ErrorKind::NotADirectory, - "corpus path component is not a directory", - )); - } - Ok(Dir::from_std_file(file.into_std())) -} - -fn open_file_nofollow(parent: &Dir, name: &OsStr) -> std::io::Result { - let mut options = OpenOptions::new(); - options.read(true); - options.follow(FollowSymlinks::No); - parent.open_with(name, &options) -} - -fn write_corpus(root: &Path, files: &[ProviderArtifactCorpusFileV1]) -> Result<()> { - let root_directory = open_corpus_root(root, true)?.ok_or_else(|| { - anyhow!( - "created corpus root could not be opened: {}", - root.display() - ) - })?; - let actual = read_actual_corpus_from_directory(&root_directory, files)?; - let expected_paths = files - .iter() - .map(ProviderArtifactCorpusFileV1::relative_path) - .collect::>(); - if let Some(unexpected) = actual - .iter() - .find(|file| !expected_paths.contains(file.relative_path())) - { - bail!( - "refusing to generate over unexpected corpus entry {}", - unexpected.relative_path() - ); - } - - let directories = prepare_corpus_directories(&root_directory, root, files)?; - preflight_corpus_write(&directories, files)?; - for file in files { - let (parent, leaf) = corpus_file_parts(file.relative_path()); - let directory = directories.get(parent).ok_or_else(|| { - anyhow!( - "corpus file parent was not retained: {}", - file.relative_path() - ) - })?; - let path = root.join(file.relative_path()); - replace_corpus_file(directory, OsStr::new(leaf), file.bytes(), &path)?; - println!(" wrote {}", path.display()); - } - Ok(()) -} - -fn prepare_corpus_directories( - root: &Dir, - root_display: &Path, - files: &[ProviderArtifactCorpusFileV1], -) -> Result> { - let mut directories = BTreeMap::new(); - directories.insert( - String::new(), - root.try_clone() - .context("failed to retain the corpus root directory")?, - ); - - for relative_path in expected_directories(files) { - let (parent, name) = corpus_file_parts(&relative_path); - let parent_directory = directories - .get(parent) - .ok_or_else(|| anyhow!("corpus directory parent was not retained: {relative_path}"))?; - let directory = ensure_corpus_directory( - parent_directory, - OsStr::new(name), - &root_display.join(&relative_path), - )?; - directories.insert(relative_path, directory); - } - - Ok(directories) -} - -fn preflight_corpus_write( - directories: &BTreeMap, - files: &[ProviderArtifactCorpusFileV1], -) -> Result<()> { - for file in files { - let (parent, leaf) = corpus_file_parts(file.relative_path()); - let directory = directories.get(parent).ok_or_else(|| { - anyhow!( - "corpus file parent was not retained: {}", - file.relative_path() - ) - })?; - validate_existing_corpus_file( - directory, - OsStr::new(leaf), - Path::new(file.relative_path()), - )?; - } - Ok(()) -} - -fn corpus_file_parts(relative_path: &str) -> (&str, &str) { - relative_path - .rsplit_once('/') - .unwrap_or(("", relative_path)) -} - -fn validate_existing_corpus_file(parent: &Dir, leaf: &OsStr, display_path: &Path) -> Result<()> { - match parent.symlink_metadata(leaf) { - Ok(metadata) if metadata.file_type().is_symlink() => { - bail!( - "refusing to replace corpus symlink {}", - display_path.display() - ); - } - Ok(metadata) if !metadata.is_file() => { - bail!( - "refusing to replace non-file corpus entry {}", - display_path.display() - ); - } - Ok(_) => Ok(()), - Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()), - Err(error) => Err(error) - .with_context(|| format!("failed to inspect corpus file {}", display_path.display())), - } -} - -fn replace_corpus_file( - parent: &Dir, - leaf: &OsStr, - bytes: &[u8], - display_path: &Path, -) -> Result<()> { - validate_existing_corpus_file(parent, leaf, display_path)?; - let display_parent = display_path.parent().ok_or_else(|| { - anyhow!( - "corpus file has no display parent: {}", - display_path.display() - ) - })?; - let (temporary_name, mut temporary_file) = create_temporary_file(parent, display_parent)?; - - let write_result = temporary_file - .write_all(bytes) - .with_context(|| { - format!( - "failed to write temporary corpus file {}", - display_parent.join(&temporary_name).display() - ) - }) - .and_then(|()| { - temporary_file.sync_all().with_context(|| { - format!( - "failed to sync temporary corpus file {}", - display_parent.join(&temporary_name).display() - ) - }) - }); - drop(temporary_file); - if let Err(error) = write_result { - remove_temporary_file(parent, &temporary_name); - return Err(error); - } - - validate_existing_corpus_file(parent, leaf, display_path)?; - if let Err(error) = replace_temporary_file(parent, &temporary_name, leaf, display_path) { - remove_temporary_file(parent, &temporary_name); - return Err(error); - } - Ok(()) -} - -fn create_temporary_file(parent: &Dir, display_parent: &Path) -> Result<(OsString, File)> { - for _ in 0..MAX_TEMP_FILE_ATTEMPTS { - let sequence = NEXT_TEMP_FILE.fetch_add(1, Ordering::Relaxed); - let name = OsString::from(format!( - ".echo-provider-artifact-{}-{sequence}.tmp", - std::process::id() - )); - let mut options = OpenOptions::new(); - options.write(true).create_new(true); - options.follow(FollowSymlinks::No); - match parent.open_with(&name, &options) { - Ok(file) => return Ok((name, file)), - Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => {} - Err(error) => { - return Err(error).with_context(|| { - format!( - "failed to create temporary corpus file {}", - display_parent.join(&name).display() - ) - }); - } - } - } - bail!( - "failed to reserve a temporary corpus file in {} after {} attempts", - display_parent.display(), - MAX_TEMP_FILE_ATTEMPTS - ); -} - -fn replace_temporary_file( - parent: &Dir, - temporary_name: &OsStr, - destination_name: &OsStr, - destination_display: &Path, -) -> Result<()> { - replace_temporary_file_with( - temporary_name, - destination_name, - destination_display, - |from, to| parent.rename(from, parent, to), - ) -} - -fn replace_temporary_file_with( - temporary_name: &OsStr, - destination_name: &OsStr, - destination_display: &Path, - replace: impl FnOnce(&OsStr, &OsStr) -> std::io::Result<()>, -) -> Result<()> { - replace(temporary_name, destination_name).with_context(|| { - format!( - "failed to replace corpus file {} from {}", - destination_display.display(), - temporary_name.to_string_lossy() - ) - }) -} - -fn remove_temporary_file(parent: &Dir, name: &OsStr) { - drop(parent.remove_file(name)); -} - -#[cfg(test)] -mod tests { - use super::{ - open_dir_nofollow, replace_corpus_file, replace_temporary_file_with, NEXT_TEMP_FILE, - }; - use anyhow::Result; - use cap_std::{ambient_authority, fs::Dir}; - use std::ffi::OsStr; - use std::path::{Path, PathBuf}; - use std::sync::atomic::Ordering; - - fn test_directory(label: &str) -> PathBuf { - std::env::temp_dir().join(format!( - "echo-provider-{label}-{}-{}", - std::process::id(), - NEXT_TEMP_FILE.fetch_add(1, Ordering::Relaxed) - )) - } - - #[test] - fn failed_replacement_preserves_existing_destination() -> Result<()> { - let root = test_directory("replacement-test"); - std::fs::create_dir(&root)?; - let temporary = root.join("temporary"); - let destination = root.join("destination"); - std::fs::write(&temporary, b"replacement")?; - std::fs::write(&destination, b"admitted")?; - - let result = replace_temporary_file_with( - OsStr::new("temporary"), - OsStr::new("destination"), - &destination, - |_, _| Err(std::io::Error::other("injected replacement failure")), - ); - - assert!(result.is_err()); - assert_eq!(std::fs::read(&destination)?, b"admitted"); - assert_eq!(std::fs::read(&temporary)?, b"replacement"); - std::fs::remove_dir_all(root)?; - Ok(()) - } - - #[cfg(unix)] - #[test] - fn retained_parent_handle_prevents_symlink_redirection() -> Result<()> { - use std::os::unix::fs::symlink; - - let root = test_directory("parent-swap"); - let outside = test_directory("parent-swap-outside"); - let evidence = root.join("evidence"); - let parked = root.join("evidence-parked"); - std::fs::create_dir_all(&evidence)?; - std::fs::create_dir_all(&outside)?; - let root_directory = Dir::open_ambient_dir(&root, ambient_authority())?; - let evidence_directory = open_dir_nofollow(&root_directory, OsStr::new("evidence"))?; - - std::fs::rename(&evidence, &parked)?; - symlink(&outside, &evidence)?; - - replace_corpus_file( - &evidence_directory, - OsStr::new("artifact"), - b"canonical", - Path::new("evidence/artifact"), - )?; - - assert!(!outside.join("artifact").exists()); - assert_eq!(std::fs::read(parked.join("artifact"))?, b"canonical"); - std::fs::remove_dir_all(root)?; - std::fs::remove_dir_all(outside)?; - Ok(()) - } -} diff --git a/crates/echo-wesley-gen/src/bin/echo-edict-provider-assets.rs b/crates/echo-wesley-gen/src/bin/echo-edict-provider-assets.rs new file mode 100644 index 00000000..6ca0f91b --- /dev/null +++ b/crates/echo-wesley-gen/src/bin/echo-edict-provider-assets.rs @@ -0,0 +1,478 @@ +// SPDX-License-Identifier: Apache-2.0 +// © James Ross Ω FLYING•ROBOTS +#![allow(clippy::print_stdout, clippy::print_stderr)] +//! Synchronizes exact package-local assets for the Echo Edict provider tools. + +use anyhow::{anyhow, bail, Context, Result}; +use clap::Parser; +use echo_wesley_gen::provider_corpus::{diff_exact_corpus_files_v1, ProviderArtifactCorpusFileV1}; +use echo_wesley_gen::provider_corpus_fs::{read_actual_corpus, write_corpus}; +use std::ffi::OsString; +use std::path::{Path, PathBuf}; +use std::process::Command; + +const MAX_OWNER_FILE_BYTES: u64 = 32 * 1024 * 1024; +const MAX_OWNER_TOTAL_BYTES: usize = 64 * 1024 * 1024; + +#[derive(Clone, Copy)] +struct AssetSpec { + carrier: &'static str, + owner: &'static str, + corroborating_owner: Option<&'static str>, +} + +impl AssetSpec { + const fn new(carrier: &'static str, owner: &'static str) -> Self { + Self { + carrier, + owner, + corroborating_owner: None, + } + } + + const fn corroborated( + carrier: &'static str, + package_copy: &'static str, + authoritative_owner: &'static str, + ) -> Self { + Self { + carrier, + owner: authoritative_owner, + corroborating_owner: Some(package_copy), + } + } +} + +const ASSETS: [AssetSpec; 35] = [ + AssetSpec::new( + "edict-provider/contracts/v1/edict-provider-contracts.cddl", + "schemas/edict-provider/contracts/v1/edict-provider-contracts.cddl", + ), + AssetSpec::new( + "edict-provider/contracts/v1/manifest.json", + "schemas/edict-provider/contracts/v1/manifest.json", + ), + AssetSpec::new( + "edict-provider/echo-provider-semantics-v1.json", + "schemas/edict-provider/echo-provider-semantics-v1.json", + ), + AssetSpec::new( + "edict-provider/generation-settings-v1.json", + "schemas/edict-provider/generation-settings-v1.json", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/components/lowerer.echo-dpo.component.wasm", + "schemas/edict-provider/package/v1/components/lowerer.echo-dpo.component.wasm", + "schemas/edict-provider/components/v1/lowerer.echo-dpo.component.wasm", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/components/verifier.echo-dpo.component.wasm", + "schemas/edict-provider/package/v1/components/verifier.echo-dpo.component.wasm", + "schemas/edict-provider/components/v1/verifier.echo-dpo.component.wasm", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/evidence/provenance.provider-generation.json", + "schemas/edict-provider/package/v1/generated/evidence/provenance.provider-generation.json", + "schemas/edict-provider/generated/v1/evidence/provenance.provider-generation.json", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/evidence/review.provider-generation.json", + "schemas/edict-provider/package/v1/generated/evidence/review.provider-generation.json", + "schemas/edict-provider/generated/v1/evidence/review.provider-generation.json", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/primary/authority-facts.echo-dpo.cbor", + "schemas/edict-provider/package/v1/generated/primary/authority-facts.echo-dpo.cbor", + "schemas/edict-provider/generated/v1/primary/authority-facts.echo-dpo.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/primary/authority-facts.echo-lawpack.cbor", + "schemas/edict-provider/package/v1/generated/primary/authority-facts.echo-lawpack.cbor", + "schemas/edict-provider/generated/v1/primary/authority-facts.echo-lawpack.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/primary/generated-artifact-profile.echo-dpo-registration.cbor", + "schemas/edict-provider/package/v1/generated/primary/generated-artifact-profile.echo-dpo-registration.cbor", + "schemas/edict-provider/generated/v1/primary/generated-artifact-profile.echo-dpo-registration.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/primary/lawpack.echo-dpo.cbor", + "schemas/edict-provider/package/v1/generated/primary/lawpack.echo-dpo.cbor", + "schemas/edict-provider/generated/v1/primary/lawpack.echo-dpo.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/primary/schema.echo-provider-artifacts.cddl", + "schemas/edict-provider/package/v1/generated/primary/schema.echo-provider-artifacts.cddl", + "schemas/edict-provider/generated/v1/primary/schema.echo-provider-artifacts.cddl", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/primary/target-profile.echo-dpo.cbor", + "schemas/edict-provider/package/v1/generated/primary/target-profile.echo-dpo.cbor", + "schemas/edict-provider/generated/v1/primary/target-profile.echo-dpo.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/resources/resource.conformance-corpus.cbor", + "schemas/edict-provider/package/v1/generated/resources/resource.conformance-corpus.cbor", + "schemas/edict-provider/generated/v1/resources/resource.conformance-corpus.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/resources/resource.lawpack-compatibility.cbor", + "schemas/edict-provider/package/v1/generated/resources/resource.lawpack-compatibility.cbor", + "schemas/edict-provider/generated/v1/resources/resource.lawpack-compatibility.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/resources/resource.lawpack-exports.cbor", + "schemas/edict-provider/package/v1/generated/resources/resource.lawpack-exports.cbor", + "schemas/edict-provider/generated/v1/resources/resource.lawpack-exports.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/resources/resource.lawpack-target-adapter.cbor", + "schemas/edict-provider/package/v1/generated/resources/resource.lawpack-target-adapter.cbor", + "schemas/edict-provider/generated/v1/resources/resource.lawpack-target-adapter.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/resources/resource.lawpack-verifier.cbor", + "schemas/edict-provider/package/v1/generated/resources/resource.lawpack-verifier.cbor", + "schemas/edict-provider/generated/v1/resources/resource.lawpack-verifier.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/resources/resource.target-bundle-profile.cbor", + "schemas/edict-provider/package/v1/generated/resources/resource.target-bundle-profile.cbor", + "schemas/edict-provider/generated/v1/resources/resource.target-bundle-profile.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/resources/resource.target-cost-algebra.cbor", + "schemas/edict-provider/package/v1/generated/resources/resource.target-cost-algebra.cbor", + "schemas/edict-provider/generated/v1/resources/resource.target-cost-algebra.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/resources/resource.target-footprint-algebra.cbor", + "schemas/edict-provider/package/v1/generated/resources/resource.target-footprint-algebra.cbor", + "schemas/edict-provider/generated/v1/resources/resource.target-footprint-algebra.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/resources/resource.target-intrinsics.cbor", + "schemas/edict-provider/package/v1/generated/resources/resource.target-intrinsics.cbor", + "schemas/edict-provider/generated/v1/resources/resource.target-intrinsics.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/resources/resource.target-ir.cbor", + "schemas/edict-provider/package/v1/generated/resources/resource.target-ir.cbor", + "schemas/edict-provider/generated/v1/resources/resource.target-ir.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/resources/resource.target-lowerer-contract.cbor", + "schemas/edict-provider/package/v1/generated/resources/resource.target-lowerer-contract.cbor", + "schemas/edict-provider/generated/v1/resources/resource.target-lowerer-contract.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/resources/resource.target-obstruction-taxonomy.cbor", + "schemas/edict-provider/package/v1/generated/resources/resource.target-obstruction-taxonomy.cbor", + "schemas/edict-provider/generated/v1/resources/resource.target-obstruction-taxonomy.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/resources/resource.target-operation-profiles.cbor", + "schemas/edict-provider/package/v1/generated/resources/resource.target-operation-profiles.cbor", + "schemas/edict-provider/generated/v1/resources/resource.target-operation-profiles.cbor", + ), + AssetSpec::corroborated( + "edict-provider/package/v1/generated/resources/resource.target-verifier-contract.cbor", + "schemas/edict-provider/package/v1/generated/resources/resource.target-verifier-contract.cbor", + "schemas/edict-provider/generated/v1/resources/resource.target-verifier-contract.cbor", + ), + AssetSpec::new( + "edict-provider/package/v1/provider-manifest.echo.json", + "schemas/edict-provider/package/v1/provider-manifest.echo.json", + ), + AssetSpec::new("repository/Cargo.lock.source", "Cargo.lock"), + AssetSpec::new("repository/Cargo.toml.source", "Cargo.toml"), + AssetSpec::new( + "repository/crates/echo-edict-canonical/Cargo.toml.source", + "crates/echo-edict-canonical/Cargo.toml", + ), + AssetSpec::new( + "repository/crates/echo-edict-canonical/src/lib.rs.source", + "crates/echo-edict-canonical/src/lib.rs", + ), + AssetSpec::new( + "repository/crates/echo-wesley-gen/Cargo.toml.source", + "crates/echo-wesley-gen/Cargo.toml", + ), + AssetSpec::new( + "repository/rust-toolchain.toml.source", + "rust-toolchain.toml", + ), +]; + +#[derive(Parser)] +#[command( + author, + version, + about = "Synchronizes exact package-local Echo Edict provider assets" +)] +struct Args { + /// Replace the exact package-local carrier tree from its fixed owners. + #[arg(long)] + write: bool, + + /// Prove Cargo selects exactly the fixed carrier tree for its archive. + #[arg(long)] + check_package_list: bool, + + /// Repository root containing the fixed owner paths. + #[arg(long)] + workspace_root: Option, + + /// Override the carrier root, primarily for isolated check/write witnesses. + #[arg(long)] + assets_root: Option, +} + +fn main() -> Result<()> { + let args = Args::parse(); + let crate_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let workspace_root = match args.workspace_root { + Some(root) => root, + None => crate_root.join("../.."), + }; + let assets_root = args + .assets_root + .unwrap_or_else(|| crate_root.join("assets/v1")); + // Write mode intentionally permits a stale package copy so authoritative + // generated/component owners can break the regeneration cycle. Read-only + // check mode still requires every distribution copy to corroborate them. + let expected = load_expected_assets(&workspace_root, !args.write)?; + + if args.write { + write_corpus(&assets_root, &expected)?; + println!("Package-local provider assets synchronized"); + } else { + check_assets(&assets_root, &expected)?; + println!("Package-local provider assets are current"); + } + + if args.check_package_list { + check_package_list(&workspace_root, &expected)?; + println!("Cargo package selects the exact provider asset tree"); + } + Ok(()) +} + +fn load_expected_assets( + root: &Path, + require_corroboration: bool, +) -> Result> { + let mut files = Vec::with_capacity(ASSETS.len()); + let mut total_bytes = 0usize; + for spec in ASSETS { + let bytes = read_owner(root, spec.owner)?; + total_bytes = total_bytes + .checked_add(bytes.len()) + .ok_or_else(|| anyhow!("provider asset owner total byte length overflow"))?; + if total_bytes > MAX_OWNER_TOTAL_BYTES { + bail!("provider asset owners exceed total byte limit {MAX_OWNER_TOTAL_BYTES}"); + } + if require_corroboration { + let Some(corroborating_owner) = spec.corroborating_owner else { + files.push(ProviderArtifactCorpusFileV1::new(spec.carrier, &bytes)?); + continue; + }; + let corroborating_bytes = read_owner(root, corroborating_owner)?; + if bytes != corroborating_bytes { + bail!( + "provider asset owner {} differs from corroborating owner {}", + spec.owner, + corroborating_owner + ); + } + } + files.push(ProviderArtifactCorpusFileV1::new(spec.carrier, &bytes)?); + } + Ok(files) +} + +fn read_owner(root: &Path, relative_path: &str) -> Result> { + let path = root.join(relative_path); + let metadata = std::fs::symlink_metadata(&path) + .with_context(|| format!("failed to inspect provider asset owner {}", path.display()))?; + if metadata.file_type().is_symlink() || !metadata.is_file() { + bail!( + "provider asset owner is not a regular non-symlink file: {}", + path.display() + ); + } + if metadata.len() > MAX_OWNER_FILE_BYTES { + bail!( + "provider asset owner {} exceeds byte limit {MAX_OWNER_FILE_BYTES}", + path.display() + ); + } + let bytes = std::fs::read(&path) + .with_context(|| format!("failed to read provider asset owner {}", path.display()))?; + if bytes.len() as u64 != metadata.len() { + bail!( + "provider asset owner changed length while being read: {}", + path.display() + ); + } + Ok(bytes) +} + +fn check_assets(root: &Path, expected: &[ProviderArtifactCorpusFileV1]) -> Result<()> { + let actual = read_actual_corpus(root, expected)?; + let drift = diff_exact_corpus_files_v1(expected, &actual)?; + if drift.is_empty() { + return Ok(()); + } + + eprintln!("Package-local provider asset drift:"); + for entry in &drift { + eprintln!(" {}: {}", entry.kind().as_str(), entry.relative_path()); + } + bail!("package-local provider assets are not current") +} + +fn check_package_list( + workspace_root: &Path, + expected: &[ProviderArtifactCorpusFileV1], +) -> Result<()> { + let cargo: OsString = std::env::var_os("CARGO").unwrap_or_else(|| OsString::from("cargo")); + let manifest = workspace_root.join("crates/echo-wesley-gen/Cargo.toml"); + let output = Command::new(cargo) + .current_dir(workspace_root) + .arg("package") + .arg("--manifest-path") + .arg(&manifest) + .arg("--allow-dirty") + .arg("--list") + .output() + .context("failed to execute cargo package --list for echo-wesley-gen")?; + if !output.status.success() { + bail!( + "cargo package --list failed: {}", + String::from_utf8_lossy(&output.stderr).trim() + ); + } + + let stdout = String::from_utf8(output.stdout).context("cargo package list is not UTF-8")?; + let actual_assets = stdout + .lines() + .filter(|path| path.starts_with("assets/v1/")) + .map(str::to_owned) + .collect::>(); + let expected_assets = expected + .iter() + .map(|file| format!("assets/v1/{}", file.relative_path())) + .collect::>(); + check_package_asset_inventory(actual_assets, expected_assets) +} + +fn check_package_asset_inventory( + mut actual_assets: Vec, + mut expected_assets: Vec, +) -> Result<()> { + actual_assets.sort_unstable(); + expected_assets.sort_unstable(); + if actual_assets != expected_assets { + bail!( + "cargo package provider asset inventory differs: expected {} entries, found {}\n\ + expected paths:\n {}\n\ + actual paths:\n {}", + expected_assets.len(), + actual_assets.len(), + expected_assets.join("\n "), + actual_assets.join("\n ") + ); + } + Ok(()) +} + +#[cfg(test)] +#[allow(clippy::expect_used)] +mod tests { + use super::{check_package_asset_inventory, load_expected_assets, ASSETS}; + use anyhow::Result; + use std::path::{Path, PathBuf}; + use std::sync::atomic::{AtomicU64, Ordering}; + + static NEXT_TEST_DIRECTORY: AtomicU64 = AtomicU64::new(0); + + #[test] + fn package_asset_inventory_ignores_listing_order_and_names_exact_drift() { + let expected = vec!["assets/v1/alpha".to_owned(), "assets/v1/bravo".to_owned()]; + check_package_asset_inventory( + vec!["assets/v1/bravo".to_owned(), "assets/v1/alpha".to_owned()], + expected.clone(), + ) + .expect("Cargo listing order does not change the selected asset inventory"); + + let error = check_package_asset_inventory( + vec!["assets/v1/alpha".to_owned(), "assets/v1/charlie".to_owned()], + expected, + ) + .expect_err("same-length asset substitution reports exact paths"); + let detail = error.to_string(); + assert!(detail.contains("assets/v1/bravo")); + assert!(detail.contains("assets/v1/charlie")); + } + + #[test] + fn staged_write_uses_authority_while_strict_check_requires_package_corroboration() -> Result<()> + { + let sequence = NEXT_TEST_DIRECTORY.fetch_add(1, Ordering::Relaxed); + let root = std::env::temp_dir().join(format!( + "echo-provider-asset-authority-{}-{sequence}", + std::process::id() + )); + materialize_owner_fixture(&root)?; + + let generated = ASSETS + .iter() + .find(|spec| spec.carrier.ends_with("resource.target-ir.cbor")) + .copied() + .expect("fixed generated asset exists"); + let package_copy = root.join( + generated + .corroborating_owner + .expect("generated asset has a package corroboration"), + ); + std::fs::write(&package_copy, b"stale package copy")?; + + let strict = load_expected_assets(&root, true); + assert!(strict.is_err()); + let staged = load_expected_assets(&root, false)?; + let staged_file = staged + .iter() + .find(|file| file.relative_path() == generated.carrier) + .expect("staged carrier is present"); + assert_eq!( + staged_file.bytes(), + std::fs::read(root.join(generated.owner))? + ); + + std::fs::copy(root.join(generated.owner), &package_copy)?; + assert!(load_expected_assets(&root, true).is_ok()); + std::fs::remove_dir_all(root)?; + Ok(()) + } + + fn materialize_owner_fixture(root: &Path) -> Result<()> { + let source_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../.."); + for spec in ASSETS { + copy_owner(&source_root, root, spec.owner)?; + if let Some(corroborating_owner) = spec.corroborating_owner { + copy_owner(&source_root, root, corroborating_owner)?; + } + } + Ok(()) + } + + fn copy_owner(source_root: &Path, target_root: &Path, relative: &str) -> Result<()> { + let target = target_root.join(relative); + if let Some(parent) = target.parent() { + std::fs::create_dir_all(parent)?; + } + std::fs::copy(source_root.join(relative), target)?; + Ok(()) + } +} diff --git a/crates/echo-wesley-gen/src/bin/echo-edict-provider-package.rs b/crates/echo-wesley-gen/src/bin/echo-edict-provider-package.rs new file mode 100644 index 00000000..900ae2eb --- /dev/null +++ b/crates/echo-wesley-gen/src/bin/echo-edict-provider-package.rs @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: Apache-2.0 +// © James Ross Ω FLYING•ROBOTS +#![allow(clippy::print_stdout, clippy::print_stderr)] +//! Generates or checks Echo's deterministic digest-locked Edict provider package. + +use anyhow::{bail, Result}; +use clap::Parser; +use echo_wesley_gen::provider_artifacts::generate_provider_primary_artifacts_v1; +use echo_wesley_gen::provider_contract_pack::admit_provider_contract_pack_v1; +use echo_wesley_gen::provider_corpus::{ + checked_provider_artifact_corpus_v1, checked_provider_generator_source_bundle_v1, + diff_exact_corpus_files_v1, ProviderArtifactCorpusFileV1, +}; +use echo_wesley_gen::provider_corpus_fs::{read_actual_corpus, write_corpus}; +use echo_wesley_gen::provider_generation::build_provider_generation_input_v1; +use echo_wesley_gen::provider_package::{ + assemble_provider_package_v1, ProviderPackageComponentMaterialV1, ProviderPackageV1, +}; +use echo_wesley_gen::provider_provenance::generate_provider_generation_provenance_v1; +use echo_wesley_gen::provider_review::generate_provider_generation_review_v1; +use std::path::PathBuf; + +const SOURCE: &[u8] = + include_bytes!("../../assets/v1/edict-provider/echo-provider-semantics-v1.json"); +const SETTINGS: &[u8] = + include_bytes!("../../assets/v1/edict-provider/generation-settings-v1.json"); +const CONTRACT_CDDL: &[u8] = + include_bytes!("../../assets/v1/edict-provider/contracts/v1/edict-provider-contracts.cddl"); +const CONTRACT_MANIFEST: &[u8] = + include_bytes!("../../assets/v1/edict-provider/contracts/v1/manifest.json"); +const LOWERER: &[u8] = include_bytes!( + "../../assets/v1/edict-provider/package/v1/components/lowerer.echo-dpo.component.wasm" +); +const VERIFIER: &[u8] = include_bytes!( + "../../assets/v1/edict-provider/package/v1/components/verifier.echo-dpo.component.wasm" +); + +#[derive(Parser)] +#[command( + author, + version, + about = "Generates or checks Echo's deterministic digest-locked Edict provider package" +)] +struct Args { + /// Output directory for the checked provider package. + #[arg(long, default_value = "schemas/edict-provider/package/v1")] + out: PathBuf, + + /// Report package drift without creating, deleting, or rewriting files. + #[arg(long)] + check: bool, +} + +fn main() -> Result<()> { + let args = Args::parse(); + let contract_pack = admit_provider_contract_pack_v1(CONTRACT_CDDL, CONTRACT_MANIFEST)?; + let input = build_provider_generation_input_v1(SOURCE, &contract_pack, SETTINGS)?; + let primary = generate_provider_primary_artifacts_v1(&input, &contract_pack)?; + let generator = checked_provider_generator_source_bundle_v1()?.generator_material()?; + let provenance = generate_provider_generation_provenance_v1(&input, &primary, &generator)?; + let review = generate_provider_generation_review_v1(&input, &provenance)?; + let package = assemble_provider_package_v1( + &input, + &primary, + &generator, + &provenance, + &review, + vec![ + ProviderPackageComponentMaterialV1::new("lowerer.echo-dpo", LOWERER)?, + ProviderPackageComponentMaterialV1::new("verifier.echo-dpo", VERIFIER)?, + ], + )?; + + // The package crossing consumes the checked #652 publication. It must not + // silently publish a freshly rendered but unchecked generation occurrence. + verify_checked_generated_members(&package)?; + let files = corpus_files(&package)?; + + if args.check { + let actual = read_actual_corpus(&args.out, &files)?; + let drift = diff_exact_corpus_files_v1(&files, &actual)?; + if drift.is_empty() { + println!("Provider package is current in {}", args.out.display()); + return Ok(()); + } + + eprintln!("Provider package drift:"); + for entry in &drift { + eprintln!(" {}: {}", entry.kind().as_str(), entry.relative_path()); + } + bail!("provider package is not current"); + } + + write_corpus(&args.out, &files)?; + println!("Provider package generated in {}", args.out.display()); + Ok(()) +} + +fn verify_checked_generated_members(package: &ProviderPackageV1) -> Result<()> { + let checked = checked_provider_artifact_corpus_v1()?; + let packaged = package + .files() + .iter() + .filter(|file| file.relative_path().starts_with("generated/")) + .collect::>(); + + if packaged.len() != checked.files().len() { + bail!( + "provider package generated-member count {} does not match checked corpus count {}", + packaged.len(), + checked.files().len() + ); + } + + for (packaged_file, checked_file) in packaged.iter().zip(checked.files()) { + let expected_path = format!("generated/{}", checked_file.relative_path()); + if packaged_file.relative_path() != expected_path { + bail!( + "provider package generated member {} does not match checked path {}", + packaged_file.relative_path(), + expected_path + ); + } + if packaged_file.bytes() != checked_file.bytes() { + bail!( + "provider package generated member {} does not match the checked exact bytes", + packaged_file.relative_path() + ); + } + } + + Ok(()) +} + +fn corpus_files(package: &ProviderPackageV1) -> Result> { + let mut files = Vec::with_capacity(package.files().len()); + for file in package.files() { + files.push(ProviderArtifactCorpusFileV1::new( + file.relative_path(), + file.bytes(), + )?); + } + Ok(files) +} diff --git a/crates/echo-wesley-gen/src/lib.rs b/crates/echo-wesley-gen/src/lib.rs index cff73a6c..d2538d5a 100644 --- a/crates/echo-wesley-gen/src/lib.rs +++ b/crates/echo-wesley-gen/src/lib.rs @@ -36,6 +36,12 @@ pub mod provider_review; /// Exact generator source identity and checked provider artifact corpus. pub mod provider_corpus; +/// Capability-bounded check/write boundary for exact provider corpora. +pub mod provider_corpus_fs; + +/// Deterministic digest-locked Echo Edict provider package. +pub mod provider_package; + /// Imported Wesley runtime optic artifact ready for Echo registration. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ImportedRuntimeOpticArtifact { diff --git a/crates/echo-wesley-gen/src/main.rs b/crates/echo-wesley-gen/src/main.rs index efbb9584..8ee10072 100644 --- a/crates/echo-wesley-gen/src/main.rs +++ b/crates/echo-wesley-gen/src/main.rs @@ -2132,7 +2132,7 @@ mod wesley_core_version_pinned { let exact_dependency = format!("wesley-core = \"={WESLEY_CORE_VERSION}\""); assert!( - include_str!("../Cargo.toml") + include_str!("../assets/v1/repository/crates/echo-wesley-gen/Cargo.toml.source") .lines() .any(|line| line.trim() == exact_dependency), "Wesley provenance must match the exact wesley-core dependency" diff --git a/crates/echo-wesley-gen/src/provider_artifacts.rs b/crates/echo-wesley-gen/src/provider_artifacts.rs index 4bb48026..c52afb72 100644 --- a/crates/echo-wesley-gen/src/provider_artifacts.rs +++ b/crates/echo-wesley-gen/src/provider_artifacts.rs @@ -29,9 +29,10 @@ use crate::provider_contract_pack::{ }; use crate::provider_generation::ProviderGenerationInputV1; use crate::provider_semantics::{ - ArtifactResourceDeclaration, ArtifactResourceProvision, AuthorityClass, - AuthorityFactSourceKind, EffectKindHint, ExecutionClass, GeneratedArtifactDeclaration, - GeneratedArtifactKind, LawpackVerifierDeclaration, OpticKind, ProviderSemanticSourceV1, + generated_resource_root, ArtifactResourceDeclaration, ArtifactResourceProvision, + AuthorityClass, AuthorityFactSourceKind, EffectKindHint, ExecutionClass, + GeneratedArtifactDeclaration, GeneratedArtifactKind, LawpackVerifierDeclaration, OpticKind, + ProviderSemanticSourceV1, }; const SCHEMA_ROLE: &str = "schema.echo-provider-artifacts"; @@ -898,30 +899,13 @@ fn required_generated_roots(source: &ProviderSemanticSourceV1) -> Vec<&str> { } fn resource_root(role: &str) -> Result<&'static str, ProviderArtifactGenerationError> { - let root = match role { - "resource.conformance-corpus" => "echo-provider-conformance-corpus", - "resource.lawpack-compatibility" => "echo-provider-lawpack-compatibility", - "resource.lawpack-exports" => "lawpack-exports", - "resource.lawpack-target-adapter" => "echo-provider-lawpack-target-adapter", - "resource.lawpack-verifier" => "echo-provider-lawpack-verifier", - "resource.target-bundle-profile" => "echo-dpo-bundle", - "resource.target-cost-algebra" => "echo-dpo-cost", - "resource.target-footprint-algebra" => "echo-dpo-footprint", - "resource.target-intrinsics" => "intrinsics-document", - "resource.target-ir" => "echo-span-ir", - "resource.target-lowerer-contract" => "echo-dpo-lowerer", - "resource.target-obstruction-taxonomy" => "echo-dpo-obstructions", - "resource.target-operation-profiles" => "operation-profiles-document", - "resource.target-verifier-contract" => "echo-dpo-verifier", - _ => { - return Err(ProviderArtifactGenerationError::new( - ProviderArtifactGenerationErrorKind::ResourceClosureMismatch, - role, - "generated-resource-root", - )); - } - }; - Ok(root) + generated_resource_root(role).ok_or_else(|| { + ProviderArtifactGenerationError::new( + ProviderArtifactGenerationErrorKind::ResourceClosureMismatch, + role, + "generated-resource-root", + ) + }) } fn resource_edict_contract(role: &str) -> Option<&'static str> { diff --git a/crates/echo-wesley-gen/src/provider_corpus.rs b/crates/echo-wesley-gen/src/provider_corpus.rs index 86e3c215..46d13814 100644 --- a/crates/echo-wesley-gen/src/provider_corpus.rs +++ b/crates/echo-wesley-gen/src/provider_corpus.rs @@ -349,20 +349,26 @@ pub fn build_provider_generator_source_bundle_v1( /// contract. No filesystem or environment discovery is performed. pub fn checked_provider_generator_source_bundle_v1( ) -> Result { - let sources: [(&str, &[u8]); 16] = [ - ("Cargo.lock", include_bytes!("../../../Cargo.lock")), - ("Cargo.toml", include_bytes!("../../../Cargo.toml")), + let sources: [(&str, &[u8]); 17] = [ + ( + "Cargo.lock", + include_bytes!("../assets/v1/repository/Cargo.lock.source"), + ), + ( + "Cargo.toml", + include_bytes!("../assets/v1/repository/Cargo.toml.source"), + ), ( "crates/echo-edict-canonical/Cargo.toml", - include_bytes!("../../echo-edict-canonical/Cargo.toml"), + include_bytes!("../assets/v1/repository/crates/echo-edict-canonical/Cargo.toml.source"), ), ( "crates/echo-edict-canonical/src/lib.rs", - include_bytes!("../../echo-edict-canonical/src/lib.rs"), + include_bytes!("../assets/v1/repository/crates/echo-edict-canonical/src/lib.rs.source"), ), ( "crates/echo-wesley-gen/Cargo.toml", - include_bytes!("../Cargo.toml"), + include_bytes!("../assets/v1/repository/crates/echo-wesley-gen/Cargo.toml.source"), ), ( "crates/echo-wesley-gen/src/bin/echo-edict-provider-artifacts.rs", @@ -388,6 +394,10 @@ pub fn checked_provider_generator_source_bundle_v1( "crates/echo-wesley-gen/src/provider_corpus.rs", include_bytes!("provider_corpus.rs"), ), + ( + "crates/echo-wesley-gen/src/provider_corpus_fs.rs", + include_bytes!("provider_corpus_fs.rs"), + ), ( "crates/echo-wesley-gen/src/provider_generation.rs", include_bytes!("provider_generation.rs"), @@ -406,7 +416,7 @@ pub fn checked_provider_generator_source_bundle_v1( ), ( "rust-toolchain.toml", - include_bytes!("../../../rust-toolchain.toml"), + include_bytes!("../assets/v1/repository/rust-toolchain.toml.source"), ), ]; let mut files = Vec::with_capacity(sources.len()); @@ -478,6 +488,158 @@ impl ProviderArtifactCorpusV1 { } } +/// Loads the current checked 22-file provider corpus introduced by #652. +/// +/// This is an explicit source boundary for later package assembly. It performs +/// no filesystem, registry, environment, process, clock, or network discovery. +/// +/// # Errors +/// +/// Returns a structured error if a checked logical path no longer satisfies the +/// bounded v1 corpus grammar. +pub fn checked_provider_artifact_corpus_v1( +) -> Result { + let checked: [(&str, &[u8]); EXPECTED_CORPUS_FILE_COUNT] = [ + ( + "evidence/provenance.provider-generation.json", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/evidence/provenance.provider-generation.json" + ), + ), + ( + "evidence/review.provider-generation.json", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/evidence/review.provider-generation.json" + ), + ), + ( + "primary/authority-facts.echo-dpo.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/primary/authority-facts.echo-dpo.cbor" + ), + ), + ( + "primary/authority-facts.echo-lawpack.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/primary/authority-facts.echo-lawpack.cbor" + ), + ), + ( + "primary/generated-artifact-profile.echo-dpo-registration.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/primary/generated-artifact-profile.echo-dpo-registration.cbor" + ), + ), + ( + "primary/lawpack.echo-dpo.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/primary/lawpack.echo-dpo.cbor" + ), + ), + ( + "primary/schema.echo-provider-artifacts.cddl", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/primary/schema.echo-provider-artifacts.cddl" + ), + ), + ( + "primary/target-profile.echo-dpo.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/primary/target-profile.echo-dpo.cbor" + ), + ), + ( + "resources/resource.conformance-corpus.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/resources/resource.conformance-corpus.cbor" + ), + ), + ( + "resources/resource.lawpack-compatibility.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/resources/resource.lawpack-compatibility.cbor" + ), + ), + ( + "resources/resource.lawpack-exports.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/resources/resource.lawpack-exports.cbor" + ), + ), + ( + "resources/resource.lawpack-target-adapter.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/resources/resource.lawpack-target-adapter.cbor" + ), + ), + ( + "resources/resource.lawpack-verifier.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/resources/resource.lawpack-verifier.cbor" + ), + ), + ( + "resources/resource.target-bundle-profile.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/resources/resource.target-bundle-profile.cbor" + ), + ), + ( + "resources/resource.target-cost-algebra.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/resources/resource.target-cost-algebra.cbor" + ), + ), + ( + "resources/resource.target-footprint-algebra.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/resources/resource.target-footprint-algebra.cbor" + ), + ), + ( + "resources/resource.target-intrinsics.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/resources/resource.target-intrinsics.cbor" + ), + ), + ( + "resources/resource.target-ir.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/resources/resource.target-ir.cbor" + ), + ), + ( + "resources/resource.target-lowerer-contract.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/resources/resource.target-lowerer-contract.cbor" + ), + ), + ( + "resources/resource.target-obstruction-taxonomy.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/resources/resource.target-obstruction-taxonomy.cbor" + ), + ), + ( + "resources/resource.target-operation-profiles.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/resources/resource.target-operation-profiles.cbor" + ), + ), + ( + "resources/resource.target-verifier-contract.cbor", + include_bytes!( + "../assets/v1/edict-provider/package/v1/generated/resources/resource.target-verifier-contract.cbor" + ), + ), + ]; + let files = checked + .into_iter() + .map(|(path, bytes)| ProviderArtifactCorpusFileV1::new(path, bytes)) + .collect::, _>>()?; + Ok(ProviderArtifactCorpusV1 { files }) +} + /// Stable kinds of checked-corpus drift. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum ProviderArtifactCorpusDriftKind { @@ -620,7 +782,23 @@ pub fn diff_provider_artifact_corpus_v1( expected: &ProviderArtifactCorpusV1, actual: &[ProviderArtifactCorpusFileV1], ) -> Result, ProviderArtifactCorpusError> { - let expected_by_path = corpus_inventory(&expected.files, "expected-corpus")?; + diff_exact_corpus_files_v1(&expected.files, actual) +} + +/// Compares two exact file inventories without imposing a corpus-specific size. +/// +/// The returned drift is sorted first by `missing`, `changed`, `unexpected`, +/// and then lexicographically by relative path. This function is pure and does +/// not read, write, create, normalize, or delete filesystem entries. +/// +/// # Errors +/// +/// Returns a stable failure if either inventory repeats a relative path. +pub fn diff_exact_corpus_files_v1( + expected: &[ProviderArtifactCorpusFileV1], + actual: &[ProviderArtifactCorpusFileV1], +) -> Result, ProviderArtifactCorpusError> { + let expected_by_path = corpus_inventory(expected, "expected-corpus")?; let actual_by_path = corpus_inventory(actual, "actual-corpus")?; let mut drift = Vec::new(); @@ -689,7 +867,7 @@ fn validate_source_path(path: &str) -> Result<(), ProviderArtifactCorpusError> { Ok(()) } -fn validate_corpus_path(path: &str) -> Result<(), ProviderArtifactCorpusError> { +pub(crate) fn validate_corpus_path(path: &str) -> Result<(), ProviderArtifactCorpusError> { if path.len() > MAX_CORPUS_PATH_BYTES { return Err(ProviderArtifactCorpusError::new( ProviderArtifactCorpusErrorKind::CorpusPathInvalid, diff --git a/crates/echo-wesley-gen/src/provider_corpus_fs.rs b/crates/echo-wesley-gen/src/provider_corpus_fs.rs new file mode 100644 index 00000000..d8b5e4f7 --- /dev/null +++ b/crates/echo-wesley-gen/src/provider_corpus_fs.rs @@ -0,0 +1,833 @@ +// SPDX-License-Identifier: Apache-2.0 +// © James Ross Ω FLYING•ROBOTS +//! Safe exact-tree filesystem access for deterministic provider corpora. + +use crate::provider_corpus::ProviderArtifactCorpusFileV1; +use anyhow::{anyhow, bail, Context, Result}; +use cap_fs_ext::{FollowSymlinks, OpenOptionsFollowExt, OpenOptionsMaybeDirExt}; +use cap_std::{ + ambient_authority, + fs::{Dir, File, OpenOptions}, +}; +use std::collections::{BTreeMap, BTreeSet}; +use std::ffi::{OsStr, OsString}; +use std::io::{Read, Write}; +use std::path::Path; +use std::sync::atomic::{AtomicU64, Ordering}; + +const NON_REGULAR_ENTRY_BYTES: &[u8] = b"echo.provider-artifact-corpus.non-regular/v1"; +const UNEXPECTED_ENTRY_BYTES: &[u8] = b"echo.provider-artifact-corpus.unexpected/v1"; +const CHANGED_ENTRY_BYTES: &[u8] = b"echo.provider-artifact-corpus.changed/v1"; +const ALTERNATE_CHANGED_ENTRY_BYTES: &[u8] = b"echo.provider-artifact-corpus.changed-alt/v1"; +const MAX_ACTUAL_CORPUS_FILE_BYTES: usize = 32 * 1024 * 1024; +const MAX_ACTUAL_CORPUS_FILE_BYTES_U64: u64 = 32 * 1024 * 1024; +const MAX_EXPECTED_CORPUS_FILE_COUNT: usize = 256; +const MAX_EXPECTED_CORPUS_TOTAL_BYTES: usize = 64 * 1024 * 1024; +const MAX_ACTUAL_CORPUS_ENTRY_COUNT: usize = 1_024; +const MAX_ACTUAL_CORPUS_READ_BYTES: usize = + MAX_EXPECTED_CORPUS_TOTAL_BYTES + MAX_EXPECTED_CORPUS_FILE_COUNT; +const MAX_TEMP_FILE_ATTEMPTS: usize = 1_024; + +static NEXT_TEMP_FILE: AtomicU64 = AtomicU64::new(0); + +/// Reads the exact corpus tree without following symbolic links. +pub fn read_actual_corpus( + root: &Path, + expected: &[ProviderArtifactCorpusFileV1], +) -> Result> { + let expected_by_path = validate_expected_corpus(expected)?; + let Some(root_directory) = open_corpus_root(root, false)? else { + return Ok(Vec::new()); + }; + + read_actual_corpus_from_directory(&root_directory, expected, &expected_by_path) +} + +fn read_actual_corpus_from_directory<'a>( + root: &Dir, + expected: &'a [ProviderArtifactCorpusFileV1], + expected_by_path: &BTreeMap<&'a str, &'a [u8]>, +) -> Result> { + let expected_directories = expected_directories(expected); + let mut actual = Vec::new(); + let mut budget = ActualCorpusReadBudget::default(); + read_actual_directory( + root, + "", + &expected_directories, + expected_by_path, + &mut budget, + &mut actual, + )?; + Ok(actual) +} + +fn validate_expected_corpus( + expected: &[ProviderArtifactCorpusFileV1], +) -> Result> { + if expected.is_empty() { + bail!("expected corpus inventory must not be empty"); + } + if expected.len() > MAX_EXPECTED_CORPUS_FILE_COUNT { + bail!("expected corpus inventory exceeds file limit {MAX_EXPECTED_CORPUS_FILE_COUNT}"); + } + + let mut total_bytes = 0usize; + let mut inventory = BTreeMap::new(); + let mut previous_path: Option<&str> = None; + for file in expected { + if let Some(previous) = previous_path { + if previous.as_bytes() >= file.relative_path().as_bytes() { + bail!( + "expected corpus paths must be strictly sorted: {previous} then {}", + file.relative_path() + ); + } + } + if file.bytes().len() > MAX_ACTUAL_CORPUS_FILE_BYTES { + bail!( + "expected corpus file {} exceeds byte limit {MAX_ACTUAL_CORPUS_FILE_BYTES}", + file.relative_path() + ); + } + total_bytes = total_bytes + .checked_add(file.bytes().len()) + .ok_or_else(|| anyhow!("expected corpus total byte length overflow"))?; + if total_bytes > MAX_EXPECTED_CORPUS_TOTAL_BYTES { + bail!("expected corpus exceeds total byte limit {MAX_EXPECTED_CORPUS_TOTAL_BYTES}"); + } + inventory.insert(file.relative_path(), file.bytes()); + previous_path = Some(file.relative_path()); + } + for file in expected { + for (separator, _) in file.relative_path().match_indices('/') { + let ancestor = &file.relative_path()[..separator]; + if inventory.contains_key(ancestor) { + bail!("expected corpus path is both file and directory: {ancestor}"); + } + } + } + let directory_count = expected_directories(expected).len(); + let expected_entry_count = expected + .len() + .checked_add(directory_count) + .ok_or_else(|| anyhow!("expected corpus entry count overflow"))?; + if expected_entry_count > MAX_ACTUAL_CORPUS_ENTRY_COUNT { + bail!("expected corpus tree exceeds entry limit {MAX_ACTUAL_CORPUS_ENTRY_COUNT}"); + } + Ok(inventory) +} + +#[derive(Default)] +struct ActualCorpusReadBudget { + entries: usize, + read_bytes: usize, +} + +impl ActualCorpusReadBudget { + fn observe_entry(&mut self) -> Result<()> { + self.entries = self + .entries + .checked_add(1) + .ok_or_else(|| anyhow!("actual corpus entry count overflow"))?; + if self.entries > MAX_ACTUAL_CORPUS_ENTRY_COUNT { + bail!("actual corpus exceeds entry limit {MAX_ACTUAL_CORPUS_ENTRY_COUNT}"); + } + Ok(()) + } + + fn reserve_read(&mut self, bytes: usize) -> Result<()> { + self.read_bytes = self + .read_bytes + .checked_add(bytes) + .ok_or_else(|| anyhow!("actual corpus read byte count overflow"))?; + if self.read_bytes > MAX_ACTUAL_CORPUS_READ_BYTES { + bail!("actual corpus exceeds read limit {MAX_ACTUAL_CORPUS_READ_BYTES}"); + } + Ok(()) + } +} + +fn expected_directories(expected: &[ProviderArtifactCorpusFileV1]) -> BTreeSet { + let mut directories = BTreeSet::new(); + for file in expected { + let components = file.relative_path().split('/').collect::>(); + for end in 1..components.len() { + directories.insert(components[..end].join("/")); + } + } + directories +} + +fn read_actual_directory( + directory: &Dir, + relative_directory: &str, + expected_directories: &BTreeSet, + expected_by_path: &BTreeMap<&str, &[u8]>, + budget: &mut ActualCorpusReadBudget, + actual: &mut Vec, +) -> Result<()> { + let mut entries = Vec::new(); + for entry in directory + .entries() + .with_context(|| format!("failed to read corpus directory {relative_directory}"))? + { + budget.observe_entry()?; + entries.push(entry.with_context(|| { + format!("failed to enumerate corpus directory {relative_directory}") + })?); + } + entries.sort_by_key(cap_std::fs::DirEntry::file_name); + + for entry in entries { + let raw_file_name = entry.file_name(); + let diagnostic_path = Path::new(relative_directory).join(&raw_file_name); + let file_name = utf8_file_name(raw_file_name.clone(), &diagnostic_path)?; + let relative_path = if relative_directory.is_empty() { + file_name + } else { + format!("{relative_directory}/{file_name}") + }; + let file_type = entry + .file_type() + .with_context(|| format!("failed to inspect corpus entry {relative_path}"))?; + + if file_type.is_file() { + let Some(expected_bytes) = expected_by_path.get(relative_path.as_str()).copied() else { + actual.push(ProviderArtifactCorpusFileV1::new( + relative_path, + UNEXPECTED_ENTRY_BYTES, + )?); + continue; + }; + let file = open_file_nofollow(directory, &raw_file_name) + .with_context(|| format!("failed to open corpus file {relative_path}"))?; + let metadata = file + .metadata() + .with_context(|| format!("failed to inspect corpus file {relative_path}"))?; + if !metadata.is_file() + || metadata.len() > MAX_ACTUAL_CORPUS_FILE_BYTES_U64 + || metadata.len() != expected_bytes.len() as u64 + { + actual.push(ProviderArtifactCorpusFileV1::new( + relative_path, + changed_entry_bytes(expected_bytes), + )?); + } else { + let read_limit = expected_bytes.len().checked_add(1).ok_or_else(|| { + anyhow!("actual corpus read limit overflow for {relative_path}") + })?; + budget.reserve_read(read_limit)?; + let mut bytes = Vec::new(); + file.take(read_limit as u64) + .read_to_end(&mut bytes) + .with_context(|| format!("failed to read corpus file {relative_path}"))?; + let exact_bytes = if bytes.len() == expected_bytes.len() { + &bytes + } else { + changed_entry_bytes(expected_bytes) + }; + actual.push(ProviderArtifactCorpusFileV1::new( + relative_path, + exact_bytes, + )?); + } + } else if file_type.is_dir() && expected_directories.contains(&relative_path) { + let child = open_dir_nofollow(directory, &raw_file_name) + .with_context(|| format!("failed to open corpus directory {relative_path}"))?; + read_actual_directory( + &child, + &relative_path, + expected_directories, + expected_by_path, + budget, + actual, + )?; + } else { + let marker = expected_by_path + .get(relative_path.as_str()) + .map_or(NON_REGULAR_ENTRY_BYTES, |expected| { + changed_entry_bytes(expected) + }); + actual.push(ProviderArtifactCorpusFileV1::new(relative_path, marker)?); + } + } + Ok(()) +} + +fn changed_entry_bytes(expected: &[u8]) -> &'static [u8] { + if expected == CHANGED_ENTRY_BYTES { + ALTERNATE_CHANGED_ENTRY_BYTES + } else { + CHANGED_ENTRY_BYTES + } +} + +fn utf8_file_name(file_name: OsString, path: &Path) -> Result { + file_name.into_string().map_err(|name| { + anyhow!( + "corpus entry name is not UTF-8 at {}: {}", + path.display(), + name.to_string_lossy() + ) + }) +} + +fn open_corpus_root(root: &Path, create: bool) -> Result> { + let absolute = std::path::absolute(root) + .with_context(|| format!("failed to resolve corpus root {}", root.display()))?; + let Some(name) = absolute.file_name() else { + return Dir::open_ambient_dir(&absolute, ambient_authority()) + .map(Some) + .with_context(|| format!("failed to open corpus root {}", absolute.display())); + }; + let parent_path = absolute.parent().ok_or_else(|| { + anyhow!( + "corpus root has no parent directory: {}", + absolute.display() + ) + })?; + + if create { + std::fs::create_dir_all(parent_path).with_context(|| { + format!( + "failed to create corpus root parent {}", + parent_path.display() + ) + })?; + } + let parent = match Dir::open_ambient_dir(parent_path, ambient_authority()) { + Ok(parent) => parent, + Err(error) if !create && error.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(error) => { + return Err(error).with_context(|| { + format!( + "failed to open corpus root parent {}", + parent_path.display() + ) + }); + } + }; + + if create { + ensure_corpus_directory(&parent, name, &absolute).map(Some) + } else { + open_existing_corpus_directory(&parent, name, &absolute) + } +} + +fn open_existing_corpus_directory( + parent: &Dir, + name: &OsStr, + display_path: &Path, +) -> Result> { + match parent.symlink_metadata(name) { + Ok(metadata) if metadata.file_type().is_symlink() => { + bail!( + "refusing to traverse corpus directory symlink {}", + display_path.display() + ); + } + Ok(metadata) if !metadata.is_dir() => { + bail!( + "corpus directory path is not a directory: {}", + display_path.display() + ); + } + Ok(_) => open_dir_nofollow(parent, name) + .map(Some) + .with_context(|| format!("failed to open corpus directory {}", display_path.display())), + Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None), + Err(error) => Err(error).with_context(|| { + format!( + "failed to inspect corpus directory {}", + display_path.display() + ) + }), + } +} + +fn ensure_corpus_directory(parent: &Dir, name: &OsStr, display_path: &Path) -> Result { + if let Some(directory) = open_existing_corpus_directory(parent, name, display_path)? { + return Ok(directory); + } + + match parent.create_dir(name) { + Ok(()) => {} + Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => {} + Err(error) => { + return Err(error).with_context(|| { + format!( + "failed to create corpus directory {}", + display_path.display() + ) + }); + } + } + open_existing_corpus_directory(parent, name, display_path)?.ok_or_else(|| { + anyhow!( + "created corpus directory disappeared before it could be opened: {}", + display_path.display() + ) + }) +} + +fn open_dir_nofollow(parent: &Dir, name: &OsStr) -> std::io::Result { + let mut options = OpenOptions::new(); + options.read(true); + options.follow(FollowSymlinks::No); + options.maybe_dir(true); + let file = parent.open_with(name, &options)?; + if !file.metadata()?.is_dir() { + return Err(std::io::Error::new( + std::io::ErrorKind::NotADirectory, + "corpus path component is not a directory", + )); + } + Ok(Dir::from_std_file(file.into_std())) +} + +fn open_file_nofollow(parent: &Dir, name: &OsStr) -> std::io::Result { + let mut options = OpenOptions::new(); + options.read(true); + options.follow(FollowSymlinks::No); + parent.open_with(name, &options) +} + +/// Writes an exact corpus tree after refusing unsafe or unexpected entries. +#[allow(clippy::print_stdout)] +pub fn write_corpus(root: &Path, files: &[ProviderArtifactCorpusFileV1]) -> Result<()> { + let expected_by_path = validate_expected_corpus(files)?; + let root_directory = open_corpus_root(root, true)?.ok_or_else(|| { + anyhow!( + "created corpus root could not be opened: {}", + root.display() + ) + })?; + let actual = read_actual_corpus_from_directory(&root_directory, files, &expected_by_path)?; + if let Some(unexpected) = actual + .iter() + .find(|file| !expected_by_path.contains_key(file.relative_path())) + { + bail!( + "refusing to generate over unexpected corpus entry {}", + unexpected.relative_path() + ); + } + + let directories = prepare_corpus_directories(&root_directory, root, files)?; + preflight_corpus_write(&directories, files)?; + for file in files { + let (parent, leaf) = corpus_file_parts(file.relative_path()); + let directory = directories.get(parent).ok_or_else(|| { + anyhow!( + "corpus file parent was not retained: {}", + file.relative_path() + ) + })?; + let path = root.join(file.relative_path()); + replace_corpus_file(directory, OsStr::new(leaf), file.bytes(), &path)?; + println!(" wrote {}", path.display()); + } + Ok(()) +} + +fn prepare_corpus_directories( + root: &Dir, + root_display: &Path, + files: &[ProviderArtifactCorpusFileV1], +) -> Result> { + let mut directories = BTreeMap::new(); + directories.insert( + String::new(), + root.try_clone() + .context("failed to retain the corpus root directory")?, + ); + + for relative_path in expected_directories(files) { + let (parent, name) = corpus_file_parts(&relative_path); + let parent_directory = directories + .get(parent) + .ok_or_else(|| anyhow!("corpus directory parent was not retained: {relative_path}"))?; + let directory = ensure_corpus_directory( + parent_directory, + OsStr::new(name), + &root_display.join(&relative_path), + )?; + directories.insert(relative_path, directory); + } + + Ok(directories) +} + +fn preflight_corpus_write( + directories: &BTreeMap, + files: &[ProviderArtifactCorpusFileV1], +) -> Result<()> { + for file in files { + let (parent, leaf) = corpus_file_parts(file.relative_path()); + let directory = directories.get(parent).ok_or_else(|| { + anyhow!( + "corpus file parent was not retained: {}", + file.relative_path() + ) + })?; + validate_existing_corpus_file( + directory, + OsStr::new(leaf), + Path::new(file.relative_path()), + )?; + } + Ok(()) +} + +fn corpus_file_parts(relative_path: &str) -> (&str, &str) { + relative_path + .rsplit_once('/') + .unwrap_or(("", relative_path)) +} + +fn validate_existing_corpus_file(parent: &Dir, leaf: &OsStr, display_path: &Path) -> Result<()> { + match parent.symlink_metadata(leaf) { + Ok(metadata) if metadata.file_type().is_symlink() => { + bail!( + "refusing to replace corpus symlink {}", + display_path.display() + ); + } + Ok(metadata) if !metadata.is_file() => { + bail!( + "refusing to replace non-file corpus entry {}", + display_path.display() + ); + } + Ok(_) => Ok(()), + Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()), + Err(error) => Err(error) + .with_context(|| format!("failed to inspect corpus file {}", display_path.display())), + } +} + +fn replace_corpus_file( + parent: &Dir, + leaf: &OsStr, + bytes: &[u8], + display_path: &Path, +) -> Result<()> { + validate_existing_corpus_file(parent, leaf, display_path)?; + let display_parent = display_path.parent().ok_or_else(|| { + anyhow!( + "corpus file has no display parent: {}", + display_path.display() + ) + })?; + let (temporary_name, mut temporary_file) = create_temporary_file(parent, display_parent)?; + + let write_result = temporary_file + .write_all(bytes) + .with_context(|| { + format!( + "failed to write temporary corpus file {}", + display_parent.join(&temporary_name).display() + ) + }) + .and_then(|()| { + temporary_file.sync_all().with_context(|| { + format!( + "failed to sync temporary corpus file {}", + display_parent.join(&temporary_name).display() + ) + }) + }); + drop(temporary_file); + if let Err(error) = write_result { + remove_temporary_file(parent, &temporary_name); + return Err(error); + } + + validate_existing_corpus_file(parent, leaf, display_path)?; + if let Err(error) = replace_temporary_file(parent, &temporary_name, leaf, display_path) { + remove_temporary_file(parent, &temporary_name); + return Err(error); + } + Ok(()) +} + +fn create_temporary_file(parent: &Dir, display_parent: &Path) -> Result<(OsString, File)> { + for _ in 0..MAX_TEMP_FILE_ATTEMPTS { + let sequence = NEXT_TEMP_FILE.fetch_add(1, Ordering::Relaxed); + let name = OsString::from(format!( + ".echo-provider-artifact-{}-{sequence}.tmp", + std::process::id() + )); + let mut options = OpenOptions::new(); + options.write(true).create_new(true); + options.follow(FollowSymlinks::No); + match parent.open_with(&name, &options) { + Ok(file) => return Ok((name, file)), + Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => {} + Err(error) => { + return Err(error).with_context(|| { + format!( + "failed to create temporary corpus file {}", + display_parent.join(&name).display() + ) + }); + } + } + } + bail!( + "failed to reserve a temporary corpus file in {} after {} attempts", + display_parent.display(), + MAX_TEMP_FILE_ATTEMPTS + ); +} + +fn replace_temporary_file( + parent: &Dir, + temporary_name: &OsStr, + destination_name: &OsStr, + destination_display: &Path, +) -> Result<()> { + replace_temporary_file_with( + temporary_name, + destination_name, + destination_display, + |from, to| parent.rename(from, parent, to), + ) +} + +fn replace_temporary_file_with( + temporary_name: &OsStr, + destination_name: &OsStr, + destination_display: &Path, + replace: impl FnOnce(&OsStr, &OsStr) -> std::io::Result<()>, +) -> Result<()> { + replace(temporary_name, destination_name).with_context(|| { + format!( + "failed to replace corpus file {} from {}", + destination_display.display(), + temporary_name.to_string_lossy() + ) + }) +} + +fn remove_temporary_file(parent: &Dir, name: &OsStr) { + drop(parent.remove_file(name)); +} + +#[cfg(test)] +mod tests { + use super::{ + open_dir_nofollow, read_actual_corpus, replace_corpus_file, replace_temporary_file_with, + write_corpus, NEXT_TEMP_FILE, + }; + use crate::provider_corpus::ProviderArtifactCorpusFileV1; + use anyhow::Result; + use cap_std::{ambient_authority, fs::Dir}; + use std::ffi::OsStr; + use std::path::{Path, PathBuf}; + use std::sync::atomic::Ordering; + + fn test_directory(label: &str) -> PathBuf { + let path = std::env::temp_dir().join(format!( + "echo-provider-{label}-{}-{}", + std::process::id(), + NEXT_TEMP_FILE.fetch_add(1, Ordering::Relaxed) + )); + drop(std::fs::remove_dir_all(&path)); + path + } + + #[test] + fn failed_replacement_preserves_existing_destination() -> Result<()> { + let root = test_directory("replacement-test"); + std::fs::create_dir(&root)?; + let temporary = root.join("temporary"); + let destination = root.join("destination"); + std::fs::write(&temporary, b"replacement")?; + std::fs::write(&destination, b"admitted")?; + + let result = replace_temporary_file_with( + OsStr::new("temporary"), + OsStr::new("destination"), + &destination, + |_, _| Err(std::io::Error::other("injected replacement failure")), + ); + + assert!(result.is_err()); + assert_eq!(std::fs::read(&destination)?, b"admitted"); + assert_eq!(std::fs::read(&temporary)?, b"replacement"); + std::fs::remove_dir_all(root)?; + Ok(()) + } + + #[test] + fn invalid_expected_inventories_refuse_before_root_creation() -> Result<()> { + let duplicate_root = test_directory("duplicate-inventory"); + let duplicate = vec![ + ProviderArtifactCorpusFileV1::new("same", b"first")?, + ProviderArtifactCorpusFileV1::new("same", b"second")?, + ]; + let Err(duplicate_error) = write_corpus(&duplicate_root, &duplicate) else { + anyhow::bail!("duplicate expected paths did not fail closed"); + }; + assert!(duplicate_error.to_string().contains("strictly sorted")); + assert!(!duplicate_root.exists()); + + let unordered_root = test_directory("unordered-inventory"); + let unordered = vec![ + ProviderArtifactCorpusFileV1::new("z", b"last")?, + ProviderArtifactCorpusFileV1::new("a", b"first")?, + ]; + let Err(unordered_error) = write_corpus(&unordered_root, &unordered) else { + anyhow::bail!("unordered expected paths did not fail closed"); + }; + assert!(unordered_error.to_string().contains("strictly sorted")); + assert!(!unordered_root.exists()); + + let empty_root = test_directory("empty-inventory"); + let Err(empty_error) = write_corpus(&empty_root, &[]) else { + anyhow::bail!("an empty expected inventory did not fail closed"); + }; + assert!(empty_error.to_string().contains("must not be empty")); + assert!(!empty_root.exists()); + + let prefix_root = test_directory("prefix-inventory"); + let prefix_conflict = vec![ + ProviderArtifactCorpusFileV1::new("artifact", b"file")?, + ProviderArtifactCorpusFileV1::new("artifact/child", b"child")?, + ]; + let Err(prefix_error) = write_corpus(&prefix_root, &prefix_conflict) else { + anyhow::bail!("a file/directory prefix conflict did not fail closed"); + }; + assert!(prefix_error.to_string().contains("both file and directory")); + assert!(!prefix_root.exists()); + + let interposed_prefix_root = test_directory("interposed-prefix-inventory"); + let interposed_prefix_conflict = vec![ + ProviderArtifactCorpusFileV1::new("artifact", b"file")?, + ProviderArtifactCorpusFileV1::new("artifact.", b"interposed")?, + ProviderArtifactCorpusFileV1::new("artifact/child", b"child")?, + ]; + let Err(interposed_prefix_error) = + write_corpus(&interposed_prefix_root, &interposed_prefix_conflict) + else { + anyhow::bail!("an interposed file/directory conflict did not fail closed"); + }; + assert!(interposed_prefix_error + .to_string() + .contains("both file and directory")); + assert!(!interposed_prefix_root.exists()); + + let overfull_root = test_directory("overfull-inventory"); + let overfull = (0..257) + .map(|index| ProviderArtifactCorpusFileV1::new(format!("entry-{index:04}"), &[])) + .collect::, _>>()?; + let Err(overfull_error) = write_corpus(&overfull_root, &overfull) else { + anyhow::bail!("an excessive expected inventory did not fail closed"); + }; + assert!(overfull_error.to_string().contains("file limit 256")); + assert!(!overfull_root.exists()); + + let deep_root = test_directory("deep-inventory"); + let deep = (0..5) + .map(|branch| { + let path = std::iter::once(format!("b{branch}")) + .chain(std::iter::repeat_n("s".to_owned(), 210)) + .chain(std::iter::once("file".to_owned())) + .collect::>() + .join("/"); + ProviderArtifactCorpusFileV1::new(path, &[]) + }) + .collect::, _>>()?; + let Err(deep_error) = write_corpus(&deep_root, &deep) else { + anyhow::bail!("an excessive expected directory tree did not fail closed"); + }; + assert!(deep_error + .to_string() + .contains("tree exceeds entry limit 1024")); + assert!(!deep_root.exists()); + Ok(()) + } + + #[test] + fn actual_tree_scan_is_bounded_and_does_not_read_unexpected_files() -> Result<()> { + const ADVERSARIAL_ENTRY_COUNT: usize = 1_025; + const UNEXPECTED_MARKER: &[u8] = b"echo.provider-artifact-corpus.unexpected/v1"; + + let unexpected_root = test_directory("unexpected-content"); + std::fs::create_dir(&unexpected_root)?; + std::fs::write( + unexpected_root.join("operator-owned"), + b"operator-secret-bytes", + )?; + let expected = vec![ProviderArtifactCorpusFileV1::new("expected", b"expected")?]; + let actual = read_actual_corpus(&unexpected_root, &expected)?; + assert_eq!(actual.len(), 1); + assert_eq!(actual[0].relative_path(), "operator-owned"); + assert_eq!(actual[0].bytes(), UNEXPECTED_MARKER); + assert_eq!( + std::fs::read(unexpected_root.join("operator-owned"))?, + b"operator-secret-bytes" + ); + std::fs::remove_dir_all(unexpected_root)?; + + let marker_root = test_directory("marker-collision"); + std::fs::create_dir_all(marker_root.join("expected"))?; + let marker_expected = vec![ProviderArtifactCorpusFileV1::new( + "expected", + super::NON_REGULAR_ENTRY_BYTES, + )?]; + let marker_actual = read_actual_corpus(&marker_root, &marker_expected)?; + assert_ne!(marker_actual[0].bytes(), marker_expected[0].bytes()); + std::fs::remove_dir_all(marker_root)?; + + let crowded_root = test_directory("entry-budget"); + std::fs::create_dir(&crowded_root)?; + for index in 0..ADVERSARIAL_ENTRY_COUNT { + std::fs::write(crowded_root.join(format!("unexpected-{index:04}")), [])?; + } + let Err(error) = read_actual_corpus(&crowded_root, &expected) else { + anyhow::bail!("an excessive actual inventory did not fail closed"); + }; + assert!(error.to_string().contains("entry limit 1024")); + assert_eq!( + std::fs::read_dir(&crowded_root)?.count(), + ADVERSARIAL_ENTRY_COUNT + ); + std::fs::remove_dir_all(crowded_root)?; + Ok(()) + } + + #[cfg(unix)] + #[test] + fn retained_parent_handle_prevents_symlink_redirection() -> Result<()> { + use std::os::unix::fs::symlink; + + let root = test_directory("parent-swap"); + let outside = test_directory("parent-swap-outside"); + let evidence = root.join("evidence"); + let parked = root.join("evidence-parked"); + std::fs::create_dir_all(&evidence)?; + std::fs::create_dir_all(&outside)?; + let root_directory = Dir::open_ambient_dir(&root, ambient_authority())?; + let evidence_directory = open_dir_nofollow(&root_directory, OsStr::new("evidence"))?; + + std::fs::rename(&evidence, &parked)?; + symlink(&outside, &evidence)?; + + replace_corpus_file( + &evidence_directory, + OsStr::new("artifact"), + b"canonical", + Path::new("evidence/artifact"), + )?; + + assert!(!outside.join("artifact").exists()); + assert_eq!(std::fs::read(parked.join("artifact"))?, b"canonical"); + std::fs::remove_dir_all(root)?; + std::fs::remove_dir_all(outside)?; + Ok(()) + } +} diff --git a/crates/echo-wesley-gen/src/provider_package.rs b/crates/echo-wesley-gen/src/provider_package.rs new file mode 100644 index 00000000..b1aa7256 --- /dev/null +++ b/crates/echo-wesley-gen/src/provider_package.rs @@ -0,0 +1,1800 @@ +// SPDX-License-Identifier: Apache-2.0 +// © James Ross Ω FLYING•ROBOTS +//! Pure assembly and admission of the digest-locked Echo Edict provider package. +//! +//! The Edict provider manifest is a typed JSON interchange envelope, not an +//! Edict canonical-CBOR artifact. Echo therefore owns its exact deterministic +//! JSON rendering and a separate, non-self-referential package identity. The +//! package identity binds the manifest semantics plus the raw exact-byte +//! identity of every non-manifest member. It does not install a provider or +//! grant Echo runtime authority. + +use std::fmt; + +use echo_edict_canonical::{ + decode_canonical_cbor_v1, digest_canonical_value_v1, encode_canonical_cbor_v1, CanonicalValueV1, +}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use wesley_core::{ + GenerationArtifactReferenceV1, GenerationContractError, GenerationContractErrorKind, + GenerationProvenanceManifestV1, GenerationReviewV1, +}; + +use crate::provider_artifacts::{ + GeneratedProviderSchemaV1, ProviderPrimaryArtifactsV1, SchemaValidatedCanonicalProviderOutputV1, +}; +use crate::provider_corpus::{render_provider_artifact_corpus_v1, validate_corpus_path}; +use crate::provider_generation::ProviderGenerationInputV1; +use crate::provider_provenance::{ProviderGenerationProvenanceV1, ProviderGeneratorMaterialV1}; +use crate::provider_review::ProviderGenerationReviewV1; +use crate::provider_semantics::{ + ArtifactSchemaFormat, GeneratedArtifactDeclaration, GeneratedArtifactKind, + ProviderComponentDeclaration, ProviderComponentKind, +}; + +/// Domain framing the Echo-owned package-closure identity. +pub const ECHO_PROVIDER_PACKAGE_CLOSURE_DOMAIN_V1: &str = "echo.edict-provider-package-closure/v1"; +/// Exact deterministic JSON rendering contract for the Edict manifest. +pub const ECHO_PROVIDER_MANIFEST_ENCODING_V1: &str = "echo.provider-manifest.pretty-json/v1"; +/// Logical package path of the derived provider manifest. +pub const ECHO_PROVIDER_MANIFEST_PATH_V1: &str = "provider-manifest.echo.json"; + +const PROVIDER_MANIFEST_API_V1: &str = "edict.provider-manifest/v1"; +const PROVIDER_ABI_V1: &str = "edict:target-provider@1.0.0"; +const PROVIDER_COORDINATE_V1: &str = "echo.edict-provider@1"; +const PROVIDER_MANIFEST_ROLE_V1: &str = "provider-manifest.echo"; +const PROVIDER_MANIFEST_COORDINATE_V1: &str = "echo.edict-provider-manifest@1"; +const SEMANTIC_SOURCE_COORDINATE_V1: &str = "echo.semantic-schema@1"; +const GENERATOR_COORDINATE_V1: &str = "echo-wesley-gen.provider-artifact-generator@1"; +const EXPECTED_ARTIFACT_COUNT: usize = 10; +const EXPECTED_SCHEMA_BINDING_COUNT: usize = 24; +const EXPECTED_MEMBER_COUNT: usize = 24; +const EXPECTED_FILE_COUNT: usize = 25; +const MAX_COMPONENT_BYTES: usize = 16 * 1024 * 1024; +const MAX_PACKAGE_BYTES: usize = 64 * 1024 * 1024; +const MAX_MANIFEST_BYTES: usize = 1024 * 1024; +const MAX_EVIDENCE_BYTES: usize = 4 * 1024 * 1024; +const MAX_DIAGNOSTIC_BYTES: usize = 256; + +const EXPECTED_PACKAGE_PATHS: [&str; EXPECTED_FILE_COUNT] = [ + "components/lowerer.echo-dpo.component.wasm", + "components/verifier.echo-dpo.component.wasm", + "generated/evidence/provenance.provider-generation.json", + "generated/evidence/review.provider-generation.json", + "generated/primary/authority-facts.echo-dpo.cbor", + "generated/primary/authority-facts.echo-lawpack.cbor", + "generated/primary/generated-artifact-profile.echo-dpo-registration.cbor", + "generated/primary/lawpack.echo-dpo.cbor", + "generated/primary/schema.echo-provider-artifacts.cddl", + "generated/primary/target-profile.echo-dpo.cbor", + "generated/resources/resource.conformance-corpus.cbor", + "generated/resources/resource.lawpack-compatibility.cbor", + "generated/resources/resource.lawpack-exports.cbor", + "generated/resources/resource.lawpack-target-adapter.cbor", + "generated/resources/resource.lawpack-verifier.cbor", + "generated/resources/resource.target-bundle-profile.cbor", + "generated/resources/resource.target-cost-algebra.cbor", + "generated/resources/resource.target-footprint-algebra.cbor", + "generated/resources/resource.target-intrinsics.cbor", + "generated/resources/resource.target-ir.cbor", + "generated/resources/resource.target-lowerer-contract.cbor", + "generated/resources/resource.target-obstruction-taxonomy.cbor", + "generated/resources/resource.target-operation-profiles.cbor", + "generated/resources/resource.target-verifier-contract.cbor", + ECHO_PROVIDER_MANIFEST_PATH_V1, +]; + +/// Stable failures returned by provider-package assembly and admission. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ProviderPackageErrorKind { + /// Explicit component material was empty, oversized, or named an invalid role. + ComponentInvalid, + /// A source-declared component had no exact supplied bytes. + ComponentMissing, + /// A component role was supplied more than once. + ComponentDuplicate, + /// Supplied component material did not belong to the source-owned closure. + ComponentUnexpected, + /// The verified generated corpus could not be reproduced from exact inputs. + GeneratedCorpusInvalid, + /// A generated artifact did not match its source declaration. + ArtifactMismatch, + /// A required generated artifact was absent. + ArtifactMissing, + /// A package path was unsafe or outside the fixed v1 inventory. + PackagePathInvalid, + /// The complete explicit package exceeded the fixed v1 byte bound. + PackageSizeExceeded, + /// A required package member was absent. + PackageMemberMissing, + /// A package member path was repeated. + PackageMemberDuplicate, + /// A package contained a member outside the exact v1 closure. + PackageMemberUnexpected, + /// Supplied package files were not in strict path order. + PackageMemberOutOfOrder, + /// The manifest was not strict JSON matching the Echo v1 mirror. + ManifestMalformed, + /// The manifest bytes were an alternate rendering of the typed value. + ManifestNoncanonical, + /// The manifest selected a wrong API, ABI, role, coordinate, or closure. + ManifestContractMismatch, + /// A routed artifact's bytes did not reproduce its manifest digest. + ArtifactDigestMismatch, + /// A routed artifact used an incompatible provenance envelope. + ArtifactSourceMismatch, + /// Packaged Wesley provenance disagreed with generated routes or primary bytes. + GenerationProvenanceMismatch, + /// Packaged Wesley review disagreed with the canonical provenance projection. + GenerationReviewMismatch, + /// The manifest schema-domain closure differed from the exact source contract. + SchemaBindingMismatch, + /// A digest review string was not strict lowercase SHA-256. + DigestInvalid, + /// The canonical package-closure value could not be encoded or hashed. + PackageIdentityFailed, + /// The manifest provider identity, computed closure, and caller pin disagreed. + ProviderIdentityMismatch, + /// Deterministic manifest serialization failed. + ManifestSerializationFailed, +} + +impl ProviderPackageErrorKind { + const fn label(self) -> &'static str { + match self { + Self::ComponentInvalid => "component-invalid", + Self::ComponentMissing => "component-missing", + Self::ComponentDuplicate => "component-duplicate", + Self::ComponentUnexpected => "component-unexpected", + Self::GeneratedCorpusInvalid => "generated-corpus-invalid", + Self::ArtifactMismatch => "artifact-mismatch", + Self::ArtifactMissing => "artifact-missing", + Self::PackagePathInvalid => "package-path-invalid", + Self::PackageSizeExceeded => "package-size-exceeded", + Self::PackageMemberMissing => "package-member-missing", + Self::PackageMemberDuplicate => "package-member-duplicate", + Self::PackageMemberUnexpected => "package-member-unexpected", + Self::PackageMemberOutOfOrder => "package-member-out-of-order", + Self::ManifestMalformed => "manifest-malformed", + Self::ManifestNoncanonical => "manifest-noncanonical", + Self::ManifestContractMismatch => "manifest-contract-mismatch", + Self::ArtifactDigestMismatch => "artifact-digest-mismatch", + Self::ArtifactSourceMismatch => "artifact-source-mismatch", + Self::GenerationProvenanceMismatch => "generation-provenance-mismatch", + Self::GenerationReviewMismatch => "generation-review-mismatch", + Self::SchemaBindingMismatch => "schema-binding-mismatch", + Self::DigestInvalid => "digest-invalid", + Self::PackageIdentityFailed => "package-identity-failed", + Self::ProviderIdentityMismatch => "provider-identity-mismatch", + Self::ManifestSerializationFailed => "manifest-serialization-failed", + } + } +} + +/// Structured provider-package failure. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ProviderPackageError { + kind: ProviderPackageErrorKind, + subject: String, + reference: String, + wesley_contract_kind: Option, +} + +impl ProviderPackageError { + /// Returns the stable failure category. + #[must_use] + pub const fn kind(&self) -> ProviderPackageErrorKind { + self.kind + } + + /// Returns the member, role, or contract being validated. + #[must_use] + pub fn subject(&self) -> &str { + &self.subject + } + + /// Returns the conflicting or rejected identity. + #[must_use] + pub fn reference(&self) -> &str { + &self.reference + } + + /// Returns the typed Wesley cause for provenance or review rejection. + #[must_use] + pub const fn wesley_contract_kind(&self) -> Option { + self.wesley_contract_kind + } + + fn new( + kind: ProviderPackageErrorKind, + subject: impl Into, + reference: impl Into, + ) -> Self { + Self { + kind, + subject: bounded_diagnostic(subject.into()), + reference: bounded_diagnostic(reference.into()), + wesley_contract_kind: None, + } + } + + fn wesley(mut self, error: &GenerationContractError) -> Self { + self.wesley_contract_kind = Some(error.kind); + self + } +} + +impl fmt::Display for ProviderPackageError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + formatter, + "provider package {}: {} -> {}", + self.kind.label(), + self.subject, + self.reference + ) + } +} + +impl std::error::Error for ProviderPackageError {} + +/// Required digest-locked resource reference in the generated Edict manifest. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct ProviderManifestResourceRefV1 { + /// Stable owner-defined resource coordinate. + pub coordinate: String, + /// Strict lowercase SHA-256 review rendering. + pub digest: String, +} + +/// Artifact kinds mirrored from Edict's `edict.provider-manifest/v1` envelope. +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum ProviderManifestArtifactKindV1 { + /// Canonical Edict lawpack manifest. + Lawpack, + /// Canonical target-profile manifest. + TargetProfile, + /// Canonical authority-facts document. + AuthorityFacts, + /// Provider manifest artifact; excluded from this manifest's own inventory. + ProviderManifest, + /// Non-authoritative generation review JSON. + ReviewArtifact, + /// Echo generated-artifact profile. + GeneratedArtifactProfile, + /// Wesley generation-provenance JSON. + GenerationProvenance, + /// Self-contained generated CDDL schema. + ArtifactSchema, + /// Target-provider lowerer component. + Lowerer, + /// Target-provider verifier component. + Verifier, +} + +impl ProviderManifestArtifactKindV1 { + const fn as_str(self) -> &'static str { + match self { + Self::Lawpack => "lawpack", + Self::TargetProfile => "targetProfile", + Self::AuthorityFacts => "authorityFacts", + Self::ProviderManifest => "providerManifest", + Self::ReviewArtifact => "reviewArtifact", + Self::GeneratedArtifactProfile => "generatedArtifactProfile", + Self::GenerationProvenance => "generationProvenance", + Self::ArtifactSchema => "artifactSchema", + Self::Lowerer => "lowerer", + Self::Verifier => "verifier", + } + } +} + +/// Provenance envelope for one manifest artifact route. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(tag = "kind", rename_all = "camelCase", deny_unknown_fields)] +pub enum ProviderManifestArtifactSourceV1 { + /// Generated metadata bound to exact authored source and generator identities. + Generated { + /// Raw exact-byte semantic-source reference. + #[serde(rename = "semanticSource")] + semantic_source: ProviderManifestResourceRefV1, + /// Raw exact-byte generator source-bundle reference. + generator: ProviderManifestResourceRefV1, + }, + /// Executable component bound to its own exact raw bytes. + Component { + /// Resource-identical component reference. + component: ProviderManifestResourceRefV1, + }, +} + +/// One deterministic artifact route in the Edict provider manifest. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct ProviderManifestArtifactV1 { + /// Unique package-local role. + pub role: String, + /// Generic Edict artifact class. + pub artifact_kind: ProviderManifestArtifactKindV1, + /// Digest-locked semantic or exact-byte artifact identity. + pub resource: ProviderManifestResourceRefV1, + /// Generated or component provenance route. + pub source: ProviderManifestArtifactSourceV1, +} + +/// Schema formats accepted by the v1 Edict host. +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum ProviderManifestSchemaFormatV1 { + /// Self-contained generated CDDL. + SelfContainedCddlV1, +} + +impl ProviderManifestSchemaFormatV1 { + const fn as_str(self) -> &'static str { + match self { + Self::SelfContainedCddlV1 => "selfContainedCddlV1", + } + } +} + +/// One immutable domain-to-schema binding in the Edict provider manifest. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct ProviderManifestSchemaBindingV1 { + /// Canonical artifact domain. + pub domain: String, + /// Manifest role containing the self-contained schema. + pub schema_role: String, + /// Frozen schema format. + pub format: ProviderManifestSchemaFormatV1, + /// Exact CDDL root rule. + pub root_rule: String, +} + +/// Deterministically serialized mirror of Edict's provider-manifest v1 value. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct ProviderManifestV1 { + /// Exact Edict manifest API version. + pub api_version: String, + /// Exact frozen target-provider component ABI. + pub provider_abi: String, + /// Echo-owned non-self-referential provider package identity. + pub provider: ProviderManifestResourceRefV1, + /// Ten exact generated/component artifact routes. + pub artifacts: Vec, + /// Twenty-four exact immutable schema-domain bindings. + pub schema_bindings: Vec, +} + +/// Explicit exact bytes for one source-declared provider component. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ProviderPackageComponentMaterialV1 { + role: String, + bytes: Vec, +} + +impl ProviderPackageComponentMaterialV1 { + /// Constructs bounded component material without path or registry discovery. + /// + /// # Errors + /// + /// Returns a stable error for an empty/oversized byte string or empty role. + pub fn new(role: impl Into, bytes: &[u8]) -> Result { + let role = role.into(); + if role.is_empty() || bytes.is_empty() || bytes.len() > MAX_COMPONENT_BYTES { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ComponentInvalid, + role, + bytes.len().to_string(), + )); + } + Ok(Self { + role, + bytes: bytes.to_vec(), + }) + } + + /// Returns the source-declared manifest role. + #[must_use] + pub fn role(&self) -> &str { + &self.role + } + + /// Returns the exact component bytes. + #[must_use] + pub fn bytes(&self) -> &[u8] { + &self.bytes + } +} + +/// One exact logical file in a provider package. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ProviderPackageFileV1 { + relative_path: String, + bytes: Vec, +} + +impl ProviderPackageFileV1 { + /// Constructs a package file with a safe relative POSIX path. + /// + /// # Errors + /// + /// Returns a stable error for paths outside the bounded corpus grammar. + pub fn new( + relative_path: impl Into, + bytes: &[u8], + ) -> Result { + let relative_path = relative_path.into(); + validate_corpus_path(&relative_path).map_err(|error| { + ProviderPackageError::new( + ProviderPackageErrorKind::PackagePathInvalid, + relative_path.clone(), + error.reference(), + ) + })?; + Ok(Self { + relative_path, + bytes: bytes.to_vec(), + }) + } + + /// Returns the slash-normalized logical package path. + #[must_use] + pub fn relative_path(&self) -> &str { + &self.relative_path + } + + /// Returns the exact file bytes. + #[must_use] + pub fn bytes(&self) -> &[u8] { + &self.bytes + } +} + +/// One non-manifest physical member bound into the provider closure. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ProviderPackageMemberV1 { + file: ProviderPackageFileV1, + raw_digest: String, +} + +impl ProviderPackageMemberV1 { + /// Returns the member's logical package path. + #[must_use] + pub fn relative_path(&self) -> &str { + self.file.relative_path() + } + + /// Returns the member's exact bytes. + #[must_use] + pub fn bytes(&self) -> &[u8] { + self.file.bytes() + } + + /// Returns raw SHA-256 of the exact package occurrence. + #[must_use] + pub fn raw_digest(&self) -> &str { + &self.raw_digest + } + + fn new(file: ProviderPackageFileV1) -> Self { + let raw_digest = raw_sha256(file.bytes()); + Self { file, raw_digest } + } +} + +/// Complete deterministic package materials assembled from exact inputs. +/// +/// This value proves package construction only. It is not Edict schema or +/// component compatibility proof and is not Echo runtime authority. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ProviderPackageV1 { + manifest: ProviderManifestV1, + manifest_bytes: Vec, + manifest_content_reference: GenerationArtifactReferenceV1, + closure_bytes: Vec, + members: Vec, + files: Vec, +} + +impl ProviderPackageV1 { + /// Returns the typed provider manifest. + #[must_use] + pub const fn manifest(&self) -> &ProviderManifestV1 { + &self.manifest + } + + /// Returns exact deterministic pretty-JSON manifest bytes. + #[must_use] + pub fn manifest_bytes(&self) -> &[u8] { + &self.manifest_bytes + } + + /// Returns the externally pinnable package-closure identity. + #[must_use] + pub const fn provider_reference(&self) -> &ProviderManifestResourceRefV1 { + &self.manifest.provider + } + + /// Returns raw exact-byte identity of the derived manifest occurrence. + #[must_use] + pub const fn manifest_content_reference(&self) -> &GenerationArtifactReferenceV1 { + &self.manifest_content_reference + } + + /// Returns the exact canonical-CBOR package-closure preimage value bytes. + #[must_use] + pub fn closure_bytes(&self) -> &[u8] { + &self.closure_bytes + } + + /// Returns all 24 non-manifest members in exact path order. + #[must_use] + pub fn members(&self) -> &[ProviderPackageMemberV1] { + &self.members + } + + /// Returns all 25 package files in exact path order. + #[must_use] + pub fn files(&self) -> &[ProviderPackageFileV1] { + &self.files + } +} + +/// Opaque proof that explicit package bytes match their caller-pinned digest root. +/// +/// This proof authenticates inventory, manifest routing, packaged Wesley +/// evidence, and exact bytes. Edict schema construction and component preflight +/// remain separate required crossings before guest execution. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DigestAdmittedProviderPackageV1 { + manifest: ProviderManifestV1, + provider_reference: ProviderManifestResourceRefV1, + files: Vec, +} + +impl DigestAdmittedProviderPackageV1 { + /// Returns the exact manifest admitted by the package boundary. + #[must_use] + pub const fn manifest(&self) -> &ProviderManifestV1 { + &self.manifest + } + + /// Returns the caller-pinned, recomputed provider package identity. + #[must_use] + pub const fn provider_reference(&self) -> &ProviderManifestResourceRefV1 { + &self.provider_reference + } + + /// Returns the admitted exact file closure retained in memory. + #[must_use] + pub fn files(&self) -> &[ProviderPackageFileV1] { + &self.files + } +} + +/// Assembles the deterministic package from verified generated values and exact components. +/// +/// This function is pure. It performs no filesystem, registry, environment, +/// process, clock, network, component execution, or Echo runtime admission. +/// +/// # Errors +/// +/// Returns a structured error when provenance, review, generated artifacts, +/// source-owned component declarations, member inventory, serialization, or +/// package identity disagree. +pub fn assemble_provider_package_v1( + input: &ProviderGenerationInputV1, + primary: &ProviderPrimaryArtifactsV1, + generator: &ProviderGeneratorMaterialV1, + provenance: &ProviderGenerationProvenanceV1, + review: &ProviderGenerationReviewV1, + mut components: Vec, +) -> Result { + let corpus = render_provider_artifact_corpus_v1(input, primary, generator, provenance, review) + .map_err(|error| { + ProviderPackageError::new( + ProviderPackageErrorKind::GeneratedCorpusInvalid, + error.subject(), + error.reference(), + ) + })?; + let source = input.semantic_source().source(); + let package_declaration = &source.package_manifest; + if package_declaration.role != PROVIDER_MANIFEST_ROLE_V1 + || package_declaration.coordinate != PROVIDER_MANIFEST_COORDINATE_V1 + || package_declaration.schema_contract != PROVIDER_MANIFEST_API_V1 + || package_declaration.provider_abi != PROVIDER_ABI_V1 + || package_declaration.provider_coordinate != PROVIDER_COORDINATE_V1 + { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ManifestContractMismatch, + &package_declaration.role, + &package_declaration.coordinate, + )); + } + + let semantic_source = input + .source_artifacts() + .iter() + .find(|artifact| artifact.coordinate == source.coordinate) + .ok_or_else(|| { + ProviderPackageError::new( + ProviderPackageErrorKind::ArtifactMissing, + "semanticSource", + &source.coordinate, + ) + })? + .reference(); + let generated_source = ProviderManifestArtifactSourceV1::Generated { + semantic_source: manifest_ref(&semantic_source.coordinate, &semantic_source.digest), + generator: manifest_ref( + &generator.identity().coordinate, + &generator.identity().digest, + ), + }; + + let mut artifacts = Vec::with_capacity(EXPECTED_ARTIFACT_COUNT); + for declaration in &source.generated_artifacts { + artifacts.push(generated_manifest_artifact( + declaration, + primary, + provenance, + review, + &generated_source, + )?); + } + + components.sort_by(|left, right| left.role.as_bytes().cmp(right.role.as_bytes())); + if let Some(pair) = components + .windows(2) + .find(|pair| pair[0].role == pair[1].role) + { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ComponentDuplicate, + &pair[0].role, + &pair[1].role, + )); + } + let mut component_members = Vec::with_capacity(package_declaration.components.len()); + for declaration in &package_declaration.components { + let material = components + .iter() + .find(|material| material.role == declaration.role) + .ok_or_else(|| { + ProviderPackageError::new( + ProviderPackageErrorKind::ComponentMissing, + &declaration.role, + &declaration.coordinate, + ) + })?; + let resource = manifest_ref(&declaration.coordinate, &raw_sha256(&material.bytes)); + artifacts.push(component_manifest_artifact(declaration, resource.clone())); + component_members.push(ProviderPackageMemberV1::new(ProviderPackageFileV1::new( + format!("components/{}.component.wasm", declaration.role), + &material.bytes, + )?)); + } + if let Some(material) = components.iter().find(|material| { + !package_declaration + .components + .iter() + .any(|declaration| declaration.role == material.role) + }) { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ComponentUnexpected, + &material.role, + "packageManifest.components", + )); + } + artifacts.sort_by(|left, right| left.role.as_bytes().cmp(right.role.as_bytes())); + + let schema_bindings = source + .schema_bindings + .iter() + .map(|binding| ProviderManifestSchemaBindingV1 { + domain: binding.domain.clone(), + schema_role: binding.schema_role.clone(), + format: match binding.format { + ArtifactSchemaFormat::SelfContainedCddlV1 => { + ProviderManifestSchemaFormatV1::SelfContainedCddlV1 + } + }, + root_rule: binding.root_rule.clone(), + }) + .collect::>(); + + let mut members = corpus + .files() + .iter() + .map(|file| { + ProviderPackageFileV1::new(format!("generated/{}", file.relative_path()), file.bytes()) + .map(ProviderPackageMemberV1::new) + }) + .collect::, _>>()?; + members.extend(component_members); + members.sort_by(|left, right| { + left.relative_path() + .as_bytes() + .cmp(right.relative_path().as_bytes()) + }); + validate_member_count_and_order(&members)?; + + let provider_digest = package_closure_digest(&artifacts, &schema_bindings, &members)?; + let manifest = ProviderManifestV1 { + api_version: PROVIDER_MANIFEST_API_V1.to_owned(), + provider_abi: PROVIDER_ABI_V1.to_owned(), + provider: manifest_ref(PROVIDER_COORDINATE_V1, &provider_digest), + artifacts, + schema_bindings, + }; + let manifest_bytes = render_manifest(&manifest)?; + let manifest_content_reference = + GenerationArtifactReferenceV1::for_bytes(PROVIDER_MANIFEST_COORDINATE_V1, &manifest_bytes) + .map_err(|_error| { + ProviderPackageError::new( + ProviderPackageErrorKind::ManifestSerializationFailed, + PROVIDER_MANIFEST_COORDINATE_V1, + "wesley-content-reference", + ) + })?; + let closure_value = + package_closure_value(&manifest.artifacts, &manifest.schema_bindings, &members)?; + let closure_bytes = encode_canonical_cbor_v1(&closure_value).map_err(|_error| { + ProviderPackageError::new( + ProviderPackageErrorKind::PackageIdentityFailed, + ECHO_PROVIDER_PACKAGE_CLOSURE_DOMAIN_V1, + "canonical-cbor", + ) + })?; + let mut files = members + .iter() + .map(|member| member.file.clone()) + .collect::>(); + files.push(ProviderPackageFileV1::new( + ECHO_PROVIDER_MANIFEST_PATH_V1, + &manifest_bytes, + )?); + files.sort_by(|left, right| { + left.relative_path + .as_bytes() + .cmp(right.relative_path.as_bytes()) + }); + validate_file_inventory(&files)?; + validate_total_package_size(&files)?; + + Ok(ProviderPackageV1 { + manifest, + manifest_bytes, + manifest_content_reference, + closure_bytes, + members, + files, + }) +} + +/// Digest-admits an explicit in-memory package against a caller-supplied identity. +/// +/// The boundary requires the exact v1 file inventory and rendering, reproduces +/// every routed artifact identity, binds every non-manifest member into the +/// package root, and returns no partial package on failure. It performs no I/O, +/// does not replace Edict schema/component preflight, and grants no Echo runtime +/// authority. +/// +/// # Errors +/// +/// Returns a structured error for malformed or alternate manifest bytes, +/// missing/duplicate/unexpected/out-of-order files, wrong artifact bytes or +/// provenance, schema-closure disagreement, or provider-root disagreement. +pub fn admit_provider_package_v1( + files: Vec, + expected_provider: &ProviderManifestResourceRefV1, +) -> Result { + validate_file_inventory(&files)?; + validate_total_package_size(&files)?; + let manifest_file = file_by_path(&files, ECHO_PROVIDER_MANIFEST_PATH_V1)?; + validate_member_size(manifest_file, MAX_MANIFEST_BYTES)?; + let manifest: ProviderManifestV1 = + serde_json::from_slice(manifest_file.bytes()).map_err(|_error| { + ProviderPackageError::new( + ProviderPackageErrorKind::ManifestMalformed, + ECHO_PROVIDER_MANIFEST_PATH_V1, + ECHO_PROVIDER_MANIFEST_ENCODING_V1, + ) + })?; + if render_manifest(&manifest)? != manifest_file.bytes() { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ManifestNoncanonical, + ECHO_PROVIDER_MANIFEST_PATH_V1, + raw_sha256(manifest_file.bytes()), + )); + } + validate_expected_provider(&manifest.provider, expected_provider)?; + validate_manifest_contract(&manifest, &files)?; + + let members = files + .iter() + .filter(|file| file.relative_path != ECHO_PROVIDER_MANIFEST_PATH_V1) + .cloned() + .map(ProviderPackageMemberV1::new) + .collect::>(); + validate_member_count_and_order(&members)?; + let computed = manifest_ref( + PROVIDER_COORDINATE_V1, + &package_closure_digest(&manifest.artifacts, &manifest.schema_bindings, &members)?, + ); + if manifest.provider != computed || expected_provider != &computed { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ProviderIdentityMismatch, + PROVIDER_COORDINATE_V1, + computed.digest, + )); + } + + Ok(DigestAdmittedProviderPackageV1 { + manifest, + provider_reference: computed, + files, + }) +} + +fn generated_manifest_artifact( + declaration: &GeneratedArtifactDeclaration, + primary: &ProviderPrimaryArtifactsV1, + provenance: &ProviderGenerationProvenanceV1, + review: &ProviderGenerationReviewV1, + source: &ProviderManifestArtifactSourceV1, +) -> Result { + let (artifact_kind, resource) = match declaration.kind { + GeneratedArtifactKind::Lawpack => ( + ProviderManifestArtifactKindV1::Lawpack, + canonical_artifact_ref(declaration, primary)?, + ), + GeneratedArtifactKind::TargetProfile => ( + ProviderManifestArtifactKindV1::TargetProfile, + canonical_artifact_ref(declaration, primary)?, + ), + GeneratedArtifactKind::AuthorityFacts => ( + ProviderManifestArtifactKindV1::AuthorityFacts, + canonical_artifact_ref(declaration, primary)?, + ), + GeneratedArtifactKind::ProviderManifest => { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ManifestContractMismatch, + &declaration.role, + &declaration.coordinate, + )); + } + GeneratedArtifactKind::ReviewArtifact => { + require_declared_json( + declaration, + review.role(), + review.coordinate(), + review.schema_contract(), + )?; + ( + ProviderManifestArtifactKindV1::ReviewArtifact, + manifest_ref(review.coordinate(), &review.content_reference().digest), + ) + } + GeneratedArtifactKind::GeneratedArtifactProfile => ( + ProviderManifestArtifactKindV1::GeneratedArtifactProfile, + canonical_artifact_ref(declaration, primary)?, + ), + GeneratedArtifactKind::GenerationProvenance => { + require_declared_json( + declaration, + provenance.role(), + provenance.coordinate(), + provenance.schema_contract(), + )?; + ( + ProviderManifestArtifactKindV1::GenerationProvenance, + manifest_ref( + provenance.coordinate(), + &provenance.content_reference().digest, + ), + ) + } + GeneratedArtifactKind::ArtifactSchema => { + require_declared_schema(declaration, primary.schema())?; + ( + ProviderManifestArtifactKindV1::ArtifactSchema, + manifest_ref( + primary.schema().coordinate(), + &primary.schema().content_reference().digest, + ), + ) + } + }; + Ok(ProviderManifestArtifactV1 { + role: declaration.role.clone(), + artifact_kind, + resource, + source: source.clone(), + }) +} + +fn canonical_artifact_ref( + declaration: &GeneratedArtifactDeclaration, + primary: &ProviderPrimaryArtifactsV1, +) -> Result { + let artifact = primary.artifact(&declaration.role).ok_or_else(|| { + ProviderPackageError::new( + ProviderPackageErrorKind::ArtifactMissing, + &declaration.role, + &declaration.coordinate, + ) + })?; + require_declared_canonical(declaration, artifact)?; + Ok(manifest_ref( + artifact.coordinate(), + artifact.domain_framed_digest(), + )) +} + +fn require_declared_canonical( + declaration: &GeneratedArtifactDeclaration, + artifact: &SchemaValidatedCanonicalProviderOutputV1, +) -> Result<(), ProviderPackageError> { + if declaration.role != artifact.role() + || declaration.coordinate != artifact.coordinate() + || declaration.schema_contract != artifact.schema_contract() + { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ArtifactMismatch, + &declaration.role, + artifact.coordinate(), + )); + } + Ok(()) +} + +fn require_declared_schema( + declaration: &GeneratedArtifactDeclaration, + schema: &GeneratedProviderSchemaV1, +) -> Result<(), ProviderPackageError> { + if declaration.role != schema.role() || declaration.coordinate != schema.coordinate() { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ArtifactMismatch, + &declaration.role, + schema.coordinate(), + )); + } + Ok(()) +} + +fn require_declared_json( + declaration: &GeneratedArtifactDeclaration, + role: &str, + coordinate: &str, + schema_contract: &str, +) -> Result<(), ProviderPackageError> { + if declaration.role != role + || declaration.coordinate != coordinate + || declaration.schema_contract != schema_contract + { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ArtifactMismatch, + &declaration.role, + coordinate, + )); + } + Ok(()) +} + +fn component_manifest_artifact( + declaration: &ProviderComponentDeclaration, + resource: ProviderManifestResourceRefV1, +) -> ProviderManifestArtifactV1 { + ProviderManifestArtifactV1 { + role: declaration.role.clone(), + artifact_kind: match declaration.kind { + ProviderComponentKind::Lowerer => ProviderManifestArtifactKindV1::Lowerer, + ProviderComponentKind::Verifier => ProviderManifestArtifactKindV1::Verifier, + }, + resource: resource.clone(), + source: ProviderManifestArtifactSourceV1::Component { + component: resource, + }, + } +} + +fn validate_manifest_contract( + manifest: &ProviderManifestV1, + files: &[ProviderPackageFileV1], +) -> Result<(), ProviderPackageError> { + if manifest.api_version != PROVIDER_MANIFEST_API_V1 + || manifest.provider_abi != PROVIDER_ABI_V1 + || manifest.provider.coordinate != PROVIDER_COORDINATE_V1 + { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ManifestContractMismatch, + &manifest.api_version, + &manifest.provider_abi, + )); + } + strict_digest_bytes(&manifest.provider.digest)?; + let specs = artifact_specs(); + if manifest.artifacts.len() != specs.len() { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ManifestContractMismatch, + "artifacts", + manifest.artifacts.len().to_string(), + )); + } + for (artifact, spec) in manifest.artifacts.iter().zip(specs) { + if artifact.role != spec.role + || artifact.artifact_kind != spec.artifact_kind + || artifact.resource.coordinate != spec.coordinate + { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ManifestContractMismatch, + &artifact.role, + &artifact.resource.coordinate, + )); + } + strict_digest_bytes(&artifact.resource.digest)?; + validate_artifact_source(artifact, spec.component)?; + let file = file_by_path(files, spec.path)?; + if spec.component && (file.bytes().is_empty() || file.bytes().len() > MAX_COMPONENT_BYTES) { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ComponentInvalid, + spec.role, + file.bytes().len().to_string(), + )); + } + let actual_digest = match spec.digest_domain { + Some(domain) => { + let value = decode_canonical_cbor_v1(file.bytes()).map_err(|_error| { + ProviderPackageError::new( + ProviderPackageErrorKind::ArtifactDigestMismatch, + spec.role, + "canonical-cbor", + ) + })?; + digest_canonical_value_v1(domain, &value).map_err(|_error| { + ProviderPackageError::new( + ProviderPackageErrorKind::ArtifactDigestMismatch, + spec.role, + domain, + ) + })? + } + None => raw_sha256(file.bytes()), + }; + if artifact.resource.digest != actual_digest { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ArtifactDigestMismatch, + spec.role, + actual_digest, + )); + } + } + validate_generated_source_closure(&manifest.artifacts)?; + validate_generation_evidence(manifest, files)?; + validate_schema_bindings(&manifest.schema_bindings) +} + +fn validate_expected_provider( + manifest_provider: &ProviderManifestResourceRefV1, + expected_provider: &ProviderManifestResourceRefV1, +) -> Result<(), ProviderPackageError> { + strict_digest_bytes(&manifest_provider.digest)?; + strict_digest_bytes(&expected_provider.digest)?; + if expected_provider.coordinate != PROVIDER_COORDINATE_V1 + || manifest_provider != expected_provider + { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ProviderIdentityMismatch, + &expected_provider.coordinate, + &expected_provider.digest, + )); + } + Ok(()) +} + +fn validate_generation_evidence( + manifest: &ProviderManifestV1, + files: &[ProviderPackageFileV1], +) -> Result<(), ProviderPackageError> { + let provenance_file = file_by_path( + files, + "generated/evidence/provenance.provider-generation.json", + )?; + validate_member_size(provenance_file, MAX_EVIDENCE_BYTES)?; + let provenance: GenerationProvenanceManifestV1 = + serde_json::from_slice(provenance_file.bytes()).map_err(|_error| { + ProviderPackageError::new( + ProviderPackageErrorKind::GenerationProvenanceMismatch, + "provenance.provider-generation", + "wesley-generation-provenance-json/v1", + ) + })?; + let canonical_provenance = provenance.canonical_bytes().map_err(|error| { + ProviderPackageError::new( + ProviderPackageErrorKind::GenerationProvenanceMismatch, + "provenance.provider-generation", + "wesley-generation-provenance-contract/v1", + ) + .wesley(&error) + })?; + if canonical_provenance != provenance_file.bytes() { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::GenerationProvenanceMismatch, + "provenance.provider-generation", + "noncanonical-json", + )); + } + + let (manifest_source, manifest_generator) = manifest + .artifacts + .iter() + .find_map(|artifact| match &artifact.source { + ProviderManifestArtifactSourceV1::Generated { + semantic_source, + generator, + } => Some((semantic_source, generator)), + ProviderManifestArtifactSourceV1::Component { .. } => None, + }) + .ok_or_else(|| { + ProviderPackageError::new( + ProviderPackageErrorKind::GenerationProvenanceMismatch, + "artifacts", + "generated-source-missing", + ) + })?; + if provenance.generator.coordinate != manifest_generator.coordinate + || provenance.generator.digest != manifest_generator.digest + { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::GenerationProvenanceMismatch, + "generator", + &provenance.generator.digest, + )); + } + let provenance_source = provenance + .source_artifacts + .iter() + .find(|reference| reference.coordinate == SEMANTIC_SOURCE_COORDINATE_V1) + .ok_or_else(|| { + ProviderPackageError::new( + ProviderPackageErrorKind::GenerationProvenanceMismatch, + "sourceArtifacts", + SEMANTIC_SOURCE_COORDINATE_V1, + ) + })?; + if provenance_source.coordinate != manifest_source.coordinate + || provenance_source.digest != manifest_source.digest + { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::GenerationProvenanceMismatch, + SEMANTIC_SOURCE_COORDINATE_V1, + &provenance_source.digest, + )); + } + let expected_emitted = expected_emitted_references(files)?; + if provenance.emitted_artifacts != expected_emitted { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::GenerationProvenanceMismatch, + "emittedArtifacts", + provenance.emitted_artifacts.len().to_string(), + )); + } + + let review_file = file_by_path(files, "generated/evidence/review.provider-generation.json")?; + validate_member_size(review_file, MAX_EVIDENCE_BYTES)?; + let review: GenerationReviewV1 = + serde_json::from_slice(review_file.bytes()).map_err(|_error| { + ProviderPackageError::new( + ProviderPackageErrorKind::GenerationReviewMismatch, + "review.provider-generation", + "wesley-generation-review-json/v1", + ) + })?; + let canonical_review = review.canonical_bytes().map_err(|error| { + ProviderPackageError::new( + ProviderPackageErrorKind::GenerationReviewMismatch, + "review.provider-generation", + "wesley-generation-review-contract/v1", + ) + .wesley(&error) + })?; + if canonical_review != review_file.bytes() + || review.authoritative() + || review.generation_input_digest != provenance.generation_input_digest + || review.generator != provenance.generator + || review.source_artifacts != provenance.source_artifacts + || review.emitted_artifacts != provenance.emitted_artifacts + || review.projection_roles != expected_projection_roles() + || review.provenance_manifest_digest + != provenance.digest().map_err(|error| { + ProviderPackageError::new( + ProviderPackageErrorKind::GenerationProvenanceMismatch, + "provenance.provider-generation", + "wesley-provenance-digest/v1", + ) + .wesley(&error) + })? + { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::GenerationReviewMismatch, + "review.provider-generation", + "provenance-projection", + )); + } + Ok(()) +} + +fn expected_emitted_references( + files: &[ProviderPackageFileV1], +) -> Result, ProviderPackageError> { + let mut references = artifact_specs() + .into_iter() + .filter(|spec| { + !spec.component + && !matches!( + spec.artifact_kind, + ProviderManifestArtifactKindV1::GenerationProvenance + | ProviderManifestArtifactKindV1::ReviewArtifact + ) + }) + .map(|spec| { + let file = file_by_path(files, spec.path)?; + Ok(GenerationArtifactReferenceV1 { + coordinate: spec.coordinate.to_owned(), + digest: raw_sha256(file.bytes()), + }) + }) + .collect::, ProviderPackageError>>()?; + references.sort_by(|left, right| left.coordinate.cmp(&right.coordinate)); + Ok(references) +} + +fn expected_projection_roles() -> Vec { + [ + "authority-facts.echo-dpo", + "authority-facts.echo-lawpack", + "generated-artifact-profile.echo-dpo-registration", + "lawpack.echo-dpo", + "schema.echo-provider-artifacts", + "target-profile.echo-dpo", + ] + .into_iter() + .map(str::to_owned) + .collect() +} + +fn validate_generated_source_closure( + artifacts: &[ProviderManifestArtifactV1], +) -> Result<(), ProviderPackageError> { + let expected = artifacts + .iter() + .find_map(|artifact| match &artifact.source { + ProviderManifestArtifactSourceV1::Generated { + semantic_source, + generator, + } => Some((semantic_source, generator)), + ProviderManifestArtifactSourceV1::Component { .. } => None, + }); + let Some((expected_source, expected_generator)) = expected else { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ArtifactSourceMismatch, + "artifacts", + "generated-source-missing", + )); + }; + for artifact in artifacts { + if let ProviderManifestArtifactSourceV1::Generated { + semantic_source, + generator, + } = &artifact.source + { + if semantic_source != expected_source || generator != expected_generator { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::ArtifactSourceMismatch, + &artifact.role, + &semantic_source.digest, + )); + } + } + } + Ok(()) +} + +fn validate_artifact_source( + artifact: &ProviderManifestArtifactV1, + component: bool, +) -> Result<(), ProviderPackageError> { + match (&artifact.source, component) { + (ProviderManifestArtifactSourceV1::Component { component }, true) + if component == &artifact.resource => + { + Ok(()) + } + ( + ProviderManifestArtifactSourceV1::Generated { + semantic_source, + generator, + }, + false, + ) if semantic_source.coordinate == SEMANTIC_SOURCE_COORDINATE_V1 + && generator.coordinate == GENERATOR_COORDINATE_V1 => + { + strict_digest_bytes(&semantic_source.digest)?; + strict_digest_bytes(&generator.digest)?; + Ok(()) + } + _ => Err(ProviderPackageError::new( + ProviderPackageErrorKind::ArtifactSourceMismatch, + &artifact.role, + artifact.resource.coordinate.clone(), + )), + } +} + +fn validate_schema_bindings( + bindings: &[ProviderManifestSchemaBindingV1], +) -> Result<(), ProviderPackageError> { + let expected = schema_binding_specs(); + if bindings.len() != expected.len() { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::SchemaBindingMismatch, + "schemaBindings", + bindings.len().to_string(), + )); + } + for (binding, (domain, root_rule)) in bindings.iter().zip(expected) { + if binding.domain != domain + || binding.schema_role != "schema.echo-provider-artifacts" + || binding.format != ProviderManifestSchemaFormatV1::SelfContainedCddlV1 + || binding.root_rule != root_rule + { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::SchemaBindingMismatch, + &binding.domain, + &binding.root_rule, + )); + } + } + Ok(()) +} + +fn package_closure_digest( + artifacts: &[ProviderManifestArtifactV1], + schema_bindings: &[ProviderManifestSchemaBindingV1], + members: &[ProviderPackageMemberV1], +) -> Result { + let value = package_closure_value(artifacts, schema_bindings, members)?; + digest_canonical_value_v1(ECHO_PROVIDER_PACKAGE_CLOSURE_DOMAIN_V1, &value).map_err(|_error| { + ProviderPackageError::new( + ProviderPackageErrorKind::PackageIdentityFailed, + ECHO_PROVIDER_PACKAGE_CLOSURE_DOMAIN_V1, + "canonical-cbor", + ) + }) +} + +fn package_closure_value( + artifacts: &[ProviderManifestArtifactV1], + schema_bindings: &[ProviderManifestSchemaBindingV1], + members: &[ProviderPackageMemberV1], +) -> Result { + let artifact_values = artifacts + .iter() + .map(|artifact| { + Ok(CanonicalValueV1::Array(vec![ + text(&artifact.role), + text(artifact.artifact_kind.as_str()), + text(&artifact.resource.coordinate), + CanonicalValueV1::Bytes(strict_digest_bytes(&artifact.resource.digest)?), + source_value(&artifact.source)?, + ])) + }) + .collect::, ProviderPackageError>>()?; + let binding_values = schema_bindings + .iter() + .map(|binding| { + CanonicalValueV1::Array(vec![ + text(&binding.domain), + text(&binding.schema_role), + text(binding.format.as_str()), + text(&binding.root_rule), + ]) + }) + .collect(); + let member_values = members + .iter() + .map(|member| { + Ok(CanonicalValueV1::Array(vec![ + text(member.relative_path()), + CanonicalValueV1::Bytes(strict_digest_bytes(&member.raw_digest)?), + ])) + }) + .collect::, ProviderPackageError>>()?; + Ok(CanonicalValueV1::Array(vec![ + text(PROVIDER_MANIFEST_ROLE_V1), + text(PROVIDER_MANIFEST_COORDINATE_V1), + text(ECHO_PROVIDER_MANIFEST_ENCODING_V1), + text(PROVIDER_MANIFEST_API_V1), + text(PROVIDER_ABI_V1), + text(PROVIDER_COORDINATE_V1), + CanonicalValueV1::Array(artifact_values), + CanonicalValueV1::Array(binding_values), + CanonicalValueV1::Array(member_values), + ])) +} + +fn source_value( + source: &ProviderManifestArtifactSourceV1, +) -> Result { + match source { + ProviderManifestArtifactSourceV1::Generated { + semantic_source, + generator, + } => Ok(CanonicalValueV1::Array(vec![ + text("generated"), + text(&semantic_source.coordinate), + CanonicalValueV1::Bytes(strict_digest_bytes(&semantic_source.digest)?), + text(&generator.coordinate), + CanonicalValueV1::Bytes(strict_digest_bytes(&generator.digest)?), + ])), + ProviderManifestArtifactSourceV1::Component { component } => { + Ok(CanonicalValueV1::Array(vec![ + text("component"), + text(&component.coordinate), + CanonicalValueV1::Bytes(strict_digest_bytes(&component.digest)?), + ])) + } + } +} + +fn validate_member_count_and_order( + members: &[ProviderPackageMemberV1], +) -> Result<(), ProviderPackageError> { + if members.len() != EXPECTED_MEMBER_COUNT { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::PackageMemberMissing, + "package-members", + members.len().to_string(), + )); + } + for pair in members.windows(2) { + match pair[0] + .relative_path() + .as_bytes() + .cmp(pair[1].relative_path().as_bytes()) + { + std::cmp::Ordering::Less => {} + std::cmp::Ordering::Equal => { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::PackageMemberDuplicate, + pair[0].relative_path(), + pair[1].relative_path(), + )); + } + std::cmp::Ordering::Greater => { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::PackageMemberOutOfOrder, + pair[0].relative_path(), + pair[1].relative_path(), + )); + } + } + } + Ok(()) +} + +fn validate_file_inventory(files: &[ProviderPackageFileV1]) -> Result<(), ProviderPackageError> { + for pair in files.windows(2) { + match pair[0] + .relative_path + .as_bytes() + .cmp(pair[1].relative_path.as_bytes()) + { + std::cmp::Ordering::Less => {} + std::cmp::Ordering::Equal => { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::PackageMemberDuplicate, + &pair[0].relative_path, + &pair[1].relative_path, + )); + } + std::cmp::Ordering::Greater => { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::PackageMemberOutOfOrder, + &pair[0].relative_path, + &pair[1].relative_path, + )); + } + } + } + if let Some(file) = files + .iter() + .find(|file| !EXPECTED_PACKAGE_PATHS.contains(&file.relative_path.as_str())) + { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::PackageMemberUnexpected, + &file.relative_path, + "provider-package/v1", + )); + } + if let Some(path) = EXPECTED_PACKAGE_PATHS + .iter() + .find(|path| !files.iter().any(|file| file.relative_path == **path)) + { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::PackageMemberMissing, + *path, + "provider-package/v1", + )); + } + if files.len() != EXPECTED_FILE_COUNT { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::PackageMemberDuplicate, + "provider-package/v1", + files.len().to_string(), + )); + } + Ok(()) +} + +fn validate_total_package_size( + files: &[ProviderPackageFileV1], +) -> Result<(), ProviderPackageError> { + let total_bytes = files.iter().try_fold(0_usize, |total, file| { + total.checked_add(file.bytes.len()).ok_or_else(|| { + ProviderPackageError::new( + ProviderPackageErrorKind::PackageSizeExceeded, + "provider-package/v1", + "byte-length-overflow", + ) + }) + })?; + if total_bytes > MAX_PACKAGE_BYTES { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::PackageSizeExceeded, + "provider-package/v1", + total_bytes.to_string(), + )); + } + Ok(()) +} + +fn validate_member_size( + file: &ProviderPackageFileV1, + maximum: usize, +) -> Result<(), ProviderPackageError> { + if file.bytes().len() > maximum { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::PackageSizeExceeded, + file.relative_path(), + file.bytes().len().to_string(), + )); + } + Ok(()) +} + +fn file_by_path<'a>( + files: &'a [ProviderPackageFileV1], + path: &str, +) -> Result<&'a ProviderPackageFileV1, ProviderPackageError> { + files + .iter() + .find(|file| file.relative_path == path) + .ok_or_else(|| { + ProviderPackageError::new( + ProviderPackageErrorKind::PackageMemberMissing, + path, + "provider-package/v1", + ) + }) +} + +fn render_manifest(manifest: &ProviderManifestV1) -> Result, ProviderPackageError> { + let mut bytes = serde_json::to_vec_pretty(manifest).map_err(|_error| { + ProviderPackageError::new( + ProviderPackageErrorKind::ManifestSerializationFailed, + PROVIDER_MANIFEST_COORDINATE_V1, + ECHO_PROVIDER_MANIFEST_ENCODING_V1, + ) + })?; + bytes.push(b'\n'); + Ok(bytes) +} + +fn manifest_ref(coordinate: &str, digest: &str) -> ProviderManifestResourceRefV1 { + ProviderManifestResourceRefV1 { + coordinate: coordinate.to_owned(), + digest: digest.to_owned(), + } +} + +fn raw_sha256(bytes: &[u8]) -> String { + format!("sha256:{}", hex::encode(Sha256::digest(bytes))) +} + +fn strict_digest_bytes(digest: &str) -> Result, ProviderPackageError> { + let Some(hex_digest) = digest.strip_prefix("sha256:") else { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::DigestInvalid, + "sha256", + digest, + )); + }; + if hex_digest.len() != 64 + || !hex_digest + .as_bytes() + .iter() + .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(byte)) + { + return Err(ProviderPackageError::new( + ProviderPackageErrorKind::DigestInvalid, + "sha256", + digest, + )); + } + hex::decode(hex_digest).map_err(|_error| { + ProviderPackageError::new(ProviderPackageErrorKind::DigestInvalid, "sha256", digest) + }) +} + +fn text(value: &str) -> CanonicalValueV1 { + CanonicalValueV1::Text(value.to_owned()) +} + +fn bounded_diagnostic(mut value: String) -> String { + if value.len() <= MAX_DIAGNOSTIC_BYTES { + return value; + } + let mut end = MAX_DIAGNOSTIC_BYTES; + while !value.is_char_boundary(end) { + end -= 1; + } + value.truncate(end); + value +} + +#[derive(Clone, Copy)] +struct ArtifactSpec { + role: &'static str, + artifact_kind: ProviderManifestArtifactKindV1, + coordinate: &'static str, + path: &'static str, + digest_domain: Option<&'static str>, + component: bool, +} + +fn artifact_specs() -> [ArtifactSpec; EXPECTED_ARTIFACT_COUNT] { + [ + ArtifactSpec { + role: "authority-facts.echo-dpo", + artifact_kind: ProviderManifestArtifactKindV1::AuthorityFacts, + coordinate: "echo.dpo-authority-facts@1", + path: "generated/primary/authority-facts.echo-dpo.cbor", + digest_domain: Some("edict.authority-facts/v1"), + component: false, + }, + ArtifactSpec { + role: "authority-facts.echo-lawpack", + artifact_kind: ProviderManifestArtifactKindV1::AuthorityFacts, + coordinate: "echo.dpo-lawpack-authority-facts@1", + path: "generated/primary/authority-facts.echo-lawpack.cbor", + digest_domain: Some("edict.authority-facts/v1"), + component: false, + }, + ArtifactSpec { + role: "generated-artifact-profile.echo-dpo-registration", + artifact_kind: ProviderManifestArtifactKindV1::GeneratedArtifactProfile, + coordinate: "echo.dpo.registration/v1", + path: "generated/primary/generated-artifact-profile.echo-dpo-registration.cbor", + digest_domain: Some("echo.generated-artifact-profile/v1"), + component: false, + }, + ArtifactSpec { + role: "lawpack.echo-dpo", + artifact_kind: ProviderManifestArtifactKindV1::Lawpack, + coordinate: "echo.dpo-lawpack@1", + path: "generated/primary/lawpack.echo-dpo.cbor", + digest_domain: Some("edict.lawpack/v1"), + component: false, + }, + ArtifactSpec { + role: "lowerer.echo-dpo", + artifact_kind: ProviderManifestArtifactKindV1::Lowerer, + coordinate: "echo.dpo.lowerer/component@1", + path: "components/lowerer.echo-dpo.component.wasm", + digest_domain: None, + component: true, + }, + ArtifactSpec { + role: "provenance.provider-generation", + artifact_kind: ProviderManifestArtifactKindV1::GenerationProvenance, + coordinate: "echo.edict-provider-generation-provenance@1", + path: "generated/evidence/provenance.provider-generation.json", + digest_domain: None, + component: false, + }, + ArtifactSpec { + role: "review.provider-generation", + artifact_kind: ProviderManifestArtifactKindV1::ReviewArtifact, + coordinate: "echo.edict-provider-generation-review@1", + path: "generated/evidence/review.provider-generation.json", + digest_domain: None, + component: false, + }, + ArtifactSpec { + role: "schema.echo-provider-artifacts", + artifact_kind: ProviderManifestArtifactKindV1::ArtifactSchema, + coordinate: "echo.provider-artifacts.cddl@1", + path: "generated/primary/schema.echo-provider-artifacts.cddl", + digest_domain: None, + component: false, + }, + ArtifactSpec { + role: "target-profile.echo-dpo", + artifact_kind: ProviderManifestArtifactKindV1::TargetProfile, + coordinate: "echo.dpo@1", + path: "generated/primary/target-profile.echo-dpo.cbor", + digest_domain: Some("edict.target-profile/v1"), + component: false, + }, + ArtifactSpec { + role: "verifier.echo-dpo", + artifact_kind: ProviderManifestArtifactKindV1::Verifier, + coordinate: "echo.dpo.verifier/component@1", + path: "components/verifier.echo-dpo.component.wasm", + digest_domain: None, + component: true, + }, + ] +} + +fn schema_binding_specs() -> [(&'static str, &'static str); EXPECTED_SCHEMA_BINDING_COUNT] { + [ + ( + "echo.dpo-lawpack.adapter.echo-dpo@1", + "echo-provider-lawpack-target-adapter", + ), + ( + "echo.dpo-lawpack.compatibility@1", + "echo-provider-lawpack-compatibility", + ), + ("echo.dpo-lawpack.exports@1", "lawpack-exports"), + ( + "echo.dpo-lawpack.verifier@1", + "echo-provider-lawpack-verifier", + ), + ("echo.dpo.bundle/v1", "echo-dpo-bundle"), + ("echo.dpo.cost/v1", "echo-dpo-cost"), + ("echo.dpo.fixtures/v1", "echo-provider-conformance-corpus"), + ("echo.dpo.footprint/v1", "echo-dpo-footprint"), + ("echo.dpo.intrinsics/v1", "intrinsics-document"), + ("echo.dpo.lowerer/v1", "echo-dpo-lowerer"), + ("echo.dpo.obstructions/v1", "echo-dpo-obstructions"), + ( + "echo.dpo.operation-profiles/v1", + "operation-profiles-document", + ), + ("echo.dpo.verifier/v1", "echo-dpo-verifier"), + ( + "echo.generated-artifact-profile/v1", + "generated-artifact-profile", + ), + ("echo.generated-artifact/v1", "generated-artifact"), + ("echo.review-payload/v1", "review-payload"), + ("echo.span-ir/v1", "echo-span-ir"), + ("echo.verifier-report/v1", "verifier-report"), + ("edict.authority-facts/v1", "authority-facts"), + ("edict.core.module/v1", "core-module"), + ("edict.lawpack/v1", "lawpack-manifest"), + ("edict.lowering-requirements/v1", "lowering-requirements"), + ("edict.target-ir.artifact/v1", "target-ir-artifact"), + ("edict.target-profile/v1", "target-profile-manifest"), + ] +} diff --git a/crates/echo-wesley-gen/src/provider_semantics.rs b/crates/echo-wesley-gen/src/provider_semantics.rs index 8cd50153..491bae3f 100644 --- a/crates/echo-wesley-gen/src/provider_semantics.rs +++ b/crates/echo-wesley-gen/src/provider_semantics.rs @@ -702,6 +702,32 @@ pub struct PackageManifestProjectionDeclaration { pub provider_abi: String, /// Digest-bound provider resource coordinate selected during assembly. pub provider_coordinate: String, + /// Exact executable components bound by the package-root manifest. + pub components: Vec, +} + +/// One source-owned executable component declaration for provider packaging. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct ProviderComponentDeclaration { + /// Unique provider-manifest role. + pub role: String, + /// Frozen target-provider component class. + pub kind: ProviderComponentKind, + /// Stable component resource coordinate before its exact digest is known. + pub coordinate: String, + /// Exact frozen WIT world implemented by the component. + pub contract: String, +} + +/// Executable component classes admitted by the first provider package. +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum ProviderComponentKind { + /// Deterministic Edict semantic-to-Target-IR lowerer. + Lowerer, + /// Independent Target-IR semantic verifier. + Verifier, } /// Source identity bound by one generated authority-facts document. @@ -921,6 +947,10 @@ pub enum ProviderSemanticSourceErrorKind { SelfReferentialManifestInventory, /// The package-root manifest projection selected the wrong v1 identity. ProviderManifestProjectionMismatch, + /// A provider component selected the wrong role, coordinate, or WIT world. + ProviderComponentProjectionMismatch, + /// The package omitted or duplicated a required provider component class. + ProviderComponentClosureMismatch, /// An authority-facts artifact selected an incompatible source projection. AuthorityFactProjectionMismatch, /// A lawpack or target-profile projection was incomplete or contradictory. @@ -984,6 +1014,8 @@ impl ProviderSemanticSourceErrorKind { Self::ArtifactContractOwnerMismatch => "artifact-contract-owner-mismatch", Self::SelfReferentialManifestInventory => "self-referential-manifest-inventory", Self::ProviderManifestProjectionMismatch => "provider-manifest-projection-mismatch", + Self::ProviderComponentProjectionMismatch => "provider-component-projection-mismatch", + Self::ProviderComponentClosureMismatch => "provider-component-closure-mismatch", Self::AuthorityFactProjectionMismatch => "authority-fact-projection-mismatch", Self::ArtifactClosureMismatch => "artifact-closure-mismatch", Self::GenerationProvenanceContractMismatch => "generation-provenance-contract-mismatch", @@ -1196,6 +1228,10 @@ fn normalize_source(source: &mut ProviderSemanticSourceV1) { source .generated_artifacts .sort_by(|left, right| left.role.cmp(&right.role)); + source + .package_manifest + .components + .sort_by(|left, right| left.role.cmp(&right.role)); source .invocation_inputs .sort_by(|left, right| left.role.cmp(&right.role)); @@ -1282,6 +1318,10 @@ fn validate_identities( "packageManifest.providerCoordinate", &source.package_manifest.provider_coordinate, )); + for component in &source.package_manifest.components { + validate_component_identity(component)?; + coordinates.push(("packageManifest.components", &component.coordinate)); + } coordinates.sort_by(|left, right| left.1.cmp(right.1).then(left.0.cmp(right.0))); if let Some(window) = coordinates .windows(2) @@ -1298,6 +1338,14 @@ fn validate_identities( "generatedArtifacts.role", source.generated_artifacts.iter().map(|item| &item.role), )?; + ensure_unique_keys( + "packageManifest.components.role", + source + .package_manifest + .components + .iter() + .map(|item| &item.role), + )?; ensure_unique_keys( "artifactResources.role", source.artifact_resources.iter().map(|item| &item.role), @@ -1314,6 +1362,13 @@ fn validate_identities( .iter() .map(|item| &item.role) .collect::>(); + package_roles.extend( + source + .package_manifest + .components + .iter() + .map(|item| &item.role), + ); package_roles.push(&source.package_manifest.role); ensure_unique_keys("packageRoles", package_roles.into_iter())?; ensure_unique_keys( @@ -1439,6 +1494,17 @@ fn validate_package_manifest_identity( ) } +fn validate_component_identity( + component: &ProviderComponentDeclaration, +) -> Result<(), ProviderSemanticSourceError> { + require_nonempty("packageManifest.components.role", &component.role)?; + require_nonempty( + "packageManifest.components.coordinate", + &component.coordinate, + )?; + require_nonempty("packageManifest.components.contract", &component.contract) +} + fn collect_fact_identities<'a, T: HasIdentity>( family: &'static str, facts: &'a [T], @@ -2750,6 +2816,56 @@ fn validate_generated_artifact_contracts( &source.package_manifest.provider_coordinate, )); } + for (kind, label) in [ + (ProviderComponentKind::Lowerer, "lowerer"), + (ProviderComponentKind::Verifier, "verifier"), + ] { + let actual = source + .package_manifest + .components + .iter() + .filter(|component| component.kind == kind) + .count(); + if actual != 1 { + return Err(ProviderSemanticSourceError::new( + ProviderSemanticSourceErrorKind::ProviderComponentClosureMismatch, + "packageManifest.components", + if actual == 0 { + label.to_owned() + } else { + format!("{label}:{actual}") + }, + )); + } + } + for component in &source.package_manifest.components { + let (expected_role, expected_coordinate, expected_contract) = match component.kind { + ProviderComponentKind::Lowerer => ( + "lowerer.echo-dpo", + "echo.dpo.lowerer/component@1", + "edict:target-provider/lowerer@1.0.0", + ), + ProviderComponentKind::Verifier => ( + "verifier.echo-dpo", + "echo.dpo.verifier/component@1", + "edict:target-provider/verifier@1.0.0", + ), + }; + let mismatch = [ + (component.role.as_str(), expected_role), + (component.coordinate.as_str(), expected_coordinate), + (component.contract.as_str(), expected_contract), + ] + .into_iter() + .find_map(|(actual, expected)| (actual != expected).then_some(actual)); + if let Some(reference) = mismatch { + return Err(ProviderSemanticSourceError::new( + ProviderSemanticSourceErrorKind::ProviderComponentProjectionMismatch, + &component.role, + reference, + )); + } + } for (kind, expected_count) in [ (GeneratedArtifactKind::Lawpack, 1), (GeneratedArtifactKind::TargetProfile, 1), @@ -3319,6 +3435,28 @@ fn validate_invocation_schema_bindings( .iter() .map(|input| input.domain.as_str()) .collect::>(); + let mut owner_bindings = source + .generated_artifacts + .iter() + .filter_map(|artifact| { + generated_artifact_owner_schema(artifact.kind) + .map(|(domain, root)| (artifact.role.as_str(), domain, root)) + }) + .collect::>(); + owner_bindings.extend( + source + .artifact_resources + .iter() + .filter(|resource| resource.provision == ArtifactResourceProvision::Generated) + .filter_map(|resource| { + generated_resource_root(&resource.role) + .map(|root| (resource.role.as_str(), resource.coordinate.as_str(), root)) + }), + ); + let owner_domains = owner_bindings + .iter() + .map(|(_, domain, _)| *domain) + .collect::>(); for output in &source.invocation_outputs { let (expected_domain, _) = expected_output_contract(output.kind); if output.domain != expected_domain { @@ -3333,6 +3471,7 @@ fn validate_invocation_schema_bindings( for binding in &source.schema_bindings { if !output_domains.contains(binding.domain.as_str()) && !input_domains.contains(binding.domain.as_str()) + && !owner_domains.contains(binding.domain.as_str()) { return Err(ProviderSemanticSourceError::new( ProviderSemanticSourceErrorKind::UnexpectedSchemaBinding, @@ -3358,6 +3497,23 @@ fn validate_invocation_schema_bindings( } } + for (role, domain, expected_root) in owner_bindings { + let Some(binding) = bindings.get(domain) else { + return Err(ProviderSemanticSourceError::new( + ProviderSemanticSourceErrorKind::MissingSchemaBinding, + role, + domain, + )); + }; + if binding.root_rule != expected_root { + return Err(ProviderSemanticSourceError::new( + ProviderSemanticSourceErrorKind::SchemaRootMismatch, + role, + &binding.root_rule, + )); + } + } + let artifact_roles = source .generated_artifacts .iter() @@ -3439,6 +3595,48 @@ fn expected_output_contract(kind: InvocationOutputKind) -> (&'static str, &'stat } } +const fn generated_artifact_owner_schema( + kind: GeneratedArtifactKind, +) -> Option<(&'static str, &'static str)> { + match kind { + GeneratedArtifactKind::Lawpack => Some(("edict.lawpack/v1", "lawpack-manifest")), + GeneratedArtifactKind::TargetProfile => { + Some(("edict.target-profile/v1", "target-profile-manifest")) + } + GeneratedArtifactKind::AuthorityFacts => { + Some(("edict.authority-facts/v1", "authority-facts")) + } + GeneratedArtifactKind::GeneratedArtifactProfile => Some(( + "echo.generated-artifact-profile/v1", + "generated-artifact-profile", + )), + GeneratedArtifactKind::ProviderManifest + | GeneratedArtifactKind::ReviewArtifact + | GeneratedArtifactKind::GenerationProvenance + | GeneratedArtifactKind::ArtifactSchema => None, + } +} + +pub(crate) fn generated_resource_root(role: &str) -> Option<&'static str> { + match role { + "resource.conformance-corpus" => Some("echo-provider-conformance-corpus"), + "resource.lawpack-compatibility" => Some("echo-provider-lawpack-compatibility"), + "resource.lawpack-exports" => Some("lawpack-exports"), + "resource.lawpack-target-adapter" => Some("echo-provider-lawpack-target-adapter"), + "resource.lawpack-verifier" => Some("echo-provider-lawpack-verifier"), + "resource.target-bundle-profile" => Some("echo-dpo-bundle"), + "resource.target-cost-algebra" => Some("echo-dpo-cost"), + "resource.target-footprint-algebra" => Some("echo-dpo-footprint"), + "resource.target-intrinsics" => Some("intrinsics-document"), + "resource.target-ir" => Some("echo-span-ir"), + "resource.target-lowerer-contract" => Some("echo-dpo-lowerer"), + "resource.target-obstruction-taxonomy" => Some("echo-dpo-obstructions"), + "resource.target-operation-profiles" => Some("operation-profiles-document"), + "resource.target-verifier-contract" => Some("echo-dpo-verifier"), + _ => None, + } +} + const fn expected_input_domain(kind: InvocationInputKind) -> &'static str { match kind { InvocationInputKind::Core => "edict.core.module/v1", @@ -3489,6 +3687,7 @@ fn expected_schema_root(domain: &str) -> Option<&'static str> { "edict.lowering-requirements/v1" => Some("lowering-requirements"), "edict.target-profile/v1" => Some("target-profile-manifest"), "edict.target-ir.artifact/v1" => Some("target-ir-artifact"), + "echo.generated-artifact-profile/v1" => Some("generated-artifact-profile"), "echo.generated-artifact/v1" => Some("generated-artifact"), "echo.review-payload/v1" => Some("review-payload"), "echo.verifier-report/v1" => Some("verifier-report"), diff --git a/crates/echo-wesley-gen/tests/provider_artifact_corpus.rs b/crates/echo-wesley-gen/tests/provider_artifact_corpus.rs index c18ce001..481d754f 100644 --- a/crates/echo-wesley-gen/tests/provider_artifact_corpus.rs +++ b/crates/echo-wesley-gen/tests/provider_artifact_corpus.rs @@ -29,16 +29,14 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::atomic::{AtomicU64, Ordering}; -const SOURCE: &[u8] = - include_bytes!("../../../schemas/edict-provider/echo-provider-semantics-v1.json"); -const SETTINGS: &[u8] = - include_bytes!("../../../schemas/edict-provider/generation-settings-v1.json"); +const SOURCE: &[u8] = include_bytes!("../assets/v1/edict-provider/echo-provider-semantics-v1.json"); +const SETTINGS: &[u8] = include_bytes!("../assets/v1/edict-provider/generation-settings-v1.json"); const CONTRACT_CDDL: &[u8] = - include_bytes!("../../../schemas/edict-provider/contracts/v1/edict-provider-contracts.cddl"); + include_bytes!("../assets/v1/edict-provider/contracts/v1/edict-provider-contracts.cddl"); const CONTRACT_MANIFEST: &[u8] = - include_bytes!("../../../schemas/edict-provider/contracts/v1/manifest.json"); + include_bytes!("../assets/v1/edict-provider/contracts/v1/manifest.json"); -const GENERATOR_SOURCE_PATHS: [&str; 16] = [ +const GENERATOR_SOURCE_PATHS: [&str; 17] = [ "Cargo.lock", "Cargo.toml", "crates/echo-edict-canonical/Cargo.toml", @@ -50,6 +48,7 @@ const GENERATOR_SOURCE_PATHS: [&str; 16] = [ "crates/echo-wesley-gen/src/provider_canonical.rs", "crates/echo-wesley-gen/src/provider_contract_pack.rs", "crates/echo-wesley-gen/src/provider_corpus.rs", + "crates/echo-wesley-gen/src/provider_corpus_fs.rs", "crates/echo-wesley-gen/src/provider_generation.rs", "crates/echo-wesley-gen/src/provider_provenance.rs", "crates/echo-wesley-gen/src/provider_review.rs", @@ -624,7 +623,7 @@ fn check_mode_classifies_a_non_regular_expected_leaf_without_following_it() { #[test] fn checked_first_corpus_matches_the_renderer_without_missing_or_extra_files() { let corpus_root = - Path::new(env!("CARGO_MANIFEST_DIR")).join("../../schemas/edict-provider/generated/v1"); + Path::new(env!("CARGO_MANIFEST_DIR")).join("assets/v1/edict-provider/package/v1/generated"); let output = Command::new(env!("CARGO_BIN_EXE_echo-edict-provider-artifacts")) .arg("--check") .arg("--out") diff --git a/crates/echo-wesley-gen/tests/provider_canonical_contract.rs b/crates/echo-wesley-gen/tests/provider_canonical_contract.rs index 7bcedf5f..cac8c6f0 100644 --- a/crates/echo-wesley-gen/tests/provider_canonical_contract.rs +++ b/crates/echo-wesley-gen/tests/provider_canonical_contract.rs @@ -12,9 +12,9 @@ use echo_wesley_gen::provider_contract_pack::{ }; const CONTRACT_CDDL: &[u8] = - include_bytes!("../../../schemas/edict-provider/contracts/v1/edict-provider-contracts.cddl"); + include_bytes!("../assets/v1/edict-provider/contracts/v1/edict-provider-contracts.cddl"); const CONTRACT_MANIFEST: &[u8] = - include_bytes!("../../../schemas/edict-provider/contracts/v1/manifest.json"); + include_bytes!("../assets/v1/edict-provider/contracts/v1/manifest.json"); fn text(value: &str) -> CanonicalValueV1 { CanonicalValueV1::Text(value.to_owned()) diff --git a/crates/echo-wesley-gen/tests/provider_contract_pack.rs b/crates/echo-wesley-gen/tests/provider_contract_pack.rs index e66f3601..98d98a18 100644 --- a/crates/echo-wesley-gen/tests/provider_contract_pack.rs +++ b/crates/echo-wesley-gen/tests/provider_contract_pack.rs @@ -9,9 +9,9 @@ use echo_wesley_gen::provider_contract_pack::{ use serde_json::Value; const CONTRACT_CDDL: &[u8] = - include_bytes!("../../../schemas/edict-provider/contracts/v1/edict-provider-contracts.cddl"); + include_bytes!("../assets/v1/edict-provider/contracts/v1/edict-provider-contracts.cddl"); const CONTRACT_MANIFEST: &[u8] = - include_bytes!("../../../schemas/edict-provider/contracts/v1/manifest.json"); + include_bytes!("../assets/v1/edict-provider/contracts/v1/manifest.json"); const DOMAIN_ROOTS: [(&str, &str); 6] = [ ("edict.authority-facts/v1", "authority-facts"), ("edict.core.module/v1", "core-module"), diff --git a/crates/echo-wesley-gen/tests/provider_generation_input.rs b/crates/echo-wesley-gen/tests/provider_generation_input.rs index d32db651..f9bc0610 100644 --- a/crates/echo-wesley-gen/tests/provider_generation_input.rs +++ b/crates/echo-wesley-gen/tests/provider_generation_input.rs @@ -11,14 +11,12 @@ use echo_wesley_gen::provider_semantics::ProviderSemanticSourceErrorKind; use serde_json::{json, Value}; use wesley_core::compute_generation_artifact_digest_v1; -const SOURCE: &[u8] = - include_bytes!("../../../schemas/edict-provider/echo-provider-semantics-v1.json"); -const SETTINGS: &[u8] = - include_bytes!("../../../schemas/edict-provider/generation-settings-v1.json"); +const SOURCE: &[u8] = include_bytes!("../assets/v1/edict-provider/echo-provider-semantics-v1.json"); +const SETTINGS: &[u8] = include_bytes!("../assets/v1/edict-provider/generation-settings-v1.json"); const CONTRACT_CDDL: &[u8] = - include_bytes!("../../../schemas/edict-provider/contracts/v1/edict-provider-contracts.cddl"); + include_bytes!("../assets/v1/edict-provider/contracts/v1/edict-provider-contracts.cddl"); const CONTRACT_MANIFEST: &[u8] = - include_bytes!("../../../schemas/edict-provider/contracts/v1/manifest.json"); + include_bytes!("../assets/v1/edict-provider/contracts/v1/manifest.json"); fn build( source: &[u8], diff --git a/crates/echo-wesley-gen/tests/provider_generation_provenance.rs b/crates/echo-wesley-gen/tests/provider_generation_provenance.rs index 93a41030..9b145437 100644 --- a/crates/echo-wesley-gen/tests/provider_generation_provenance.rs +++ b/crates/echo-wesley-gen/tests/provider_generation_provenance.rs @@ -23,14 +23,12 @@ use wesley_core::{ GenerationProvenanceManifestV1, }; -const SOURCE: &[u8] = - include_bytes!("../../../schemas/edict-provider/echo-provider-semantics-v1.json"); -const SETTINGS: &[u8] = - include_bytes!("../../../schemas/edict-provider/generation-settings-v1.json"); +const SOURCE: &[u8] = include_bytes!("../assets/v1/edict-provider/echo-provider-semantics-v1.json"); +const SETTINGS: &[u8] = include_bytes!("../assets/v1/edict-provider/generation-settings-v1.json"); const CONTRACT_CDDL: &[u8] = - include_bytes!("../../../schemas/edict-provider/contracts/v1/edict-provider-contracts.cddl"); + include_bytes!("../assets/v1/edict-provider/contracts/v1/edict-provider-contracts.cddl"); const CONTRACT_MANIFEST: &[u8] = - include_bytes!("../../../schemas/edict-provider/contracts/v1/manifest.json"); + include_bytes!("../assets/v1/edict-provider/contracts/v1/manifest.json"); const GENERATOR_COORDINATE: &str = "echo-wesley-gen.provider-artifact-generator@1"; const GENERATOR_VERSION: &str = "0.1.0"; diff --git a/crates/echo-wesley-gen/tests/provider_generation_review.rs b/crates/echo-wesley-gen/tests/provider_generation_review.rs index 108f7d94..f3ceacbb 100644 --- a/crates/echo-wesley-gen/tests/provider_generation_review.rs +++ b/crates/echo-wesley-gen/tests/provider_generation_review.rs @@ -24,14 +24,12 @@ use wesley_core::{ compute_generation_artifact_digest_v1, GenerationContractErrorKind, GenerationReviewV1, }; -const SOURCE: &[u8] = - include_bytes!("../../../schemas/edict-provider/echo-provider-semantics-v1.json"); -const SETTINGS: &[u8] = - include_bytes!("../../../schemas/edict-provider/generation-settings-v1.json"); +const SOURCE: &[u8] = include_bytes!("../assets/v1/edict-provider/echo-provider-semantics-v1.json"); +const SETTINGS: &[u8] = include_bytes!("../assets/v1/edict-provider/generation-settings-v1.json"); const CONTRACT_CDDL: &[u8] = - include_bytes!("../../../schemas/edict-provider/contracts/v1/edict-provider-contracts.cddl"); + include_bytes!("../assets/v1/edict-provider/contracts/v1/edict-provider-contracts.cddl"); const CONTRACT_MANIFEST: &[u8] = - include_bytes!("../../../schemas/edict-provider/contracts/v1/manifest.json"); + include_bytes!("../assets/v1/edict-provider/contracts/v1/manifest.json"); const GENERATOR_COORDINATE: &str = "echo-wesley-gen.provider-artifact-generator@1"; const GENERATOR_VERSION: &str = "0.1.0"; diff --git a/crates/echo-wesley-gen/tests/provider_package.rs b/crates/echo-wesley-gen/tests/provider_package.rs new file mode 100644 index 00000000..1c9f0330 --- /dev/null +++ b/crates/echo-wesley-gen/tests/provider_package.rs @@ -0,0 +1,665 @@ +// SPDX-License-Identifier: Apache-2.0 +// © James Ross Ω FLYING•ROBOTS +#![allow(clippy::expect_used, clippy::panic)] +//! Deterministic, digest-locked Echo Edict provider package. + +use echo_wesley_gen::provider_artifacts::{ + generate_provider_primary_artifacts_v1, ProviderPrimaryArtifactsV1, +}; +use echo_wesley_gen::provider_contract_pack::{ + admit_provider_contract_pack_v1, AdmittedProviderContractPackV1, +}; +use echo_wesley_gen::provider_corpus::{ + checked_provider_artifact_corpus_v1, checked_provider_generator_source_bundle_v1, +}; +use echo_wesley_gen::provider_generation::{ + build_provider_generation_input_v1, ProviderGenerationInputV1, +}; +use echo_wesley_gen::provider_package::{ + admit_provider_package_v1, assemble_provider_package_v1, ProviderManifestArtifactKindV1, + ProviderManifestArtifactSourceV1, ProviderManifestResourceRefV1, + ProviderPackageComponentMaterialV1, ProviderPackageErrorKind, ProviderPackageFileV1, + ProviderPackageV1, ECHO_PROVIDER_MANIFEST_PATH_V1, +}; +use echo_wesley_gen::provider_provenance::{ + generate_provider_generation_provenance_v1, ProviderGenerationProvenanceV1, + ProviderGeneratorMaterialV1, +}; +use echo_wesley_gen::provider_review::{ + generate_provider_generation_review_v1, ProviderGenerationReviewV1, +}; +use wesley_core::{compute_generation_artifact_digest_v1, GenerationContractErrorKind}; + +const SOURCE: &[u8] = include_bytes!("../assets/v1/edict-provider/echo-provider-semantics-v1.json"); +const SETTINGS: &[u8] = include_bytes!("../assets/v1/edict-provider/generation-settings-v1.json"); +const CONTRACT_CDDL: &[u8] = + include_bytes!("../assets/v1/edict-provider/contracts/v1/edict-provider-contracts.cddl"); +const CONTRACT_MANIFEST: &[u8] = + include_bytes!("../assets/v1/edict-provider/contracts/v1/manifest.json"); +const LOWERER: &[u8] = include_bytes!( + "../assets/v1/edict-provider/package/v1/components/lowerer.echo-dpo.component.wasm" +); +const VERIFIER: &[u8] = include_bytes!( + "../assets/v1/edict-provider/package/v1/components/verifier.echo-dpo.component.wasm" +); + +fn admitted_pack() -> AdmittedProviderContractPackV1 { + admit_provider_contract_pack_v1(CONTRACT_CDDL, CONTRACT_MANIFEST) + .expect("checked Edict provider contract pack is admitted") +} + +fn generate() -> ( + ProviderGenerationInputV1, + ProviderPrimaryArtifactsV1, + ProviderGeneratorMaterialV1, + ProviderGenerationProvenanceV1, + ProviderGenerationReviewV1, +) { + let pack = admitted_pack(); + let input = build_provider_generation_input_v1(SOURCE, &pack, SETTINGS) + .expect("checked provider generation input builds"); + let primary = generate_provider_primary_artifacts_v1(&input, &pack) + .expect("checked primary provider artifacts generate"); + let generator = checked_provider_generator_source_bundle_v1() + .expect("checked generator source bundle builds") + .generator_material() + .expect("checked generator material builds"); + let provenance = generate_provider_generation_provenance_v1(&input, &primary, &generator) + .expect("checked provider provenance generates"); + let review = generate_provider_generation_review_v1(&input, &provenance) + .expect("checked provider review generates"); + (input, primary, generator, provenance, review) +} + +fn components(reverse: bool) -> Vec { + let mut components = vec![ + ProviderPackageComponentMaterialV1::new("lowerer.echo-dpo", LOWERER) + .expect("lowerer material is bounded"), + ProviderPackageComponentMaterialV1::new("verifier.echo-dpo", VERIFIER) + .expect("verifier material is bounded"), + ]; + if reverse { + components.reverse(); + } + components +} + +fn assemble(reverse_components: bool) -> ProviderPackageV1 { + let (input, primary, generator, provenance, review) = generate(); + assemble_provider_package_v1( + &input, + &primary, + &generator, + &provenance, + &review, + components(reverse_components), + ) + .expect("checked provider package assembles") +} + +fn replace_manifest( + package: &ProviderPackageV1, + mutate: impl FnOnce(&mut echo_wesley_gen::provider_package::ProviderManifestV1), +) -> Vec { + let mut manifest = package.manifest().clone(); + mutate(&mut manifest); + let mut bytes = serde_json::to_vec_pretty(&manifest).expect("mutated manifest serializes"); + bytes.push(b'\n'); + let mut files = package.files().to_vec(); + let index = files + .iter() + .position(|file| file.relative_path() == ECHO_PROVIDER_MANIFEST_PATH_V1) + .expect("manifest file exists"); + files[index] = ProviderPackageFileV1::new(ECHO_PROVIDER_MANIFEST_PATH_V1, &bytes) + .expect("manifest path is valid"); + files +} + +#[test] +fn checked_materials_assemble_one_digest_locked_provider_package() { + let package = assemble(false); + let reordered = assemble(true); + + assert_eq!(package, reordered); + assert_eq!(package.members().len(), 24); + assert_eq!(package.files().len(), 25); + assert_eq!(package.manifest().artifacts.len(), 10); + assert_eq!(package.manifest().schema_bindings.len(), 24); + assert_eq!( + package + .manifest() + .artifacts + .iter() + .map(|artifact| artifact.role.as_str()) + .collect::>(), + vec![ + "authority-facts.echo-dpo", + "authority-facts.echo-lawpack", + "generated-artifact-profile.echo-dpo-registration", + "lawpack.echo-dpo", + "lowerer.echo-dpo", + "provenance.provider-generation", + "review.provider-generation", + "schema.echo-provider-artifacts", + "target-profile.echo-dpo", + "verifier.echo-dpo", + ] + ); + assert!( + package + .manifest() + .artifacts + .iter() + .all(|artifact| artifact.artifact_kind + != ProviderManifestArtifactKindV1::ProviderManifest) + ); + assert!(package + .members() + .iter() + .all(|member| member.relative_path() != ECHO_PROVIDER_MANIFEST_PATH_V1)); + assert_eq!( + package + .files() + .iter() + .filter(|file| file.relative_path() == ECHO_PROVIDER_MANIFEST_PATH_V1) + .count(), + 1 + ); + assert!(package.manifest_bytes().ends_with(b"\n")); + assert_eq!(package.provider_reference(), &package.manifest().provider); + assert_ne!( + package.provider_reference().digest, + package.manifest_content_reference().digest + ); + assert!(package + .files() + .windows(2) + .all(|pair| pair[0].relative_path().as_bytes() < pair[1].relative_path().as_bytes())); + + let admitted = + admit_provider_package_v1(package.files().to_vec(), package.provider_reference()) + .expect("exact package admits against its external identity"); + assert_eq!(admitted.provider_reference(), package.provider_reference()); + assert_eq!(admitted.manifest(), package.manifest()); +} + +#[test] +fn package_generated_members_equal_the_current_checked_provider_corpus() { + let package = assemble(false); + let checked = checked_provider_artifact_corpus_v1() + .expect("checked #652 provider artifact corpus is an exact bounded value"); + assert_eq!(checked.files().len(), 22); + for checked_file in checked.files() { + let package_path = format!("generated/{}", checked_file.relative_path()); + let packaged = package + .files() + .iter() + .find(|file| file.relative_path() == package_path) + .expect("every checked #652 file is a package member"); + assert_eq!(packaged.bytes(), checked_file.bytes()); + } +} + +#[test] +fn manifest_routes_preserve_semantic_and_exact_byte_digest_classes() { + let (input, primary, generator, provenance, review) = generate(); + let package = assemble_provider_package_v1( + &input, + &primary, + &generator, + &provenance, + &review, + components(false), + ) + .expect("checked provider package assembles"); + + for output in primary.artifacts() { + let route = package + .manifest() + .artifacts + .iter() + .find(|artifact| artifact.role == output.role()) + .expect("canonical output has a manifest route"); + assert_eq!(route.resource.digest, output.domain_framed_digest()); + assert_ne!(route.resource.digest, output.content_reference().digest); + let member = package + .members() + .iter() + .find(|member| member.relative_path().contains(output.role())) + .expect("canonical output has a physical member"); + assert_eq!(member.bytes(), output.canonical_bytes()); + assert_ne!(member.raw_digest(), route.resource.digest); + } + + let schema = package + .manifest() + .artifacts + .iter() + .find(|artifact| artifact.artifact_kind == ProviderManifestArtifactKindV1::ArtifactSchema) + .expect("schema route exists"); + assert_eq!( + schema.resource.digest, + primary.schema().content_reference().digest + ); + let provenance_route = package + .manifest() + .artifacts + .iter() + .find(|artifact| { + artifact.artifact_kind == ProviderManifestArtifactKindV1::GenerationProvenance + }) + .expect("provenance route exists"); + assert_eq!( + provenance_route.resource.digest, + provenance.content_reference().digest + ); + let review_route = package + .manifest() + .artifacts + .iter() + .find(|artifact| artifact.artifact_kind == ProviderManifestArtifactKindV1::ReviewArtifact) + .expect("review route exists"); + assert_eq!( + review_route.resource.digest, + review.content_reference().digest + ); +} + +#[test] +fn generated_routes_require_one_exact_source_and_generator_reference() { + let package = assemble(false); + let files = replace_manifest(&package, |manifest| { + let source = match &mut manifest.artifacts[0].source { + ProviderManifestArtifactSourceV1::Generated { + semantic_source, .. + } => semantic_source, + ProviderManifestArtifactSourceV1::Component { .. } => { + panic!("first artifact is generated") + } + }; + source.digest = + "sha256:0000000000000000000000000000000000000000000000000000000000000000".to_owned(); + }); + let error = admit_provider_package_v1(files, package.provider_reference()) + .expect_err("generated routes cannot disagree on exact source identity"); + assert_eq!( + error.kind(), + ProviderPackageErrorKind::ArtifactSourceMismatch + ); +} + +#[test] +fn agreeing_generated_routes_must_match_packaged_wesley_provenance() { + let package = assemble(false); + let files = replace_manifest(&package, |manifest| { + for artifact in &mut manifest.artifacts { + if let ProviderManifestArtifactSourceV1::Generated { + semantic_source, .. + } = &mut artifact.source + { + semantic_source.digest = + "sha256:0000000000000000000000000000000000000000000000000000000000000000" + .to_owned(); + } + } + }); + let error = admit_provider_package_v1(files, package.provider_reference()) + .expect_err("agreeing fake routes cannot replace packaged provenance"); + assert_eq!( + error.kind(), + ProviderPackageErrorKind::GenerationProvenanceMismatch + ); +} + +#[test] +fn packaged_wesley_evidence_preserves_the_typed_contract_failure() { + let package = assemble(false); + let provenance_path = "generated/evidence/provenance.provider-generation.json"; + let provenance_file = package + .files() + .iter() + .find(|file| file.relative_path() == provenance_path) + .expect("provenance file exists"); + let mut provenance: serde_json::Value = + serde_json::from_slice(provenance_file.bytes()).expect("provenance is JSON"); + provenance["contractVersions"]["inputSchema"] = + serde_json::Value::String("wesley.wrong/v1".to_owned()); + let changed_bytes = serde_json::to_vec(&provenance).expect("changed provenance serializes"); + let changed_digest = compute_generation_artifact_digest_v1(&changed_bytes); + let mut files = replace_manifest(&package, |manifest| { + manifest + .artifacts + .iter_mut() + .find(|artifact| { + artifact.artifact_kind == ProviderManifestArtifactKindV1::GenerationProvenance + }) + .expect("provenance route exists") + .resource + .digest = changed_digest; + }); + let index = files + .iter() + .position(|file| file.relative_path() == provenance_path) + .expect("provenance file exists"); + files[index] = ProviderPackageFileV1::new(provenance_path, &changed_bytes) + .expect("provenance path is valid"); + + let error = admit_provider_package_v1(files, package.provider_reference()) + .expect_err("wrong Wesley contract version must fail before package use"); + assert_eq!( + error.kind(), + ProviderPackageErrorKind::GenerationProvenanceMismatch + ); + assert_eq!( + error.wesley_contract_kind(), + Some(GenerationContractErrorKind::ContractVersionMismatch) + ); +} + +#[test] +fn valid_looking_raw_digest_cannot_replace_a_canonical_artifact_identity() { + let (input, primary, generator, provenance, review) = generate(); + let package = assemble_provider_package_v1( + &input, + &primary, + &generator, + &provenance, + &review, + components(false), + ) + .expect("checked provider package assembles"); + let raw_lawpack_digest = primary + .artifact("lawpack.echo-dpo") + .expect("lawpack exists") + .content_reference() + .digest + .clone(); + let files = replace_manifest(&package, |manifest| { + manifest + .artifacts + .iter_mut() + .find(|artifact| artifact.role == "lawpack.echo-dpo") + .expect("lawpack route exists") + .resource + .digest = raw_lawpack_digest; + }); + let error = admit_provider_package_v1(files, package.provider_reference()) + .expect_err("raw byte identity cannot impersonate the Edict semantic identity"); + assert_eq!( + error.kind(), + ProviderPackageErrorKind::ArtifactDigestMismatch + ); +} + +#[test] +fn opaque_component_bytes_move_the_digest_root_without_proving_host_readiness() { + let baseline = assemble(false); + let (input, primary, generator, provenance, review) = generate(); + let mut changed_lowerer = LOWERER.to_vec(); + changed_lowerer[0] ^= 1; + let changed = assemble_provider_package_v1( + &input, + &primary, + &generator, + &provenance, + &review, + vec![ + ProviderPackageComponentMaterialV1::new("lowerer.echo-dpo", &changed_lowerer) + .expect("changed lowerer remains bounded explicit material"), + ProviderPackageComponentMaterialV1::new("verifier.echo-dpo", VERIFIER) + .expect("verifier remains bounded explicit material"), + ], + ) + .expect("coherently changed package assembles a different identity"); + assert_ne!(changed.provider_reference(), baseline.provider_reference()); + let error = admit_provider_package_v1(changed.files().to_vec(), baseline.provider_reference()) + .expect_err("coherent internal rebinding cannot cross the caller's old pin"); + assert_eq!( + error.kind(), + ProviderPackageErrorKind::ProviderIdentityMismatch + ); +} + +#[test] +fn package_admission_rejects_manifest_member_and_expected_root_tampering() { + let package = assemble(false); + + let mut alternate_manifest = package.files().to_vec(); + let manifest_index = alternate_manifest + .iter() + .position(|file| file.relative_path() == ECHO_PROVIDER_MANIFEST_PATH_V1) + .expect("manifest file exists"); + let mut alternate_bytes = alternate_manifest[manifest_index].bytes().to_vec(); + alternate_bytes.push(b'\n'); + alternate_manifest[manifest_index] = + ProviderPackageFileV1::new(ECHO_PROVIDER_MANIFEST_PATH_V1, &alternate_bytes) + .expect("manifest path is valid"); + let error = admit_provider_package_v1(alternate_manifest, package.provider_reference()) + .expect_err("alternate JSON occurrence must fail exact rendering"); + assert_eq!(error.kind(), ProviderPackageErrorKind::ManifestNoncanonical); + + let mut resource_tamper = package.files().to_vec(); + let resource_index = resource_tamper + .iter() + .position(|file| file.relative_path() == "generated/resources/resource.target-ir.cbor") + .expect("generated resource exists"); + let mut tampered_bytes = resource_tamper[resource_index].bytes().to_vec(); + tampered_bytes[0] ^= 1; + resource_tamper[resource_index] = ProviderPackageFileV1::new( + "generated/resources/resource.target-ir.cbor", + &tampered_bytes, + ) + .expect("resource path is valid"); + let error = admit_provider_package_v1(resource_tamper, package.provider_reference()) + .expect_err("unrouted resource tamper must move the pinned package root"); + assert_eq!( + error.kind(), + ProviderPackageErrorKind::ProviderIdentityMismatch + ); + + let mut component_tamper = package.files().to_vec(); + let lowerer_index = component_tamper + .iter() + .position(|file| file.relative_path() == "components/lowerer.echo-dpo.component.wasm") + .expect("lowerer component exists"); + let mut tampered_bytes = component_tamper[lowerer_index].bytes().to_vec(); + tampered_bytes[0] ^= 1; + component_tamper[lowerer_index] = ProviderPackageFileV1::new( + "components/lowerer.echo-dpo.component.wasm", + &tampered_bytes, + ) + .expect("component path is valid"); + let error = admit_provider_package_v1(component_tamper, package.provider_reference()) + .expect_err("routed component tamper must fail its exact manifest identity"); + assert_eq!( + error.kind(), + ProviderPackageErrorKind::ArtifactDigestMismatch + ); + + let wrong_expected = ProviderManifestResourceRefV1 { + coordinate: package.provider_reference().coordinate.clone(), + digest: "sha256:0000000000000000000000000000000000000000000000000000000000000000" + .to_owned(), + }; + let error = admit_provider_package_v1(package.files().to_vec(), &wrong_expected) + .expect_err("internally coherent package cannot replace caller identity"); + assert_eq!( + error.kind(), + ProviderPackageErrorKind::ProviderIdentityMismatch + ); +} + +#[test] +fn component_material_closure_fails_without_discovery_or_winner_selection() { + let (input, primary, generator, provenance, review) = generate(); + let error = assemble_provider_package_v1( + &input, + &primary, + &generator, + &provenance, + &review, + vec![ + ProviderPackageComponentMaterialV1::new("verifier.echo-dpo", VERIFIER) + .expect("verifier material is bounded"), + ], + ) + .expect_err("missing lowerer bytes must fail closed"); + assert_eq!(error.kind(), ProviderPackageErrorKind::ComponentMissing); + assert_eq!(error.subject(), "lowerer.echo-dpo"); + + let error = assemble_provider_package_v1( + &input, + &primary, + &generator, + &provenance, + &review, + vec![ + ProviderPackageComponentMaterialV1::new("lowerer.echo-dpo", LOWERER) + .expect("lowerer material is bounded"), + ProviderPackageComponentMaterialV1::new("lowerer.echo-dpo", VERIFIER) + .expect("conflicting duplicate remains explicit material"), + ProviderPackageComponentMaterialV1::new("verifier.echo-dpo", VERIFIER) + .expect("verifier material is bounded"), + ], + ) + .expect_err("conflicting duplicate roles must not select a winner"); + assert_eq!(error.kind(), ProviderPackageErrorKind::ComponentDuplicate); + assert_eq!(error.subject(), "lowerer.echo-dpo"); + + let mut with_unexpected = components(false); + with_unexpected.push( + ProviderPackageComponentMaterialV1::new("component.unexpected", b"explicit") + .expect("unexpected material remains bounded"), + ); + let error = assemble_provider_package_v1( + &input, + &primary, + &generator, + &provenance, + &review, + with_unexpected, + ) + .expect_err("unexpected component material must not be ignored"); + assert_eq!(error.kind(), ProviderPackageErrorKind::ComponentUnexpected); + assert_eq!(error.subject(), "component.unexpected"); +} + +#[test] +fn raw_admission_preserves_component_material_bounds() { + let package = assemble(false); + let empty_digest = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; + let mut files = replace_manifest(&package, |manifest| { + let lowerer = manifest + .artifacts + .iter_mut() + .find(|artifact| artifact.role == "lowerer.echo-dpo") + .expect("lowerer route exists"); + lowerer.resource.digest = empty_digest.to_owned(); + match &mut lowerer.source { + ProviderManifestArtifactSourceV1::Component { component } => { + component.digest = empty_digest.to_owned(); + } + ProviderManifestArtifactSourceV1::Generated { .. } => { + panic!("lowerer has component provenance") + } + } + }); + let lowerer_index = files + .iter() + .position(|file| file.relative_path() == "components/lowerer.echo-dpo.component.wasm") + .expect("lowerer file exists"); + files[lowerer_index] = + ProviderPackageFileV1::new("components/lowerer.echo-dpo.component.wasm", b"") + .expect("empty bytes do not change the safe path"); + let error = admit_provider_package_v1(files, package.provider_reference()) + .expect_err("raw admission cannot bypass the component byte bound"); + assert_eq!(error.kind(), ProviderPackageErrorKind::ComponentInvalid); +} + +#[test] +fn package_file_inventory_requires_exact_unique_path_order() { + let package = assemble(false); + + let mut reversed = package.files().to_vec(); + reversed.reverse(); + let error = admit_provider_package_v1(reversed, package.provider_reference()) + .expect_err("admission must not normalize supplied file order"); + assert_eq!( + error.kind(), + ProviderPackageErrorKind::PackageMemberOutOfOrder + ); + + let mut missing = package.files().to_vec(); + missing.remove(0); + let error = admit_provider_package_v1(missing, package.provider_reference()) + .expect_err("missing package member must fail closed"); + assert_eq!(error.kind(), ProviderPackageErrorKind::PackageMemberMissing); + + let mut duplicate = package.files().to_vec(); + duplicate.push(duplicate[0].clone()); + duplicate.sort_by(|left, right| left.relative_path().cmp(right.relative_path())); + let error = admit_provider_package_v1(duplicate, package.provider_reference()) + .expect_err("identical duplicate member remains ambiguous"); + assert_eq!( + error.kind(), + ProviderPackageErrorKind::PackageMemberDuplicate + ); + + let mut unexpected = package.files().to_vec(); + unexpected[0] = ProviderPackageFileV1::new("components/unknown.component.wasm", b"unknown") + .expect("unexpected path is syntactically safe"); + unexpected.sort_by(|left, right| left.relative_path().cmp(right.relative_path())); + let error = admit_provider_package_v1(unexpected, package.provider_reference()) + .expect_err("safe but undeclared member must fail closed"); + assert_eq!( + error.kind(), + ProviderPackageErrorKind::PackageMemberUnexpected + ); +} + +#[test] +fn manifest_contract_source_and_schema_mutations_fail_before_root_comparison() { + let package = assemble(false); + + let files = replace_manifest(&package, |manifest| { + manifest.schema_bindings[0].root_rule = "wrong-root".to_owned(); + }); + let error = admit_provider_package_v1(files, package.provider_reference()) + .expect_err("wrong existing schema route must fail as a schema disagreement"); + assert_eq!( + error.kind(), + ProviderPackageErrorKind::SchemaBindingMismatch + ); + + let generated_source = package + .manifest() + .artifacts + .iter() + .find_map(|artifact| match &artifact.source { + ProviderManifestArtifactSourceV1::Generated { .. } => Some(artifact.source.clone()), + ProviderManifestArtifactSourceV1::Component { .. } => None, + }) + .expect("generated source exists"); + let files = replace_manifest(&package, |manifest| { + manifest + .artifacts + .iter_mut() + .find(|artifact| artifact.role == "lowerer.echo-dpo") + .expect("lowerer route exists") + .source = generated_source; + }); + let error = admit_provider_package_v1(files, package.provider_reference()) + .expect_err("component role cannot use generated provenance"); + assert_eq!( + error.kind(), + ProviderPackageErrorKind::ArtifactSourceMismatch + ); + + let files = replace_manifest(&package, |manifest| { + manifest.provider.digest = + "SHA256:0000000000000000000000000000000000000000000000000000000000000000".to_owned(); + }); + let error = admit_provider_package_v1(files, package.provider_reference()) + .expect_err("digest textual aliases must not be normalized"); + assert_eq!(error.kind(), ProviderPackageErrorKind::DigestInvalid); +} diff --git a/crates/echo-wesley-gen/tests/provider_package_assets.rs b/crates/echo-wesley-gen/tests/provider_package_assets.rs new file mode 100644 index 00000000..cdb32216 --- /dev/null +++ b/crates/echo-wesley-gen/tests/provider_package_assets.rs @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: Apache-2.0 +// © James Ross Ω FLYING•ROBOTS +#![allow(clippy::expect_used, clippy::panic)] +//! Exact owner/carrier and Cargo-archive witnesses for provider package assets. + +use std::path::{Path, PathBuf}; +use std::process::{Command, Output}; +use std::sync::atomic::{AtomicU64, Ordering}; + +const EXPECTED_ASSET_FILE_COUNT: usize = 35; +static NEXT_TEMP_DIRECTORY: AtomicU64 = AtomicU64::new(0); + +struct TestDirectory(PathBuf); + +impl TestDirectory { + fn new(label: &str) -> Self { + let sequence = NEXT_TEMP_DIRECTORY.fetch_add(1, Ordering::Relaxed); + let path = std::env::temp_dir().join(format!( + "echo-provider-assets-{label}-{}-{sequence}", + std::process::id() + )); + if path.exists() { + std::fs::remove_dir_all(&path).expect("stale asset test directory is removable"); + } + Self(path) + } + + fn path(&self) -> &Path { + &self.0 + } +} + +impl Drop for TestDirectory { + fn drop(&mut self) { + drop(std::fs::remove_dir_all(&self.0)); + } +} + +fn run_assets(arguments: &[&str]) -> Output { + Command::new(env!("CARGO_BIN_EXE_echo-edict-provider-assets")) + .args(arguments) + .output() + .expect("provider asset synchronizer executes") +} + +fn count_files(path: &Path) -> usize { + std::fs::read_dir(path) + .expect("asset directory is readable") + .map(|entry| entry.expect("asset entry is readable")) + .map(|entry| { + let file_type = entry.file_type().expect("asset entry type is readable"); + if file_type.is_dir() { + count_files(&entry.path()) + } else { + usize::from(file_type.is_file()) + } + }) + .sum() +} + +#[test] +fn fixed_assets_match_their_current_owners_and_cargo_archive() { + let checked = run_assets(&["--check-package-list"]); + assert!( + checked.status.success(), + "provider asset/archive check failed:\n{}", + String::from_utf8_lossy(&checked.stderr) + ); +} + +#[test] +fn asset_check_reports_drift_without_repair_and_write_restores_exact_bytes() { + let directory = TestDirectory::new("check-write"); + let assets_root = directory.path().join("v1"); + let assets_root_text = assets_root.to_string_lossy(); + + let written = run_assets(&["--write", "--assets-root", &assets_root_text]); + assert!( + written.status.success(), + "provider asset write failed:\n{}", + String::from_utf8_lossy(&written.stderr) + ); + assert_eq!(count_files(&assets_root), EXPECTED_ASSET_FILE_COUNT); + + let changed_path = assets_root.join("repository/rust-toolchain.toml.source"); + std::fs::write(&changed_path, b"tampered toolchain") + .expect("asset carrier is deliberately changed"); + let checked = run_assets(&["--assets-root", &assets_root_text]); + assert!(!checked.status.success()); + assert!(String::from_utf8_lossy(&checked.stderr) + .contains("changed: repository/rust-toolchain.toml.source")); + assert_eq!( + std::fs::read(&changed_path).expect("changed carrier remains readable"), + b"tampered toolchain" + ); + + let restored = run_assets(&["--write", "--assets-root", &assets_root_text]); + assert!( + restored.status.success(), + "provider asset restore failed:\n{}", + String::from_utf8_lossy(&restored.stderr) + ); + let final_check = run_assets(&["--assets-root", &assets_root_text]); + assert!( + final_check.status.success(), + "restored provider assets do not check:\n{}", + String::from_utf8_lossy(&final_check.stderr) + ); +} diff --git a/crates/echo-wesley-gen/tests/provider_package_corpus.rs b/crates/echo-wesley-gen/tests/provider_package_corpus.rs new file mode 100644 index 00000000..92b29e22 --- /dev/null +++ b/crates/echo-wesley-gen/tests/provider_package_corpus.rs @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: Apache-2.0 +// © James Ross Ω FLYING•ROBOTS +#![allow(clippy::expect_used, clippy::panic)] +//! Checked filesystem publication for Echo's digest-locked Edict provider package. + +use std::collections::BTreeMap; +use std::path::{Path, PathBuf}; +use std::process::{Command, Output}; +use std::sync::atomic::{AtomicU64, Ordering}; + +use sha2::{Digest as _, Sha256}; + +const EXPECTED_PROVIDER_DIGEST: &str = + "sha256:38dd0532d4353f6260e8aaeafb149457b42f8ad94bb107ecbb74bf02f0051a92"; +const EXPECTED_MANIFEST_RAW_SHA256: &str = + "a65771b9430edd6b6ea6de727bf7569a491cfcc8bcd636d081d3e536557bfc5c"; + +const PACKAGE_PATHS: [&str; 25] = [ + "components/lowerer.echo-dpo.component.wasm", + "components/verifier.echo-dpo.component.wasm", + "generated/evidence/provenance.provider-generation.json", + "generated/evidence/review.provider-generation.json", + "generated/primary/authority-facts.echo-dpo.cbor", + "generated/primary/authority-facts.echo-lawpack.cbor", + "generated/primary/generated-artifact-profile.echo-dpo-registration.cbor", + "generated/primary/lawpack.echo-dpo.cbor", + "generated/primary/schema.echo-provider-artifacts.cddl", + "generated/primary/target-profile.echo-dpo.cbor", + "generated/resources/resource.conformance-corpus.cbor", + "generated/resources/resource.lawpack-compatibility.cbor", + "generated/resources/resource.lawpack-exports.cbor", + "generated/resources/resource.lawpack-target-adapter.cbor", + "generated/resources/resource.lawpack-verifier.cbor", + "generated/resources/resource.target-bundle-profile.cbor", + "generated/resources/resource.target-cost-algebra.cbor", + "generated/resources/resource.target-footprint-algebra.cbor", + "generated/resources/resource.target-intrinsics.cbor", + "generated/resources/resource.target-ir.cbor", + "generated/resources/resource.target-lowerer-contract.cbor", + "generated/resources/resource.target-obstruction-taxonomy.cbor", + "generated/resources/resource.target-operation-profiles.cbor", + "generated/resources/resource.target-verifier-contract.cbor", + "provider-manifest.echo.json", +]; + +static NEXT_TEMP_DIRECTORY: AtomicU64 = AtomicU64::new(0); + +struct TestDirectory(PathBuf); + +impl TestDirectory { + fn new(label: &str) -> Self { + let sequence = NEXT_TEMP_DIRECTORY.fetch_add(1, Ordering::Relaxed); + let path = std::env::temp_dir().join(format!( + "echo-provider-package-{label}-{}-{sequence}", + std::process::id() + )); + if path.exists() { + std::fs::remove_dir_all(&path).expect("stale test directory is removable"); + } + std::fs::create_dir_all(&path).expect("test directory is created"); + Self(path) + } + + fn path(&self) -> &Path { + &self.0 + } +} + +impl Drop for TestDirectory { + fn drop(&mut self) { + drop(std::fs::remove_dir_all(&self.0)); + } +} + +fn run_package(root: &Path, check: bool) -> Output { + let mut command = Command::new(env!("CARGO_BIN_EXE_echo-edict-provider-package")); + if check { + command.arg("--check"); + } + command + .arg("--out") + .arg(root) + .output() + .expect("provider package command executes") +} + +fn snapshot(root: &Path) -> BTreeMap> { + let mut files = BTreeMap::new(); + collect_files(root, root, &mut files); + files +} + +fn collect_files(root: &Path, directory: &Path, files: &mut BTreeMap>) { + let mut entries = std::fs::read_dir(directory) + .expect("package directory is readable") + .collect::, _>>() + .expect("package directory entries are readable"); + entries.sort_by_key(std::fs::DirEntry::file_name); + + for entry in entries { + let file_type = entry.file_type().expect("package entry type is readable"); + if file_type.is_dir() { + collect_files(root, &entry.path(), files); + continue; + } + if !file_type.is_file() { + continue; + } + + let relative = entry + .path() + .strip_prefix(root) + .expect("package entry is below the corpus root") + .components() + .map(|component| { + component + .as_os_str() + .to_str() + .expect("package paths are UTF-8") + }) + .collect::>() + .join("/"); + files.insert( + relative, + std::fs::read(entry.path()).expect("package file is readable"), + ); + } +} + +fn expected_paths() -> Vec { + PACKAGE_PATHS + .iter() + .map(|path| (*path).to_owned()) + .collect() +} + +#[test] +fn generation_writes_the_exact_package_and_check_mode_is_read_only() { + let directory = TestDirectory::new("write-check"); + let generated = run_package(directory.path(), false); + assert!( + generated.status.success(), + "provider package generation failed:\n{}", + String::from_utf8_lossy(&generated.stderr) + ); + + let before = snapshot(directory.path()); + assert_eq!(before.keys().cloned().collect::>(), expected_paths()); + let manifest_bytes = before + .get("provider-manifest.echo.json") + .expect("provider manifest is present"); + let manifest: serde_json::Value = + serde_json::from_slice(manifest_bytes).expect("provider manifest is JSON"); + assert_eq!( + manifest["provider"]["digest"].as_str(), + Some(EXPECTED_PROVIDER_DIGEST) + ); + assert_eq!( + hex::encode(Sha256::digest(manifest_bytes)), + EXPECTED_MANIFEST_RAW_SHA256 + ); + + let checked = run_package(directory.path(), true); + assert!( + checked.status.success(), + "provider package check failed:\n{}", + String::from_utf8_lossy(&checked.stderr) + ); + assert!(String::from_utf8_lossy(&checked.stdout).contains("is current")); + assert_eq!(snapshot(directory.path()), before); +} + +#[test] +fn check_mode_reports_drift_without_rewriting_or_creating_members() { + let directory = TestDirectory::new("no-write-check"); + let generated = run_package(directory.path(), false); + assert!( + generated.status.success(), + "provider package generation failed:\n{}", + String::from_utf8_lossy(&generated.stderr) + ); + + let changed_path = directory.path().join("provider-manifest.echo.json"); + let missing_path = directory + .path() + .join("components/lowerer.echo-dpo.component.wasm"); + let unexpected_path = directory.path().join("operator-owned.txt"); + std::fs::write(&changed_path, b"tampered manifest") + .expect("changed package manifest is written"); + std::fs::remove_file(&missing_path).expect("lowerer package member is removed"); + std::fs::write(&unexpected_path, b"operator bytes") + .expect("unexpected operator file is written"); + + let checked = run_package(directory.path(), true); + assert!(!checked.status.success()); + let stderr = String::from_utf8(checked.stderr).expect("checker diagnostics are UTF-8"); + assert!(stderr.contains("missing: components/lowerer.echo-dpo.component.wasm")); + assert!(stderr.contains("changed: provider-manifest.echo.json")); + assert!(stderr.contains("unexpected: operator-owned.txt")); + assert_eq!( + std::fs::read(&changed_path).expect("tampered manifest remains readable"), + b"tampered manifest" + ); + assert!(!missing_path.exists()); + assert_eq!( + std::fs::read(&unexpected_path).expect("operator file remains readable"), + b"operator bytes" + ); +} + +#[test] +fn generation_refuses_unexpected_entries_before_writing_package_members() { + let directory = TestDirectory::new("unexpected-entry"); + let unexpected_path = directory.path().join("operator-owned.txt"); + std::fs::write(&unexpected_path, b"operator bytes") + .expect("unexpected operator file is written"); + + let generated = run_package(directory.path(), false); + assert!(!generated.status.success()); + assert!(String::from_utf8_lossy(&generated.stderr) + .contains("refusing to generate over unexpected corpus entry operator-owned.txt")); + assert_eq!( + std::fs::read(&unexpected_path).expect("operator file remains readable"), + b"operator bytes" + ); + assert!(!directory.path().join("components").exists()); + assert!(!directory.path().join("generated").exists()); + assert!(!directory + .path() + .join("provider-manifest.echo.json") + .exists()); +} + +#[test] +fn checked_provider_package_matches_the_current_renderer() { + let package_root = + Path::new(env!("CARGO_MANIFEST_DIR")).join("assets/v1/edict-provider/package/v1"); + let checked = run_package(&package_root, true); + + assert!( + checked.status.success(), + "checked provider package drifted:\n{}", + String::from_utf8_lossy(&checked.stderr) + ); + assert!(String::from_utf8_lossy(&checked.stdout).contains("is current")); +} diff --git a/crates/echo-wesley-gen/tests/provider_primary_artifacts.rs b/crates/echo-wesley-gen/tests/provider_primary_artifacts.rs index 9cc0a105..48bfa3c6 100644 --- a/crates/echo-wesley-gen/tests/provider_primary_artifacts.rs +++ b/crates/echo-wesley-gen/tests/provider_primary_artifacts.rs @@ -19,14 +19,12 @@ use echo_wesley_gen::provider_generation::{ use serde_json::{json, Value}; use wesley_core::{compute_generation_artifact_digest_v1, GenerationContractErrorKind}; -const SOURCE: &[u8] = - include_bytes!("../../../schemas/edict-provider/echo-provider-semantics-v1.json"); -const SETTINGS: &[u8] = - include_bytes!("../../../schemas/edict-provider/generation-settings-v1.json"); +const SOURCE: &[u8] = include_bytes!("../assets/v1/edict-provider/echo-provider-semantics-v1.json"); +const SETTINGS: &[u8] = include_bytes!("../assets/v1/edict-provider/generation-settings-v1.json"); const CONTRACT_CDDL: &[u8] = - include_bytes!("../../../schemas/edict-provider/contracts/v1/edict-provider-contracts.cddl"); + include_bytes!("../assets/v1/edict-provider/contracts/v1/edict-provider-contracts.cddl"); const CONTRACT_MANIFEST: &[u8] = - include_bytes!("../../../schemas/edict-provider/contracts/v1/manifest.json"); + include_bytes!("../assets/v1/edict-provider/contracts/v1/manifest.json"); const PRIMARY_ROLES: [&str; 6] = [ "authority-facts.echo-dpo", diff --git a/crates/echo-wesley-gen/tests/provider_semantic_source.rs b/crates/echo-wesley-gen/tests/provider_semantic_source.rs index 69c2e982..32f5f3fb 100644 --- a/crates/echo-wesley-gen/tests/provider_semantic_source.rs +++ b/crates/echo-wesley-gen/tests/provider_semantic_source.rs @@ -7,13 +7,12 @@ use echo_wesley_gen::provider_semantics::{ parse_provider_semantic_source_v1, ApertureRequirementDeclaration, ArtifactResourceProvision, AuthorityClass, AuthorityFactSourceKind, AuthoritySourceKind, BoundaryKind, CoreStringCanonicalization, EffectKindHint, ExecutionClass, GeneratedArtifactKind, - InvocationInputKind, InvocationOutputKind, OpticKind, ProviderSemanticSourceErrorKind, - SemanticTypeShape, ECHO_PROVIDER_SEMANTIC_SOURCE_API_V1, + InvocationInputKind, InvocationOutputKind, OpticKind, ProviderComponentKind, + ProviderSemanticSourceErrorKind, SemanticTypeShape, ECHO_PROVIDER_SEMANTIC_SOURCE_API_V1, }; use serde_json::{json, Value}; -const SOURCE: &str = - include_str!("../../../schemas/edict-provider/echo-provider-semantics-v1.json"); +const SOURCE: &str = include_str!("../assets/v1/edict-provider/echo-provider-semantics-v1.json"); fn source_value() -> Value { serde_json::from_str(SOURCE).expect("checked source fixture is JSON") @@ -55,6 +54,35 @@ fn checked_echo_provider_semantic_source_validates() { assert_eq!(source.api_version, ECHO_PROVIDER_SEMANTIC_SOURCE_API_V1); assert_eq!(source.coordinate, "echo.semantic-schema@1"); + assert_eq!( + source + .package_manifest + .components + .iter() + .map(|component| { + ( + component.role.as_str(), + component.kind, + component.coordinate.as_str(), + component.contract.as_str(), + ) + }) + .collect::>(), + vec![ + ( + "lowerer.echo-dpo", + ProviderComponentKind::Lowerer, + "echo.dpo.lowerer/component@1", + "edict:target-provider/lowerer@1.0.0", + ), + ( + "verifier.echo-dpo", + ProviderComponentKind::Verifier, + "echo.dpo.verifier/component@1", + "edict:target-provider/verifier@1.0.0", + ), + ] + ); assert_eq!(source.operations.len(), 1); assert_eq!(source.operations[0].identity.coordinate, "a.b@1.t"); assert_eq!(source.operations[0].effect, "target.replace"); @@ -529,8 +557,38 @@ fn checked_echo_provider_semantic_source_validates() { .map(|binding| (binding.domain.as_str(), binding.root_rule.as_str())) .collect::>(), vec![ + ( + "echo.dpo-lawpack.adapter.echo-dpo@1", + "echo-provider-lawpack-target-adapter", + ), + ( + "echo.dpo-lawpack.compatibility@1", + "echo-provider-lawpack-compatibility", + ), + ("echo.dpo-lawpack.exports@1", "lawpack-exports"), + ( + "echo.dpo-lawpack.verifier@1", + "echo-provider-lawpack-verifier", + ), + ("echo.dpo.bundle/v1", "echo-dpo-bundle"), + ("echo.dpo.cost/v1", "echo-dpo-cost"), + ("echo.dpo.fixtures/v1", "echo-provider-conformance-corpus",), + ("echo.dpo.footprint/v1", "echo-dpo-footprint"), + ("echo.dpo.intrinsics/v1", "intrinsics-document"), + ("echo.dpo.lowerer/v1", "echo-dpo-lowerer"), + ("echo.dpo.obstructions/v1", "echo-dpo-obstructions"), + ( + "echo.dpo.operation-profiles/v1", + "operation-profiles-document", + ), + ("echo.dpo.verifier/v1", "echo-dpo-verifier"), + ( + "echo.generated-artifact-profile/v1", + "generated-artifact-profile", + ), ("echo.generated-artifact/v1", "generated-artifact"), ("echo.review-payload/v1", "review-payload"), + ("echo.span-ir/v1", "echo-span-ir"), ("echo.verifier-report/v1", "verifier-report"), ("edict.authority-facts/v1", "authority-facts"), ("edict.core.module/v1", "core-module"), @@ -542,6 +600,82 @@ fn checked_echo_provider_semantic_source_validates() { ); } +#[test] +fn generated_artifacts_require_their_owner_schema_bindings() { + assert_failure_tuple( + |source| { + let bindings = source["schemaBindings"] + .as_array_mut() + .expect("schema bindings"); + let index = bindings + .iter() + .position(|binding| { + binding["domain"] == Value::String("echo.generated-artifact-profile/v1".into()) + }) + .expect("generated-artifact-profile binding"); + bindings.remove(index); + }, + ( + ProviderSemanticSourceErrorKind::MissingSchemaBinding, + "generated-artifact-profile.echo-dpo-registration", + "echo.generated-artifact-profile/v1", + ), + ); + assert_failure_tuple( + |source| { + let bindings = source["schemaBindings"] + .as_array_mut() + .expect("schema bindings"); + let index = bindings + .iter() + .position(|binding| { + binding["domain"] == Value::String("echo.dpo.fixtures/v1".into()) + }) + .expect("generated resource binding"); + bindings.remove(index); + }, + ( + ProviderSemanticSourceErrorKind::MissingSchemaBinding, + "resource.conformance-corpus", + "echo.dpo.fixtures/v1", + ), + ); + assert_failure_tuple( + |source| { + let binding = source["schemaBindings"] + .as_array_mut() + .expect("schema bindings") + .iter_mut() + .find(|binding| { + binding["domain"] == Value::String("echo.generated-artifact-profile/v1".into()) + }) + .expect("generated-artifact-profile binding"); + binding["rootRule"] = Value::String("wrong-profile-root".into()); + }, + ( + ProviderSemanticSourceErrorKind::SchemaRootMismatch, + "echo.generated-artifact-profile/v1", + "wrong-profile-root", + ), + ); + assert_failure_tuple( + |source| { + let binding = source["schemaBindings"] + .as_array_mut() + .expect("schema bindings") + .iter_mut() + .find(|binding| binding["domain"] == Value::String("echo.dpo.fixtures/v1".into())) + .expect("generated resource binding"); + binding["rootRule"] = Value::String("wrong-resource-root".into()); + }, + ( + ProviderSemanticSourceErrorKind::SchemaRootMismatch, + "resource.conformance-corpus", + "wrong-resource-root", + ), + ); +} + #[test] fn set_like_source_reordering_preserves_validated_semantics() { let mut ordered = source_value(); @@ -654,6 +788,7 @@ fn set_like_source_reordering_preserves_validated_semantics() { "/targetProfileProjection/acceptedCoreAbis", "/targetProfileProjection/generatedArtifactProfileRoles", "/targetProfileProjection/acceptedLawpackAdapterAbis", + "/packageManifest/components", ] { reordered .pointer_mut(pointer) @@ -1721,6 +1856,67 @@ fn generated_artifact_and_output_contracts_are_exact() { ); } +#[test] +fn package_component_declarations_fail_closed() { + assert_failure_tuple( + |source| { + source["packageManifest"]["components"] + .as_array_mut() + .expect("package components") + .remove(0); + }, + ( + ProviderSemanticSourceErrorKind::ProviderComponentClosureMismatch, + "packageManifest.components", + "lowerer", + ), + ); + assert_failure_tuple( + |source| { + source["packageManifest"]["components"][0]["contract"] = + Value::String("edict:target-provider/verifier@1.0.0".to_owned()); + }, + ( + ProviderSemanticSourceErrorKind::ProviderComponentProjectionMismatch, + "lowerer.echo-dpo", + "edict:target-provider/verifier@1.0.0", + ), + ); + assert_failure_tuple( + |source| { + source["packageManifest"]["components"][1]["kind"] = + Value::String("lowerer".to_owned()); + }, + ( + ProviderSemanticSourceErrorKind::ProviderComponentClosureMismatch, + "packageManifest.components", + "lowerer:2", + ), + ); + assert_failure_tuple( + |source| { + source["packageManifest"]["components"][1]["role"] = + Value::String("lowerer.echo-dpo".to_owned()); + }, + ( + ProviderSemanticSourceErrorKind::DuplicateKey, + "packageManifest.components.role", + "lowerer.echo-dpo", + ), + ); + assert_failure_tuple( + |source| { + source["packageManifest"]["components"][1]["coordinate"] = + Value::String("echo.dpo.lowerer/component@1".to_owned()); + }, + ( + ProviderSemanticSourceErrorKind::DuplicateCoordinate, + "packageManifest.components", + "echo.dpo.lowerer/component@1", + ), + ); +} + #[test] fn invocation_inputs_and_outputs_require_declared_schema_roles_and_domains() { assert_failure_tuple( diff --git a/det-policy.yaml b/det-policy.yaml index fd4135b8..55fa0230 100644 --- a/det-policy.yaml +++ b/det-policy.yaml @@ -43,6 +43,10 @@ crates: class: DET_CRITICAL owner_role: "Tooling Engineer" paths: ["crates/echo-edict-canonical/**"] + echo-edict-provider-artifacts: + class: DET_CRITICAL + owner_role: "Tooling Engineer" + paths: [".gitattributes", "crates/echo-wesley-gen/src/provider_*.rs", "crates/echo-wesley-gen/src/bin/echo-edict-provider-*.rs", "crates/echo-wesley-gen/tests/provider_*.rs", "crates/echo-wesley-gen/assets/v1/**", "schemas/edict-provider/**"] echo-edict-provider-lowerer: class: DET_CRITICAL owner_role: "Tooling Engineer" diff --git a/docs/architecture/application-contract-hosting.md b/docs/architecture/application-contract-hosting.md index 945d7355..e9b683f4 100644 --- a/docs/architecture/application-contract-hosting.md +++ b/docs/architecture/application-contract-hosting.md @@ -186,6 +186,48 @@ owning root in that authenticated CDDL. Passing both checks attests provider artifact shape only. It does not make authority-facts runtime Echo authority, install a package, admit an operation, or authorize a runtime consequence. +The #655 package identity is an Echo-owned, versioned canonical-CBOR closure, +not a hash of the completed JSON manifest. It binds the manifest role and +coordinate, exact API and provider ABI, ten sorted semantic/component routes, +24 sorted schema bindings—nine invocation domains, the generated artifact +profile, and 14 generated-resource domains—and raw exact-byte hashes for all 24 +non-manifest members. The derived manifest carries that root and has its own +independent raw content identity. Canonical-CBOR routes preserve their Edict domain-framed +artifact digests while the package member table preserves raw occurrences, so +neither proposition impersonates the other. Admission requires the caller's +external provider pin and exact-reproduces packaged Wesley provenance/review. +That boundary still proves only the distribution occurrence: the Edict-owned +schema registry and component host must separately admit roots, bytes, +attestations, imports, exports, and WIT types before guest execution. + +The checked distribution is a self-contained 25-file publication: the current +22-file provider corpus introduced by #652 under `generated/`, the exact lowerer and verifier under +`components/`, and the derived manifest. Publication refuses before filesystem +access if the generated members are not byte-identical to the checked source +corpus. Its check mode inventories the exact tree through retained no-follow +directory capabilities and reports drift without repair. This distribution +copy is a release occurrence, not a second semantic authority. + +Native readiness is a later, independent crossing pinned to Edict revision +`c75c3f550d049485ba00eae0dc272c6dd6aca11f`. The exact manifest constructs the +immutable schema registry; all five canonical primaries and 14 generated +resources validate under their owning roots; every lawpack/target-profile field +is bound to the expected coordinate and independently recomputed domain-framed +digest; adjacent primary references agree; and both exact components pass host +preflight before both request kinds are validated. No guest is invoked. These +proofs establish schema, identity-graph, component-contract, and request +readiness only. They still do not install, authorize, schedule, execute, commit, +observe, or receipt anything in Echo. + +The publishable Rust crate uses a separate 35-file package-local carrier tree +for exact repository sources and provider bytes that would otherwise live above +the crate root. Carrier locations never replace the logical authored paths in +generation provenance. Generated artifacts and components remain authoritative; +their package copies are corroborating release occurrences. This distinction +permits staged regeneration without allowing a stale package to block the +carrier update required to rebuild that package. Final checks require owner, +carrier, package copy, and Cargo archive inventory to agree exactly. + The target-profile lowerer and verifier resources are generated declarative contract documents. They do not select executable implementations. The package manifest separately binds the exact provider-owned components and their frozen diff --git a/schemas/edict-provider/README.md b/schemas/edict-provider/README.md index 829f3fb8..cd0ff450 100644 --- a/schemas/edict-provider/README.md +++ b/schemas/edict-provider/README.md @@ -188,5 +188,39 @@ them with the generated semantic artifacts, and generates the package-root listed inside its own artifact inventory. Runtime `reviewPayload` invocation output is distinct from #652's build-time `reviewArtifact`. +The package closure contains the 22 generated files plus the exact lowerer and +verifier components. Its Echo-owned provider digest binds the typed manifest +routes, 24 domain-to-root bindings (nine invocation domains, the generated +artifact profile, and 14 generated-resource domains), and raw SHA-256 of all 24 +physical members without hashing the derived manifest into itself. The five routed +canonical-CBOR artifacts use their Edict domain-framed identities in the +manifest, while CDDL, Wesley JSON evidence, and components use raw exact-byte +identities. Pure digest admission also rebinds the generated routes to the +packaged provenance/review and requires an external expected provider +reference. This is package-occurrence evidence, not Edict component or schema +compatibility proof and not Echo runtime installation or authority. + +The exact 25-file distribution is checked under +[`package/v1/`](package/README.md). Its dedicated publisher first requires its +22 `generated/` members to equal the current checked provider corpus introduced +by #652 byte-for-byte, then +writes only the two exact components, those generated members, and the derived +manifest. Run `echo-edict-provider-package --check` to report drift without +creating, deleting, or rewriting package files. + +The isolated Edict c75 host gate then consumes that exact checked package. It +constructs all 24 native schema bindings, validates the five canonical primaries +and 14 generated resources, proves every owner field names the expected exact +resource digest, prepares both components, and obtains both opaque request +proofs without guest invocation. Schema-valid resource substitution, reference +swaps, authority-source disagreement, and malformed contract material fail +before execution. This does not grant Echo runtime authority. + +The crate-local `assets/v1/` tree is an exact publication carrier for the same +provider bytes plus the repository sources needed for generator provenance. +Carrier paths are physical packaging details only; authored logical source paths +remain unchanged. `echo-edict-provider-assets --check-package-list` proves +owner/carrier/package agreement and exact Cargo archive selection. + All generated files are derived artifacts. Their digests and review renderings must never be copied back into this file as authored semantic facts. diff --git a/schemas/edict-provider/components/v1/README.md b/schemas/edict-provider/components/v1/README.md index a36c394d..6cd6b3e3 100644 --- a/schemas/edict-provider/components/v1/README.md +++ b/schemas/edict-provider/components/v1/README.md @@ -22,8 +22,8 @@ is componentized with `wit-component` 0.251.0. The source WIT is the exact 7,392-byte Edict contract with SHA-256 `2971fe44def7e51d5271dfc0f04f3088aa58754cffdc847681a587605aac749e`. -The checked component is 130,679 bytes with SHA-256 -`03edee44c6bc70eb998c0c17662a214809746af3bba0740f3407c18a4016309e`. +The checked component is 131,458 bytes with SHA-256 +`d58a7838df9d7e65c72063aafad91df97cb21404be556c93e864234f3f846326`. Its sole contract attestation is the top-level custom section `edict:target-provider-contract` containing `edict:target-provider/lowerer@1.0.0`. Its only imports are the frozen WIT's @@ -34,9 +34,9 @@ capability imports. `verifier.echo-dpo.component.wasm` implements `edict:target-provider/verifier@1.0.0`. It uses the same immutable builder, authenticated Rust/Cargo identities, frozen WIT bytes, path-remapping law, and -`wit-component` version recorded above. Its checked component is 188,736 bytes +`wit-component` version recorded above. Its checked component is 189,515 bytes with SHA-256 -`e13eda6e02d5a46d2aecdec0546d53a7bf66f2580f8d5ec06e5d76710716a27b`. +`11fcaf291ffacac62800ab3fc198e29a23a591bcd30ca98828fa10a8ad1f1952`. Its sole contract attestation is the top-level custom section `edict:target-provider-contract` containing `edict:target-provider/verifier@1.0.0`. Its only imports are the frozen WIT's diff --git a/schemas/edict-provider/components/v1/lowerer.echo-dpo.component.wasm b/schemas/edict-provider/components/v1/lowerer.echo-dpo.component.wasm index a85ae30a..c98bfb79 100644 Binary files a/schemas/edict-provider/components/v1/lowerer.echo-dpo.component.wasm and b/schemas/edict-provider/components/v1/lowerer.echo-dpo.component.wasm differ diff --git a/schemas/edict-provider/components/v1/verifier.echo-dpo.component.wasm b/schemas/edict-provider/components/v1/verifier.echo-dpo.component.wasm index 0fe4b1ac..65128439 100644 Binary files a/schemas/edict-provider/components/v1/verifier.echo-dpo.component.wasm and b/schemas/edict-provider/components/v1/verifier.echo-dpo.component.wasm differ diff --git a/schemas/edict-provider/echo-provider-semantics-v1.json b/schemas/edict-provider/echo-provider-semantics-v1.json index 0f9c3433..6b52ada5 100644 --- a/schemas/edict-provider/echo-provider-semantics-v1.json +++ b/schemas/edict-provider/echo-provider-semantics-v1.json @@ -468,7 +468,21 @@ "coordinate": "echo.edict-provider-manifest@1", "schemaContract": "edict.provider-manifest/v1", "providerAbi": "edict:target-provider@1.0.0", - "providerCoordinate": "echo.edict-provider@1" + "providerCoordinate": "echo.edict-provider@1", + "components": [ + { + "role": "lowerer.echo-dpo", + "kind": "lowerer", + "coordinate": "echo.dpo.lowerer/component@1", + "contract": "edict:target-provider/lowerer@1.0.0" + }, + { + "role": "verifier.echo-dpo", + "kind": "verifier", + "coordinate": "echo.dpo.verifier/component@1", + "contract": "edict:target-provider/verifier@1.0.0" + } + ] }, "invocationInputs": [ { @@ -541,6 +555,90 @@ } ], "schemaBindings": [ + { + "domain": "echo.dpo-lawpack.adapter.echo-dpo@1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-provider-lawpack-target-adapter" + }, + { + "domain": "echo.dpo-lawpack.compatibility@1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-provider-lawpack-compatibility" + }, + { + "domain": "echo.dpo-lawpack.exports@1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "lawpack-exports" + }, + { + "domain": "echo.dpo-lawpack.verifier@1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-provider-lawpack-verifier" + }, + { + "domain": "echo.dpo.bundle/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-bundle" + }, + { + "domain": "echo.dpo.cost/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-cost" + }, + { + "domain": "echo.dpo.fixtures/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-provider-conformance-corpus" + }, + { + "domain": "echo.dpo.footprint/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-footprint" + }, + { + "domain": "echo.dpo.intrinsics/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "intrinsics-document" + }, + { + "domain": "echo.dpo.lowerer/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-lowerer" + }, + { + "domain": "echo.dpo.obstructions/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-obstructions" + }, + { + "domain": "echo.dpo.operation-profiles/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "operation-profiles-document" + }, + { + "domain": "echo.dpo.verifier/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-verifier" + }, + { + "domain": "echo.generated-artifact-profile/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "generated-artifact-profile" + }, { "domain": "echo.generated-artifact/v1", "schemaRole": "schema.echo-provider-artifacts", @@ -553,6 +651,12 @@ "format": "selfContainedCddlV1", "rootRule": "review-payload" }, + { + "domain": "echo.span-ir/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-span-ir" + }, { "domain": "echo.verifier-report/v1", "schemaRole": "schema.echo-provider-artifacts", diff --git a/schemas/edict-provider/generated/v1/evidence/provenance.provider-generation.json b/schemas/edict-provider/generated/v1/evidence/provenance.provider-generation.json index 9bdeefb2..8d25de7f 100644 --- a/schemas/edict-provider/generated/v1/evidence/provenance.provider-generation.json +++ b/schemas/edict-provider/generated/v1/evidence/provenance.provider-generation.json @@ -1 +1 @@ -{"apiVersion":"wesley.generation-provenance-manifest/v1","contractVersions":{"generatorAbi":"wesley.extension-generator/v1","inputSchema":"wesley.extension-generation-input/v1","provenanceSchema":"wesley.generation-provenance-manifest/v1"},"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:d17b03810ecc53f288aa1de457a5ba295c537c4f64046f8e3777b8f98ff3fc86"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:3911de5075d3709a3ba40419e4b67f1226961f3520ba9dfbbad78278c9bb0e96"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:df62a4ff2b56f9553c80cf400728cab3717f5f442c4c2fc415d2c89c21c41dad"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:8cc385a3f287ad6ea522766d7b4e92bc5164226586eef6b3f2ac6c5253370dd3"},{"coordinate":"echo.dpo@1","digest":"sha256:95626e5be6e6b2c1c8aa1858277f1c67487ab6724b08408eb3c0054adce6b1eb"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:19901cc33bea0699334af3cb4f9889f752652e35b81a2a9c91e7e31a52b803af"}],"generationInputDigest":"sha256:326024c55f16dfedfc887b11c24b6f6b596b08ae6b1abcc3a7269dc7728d9730","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:5325aca06a7b00900e8be3ecd3e420138cc1ff94cf615b5be4552fdfb9392447","version":"0.1.0"},"settingsDigest":"sha256:0f708e76898de6fdb8186352e81d0f5c445adf94bb6c7de9204952d9fe913d4a","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:656a68b126669a629643302d679712626a90dbd7a38b521932630a141fe426ac"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file +{"apiVersion":"wesley.generation-provenance-manifest/v1","contractVersions":{"generatorAbi":"wesley.extension-generator/v1","inputSchema":"wesley.extension-generation-input/v1","provenanceSchema":"wesley.generation-provenance-manifest/v1"},"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:d17b03810ecc53f288aa1de457a5ba295c537c4f64046f8e3777b8f98ff3fc86"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:3911de5075d3709a3ba40419e4b67f1226961f3520ba9dfbbad78278c9bb0e96"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:df62a4ff2b56f9553c80cf400728cab3717f5f442c4c2fc415d2c89c21c41dad"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:8cc385a3f287ad6ea522766d7b4e92bc5164226586eef6b3f2ac6c5253370dd3"},{"coordinate":"echo.dpo@1","digest":"sha256:95626e5be6e6b2c1c8aa1858277f1c67487ab6724b08408eb3c0054adce6b1eb"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:19901cc33bea0699334af3cb4f9889f752652e35b81a2a9c91e7e31a52b803af"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e","version":"0.1.0"},"settingsDigest":"sha256:0f708e76898de6fdb8186352e81d0f5c445adf94bb6c7de9204952d9fe913d4a","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file diff --git a/schemas/edict-provider/generated/v1/evidence/review.provider-generation.json b/schemas/edict-provider/generated/v1/evidence/review.provider-generation.json index 3bbaeaf3..3c71a269 100644 --- a/schemas/edict-provider/generated/v1/evidence/review.provider-generation.json +++ b/schemas/edict-provider/generated/v1/evidence/review.provider-generation.json @@ -1 +1 @@ -{"apiVersion":"wesley.generation-review/v1","authoritative":false,"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:d17b03810ecc53f288aa1de457a5ba295c537c4f64046f8e3777b8f98ff3fc86"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:3911de5075d3709a3ba40419e4b67f1226961f3520ba9dfbbad78278c9bb0e96"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:df62a4ff2b56f9553c80cf400728cab3717f5f442c4c2fc415d2c89c21c41dad"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:8cc385a3f287ad6ea522766d7b4e92bc5164226586eef6b3f2ac6c5253370dd3"},{"coordinate":"echo.dpo@1","digest":"sha256:95626e5be6e6b2c1c8aa1858277f1c67487ab6724b08408eb3c0054adce6b1eb"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:19901cc33bea0699334af3cb4f9889f752652e35b81a2a9c91e7e31a52b803af"}],"generationInputDigest":"sha256:326024c55f16dfedfc887b11c24b6f6b596b08ae6b1abcc3a7269dc7728d9730","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:5325aca06a7b00900e8be3ecd3e420138cc1ff94cf615b5be4552fdfb9392447","version":"0.1.0"},"projectionRoles":["authority-facts.echo-dpo","authority-facts.echo-lawpack","generated-artifact-profile.echo-dpo-registration","lawpack.echo-dpo","schema.echo-provider-artifacts","target-profile.echo-dpo"],"provenanceManifestDigest":"sha256:a76347f1e54e22b21c4298fab750e83ea7611b7a37f889d78434c53cf4527cb1","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:656a68b126669a629643302d679712626a90dbd7a38b521932630a141fe426ac"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file +{"apiVersion":"wesley.generation-review/v1","authoritative":false,"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:d17b03810ecc53f288aa1de457a5ba295c537c4f64046f8e3777b8f98ff3fc86"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:3911de5075d3709a3ba40419e4b67f1226961f3520ba9dfbbad78278c9bb0e96"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:df62a4ff2b56f9553c80cf400728cab3717f5f442c4c2fc415d2c89c21c41dad"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:8cc385a3f287ad6ea522766d7b4e92bc5164226586eef6b3f2ac6c5253370dd3"},{"coordinate":"echo.dpo@1","digest":"sha256:95626e5be6e6b2c1c8aa1858277f1c67487ab6724b08408eb3c0054adce6b1eb"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:19901cc33bea0699334af3cb4f9889f752652e35b81a2a9c91e7e31a52b803af"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e","version":"0.1.0"},"projectionRoles":["authority-facts.echo-dpo","authority-facts.echo-lawpack","generated-artifact-profile.echo-dpo-registration","lawpack.echo-dpo","schema.echo-provider-artifacts","target-profile.echo-dpo"],"provenanceManifestDigest":"sha256:b9c6d845e151f455bc7d0d6e7d209e7fbe92e3fc592145227cbb8e9b3e59c7b2","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file diff --git a/schemas/edict-provider/package/README.md b/schemas/edict-provider/package/README.md new file mode 100644 index 00000000..f52cad03 --- /dev/null +++ b/schemas/edict-provider/package/README.md @@ -0,0 +1,51 @@ + + + +# Checked Echo Edict Provider Package + +[`v1/`](v1/) is the first exact digest-locked Echo Edict provider +distribution. Its inventory is fixed at 25 files: + +- the exact lowerer and verifier component bytes under `components/`; +- the checked 22-file #652 artifact corpus under `generated/`; and +- the derived `provider-manifest.echo.json`. + +The manifest never inventories itself. Its `provider.digest` names an +Echo-owned, domain-framed canonical-CBOR closure over the manifest contract, +routes, 24 schema bindings, and raw identities of all 24 non-manifest members. +The manifest file has a separate raw content identity. + +Regenerate the package only from the repository root: + +```bash +cargo +1.90.0 run --locked -p echo-wesley-gen \ + --bin echo-edict-provider-package -- +``` + +Check the exact tree without repairing or rewriting it: + +```bash +cargo +1.90.0 run --locked -p echo-wesley-gen \ + --bin echo-edict-provider-package -- --check +``` + +The publisher fails before filesystem access unless every packaged +`generated/` member is byte-identical to the current checked provider corpus +introduced by #652. The checked +package proves a reproducible distribution occurrence. It does not by itself +prove Edict schema admission, component-host readiness, Echo installation, +runtime authority, invocation, execution, commitment, observation, or receipt. + +The separate isolated Edict c75 host gate binds this exact manifest, constructs +all 24 schema bindings, validates every canonical primary and generated +resource, proves the field-level resource identity graph, prepares both exact +components, and validates both request kinds without invoking guest code. Run +it from the repository root with: + +```bash +scripts/verify-edict-provider-host-v1.sh +``` + +The package-local archive carriers are checked independently with +`echo-edict-provider-assets --check-package-list`; carrier paths never replace +the original logical source identities recorded in generation provenance. diff --git a/schemas/edict-provider/package/v1/components/lowerer.echo-dpo.component.wasm b/schemas/edict-provider/package/v1/components/lowerer.echo-dpo.component.wasm new file mode 100644 index 00000000..c98bfb79 Binary files /dev/null and b/schemas/edict-provider/package/v1/components/lowerer.echo-dpo.component.wasm differ diff --git a/schemas/edict-provider/package/v1/components/verifier.echo-dpo.component.wasm b/schemas/edict-provider/package/v1/components/verifier.echo-dpo.component.wasm new file mode 100644 index 00000000..65128439 Binary files /dev/null and b/schemas/edict-provider/package/v1/components/verifier.echo-dpo.component.wasm differ diff --git a/schemas/edict-provider/package/v1/generated/evidence/provenance.provider-generation.json b/schemas/edict-provider/package/v1/generated/evidence/provenance.provider-generation.json new file mode 100644 index 00000000..8d25de7f --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/evidence/provenance.provider-generation.json @@ -0,0 +1 @@ +{"apiVersion":"wesley.generation-provenance-manifest/v1","contractVersions":{"generatorAbi":"wesley.extension-generator/v1","inputSchema":"wesley.extension-generation-input/v1","provenanceSchema":"wesley.generation-provenance-manifest/v1"},"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:d17b03810ecc53f288aa1de457a5ba295c537c4f64046f8e3777b8f98ff3fc86"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:3911de5075d3709a3ba40419e4b67f1226961f3520ba9dfbbad78278c9bb0e96"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:df62a4ff2b56f9553c80cf400728cab3717f5f442c4c2fc415d2c89c21c41dad"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:8cc385a3f287ad6ea522766d7b4e92bc5164226586eef6b3f2ac6c5253370dd3"},{"coordinate":"echo.dpo@1","digest":"sha256:95626e5be6e6b2c1c8aa1858277f1c67487ab6724b08408eb3c0054adce6b1eb"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:19901cc33bea0699334af3cb4f9889f752652e35b81a2a9c91e7e31a52b803af"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e","version":"0.1.0"},"settingsDigest":"sha256:0f708e76898de6fdb8186352e81d0f5c445adf94bb6c7de9204952d9fe913d4a","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/evidence/review.provider-generation.json b/schemas/edict-provider/package/v1/generated/evidence/review.provider-generation.json new file mode 100644 index 00000000..3c71a269 --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/evidence/review.provider-generation.json @@ -0,0 +1 @@ +{"apiVersion":"wesley.generation-review/v1","authoritative":false,"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:d17b03810ecc53f288aa1de457a5ba295c537c4f64046f8e3777b8f98ff3fc86"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:3911de5075d3709a3ba40419e4b67f1226961f3520ba9dfbbad78278c9bb0e96"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:df62a4ff2b56f9553c80cf400728cab3717f5f442c4c2fc415d2c89c21c41dad"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:8cc385a3f287ad6ea522766d7b4e92bc5164226586eef6b3f2ac6c5253370dd3"},{"coordinate":"echo.dpo@1","digest":"sha256:95626e5be6e6b2c1c8aa1858277f1c67487ab6724b08408eb3c0054adce6b1eb"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:19901cc33bea0699334af3cb4f9889f752652e35b81a2a9c91e7e31a52b803af"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e","version":"0.1.0"},"projectionRoles":["authority-facts.echo-dpo","authority-facts.echo-lawpack","generated-artifact-profile.echo-dpo-registration","lawpack.echo-dpo","schema.echo-provider-artifacts","target-profile.echo-dpo"],"provenanceManifestDigest":"sha256:b9c6d845e151f455bc7d0d6e7d209e7fbe92e3fc592145227cbb8e9b3e59c7b2","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/primary/authority-facts.echo-dpo.cbor b/schemas/edict-provider/package/v1/generated/primary/authority-facts.echo-dpo.cbor new file mode 100644 index 00000000..807c2186 --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/primary/authority-facts.echo-dpo.cbor @@ -0,0 +1 @@ +fsourcedkindmtargetProfilefdigestfsha256X VbZe/T~U2-jcoordinatejecho.dpo@1gbudgetsjapiVersionxedict.authority-facts/v1qoperationProfileskp.effectfuldcorexcontinuum.profile.write/v1sallowedWriteClassesgreplacereffectWriteClassesntarget.replacegreplace \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/primary/authority-facts.echo-lawpack.cbor b/schemas/edict-provider/package/v1/generated/primary/authority-facts.echo-lawpack.cbor new file mode 100644 index 00000000..a3a7b681 Binary files /dev/null and b/schemas/edict-provider/package/v1/generated/primary/authority-facts.echo-lawpack.cbor differ diff --git a/schemas/edict-provider/package/v1/generated/primary/generated-artifact-profile.echo-dpo-registration.cbor b/schemas/edict-provider/package/v1/generated/primary/generated-artifact-profile.echo-dpo-registration.cbor new file mode 100644 index 00000000..bb7a2d6e --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/primary/generated-artifact-profile.echo-dpo-registration.cbor @@ -0,0 +1 @@ +etypesha.b@1.IddkindocoreStringAliasicanonicalhraw-utf8omaxScalarValueska.b@1.Inputdkindfrecordffieldsdnamebiddtypeha.b@1.Idla.b@1.Outputdkindfrecordffieldsdnamebiddtypeha.b@1.Idma.b@1.Receiptdkindfrecordffieldsdnamebiddtypeha.b@1.Idwtarget.replace.rejecteddkindfrecordffieldsxdomain.WriteRejected.PayloaddkindfrecordffieldsjapiVersionx"echo.generated-artifact-profile/v1joperationsga.b@1.tfbudgetfp.tinyfeffectntarget.replaceiinputTypeka.b@1.InputjoutputTypela.b@1.OutputmopticContractmreplace-pointnimplementationdkindfnativejcoordinaterecho.dpo@1.replaceninvocationKindhmutationpoperationProfilexcontinuum.profile.write/v1sobstructionMappingshrejectedtdomain.WriteRejectedmtargetProfilejecho.dpo@1 \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/primary/lawpack.echo-dpo.cbor b/schemas/edict-provider/package/v1/generated/primary/lawpack.echo-dpo.cbor new file mode 100644 index 00000000..354071e7 Binary files /dev/null and b/schemas/edict-provider/package/v1/generated/primary/lawpack.echo-dpo.cbor differ diff --git a/schemas/edict-provider/package/v1/generated/primary/schema.echo-provider-artifacts.cddl b/schemas/edict-provider/package/v1/generated/primary/schema.echo-provider-artifacts.cddl new file mode 100644 index 00000000..900e2fc2 --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/primary/schema.echo-provider-artifacts.cddl @@ -0,0 +1,969 @@ +; SPDX-License-Identifier: Apache-2.0 +; edict-provider-contracts.cddl +; Generated from Edict-owned ABI fragments. DO NOT EDIT. + +; --- edict-common.cddl --- +; edict-common.cddl +; Shared CDDL types for the Edict ABIs, defined ONCE here so they cannot drift +; (EDICT-ABI-NODUP-001). Two groups: +; - resource-ref, sha256-digest, effect-failure-body, effect-kind, authority-class, +; core-type-ref: assembled with edict-target-profile.cddl and +; edict-lawpack.cddl by the build; those schemas do not redefine them. +; - operation-profile, optic-template, aperture-requirement and their refs: +; consumed by the Core/optic layer (edict-core.cddl) and referenced by the +; language/target-profile specs. +; Authoritative byte encoding: edict.canonical-cbor/v1. + +; A normative subcomponent referenced by identity plus digest. Manifests never +; embed their own self-digest in their preimage (EDICT-CORE-SELFHASH-001). +resource-ref = { id: tstr, digest: sha256-digest } + +; Digests are authoritative as typed byte values, never hex strings. Review JSON +; renders this as "sha256:<64 lowercase hex>" (EDICT-DIGEST-WIRE-001). +sha256-digest = [ algorithm: "sha256", bytes: bstr .size 32 ] + +; A named low-level failure an effect can raise. The source obstruction map +; binds it (by coordinate) and constructs a typed domain obstruction from its +; payload (EDICT-ABI-FAILURE-NAMED-001). +; An effect's `effectFailures` list MUST have unique `coordinate`s: since the +; obstruction map is keyed by coordinate, two failures sharing a coordinate (even +; with different authorityClass/payloadType) make exhaustive mapping and binder +; typing ambiguous and are rejected (EDICT-ABI-FAILURE-UNIQUE-001). +; +; Effects carry their failures as a map `{ failure-ident => effect-failure-body }` +; (see the target/lawpack effect schemas). The failure coordinate is the map +; KEY, so it is not repeated in the body and cannot disagree with the key. +effect-failure-body = { + authorityClass: authority-class, + payloadType: core-type-ref, ; typed, bounded (may be empty record) +} + +; A failure coordinate must be a bare Edict `ident` (letter/underscore then +; letters/digits/underscores) AND must not be a reserved keyword (e.g. `else`, +; `basis`, `where`, `require`, `for`, `if`). The source obstruction-map LHS only +; accepts a non-keyword `ident`, so a hyphen/dot/keyword coordinate would be +; ABI-valid yet impossible to map exhaustively in source. The regex captures the +; lexical shape; keyword exclusion is an additional validation rule +; (EDICT-ABI-FAILURE-IDENT-001). +failure-ident = tstr .regexp "[A-Za-z_][A-Za-z0-9_]*" + +effect-kind = "read" / "create" / "ensure" / "replace" / "delete" / + "append" / "reduce" / "semantic.emit" / "custom" + +authority-class = "domainMappable" / "participantOwned" / "integrityFault" / + "resourceFault" / "internalFault" + +core-type-ref = tstr ; canonical Core type coordinate + +; An operation profile supplies the optic template a Core intent resolves its +; opticKind/boundaryKind/supportPolicy/lossDisposition from. Target profiles and +; lawpacks publish these as a map `{ coordinate => operation-profile }`, so the +; coordinate is the KEY, not a value field (EDICT-OPTIC-TEMPLATE-OWNER-001, +; EDICT-ABI-OPPROFILE-UNIQUE-001). +operation-profile = { + opticTemplate: optic-template, + effectPredicate: tstr, ; coordinate of the operation-mode predicate +} + +optic-template = { + opticKind: "revelation" / "affectReintegration", + boundaryKind: "projection" / "affect", + supportPolicy: tstr, ; canonical support-policy coordinate + lossDisposition: tstr, ; canonical loss-disposition coordinate + ? basisTemplate: tstr, ; optional digest-locked basis template coord + ; the aperture requirement this template supplies. Required when the template + ; is the source of a Core optic's apertureRequirement (i.e. the intent has no + ; source `footprint <= ...`), since apertureRequirement is mandatory in Core + ; (EDICT-OPTIC-APERTURE-REF-001). + ? apertureRequirement: aperture-requirement, +} + +; apertureRequirement is a typed reference, never a free-form string. A review +; rendering may show its coordinate (EDICT-OPTIC-APERTURE-REF-001). +aperture-requirement = footprint-ceiling-ref / abstract-footprint-obligation-ref +footprint-ceiling-ref = { kind: "footprintCeiling", ref: tstr } +abstract-footprint-obligation-ref = { kind: "abstractFootprintObligation", ref: tstr } + +; --- edict-core.cddl --- +; edict-core.cddl +; Normative schema for the Edict Core v1 semantic model. +; +; Scope boundary: this file defines Core meaning and schema shape only. It does +; not define a canonical encoder, Core module hash fields, hash fixtures, target +; lowering, admission bundles, or target-owned IR. + +core-module = { + apiVersion: "edict.core/v1", + coordinate: tstr, + imports: [* core-import], + types: { * tstr => core-type }, + intents: { + tstr => core-intent }, + requiredCoreCapabilities: [* tstr], +} + +core-import = { + kind: "lawpack" / "target" / "core", + ref: resource-ref, + ? alias: tstr, +} + +; --- types --------------------------------------------------------------- + +core-type = core-scalar-type / core-record-type / core-variant-type / + core-option-type / core-list-type / core-map-type / + core-capability-ref-type + +core-scalar-type = core-bool-type / core-int-type / core-string-type / + core-bytes-type / core-unit-type + +core-bool-type = { kind: "Bool" } +core-unit-type = { kind: "Unit" } +core-int-type = { + kind: "I64" / "U64" / "I32" / "U32" / "I16" / "U16" / "I8" / "U8", +} +core-string-type = { + kind: "String", + max: uint, + canonical: "unicode-scalar-nfc" / "raw-utf8", +} +core-bytes-type = { + kind: "Bytes", + max: uint, +} +core-record-type = { + kind: "Record", + fields: { * tstr => core-type-ref }, +} +core-variant-type = { + kind: "Variant", + cases: { + tstr => variant-case-body }, +} +variant-case-body = { + ? payload: core-type-ref, +} +core-option-type = { + kind: "Option", + item: core-type-ref, +} +core-list-type = { + kind: "List", + item: core-type-ref, + max: uint, +} +core-map-type = { + kind: "Map", + key: core-type-ref, + value: core-type-ref, + max: uint, +} +core-capability-ref-type = { + kind: "CapabilityRef", + item: core-type-ref, +} + +; core-type-ref is defined in edict-common.cddl and assembled with this schema. + +; --- references and values ---------------------------------------------- + +; Local identity is alpha-stable. `id` is the compiler-owned local coordinate; +; `alphaName` is the normalized human/debug name. Source binder spelling is not +; identity. +local-ref = { + id: tstr, + alphaName: tstr, + type: core-type-ref, +} + +core-value = core-null-value / core-bool-value / core-int-value / + core-string-value / core-bytes-value / core-record-value / + core-variant-value / core-list-value / core-map-value / + core-capability-value + +core-null-value = { kind: "null" } +core-bool-value = { kind: "bool", value: bool } +core-int-value = { kind: "int", width: tstr, value: int } +core-string-value = { kind: "string", value: tstr } +core-bytes-value = { kind: "bytes", value: bstr } +core-record-value = { kind: "record", fields: { * tstr => core-value } } +core-variant-value = { + kind: "variant", + type: core-type-ref, + case: tstr, + ? payload: core-value, +} +core-list-value = { kind: "list", values: [* core-value] } +core-map-value = { kind: "map", entries: [* [key: core-value, value: core-value]] } +core-capability-value = { + kind: "capability", + receipt: sha256-digest, +} + +; Edict-authored pure helpers use a pure Core function body. The body can bind +; pure expressions and return an expression, but it cannot contain Core effect, +; guard, branch, loop, match-node, or proof-obligation nodes. +core-fn-body = { + params: [* local-ref], + body: core-pure-block, +} + +core-pure-block = { + locals: [* local-ref], + bindings: [* pure-let-node], + result: core-expr, +} + +pure-let-node = { + kind: "let", + binding: local-ref, + value: core-expr, +} + +; --- expressions and predicates ----------------------------------------- + +core-expr = local-expr / const-expr / record-expr / field-expr / + variant-expr / match-expr / call-expr / list-expr / map-expr / + if-expr + +local-expr = { kind: "local", ref: local-ref } +const-expr = { kind: "const", value: core-value } +record-expr = { kind: "record", fields: { * tstr => core-expr } } +field-expr = { kind: "field", base: core-expr, field: tstr } +variant-expr = { + kind: "variant", + type: core-type-ref, + case: tstr, + ? payload: core-expr, +} +match-expr = { + kind: "match", + scrutinee: core-expr, + arms: [+ match-arm], +} +match-arm = { + case: tstr, + ? binder: local-ref, + body: core-expr, +} +call-expr = { + kind: "call", + callee: tstr, + typeArgs: [* core-type-ref], + args: [* core-expr], +} +list-expr = { kind: "list", values: [* core-expr] } +map-expr = { kind: "map", entries: [* [key: core-expr, value: core-expr]] } +if-expr = { + kind: "if", + predicate: core-predicate, + then: core-expr, + else: core-expr, +} + +core-predicate = true-predicate / false-predicate / not-predicate / + all-predicate / any-predicate / compare-predicate / + call-predicate / obstruction-predicate + +true-predicate = { kind: "true" } +false-predicate = { kind: "false" } +not-predicate = { kind: "not", value: core-predicate } +all-predicate = { kind: "all", values: [+ core-predicate] } +any-predicate = { kind: "any", values: [+ core-predicate] } +compare-predicate = { + kind: "compare", + op: "==" / "!=" / "<" / "<=" / ">" / ">=", + left: core-expr, + right: core-expr, +} +call-predicate = { + kind: "call", + predicate: tstr, + args: [* core-expr], +} +obstruction-predicate = { + kind: "obstruction", + coordinate: failure-ident, + payload: core-expr, +} + +input-constraint = { + coordinate: tstr, + source: "where" / "compiler", + predicate: core-predicate, +} + +; --- intents, blocks, and nodes ----------------------------------------- + +core-intent = { + input: core-type-ref, + output: core-type-ref, + requiredOperationProfile: tstr, + inputConstraints: [* input-constraint], + coreEvaluationBudget: core-budget, + body: core-block, + ? optic: core-optic, +} + +core-budget = { + maxSteps: uint, + maxAllocatedBytes: uint, + maxOutputBytes: uint, +} + +core-optic = { + opticKind: "revelation" / "affectReintegration", + boundaryKind: "projection" / "affect", + apertureRequirement: aperture-requirement, + supportPolicy: tstr, + lossDisposition: tstr, +} + +core-block = { + locals: [* local-ref], + nodes: [* core-node], + result: core-expr, +} + +core-node = let-node / require-node / effect-node / guard-node / branch-node / + for-node / match-node / proof-obligation-node + +let-node = { + kind: "let", + binding: local-ref, + value: core-expr, +} +require-node = { + kind: "require", + predicate: core-predicate, + onFailure: require-failure-arm, +} +require-failure-arm = terminal-require-failure / + continue-obstructed-require-failure +terminal-require-failure = { + kind: "terminal", + reason: obstruction-reason, +} +continue-obstructed-require-failure = { + kind: "continueObstructed", + reason: obstruction-reason, +} +obstruction-reason = { + reasonKind: tstr, + payload: { * tstr => core-expr }, +} +effect-node = { + kind: "effect", + binding: local-ref, + effect: tstr, + input: core-expr, + obstructionMap: { * failure-ident => obstruction-arm }, +} +obstruction-arm = { + binder: local-ref, + value: core-expr, +} +guard-node = { + kind: "guard", + predicate: core-predicate, + obstruction: core-expr, +} +branch-node = { + kind: "branch", + predicate: core-predicate, + then: core-block, + else: core-block, +} +for-node = { + kind: "for", + binder: local-ref, + iter: core-expr, + bound: core-bound, + body: core-block, +} +match-node = { + kind: "match", + scrutinee: core-expr, + arms: [+ match-block-arm], +} +match-block-arm = { + case: tstr, + ? binder: local-ref, + body: core-block, +} +proof-obligation-node = { + kind: "proof", + coordinate: tstr, + predicate: core-predicate, +} + +core-bound = literal-bound / coordinate-bound +literal-bound = { kind: "literal", value: uint } +coordinate-bound = { kind: "coordinate", ref: tstr } + +; Shared resource-ref, sha256-digest, failure-ident, aperture-requirement, and +; core-type-ref are defined once in edict-common.cddl. + +; --- edict-lawpack.cddl --- +; edict-lawpack.cddl +; Canonical schema for the Edict lawpack manifest and export surface. +; Authoritative byte encoding: edict.canonical-cbor/v1. JSON in the prose specs +; is a review rendering generated from this schema (EDICT-ABI-NODUP-001). + +lawpack-manifest = { + apiVersion: "edict.lawpack/v1", + id: tstr, + version: tstr, + acceptedCoreAbi: [+ tstr], + dependencies: [* lawpack-dep], ; acyclic, digest-locked (EDICT-LAWPACK-DAG-001) + exports: resource-ref, + ? targetAdapters: [+ target-adapter], ; required only if any runtime effect exists + ? helperComponent: executable-component, ; executable helpers carry their own sandbox+fuel + verifier: verifier, ; classified: declarative or executable + compatibility: resource-ref, + conformanceFixtureCorpus: resource-ref, +} + +; A verifier is either a declarative ruleset (no runtime) or an executable +; component. An executable verifier MUST carry its own sandbox and fuel model, +; so the schema enforces that no executable component is left unbounded +; (EDICT-ABI-VERIFIER-BOUND-001). +verifier = declarative-verifier / executable-verifier +declarative-verifier = { class: "declarative", ruleset: resource-ref } +executable-verifier = { + class: "executable", + component: resource-ref, + sandbox: resource-ref, + fuelModel: resource-ref, +} + +; Any executable component is bounded by its own sandbox + fuel model. +executable-component = { + component: resource-ref, + sandbox: resource-ref, + fuelModel: resource-ref, +} + +lawpack-dep = { id: tstr, version: tstr, digest: sha256-digest } + +; Adapter selection keys SOLELY off the digest-locked `acceptedTargetProfile` +; (its `id` is the profile id; its `digest` pins the exact profile/version). There +; are no independent display strings that could disagree with the lock, so a +; resolver cannot bind an adapter to one target while the lock proves another +; (EDICT-LAWPACK-ADAPTER-TARGETIR-001). +target-adapter = { + acceptedTargetProfile: resource-ref, ; digest-locked, authoritative selector + acceptedTargetIr: resource-ref, ; digest-locked + adapter: resource-ref, +} + +; Shared types resource-ref and sha256-digest are defined once in +; edict-common.cddl and assembled with this schema (EDICT-ABI-NODUP-001). + +; --- export surface ------------------------------------------------------ + +lawpack-exports = { + types: [* exported-type], + constants: [* exported-constant], + pureFunctions: [* pure-function], + effects: [* semantic-effect], + obstructions: [* obstruction-def], + ; keyed by operation-profile coordinate → uniqueness enforced + ; (EDICT-ABI-OPPROFILE-UNIQUE-001) + ; operation-profile records this lawpack exports (optic templates that + ; `implements`/`profile` clauses resolve against). operation-profile is + ; defined in edict-common.cddl (EDICT-ABI-OPPROFILE-SLOT-001). + operationProfiles: { * tstr => operation-profile }, ; keyed by coordinate +} + +exported-type = { coordinate: tstr, definition: core-type-ref } +exported-constant = { coordinate: tstr, type: core-type-ref, value: any } + +; A pure helper is a discriminated union by `source`, so the schema itself +; guarantees an implementation exists (EDICT-LAWPACK-PURE-IMPL-001): +; - "edict": authored in Edict/Core; the Core body is carried inline (hashed +; with the export surface). The schema requires the `body` field. +; - "component": implemented outside Edict; carries no inline body and instead +; carries its own digest-locked `implementation` (sandbox + fuel). It does +; not depend on the optional manifest-level helperComponent. +pure-function = edict-pure-function / component-pure-function + +pure-function-common = ( + coordinate: tstr, + typeParameters: [* tstr], + parameterTypes: [* core-type-ref], ; all bounded + returnType: core-type-ref, ; bounded + costTemplate: tstr, + determinismClass: "total" / "total-with-typed-diagnostic", +) + +edict-pure-function = { + pure-function-common, + source: "edict", + body: core-fn-body, ; inline, hash-significant +} + +component-pure-function = { + pure-function-common, + source: "component", + ; the digest-locked component implementing this helper. Required at the schema + ; level so a component helper can never validate without a hash-bound, + ; sandbox+fuel-described implementation (EDICT-LAWPACK-PURE-IMPL-001). + implementation: executable-component, +} + +; core-fn-body is defined by edict-core.cddl and assembled with this lawpack +; schema. It is a pure Core function body, not an effect-capable core-block. + +semantic-effect = { + coordinate: tstr, + typeParameters: [* tstr], + inputType: core-type-ref, ; bounded + outputType: core-type-ref, ; bounded + executionClass: "proofOnly" / "runtime", ; orthogonal to writeClass + effectKindHint: effect-kind, + footprintObligation: tstr, + costObligation: tstr, + effectFailures: { * failure-ident => effect-failure-body }, ; keyed by coordinate; unique + guardSupport: bool, +} + +obstruction-def = { + coordinate: tstr, + authorityClass: authority-class, + payloadSchema: core-type-ref, ; typed, bounded (may be empty record) +} + +; effect-failure-body, effect-kind, authority-class, and core-type-ref are defined +; once in edict-common.cddl and assembled with this schema (EDICT-ABI-NODUP-001). + +; --- edict-target-profile.cddl --- +; edict-target-profile.cddl +; Canonical schema for the Edict target profile manifest. +; Authoritative byte encoding: edict.canonical-cbor/v1 +; (see SPEC_continuum-contract-bundle-v1.md). JSON in the prose specs is a +; review rendering generated from this schema; this CDDL is the single source +; of truth (EDICT-ABI-NODUP-001). + +target-profile-manifest = { + apiVersion: "edict.target-profile/v1", + id: tstr, ; e.g. "echo.dpo" + version: tstr, ; e.g. "1" + acceptedCoreAbi: [+ tstr], ; e.g. ["edict.core/v1"] + + intrinsics: resource-ref, + intrinsicNamespace: tstr, + ; publishes this profile's operation-profile records (optic templates that + ; `profile`/`implements` clauses resolve against). References an + ; operation-profiles-document (EDICT-ABI-OPPROFILE-SLOT-001). + operationProfiles: resource-ref, + footprintAlgebra: resource-ref, + costAlgebra: resource-ref, + targetIr: resource-ref, + obstructionTaxonomy: resource-ref, + verifier: resource-ref, + lowerer: resource-ref, + sandbox: resource-ref, + fuelModel: resource-ref, + + ; fields the language spec requires of every profile + bundleProfile: resource-ref, + generatedArtifactProfiles: [* resource-ref], + canonicalEncodingRules: resource-ref, + ; Reserved/deferred: will list accepted lawpack-adapter ABI ids once that + ; byte-level ABI (edict.lawpack-adapter/v1) is specified. The schema enforces + ; emptiness now — the type is the empty array, so non-empty values are + ; rejected, not merely discouraged (EDICT-ABI-LAWPACK-ADAPTER-DEFER-001). + ? acceptedLawpackAdapterAbi: [], + diagnosticAbi: resource-ref, + + ; application doctrine + applicationModel: "atomic", + readConsistency: "application-snapshot" / tstr, + guardEvaluation: "precommit-atomic" / tstr, + obstructionRollback: "no-visible-effects" / tstr, + multiTarget: bool, + ; whether the target can evaluate precommit postcondition (`guarantee`) checks + ; inside the atomic application unit (EDICT-TARGET-POSTCOND-001) + postconditionSupport: bool, + + deterministicExecution: resource-ref, + conformanceFixtureCorpus: resource-ref, +} + +; Shared types resource-ref and sha256-digest are defined once in +; edict-common.cddl and assembled with this schema by the build +; (EDICT-ABI-NODUP-001). They are not redefined here. + +; --- intrinsic signature ------------------------------------------------- +; The artifact referenced by the manifest's `intrinsics` resource-ref is the +; intrinsic-signature corpus document below. Its layout is fixed so two +; independent profiles validate/hash the corpus identically +; (EDICT-ABI-INTRINSICS-DOC-001). + +; intrinsics is a MAP keyed by coordinate, so the schema itself enforces +; coordinate uniqueness. A provider receives the resolved corpus as a +; digest-bound semantic input and resolves coordinates within that artifact. +; Each map key MUST equal its record's `coordinate` field +; (EDICT-ABI-INTRINSIC-UNIQUE-001). +intrinsics-document = { + apiVersion: "edict.target-profile.intrinsics/v1", + intrinsics: { * tstr => intrinsic }, +} + +; The artifact referenced by the manifest's `operationProfiles` resource-ref. +; operation-profile / optic-template are defined in edict-common.cddl. Keyed by +; coordinate so resolution can't pick between two same-coordinate profiles +; (EDICT-ABI-OPPROFILE-SLOT-001, EDICT-ABI-OPPROFILE-UNIQUE-001). +operation-profiles-document = { + apiVersion: "edict.target-profile.operation-profiles/v1", + profiles: { * tstr => operation-profile }, +} + +; A typed pre-lowering question that can be proposed by Watson or an agent and +; checked by the compiler. It is canonical-CBOR encoded under +; `edict.lowering-requirements/v1`; the compiler checks this artifact, not the +; prose that produced it. +lowering-requirements = { + apiVersion: "edict.lowering-requirements/v1", + operationProfile: tstr, + semanticEffects: [* semantic-effect-requirement], + requiredWriteClasses: [* write-class], + guardKinds: [* guard-kind], + atomicity: atomicity-requirement, + postconditionSupport: bool, + obstructionCoordinates: [* tstr], + footprintObligations: [* tstr], + costObligations: [* tstr], + opticContract: tstr, +} + +semantic-effect-requirement = { + coordinate: tstr, + writeClass: write-class, + guardKinds: [* guard-kind], + obstructionCoordinates: [* tstr], + footprintObligations: [* tstr], + costObligations: [* tstr], +} + +write-class = "none" / "read" / "create" / "ensure" / "append" / + "replace" / "delete" / tstr +guard-kind = "precommit-atomic" / tstr +atomicity-requirement = "atomic" / tstr + +; A genuine union: pure constructors carry no effect kind or failures; effect +; intrinsics must (EDICT-TARGET-INTRINSIC-CLASS-001). The schema enforces this, +; not a comment. + +; The intrinsic's coordinate is the intrinsics map KEY, not a value field, so the +; key and coordinate can never disagree (EDICT-ABI-INTRINSIC-UNIQUE-001). +intrinsic = pure-intrinsic / effect-intrinsic + +pure-intrinsic = { + intrinsicClass: "pure", + typeParameters: [* tstr], + argumentTypes: [* core-type-ref], + returnType: core-type-ref, + guardSupport: false, + footprintTemplate: tstr, + costTemplate: tstr, + writeClass: "none", +} + +effect-intrinsic = { + intrinsicClass: "effect", + typeParameters: [* tstr], + argumentTypes: [* core-type-ref], + returnType: core-type-ref, + effectKind: effect-kind, + ; map keyed by failure coordinate (failure-ident); the failure coordinate is + ; the key, not a value field, so uniqueness is structural + ; (EDICT-ABI-FAILURE-UNIQUE-001). + effectFailures: { * failure-ident => effect-failure-body }, + guardSupport: bool, + footprintTemplate: tstr, + costTemplate: tstr, + writeClass: "read" / "create" / "ensure" / "append" / "replace" / + "delete" / "custom", + canParticipateInAtomicGuard: bool, +} + +; effect-failure-body, effect-kind, authority-class, and core-type-ref are defined +; once in edict-common.cddl and assembled with this schema (EDICT-ABI-NODUP-001). + +; --- edict-authority-facts.cddl --- +; edict-authority-facts.cddl +; Canonical schema for the first compiler-context authority-facts document. +; Authoritative byte encoding: edict.canonical-cbor/v1. +; +; This schema is assembled with edict-common.cddl so source.digest uses the +; shared sha256-digest typed value. JSON is a review/input rendering: its +; `sha256:<64 hex>` source digest is projected to [`sha256`, 32 raw bytes] on +; the wire, and its fact arrays project to the coordinate-keyed maps below. + +authority-facts = { + apiVersion: "edict.authority-facts/v1", + source: authority-fact-source, + operationProfiles: { * tstr => authority-operation-profile-fact }, + effectWriteClasses: { * tstr => authority-write-class }, + budgets: { * tstr => authority-budget-fact }, +} + +authority-fact-source = { + kind: "lawpack" / "targetProfile", + coordinate: tstr, + digest: sha256-digest, +} + +; The map key is the source operation-profile coordinate. It is not repeated in +; the value, so a key and embedded coordinate cannot disagree. Allowed write +; classes are a canonical map-set: the class is the unique key and null is the +; unit marker. Canonical CBOR fixes key order without a second ordering rule. +authority-operation-profile-fact = { + core: tstr, + allowedWriteClasses: { * authority-write-class => null }, +} + +; The effectWriteClasses map key is the semantic effect coordinate. The budgets +; map key is the source budget coordinate. Canonical CBOR map-key uniqueness +; makes duplicate fact coordinates structurally unrepresentable. +authority-budget-fact = { + maxSteps: uint, + maxAllocatedBytes: uint, + maxOutputBytes: uint, +} + +; AuthorityFactsDocument v1 intentionally accepts only the write classes the +; current compiler model can consume. `custom` is the sole v1 custom spelling; +; arbitrary target-profile extension strings do not enter this compiler path. +authority-write-class = "none" / "read" / "create" / "ensure" / "append" / + "replace" / "delete" / "custom" + +; --- edict-target-ir.cddl --- +; SPDX-License-Identifier: Apache-2.0 +; edict-target-ir.cddl +; Canonical schema for the Edict-owned Target IR artifact envelope. +; Authoritative byte encoding: edict.canonical-cbor/v1. +; +; This schema is assembled with edict-common.cddl and edict-core.cddl. It +; deliberately reuses Core expressions, predicates, budgets, local references, +; obstruction reasons, and obstruction arms so the schema matches the value +; emitted by the canonical Target IR encoder rather than restating those types. +; It describes the structural shape of valid lowering-produced artifacts. The +; lowering and encoder contracts separately enforce semantic identifier rules +; and canonical ordering/deduplication for set-like values. + +; Target IR encoding rejects an empty target-profile coordinate before bytes +; exist, so this root tightens the shared structural resource-ref accordingly. +target-ir-resource-ref = { + id: tstr .regexp "(?s).+", + digest: sha256-digest, +} + +target-ir-artifact = { + kind: "targetIrArtifact", + domain: tstr, + targetProfile: target-ir-resource-ref, + sourceCoreCoordinate: tstr, + intents: { * tstr => target-ir-intent }, +} + +target-ir-intent = { + operationProfile: tstr, + inputConstraints: [* input-constraint], + coreEvaluationBudget: core-budget, + requirements: [* target-ir-requirement], + steps: [* target-ir-step], + result: core-expr, +} + +target-ir-requirement = { + id: tstr, + predicate: core-predicate, + onFailure: require-failure-arm, +} + +target-ir-step = { + id: tstr, + binding: local-ref, + effect: tstr, + targetIntrinsic: tstr, + input: core-expr, + obstructionFailures: [* failure-ident], + obstructionArms: { * failure-ident => obstruction-arm }, +} + + +; --- Echo provider-generated declarative contracts ----------------------- +; These roots validate provider semantic descriptions. They confer no Echo +; runtime authority and contain no package, component, or installation state. + +generated-artifact-profile = { + apiVersion: "echo.generated-artifact-profile/v1", + targetProfile: tstr, + types: { * tstr => echo-generated-type }, + operations: { * tstr => generated-operation }, +} + +echo-generated-type = echo-generated-string-alias / echo-generated-record +echo-generated-string-alias = { + kind: "coreStringAlias", + maxScalarValues: uint, + canonical: "raw-utf8", +} +echo-generated-record = { + kind: "record", + fields: [* echo-generated-record-field], +} +echo-generated-record-field = { name: tstr, type: tstr } + +generated-operation = { + inputType: tstr, + outputType: tstr, + effect: tstr, + operationProfile: tstr, + opticContract: tstr, + budget: tstr, + invocationKind: "mutation" / "observer", + implementation: { kind: "native" / "directAdapter", coordinate: tstr }, + obstructionMappings: { * tstr => tstr }, +} + +echo-provider-conformance-corpus = { + apiVersion: "echo.edict-provider.conformance-corpus/v1", + class: "declarative", + operations: { * tstr => null }, + capabilities: { * tstr => null }, + semanticEffects: { * tstr => null }, + cases: [], +} + +echo-provider-lawpack-compatibility = { + apiVersion: "echo.edict-provider.lawpack-compatibility/v1", + class: "declarative", + acceptedCoreAbi: { * tstr => null }, + acceptedTargetProfiles: { * tstr => null }, + semanticEffects: { * tstr => null }, +} + +echo-provider-lawpack-target-adapter = { + apiVersion: "echo.edict-provider.lawpack-target-adapter/v1", + class: "declarative", + targetProfile: tstr, + targetIrDomain: tstr, + effectImplementations: { * tstr => echo-effect-implementation }, +} + +echo-effect-implementation = echo-native-effect-implementation / echo-direct-effect-implementation +echo-native-effect-implementation = { + kind: "native", + capability: tstr, + writeClass: tstr, +} +echo-direct-effect-implementation = { + kind: "directAdapter", + adapter: tstr, + capability: tstr, + writeClass: tstr, +} + +echo-provider-lawpack-verifier = { + apiVersion: "echo.edict-provider.lawpack-verifier/v1", + class: "declarative", + operationObstructions: { * tstr => echo-operation-obstructions }, +} +echo-operation-obstructions = { + effect: tstr, + failureMappings: { * failure-ident => tstr }, +} + +echo-dpo-bundle = { + apiVersion: "echo.dpo.bundle/v1", + class: "declarative", + applicationModel: tstr, + readConsistency: tstr, + operationProfiles: { * tstr => null }, +} + +echo-dpo-cost = { + apiVersion: "echo.dpo.cost/v1", + class: "declarative", + capabilities: { * tstr => echo-cost-capability }, +} +echo-cost-capability = { + effect: tstr, + costTemplate: tstr, + semanticObligation: tstr, +} + +echo-dpo-footprint = { + apiVersion: "echo.dpo.footprint/v1", + class: "declarative", + capabilities: { * tstr => echo-footprint-capability }, +} +echo-footprint-capability = { + effect: tstr, + footprintTemplate: tstr, + semanticObligation: tstr, + writeClass: tstr, +} + +echo-span-ir = { + apiVersion: "echo.span-ir/v1", + class: "declarative", + domain: tstr, + targetProfile: tstr, + capabilities: { * tstr => null }, +} + +echo-dpo-lowerer = { + apiVersion: "echo.dpo.lowerer/v1", + class: "declarative", + acceptedCoreAbi: { * tstr => null }, + outputDomain: tstr, + targetProfile: tstr, + effectImplementations: { * tstr => echo-effect-implementation }, + opticContracts: { * tstr => tstr }, +} + +echo-dpo-obstructions = { + apiVersion: "echo.dpo.obstructions/v1", + class: "declarative", + effectFailures: { * tstr => { authorityClass: authority-class, payloadType: tstr } }, + domainObstructions: { * tstr => { authorityClass: authority-class, payloadSchema: tstr } }, +} + +echo-dpo-verifier = { + apiVersion: "echo.dpo.verifier/v1", + class: "declarative", + targetProfile: tstr, + targetIrDomain: tstr, + capabilities: { * tstr => null }, + operationProfiles: { * tstr => null }, + opticContracts: { * tstr => tstr }, +} + +echo-nonempty-tstr = tstr .regexp "(?s).+" + +generated-artifact = { + apiVersion: "echo.generated-artifact/v1", + profile: resource-ref, + operation: echo-nonempty-tstr, + mediaType: echo-nonempty-tstr, + bytes: bstr, +} + +review-payload = { + apiVersion: "echo.review-payload/v1", + authoritative: false, + subject: resource-ref, + mediaType: echo-nonempty-tstr, + bytes: bstr, +} + +verifier-report = { + apiVersion: "echo.verifier-report/v1", + targetIr: resource-ref, + outcome: "accepted" / "rejected", + diagnosticAbi: resource-ref, + diagnosticBytes: bstr, +} diff --git a/schemas/edict-provider/package/v1/generated/primary/target-profile.echo-dpo.cbor b/schemas/edict-provider/package/v1/generated/primary/target-profile.echo-dpo.cbor new file mode 100644 index 00000000..b3ca7861 Binary files /dev/null and b/schemas/edict-provider/package/v1/generated/primary/target-profile.echo-dpo.cbor differ diff --git a/schemas/edict-provider/package/v1/generated/resources/resource.conformance-corpus.cbor b/schemas/edict-provider/package/v1/generated/resources/resource.conformance-corpus.cbor new file mode 100644 index 00000000..4464f53c --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/resources/resource.conformance-corpus.cbor @@ -0,0 +1 @@ +ecaseseclasskdeclarativejapiVersionx)echo.edict-provider.conformance-corpus/v1joperationsga.b@1.tlcapabilitiesrecho.dpo@1.replaceosemanticEffectsntarget.replace \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/resources/resource.lawpack-compatibility.cbor b/schemas/edict-provider/package/v1/generated/resources/resource.lawpack-compatibility.cbor new file mode 100644 index 00000000..a703b6fc --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/resources/resource.lawpack-compatibility.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersionx,echo.edict-provider.lawpack-compatibility/v1oacceptedCoreAbimedict.core/v1osemanticEffectsntarget.replacevacceptedTargetProfilesjecho.dpo@1 \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/resources/resource.lawpack-exports.cbor b/schemas/edict-provider/package/v1/generated/resources/resource.lawpack-exports.cbor new file mode 100644 index 00000000..a0887cb0 --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/resources/resource.lawpack-exports.cbor @@ -0,0 +1 @@ +etypesjcoordinateha.b@1.Idjdefinitionx!StringgeffectsiinputTypeha.b@1.Idjcoordinatentarget.replacejoutputTypema.b@1.ReceiptlguardSupportncostObligationstarget.replace.costneffectFailureshrejectedkpayloadTypewtarget.replace.rejectednauthorityClassndomainMappableneffectKindHintgreplacenexecutionClassgruntimentypeParameterssfootprintObligationxtarget.replace.footprinticonstantslobstructionsjcoordinatetdomain.WriteRejectedmpayloadSchemaxdomain.WriteRejected.PayloadnauthorityClassndomainMappablempureFunctionsqoperationProfiles \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/resources/resource.lawpack-target-adapter.cbor b/schemas/edict-provider/package/v1/generated/resources/resource.lawpack-target-adapter.cbor new file mode 100644 index 00000000..5a57d65b --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/resources/resource.lawpack-target-adapter.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersionx-echo.edict-provider.lawpack-target-adapter/v1mtargetProfilejecho.dpo@1ntargetIrDomainoecho.span-ir/v1ueffectImplementationsntarget.replacedkindfnativejcapabilityrecho.dpo@1.replacejwriteClassgreplace \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/resources/resource.lawpack-verifier.cbor b/schemas/edict-provider/package/v1/generated/resources/resource.lawpack-verifier.cbor new file mode 100644 index 00000000..a6d10bd7 --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/resources/resource.lawpack-verifier.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersionx'echo.edict-provider.lawpack-verifier/v1uoperationObstructionsga.b@1.tfeffectntarget.replaceofailureMappingshrejectedtdomain.WriteRejected \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/resources/resource.target-bundle-profile.cbor b/schemas/edict-provider/package/v1/generated/resources/resource.target-bundle-profile.cbor new file mode 100644 index 00000000..4c13093f --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/resources/resource.target-bundle-profile.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersionrecho.dpo.bundle/v1oreadConsistencytapplication-snapshotpapplicationModelfatomicqoperationProfilesxcontinuum.profile.write/v1 \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/resources/resource.target-cost-algebra.cbor b/schemas/edict-provider/package/v1/generated/resources/resource.target-cost-algebra.cbor new file mode 100644 index 00000000..2242fed3 --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/resources/resource.target-cost-algebra.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersionpecho.dpo.cost/v1lcapabilitiesrecho.dpo@1.replacefeffectntarget.replacelcostTemplatestarget.replace.costrsemanticObligationstarget.replace.cost \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/resources/resource.target-footprint-algebra.cbor b/schemas/edict-provider/package/v1/generated/resources/resource.target-footprint-algebra.cbor new file mode 100644 index 00000000..3118bbd0 --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/resources/resource.target-footprint-algebra.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersionuecho.dpo.footprint/v1lcapabilitiesrecho.dpo@1.replacefeffectntarget.replacejwriteClassgreplaceqfootprintTemplatextarget.replace.footprintrsemanticObligationxtarget.replace.footprint \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/resources/resource.target-intrinsics.cbor b/schemas/edict-provider/package/v1/generated/resources/resource.target-intrinsics.cbor new file mode 100644 index 00000000..3250617a --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/resources/resource.target-intrinsics.cbor @@ -0,0 +1 @@ +japiVersionx"edict.target-profile.intrinsics/v1jintrinsicsrecho.dpo@1.replacejeffectKindgreplacejreturnTypema.b@1.ReceiptjwriteClassgreplacelcostTemplatestarget.replace.costlguardSupportmargumentTypesha.b@1.IdneffectFailureshrejectedkpayloadTypewtarget.replace.rejectednauthorityClassndomainMappablenintrinsicClassfeffectntypeParametersqfootprintTemplatextarget.replace.footprintxcanParticipateInAtomicGuard \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/resources/resource.target-ir.cbor b/schemas/edict-provider/package/v1/generated/resources/resource.target-ir.cbor new file mode 100644 index 00000000..47cd6d6a --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/resources/resource.target-ir.cbor @@ -0,0 +1 @@ +eclasskdeclarativefdomainoecho.span-ir/v1japiVersionoecho.span-ir/v1lcapabilitiesrecho.dpo@1.replacemtargetProfilejecho.dpo@1 \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/resources/resource.target-lowerer-contract.cbor b/schemas/edict-provider/package/v1/generated/resources/resource.target-lowerer-contract.cbor new file mode 100644 index 00000000..102f1d90 --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/resources/resource.target-lowerer-contract.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersionsecho.dpo.lowerer/v1loutputDomainoecho.span-ir/v1mtargetProfilejecho.dpo@1nopticContractsxcontinuum.profile.write/v1mreplace-pointoacceptedCoreAbimedict.core/v1ueffectImplementationsntarget.replacedkindfnativejcapabilityrecho.dpo@1.replacejwriteClassgreplace \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/resources/resource.target-obstruction-taxonomy.cbor b/schemas/edict-provider/package/v1/generated/resources/resource.target-obstruction-taxonomy.cbor new file mode 100644 index 00000000..a15d8a30 --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/resources/resource.target-obstruction-taxonomy.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersionxecho.dpo.obstructions/v1neffectFailureswtarget.replace.rejectedkpayloadTypewtarget.replace.rejectednauthorityClassndomainMappablerdomainObstructionstdomain.WriteRejectedmpayloadSchemaxdomain.WriteRejected.PayloadnauthorityClassndomainMappable \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/resources/resource.target-operation-profiles.cbor b/schemas/edict-provider/package/v1/generated/resources/resource.target-operation-profiles.cbor new file mode 100644 index 00000000..f4445c6a --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/resources/resource.target-operation-profiles.cbor @@ -0,0 +1 @@ +hprofilesxcontinuum.profile.write/v1mopticTemplateiopticKindsaffectReintegrationlboundaryKindfaffectmsupportPolicyx&continuum.support.carry-or-obstruct/v1olossDispositionx#continuum.support.reject-on-loss/v1sapertureRequirementcrefxtarget.replace.footprintdkindxabstractFootprintObligationoeffectPredicatex'echo.dpo.operation-mode.replace-only/v1japiVersionx*edict.target-profile.operation-profiles/v1 \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/resources/resource.target-verifier-contract.cbor b/schemas/edict-provider/package/v1/generated/resources/resource.target-verifier-contract.cbor new file mode 100644 index 00000000..6fd4f838 --- /dev/null +++ b/schemas/edict-provider/package/v1/generated/resources/resource.target-verifier-contract.cbor @@ -0,0 +1 @@ +eclasskdeclarativejapiVersiontecho.dpo.verifier/v1lcapabilitiesrecho.dpo@1.replacemtargetProfilejecho.dpo@1nopticContractsxcontinuum.profile.write/v1mreplace-pointntargetIrDomainoecho.span-ir/v1qoperationProfilesxcontinuum.profile.write/v1 \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/provider-manifest.echo.json b/schemas/edict-provider/package/v1/provider-manifest.echo.json new file mode 100644 index 00000000..b95686ea --- /dev/null +++ b/schemas/edict-provider/package/v1/provider-manifest.echo.json @@ -0,0 +1,338 @@ +{ + "apiVersion": "edict.provider-manifest/v1", + "providerAbi": "edict:target-provider@1.0.0", + "provider": { + "coordinate": "echo.edict-provider@1", + "digest": "sha256:38dd0532d4353f6260e8aaeafb149457b42f8ad94bb107ecbb74bf02f0051a92" + }, + "artifacts": [ + { + "role": "authority-facts.echo-dpo", + "artifactKind": "authorityFacts", + "resource": { + "coordinate": "echo.dpo-authority-facts@1", + "digest": "sha256:5c30871b115e82415980714281e250c2fda281afb4a4fe16c2917f93e86182a4" + }, + "source": { + "kind": "generated", + "semanticSource": { + "coordinate": "echo.semantic-schema@1", + "digest": "sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335" + }, + "generator": { + "coordinate": "echo-wesley-gen.provider-artifact-generator@1", + "digest": "sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e" + } + } + }, + { + "role": "authority-facts.echo-lawpack", + "artifactKind": "authorityFacts", + "resource": { + "coordinate": "echo.dpo-lawpack-authority-facts@1", + "digest": "sha256:37591478584a88d3dc08a620873d803a2d6fcf5dc70767aae3587e5862ee99e9" + }, + "source": { + "kind": "generated", + "semanticSource": { + "coordinate": "echo.semantic-schema@1", + "digest": "sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335" + }, + "generator": { + "coordinate": "echo-wesley-gen.provider-artifact-generator@1", + "digest": "sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e" + } + } + }, + { + "role": "generated-artifact-profile.echo-dpo-registration", + "artifactKind": "generatedArtifactProfile", + "resource": { + "coordinate": "echo.dpo.registration/v1", + "digest": "sha256:3377304d8634681821cd958427e0b8baccc37b7b08bfb342d988a08571eb83ab" + }, + "source": { + "kind": "generated", + "semanticSource": { + "coordinate": "echo.semantic-schema@1", + "digest": "sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335" + }, + "generator": { + "coordinate": "echo-wesley-gen.provider-artifact-generator@1", + "digest": "sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e" + } + } + }, + { + "role": "lawpack.echo-dpo", + "artifactKind": "lawpack", + "resource": { + "coordinate": "echo.dpo-lawpack@1", + "digest": "sha256:47006be4c2b569f13759078c560fe508e158b429cd9d4c5e47d9a7ea86b983b2" + }, + "source": { + "kind": "generated", + "semanticSource": { + "coordinate": "echo.semantic-schema@1", + "digest": "sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335" + }, + "generator": { + "coordinate": "echo-wesley-gen.provider-artifact-generator@1", + "digest": "sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e" + } + } + }, + { + "role": "lowerer.echo-dpo", + "artifactKind": "lowerer", + "resource": { + "coordinate": "echo.dpo.lowerer/component@1", + "digest": "sha256:d58a7838df9d7e65c72063aafad91df97cb21404be556c93e864234f3f846326" + }, + "source": { + "kind": "component", + "component": { + "coordinate": "echo.dpo.lowerer/component@1", + "digest": "sha256:d58a7838df9d7e65c72063aafad91df97cb21404be556c93e864234f3f846326" + } + } + }, + { + "role": "provenance.provider-generation", + "artifactKind": "generationProvenance", + "resource": { + "coordinate": "echo.edict-provider-generation-provenance@1", + "digest": "sha256:5cb085e1a35d01363a629b6933e7e88e96b38a2d841848d68320e9fb0fba8580" + }, + "source": { + "kind": "generated", + "semanticSource": { + "coordinate": "echo.semantic-schema@1", + "digest": "sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335" + }, + "generator": { + "coordinate": "echo-wesley-gen.provider-artifact-generator@1", + "digest": "sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e" + } + } + }, + { + "role": "review.provider-generation", + "artifactKind": "reviewArtifact", + "resource": { + "coordinate": "echo.edict-provider-generation-review@1", + "digest": "sha256:b9b5c7715089ba24c6ce15d9f8674a7ab2ed95225ff8127952eb4d3065d32f6c" + }, + "source": { + "kind": "generated", + "semanticSource": { + "coordinate": "echo.semantic-schema@1", + "digest": "sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335" + }, + "generator": { + "coordinate": "echo-wesley-gen.provider-artifact-generator@1", + "digest": "sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e" + } + } + }, + { + "role": "schema.echo-provider-artifacts", + "artifactKind": "artifactSchema", + "resource": { + "coordinate": "echo.provider-artifacts.cddl@1", + "digest": "sha256:19901cc33bea0699334af3cb4f9889f752652e35b81a2a9c91e7e31a52b803af" + }, + "source": { + "kind": "generated", + "semanticSource": { + "coordinate": "echo.semantic-schema@1", + "digest": "sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335" + }, + "generator": { + "coordinate": "echo-wesley-gen.provider-artifact-generator@1", + "digest": "sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e" + } + } + }, + { + "role": "target-profile.echo-dpo", + "artifactKind": "targetProfile", + "resource": { + "coordinate": "echo.dpo@1", + "digest": "sha256:f41df38156625a05c1ee8bce652ffddf04e71b54fe027eeab9d255d0d8322db0" + }, + "source": { + "kind": "generated", + "semanticSource": { + "coordinate": "echo.semantic-schema@1", + "digest": "sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335" + }, + "generator": { + "coordinate": "echo-wesley-gen.provider-artifact-generator@1", + "digest": "sha256:e57827652e86865b38a614a84fdacf715a46c4c528e133bc2942df47b29d146e" + } + } + }, + { + "role": "verifier.echo-dpo", + "artifactKind": "verifier", + "resource": { + "coordinate": "echo.dpo.verifier/component@1", + "digest": "sha256:11fcaf291ffacac62800ab3fc198e29a23a591bcd30ca98828fa10a8ad1f1952" + }, + "source": { + "kind": "component", + "component": { + "coordinate": "echo.dpo.verifier/component@1", + "digest": "sha256:11fcaf291ffacac62800ab3fc198e29a23a591bcd30ca98828fa10a8ad1f1952" + } + } + } + ], + "schemaBindings": [ + { + "domain": "echo.dpo-lawpack.adapter.echo-dpo@1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-provider-lawpack-target-adapter" + }, + { + "domain": "echo.dpo-lawpack.compatibility@1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-provider-lawpack-compatibility" + }, + { + "domain": "echo.dpo-lawpack.exports@1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "lawpack-exports" + }, + { + "domain": "echo.dpo-lawpack.verifier@1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-provider-lawpack-verifier" + }, + { + "domain": "echo.dpo.bundle/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-bundle" + }, + { + "domain": "echo.dpo.cost/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-cost" + }, + { + "domain": "echo.dpo.fixtures/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-provider-conformance-corpus" + }, + { + "domain": "echo.dpo.footprint/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-footprint" + }, + { + "domain": "echo.dpo.intrinsics/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "intrinsics-document" + }, + { + "domain": "echo.dpo.lowerer/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-lowerer" + }, + { + "domain": "echo.dpo.obstructions/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-obstructions" + }, + { + "domain": "echo.dpo.operation-profiles/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "operation-profiles-document" + }, + { + "domain": "echo.dpo.verifier/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-dpo-verifier" + }, + { + "domain": "echo.generated-artifact-profile/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "generated-artifact-profile" + }, + { + "domain": "echo.generated-artifact/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "generated-artifact" + }, + { + "domain": "echo.review-payload/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "review-payload" + }, + { + "domain": "echo.span-ir/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "echo-span-ir" + }, + { + "domain": "echo.verifier-report/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "verifier-report" + }, + { + "domain": "edict.authority-facts/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "authority-facts" + }, + { + "domain": "edict.core.module/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "core-module" + }, + { + "domain": "edict.lawpack/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "lawpack-manifest" + }, + { + "domain": "edict.lowering-requirements/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "lowering-requirements" + }, + { + "domain": "edict.target-ir.artifact/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "target-ir-artifact" + }, + { + "domain": "edict.target-profile/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "target-profile-manifest" + } + ] +} diff --git a/scripts/tests/classify_changes.test.cjs b/scripts/tests/classify_changes.test.cjs index f481e754..55ef8138 100644 --- a/scripts/tests/classify_changes.test.cjs +++ b/scripts/tests/classify_changes.test.cjs @@ -140,6 +140,10 @@ test('every workspace member has an explicit non-catch-all policy path', () => { policyYaml, /echo-edict-provider-verifier:\n\s+class: DET_CRITICAL\n\s+owner_role: "Tooling Engineer"\n\s+paths: \["crates\/echo-edict-provider-verifier\/\*\*", "schemas\/edict-provider\/components\/v1\/verifier\.echo-dpo\.component\.wasm", "tests\/edict-provider-host-v1\/\*\*"\]\n/, ); + assert.match( + policyYaml, + /echo-edict-provider-artifacts:\n\s+class: DET_CRITICAL\n\s+owner_role: "Tooling Engineer"\n\s+paths: \["\.gitattributes".*"crates\/echo-wesley-gen\/assets\/v1\/\*\*".*"schemas\/edict-provider\/\*\*"\]\n/, + ); assert.match(policyYaml, /echo-runtime-schema:\n\s+class: DET_CRITICAL\n/); assert.match(policyYaml, /echo-file-aperture:\n\s+class: DET_IMPORTANT\n/); assert.match(policyYaml, /echo-trace:\n\s+class: DET_IMPORTANT\n/); diff --git a/scripts/verify-edict-provider-host-v1.sh b/scripts/verify-edict-provider-host-v1.sh index 799721f9..06aa895f 100755 --- a/scripts/verify-edict-provider-host-v1.sh +++ b/scripts/verify-edict-provider-host-v1.sh @@ -44,6 +44,12 @@ ECHO_PROVIDER_LOWERER_COMPONENT="$component" \ --locked \ --test host_contract +CARGO_TARGET_DIR="$HOST_TARGET_DIR" \ + cargo +1.94.0 test \ + --manifest-path "$MANIFEST" \ + --locked \ + --test package_contract + CARGO_TARGET_DIR="$HOST_TARGET_DIR" \ cargo +1.94.0 test \ --manifest-path "$MANIFEST" \ diff --git a/tests/edict-provider-host-v1/Cargo.lock b/tests/edict-provider-host-v1/Cargo.lock index d0f223da..a386a14e 100644 --- a/tests/edict-provider-host-v1/Cargo.lock +++ b/tests/edict-provider-host-v1/Cargo.lock @@ -355,6 +355,7 @@ dependencies = [ "edict-provider-host-wasmtime", "edict-provider-schema", "edict-syntax", + "serde_json", "sha2", ] diff --git a/tests/edict-provider-host-v1/Cargo.toml b/tests/edict-provider-host-v1/Cargo.toml index 567d8f16..886083e3 100644 --- a/tests/edict-provider-host-v1/Cargo.toml +++ b/tests/edict-provider-host-v1/Cargo.toml @@ -16,6 +16,7 @@ resolver = "2" edict-provider-host-wasmtime = { git = "https://github.com/flyingrobots/edict", rev = "c75c3f550d049485ba00eae0dc272c6dd6aca11f" } edict-provider-schema = { git = "https://github.com/flyingrobots/edict", rev = "c75c3f550d049485ba00eae0dc272c6dd6aca11f" } edict-syntax = { git = "https://github.com/flyingrobots/edict", rev = "c75c3f550d049485ba00eae0dc272c6dd6aca11f" } +serde_json = "=1.0.150" sha2 = "=0.10.9" [lints.rust] diff --git a/tests/edict-provider-host-v1/tests/package_contract.rs b/tests/edict-provider-host-v1/tests/package_contract.rs new file mode 100644 index 00000000..972c585d --- /dev/null +++ b/tests/edict-provider-host-v1/tests/package_contract.rs @@ -0,0 +1,1312 @@ +// SPDX-License-Identifier: Apache-2.0 +// © James Ross Ω FLYING•ROBOTS +#![allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)] +//! Edict-native pre-execution readiness witness for the checked Echo package. + +use std::collections::BTreeMap; +use std::fmt::Write as _; +use std::sync::Arc; + +use edict_provider_host_wasmtime::{ + ProviderComponentHost, ProviderHostFailureKind, ProviderHostPhase, ResolvedProviderComponent, +}; +use edict_provider_schema::{ + ProviderArtifactSchemaRegistry, ProviderSchemaRegistryFailureKind, + ResolvedProviderSchemaArtifact, +}; +use edict_syntax::{ + bind_target_provider_manifest, compile_to_core, decode_canonical_cbor, encode_canonical_cbor, + encode_core_module, encode_target_ir_artifact, lower_with_builtin_lowerer, parse_module, + select_provider_component, validate_provider_lowering_request, + validate_provider_verification_request, BuiltinLowererRequest, BuiltinTargetLowerer, + CanonicalValue, CompilerContext, CoreBudget, CoreModule, ProviderArtifact, + ProviderArtifactBinding, ProviderArtifactSchemaValidator, ProviderArtifactSource, + ProviderBoundArtifact, ProviderDigest, ProviderDigestAlgorithm, ProviderInvocationKind, + ProviderInvocationValidationFailureKind, ProviderLoweringInvocationContract, + ProviderLoweringOutputKind, ProviderLoweringOutputRequest, ProviderLoweringRequest, + ProviderResourceRef, ProviderResponseLimits, ProviderSchemaFormat, ProviderSemanticInput, + ProviderSemanticInputBinding, ProviderSemanticInputKind, + ProviderVerificationInvocationContract, ProviderVerificationOutputKind, + ProviderVerificationOutputRequest, ProviderVerificationRequest, ResourceRef, + TargetEffectLowering, TargetIrLoweringFacts, TargetProviderManifest, WriteClass, + AUTHORITY_FACTS_API_VERSION, CORE_DIGEST_FRAME, CORE_MODULE_DIGEST_DOMAIN, + ECHO_DPO_TARGET_PROFILE, ECHO_SPAN_IR_DOMAIN, PROVIDER_LAWPACK_ARTIFACT_DOMAIN, + TARGET_IR_ARTIFACT_DIGEST_DOMAIN, TARGET_PROFILE_API_VERSION, TARGET_PROVIDER_PROTOCOL_VERSION, +}; +use sha2::{Digest as _, Sha256}; + +const ECHO_SOURCE: &str = "package a.b@1;\n\ + type Input = { id: String, };\n\ + type Receipt = { id: String, };\n\ + type Output = { id: String, };\n\ + intent t(input: Input) returns Output\n\ + profile p.effectful\n\ + basis none\n\ + budget <= p.tiny {\n\ + let receipt: Receipt = target.replace(input.id)\n\ + else { rejected(reason) => domain.WriteRejected };\n\ + return { id: input.id };\n\ + }"; + +const SCHEMA_ROLE: &str = "schema.echo-provider-artifacts"; +const GENERATED_ARTIFACT_PROFILE_DOMAIN: &str = "echo.generated-artifact-profile/v1"; +const GENERATED_ARTIFACT_PROFILE_ROLE: &str = "generated-artifact-profile.echo-dpo-registration"; +const LOWERABILITY_DOMAIN: &str = "edict.lowering-requirements/v1"; +const LOWERER_ROLE: &str = "lowerer.echo-dpo"; +const TARGET_IR_ROLE: &str = "target-ir.echo-dpo"; +const VERIFIER_ROLE: &str = "verifier.echo-dpo"; +const VERIFIER_REPORT_DOMAIN: &str = "echo.verifier-report/v1"; +const VERIFIER_REPORT_ROLE: &str = "verifier-report.echo-dpo"; +const EXPECTED_SCHEMA_BINDING_COUNT: usize = 24; + +const MANIFEST_BYTES: &[u8] = + include_bytes!("../../../schemas/edict-provider/package/v1/provider-manifest.echo.json"); +const SCHEMA_BYTES: &[u8] = include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/primary/schema.echo-provider-artifacts.cddl" +); +const GENERATED_ARTIFACT_PROFILE_BYTES: &[u8] = include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/primary/generated-artifact-profile.echo-dpo-registration.cbor" +); +const LAWPACK_BYTES: &[u8] = include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/primary/lawpack.echo-dpo.cbor" +); +const TARGET_PROFILE_BYTES: &[u8] = include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/primary/target-profile.echo-dpo.cbor" +); +const TARGET_AUTHORITY_BYTES: &[u8] = include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/primary/authority-facts.echo-dpo.cbor" +); +const LAWPACK_AUTHORITY_BYTES: &[u8] = include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/primary/authority-facts.echo-lawpack.cbor" +); +const LOWERER_BYTES: &[u8] = include_bytes!( + "../../../schemas/edict-provider/package/v1/components/lowerer.echo-dpo.component.wasm" +); +const VERIFIER_BYTES: &[u8] = include_bytes!( + "../../../schemas/edict-provider/package/v1/components/verifier.echo-dpo.component.wasm" +); + +struct RoutedCanonicalArtifact { + role: &'static str, + domain: &'static str, + root: &'static str, + bytes: &'static [u8], +} + +const ROUTED_CANONICAL_ARTIFACTS: &[RoutedCanonicalArtifact] = &[ + RoutedCanonicalArtifact { + role: "authority-facts.echo-dpo", + domain: AUTHORITY_FACTS_API_VERSION, + root: "authority-facts", + bytes: TARGET_AUTHORITY_BYTES, + }, + RoutedCanonicalArtifact { + role: "authority-facts.echo-lawpack", + domain: AUTHORITY_FACTS_API_VERSION, + root: "authority-facts", + bytes: LAWPACK_AUTHORITY_BYTES, + }, + RoutedCanonicalArtifact { + role: GENERATED_ARTIFACT_PROFILE_ROLE, + domain: GENERATED_ARTIFACT_PROFILE_DOMAIN, + root: "generated-artifact-profile", + bytes: GENERATED_ARTIFACT_PROFILE_BYTES, + }, + RoutedCanonicalArtifact { + role: "lawpack.echo-dpo", + domain: PROVIDER_LAWPACK_ARTIFACT_DOMAIN, + root: "lawpack-manifest", + bytes: LAWPACK_BYTES, + }, + RoutedCanonicalArtifact { + role: "target-profile.echo-dpo", + domain: TARGET_PROFILE_API_VERSION, + root: "target-profile-manifest", + bytes: TARGET_PROFILE_BYTES, + }, +]; + +struct GeneratedResourceFixture { + path: &'static str, + domain: &'static str, + root: &'static str, + bytes: &'static [u8], +} + +const GENERATED_RESOURCE_FIXTURES: &[GeneratedResourceFixture] = &[ + GeneratedResourceFixture { + path: "resource.conformance-corpus.cbor", + domain: "echo.dpo.fixtures/v1", + root: "echo-provider-conformance-corpus", + bytes: include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/resources/resource.conformance-corpus.cbor" + ), + }, + GeneratedResourceFixture { + path: "resource.lawpack-compatibility.cbor", + domain: "echo.dpo-lawpack.compatibility@1", + root: "echo-provider-lawpack-compatibility", + bytes: include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/resources/resource.lawpack-compatibility.cbor" + ), + }, + GeneratedResourceFixture { + path: "resource.lawpack-exports.cbor", + domain: "echo.dpo-lawpack.exports@1", + root: "lawpack-exports", + bytes: include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/resources/resource.lawpack-exports.cbor" + ), + }, + GeneratedResourceFixture { + path: "resource.lawpack-target-adapter.cbor", + domain: "echo.dpo-lawpack.adapter.echo-dpo@1", + root: "echo-provider-lawpack-target-adapter", + bytes: include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/resources/resource.lawpack-target-adapter.cbor" + ), + }, + GeneratedResourceFixture { + path: "resource.lawpack-verifier.cbor", + domain: "echo.dpo-lawpack.verifier@1", + root: "echo-provider-lawpack-verifier", + bytes: include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/resources/resource.lawpack-verifier.cbor" + ), + }, + GeneratedResourceFixture { + path: "resource.target-bundle-profile.cbor", + domain: "echo.dpo.bundle/v1", + root: "echo-dpo-bundle", + bytes: include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/resources/resource.target-bundle-profile.cbor" + ), + }, + GeneratedResourceFixture { + path: "resource.target-cost-algebra.cbor", + domain: "echo.dpo.cost/v1", + root: "echo-dpo-cost", + bytes: include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/resources/resource.target-cost-algebra.cbor" + ), + }, + GeneratedResourceFixture { + path: "resource.target-footprint-algebra.cbor", + domain: "echo.dpo.footprint/v1", + root: "echo-dpo-footprint", + bytes: include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/resources/resource.target-footprint-algebra.cbor" + ), + }, + GeneratedResourceFixture { + path: "resource.target-intrinsics.cbor", + domain: "echo.dpo.intrinsics/v1", + root: "intrinsics-document", + bytes: include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/resources/resource.target-intrinsics.cbor" + ), + }, + GeneratedResourceFixture { + path: "resource.target-ir.cbor", + domain: "echo.span-ir/v1", + root: "echo-span-ir", + bytes: include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/resources/resource.target-ir.cbor" + ), + }, + GeneratedResourceFixture { + path: "resource.target-lowerer-contract.cbor", + domain: "echo.dpo.lowerer/v1", + root: "echo-dpo-lowerer", + bytes: include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/resources/resource.target-lowerer-contract.cbor" + ), + }, + GeneratedResourceFixture { + path: "resource.target-obstruction-taxonomy.cbor", + domain: "echo.dpo.obstructions/v1", + root: "echo-dpo-obstructions", + bytes: include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/resources/resource.target-obstruction-taxonomy.cbor" + ), + }, + GeneratedResourceFixture { + path: "resource.target-operation-profiles.cbor", + domain: "echo.dpo.operation-profiles/v1", + root: "operation-profiles-document", + bytes: include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/resources/resource.target-operation-profiles.cbor" + ), + }, + GeneratedResourceFixture { + path: "resource.target-verifier-contract.cbor", + domain: "echo.dpo.verifier/v1", + root: "echo-dpo-verifier", + bytes: include_bytes!( + "../../../schemas/edict-provider/package/v1/generated/resources/resource.target-verifier-contract.cbor" + ), + }, +]; + +#[derive(Clone, Copy)] +struct GeneratedResourceMaterial<'a> { + coordinate: &'a str, + domain: &'a str, + bytes: &'a [u8], +} + +struct OwnerResourceReference<'a> { + expected_coordinate: &'static str, + reference: &'a CanonicalValue, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum PackageIdentityFailure { + ReferenceMalformed, + ConflictingOwnerReference, + FieldReferenceMismatch, + DuplicateMaterial, + CoordinateClosureMismatch, + DigestMismatch, + PrimaryReferenceMismatch, + AuthoritySourceMismatch, +} + +fn checked_manifest() -> TargetProviderManifest { + serde_json::from_slice(MANIFEST_BYTES) + .expect("the checked package manifest is exact typed Edict JSON") +} + +fn required_domains(manifest: &TargetProviderManifest) -> Vec<&str> { + manifest + .schema_bindings + .iter() + .map(|binding| binding.domain.as_str()) + .collect() +} + +fn declared_package_registry(manifest: &TargetProviderManifest) -> ProviderArtifactSchemaRegistry { + let proof = bind_target_provider_manifest(manifest) + .expect("the checked package manifest satisfies the Edict envelope"); + ProviderArtifactSchemaRegistry::from_manifest( + &proof, + [ResolvedProviderSchemaArtifact { + role: SCHEMA_ROLE.to_owned(), + bytes: Arc::<[u8]>::from(SCHEMA_BYTES), + }], + required_domains(manifest), + ) + .expect("every currently declared package schema binding compiles") +} + +fn package_registry(manifest: &TargetProviderManifest) -> ProviderArtifactSchemaRegistry { + let proof = bind_target_provider_manifest(manifest) + .expect("the checked package manifest satisfies the Edict envelope"); + let mut required = required_domains(manifest); + required.push(GENERATED_ARTIFACT_PROFILE_DOMAIN); + required.sort_unstable(); + required.dedup(); + ProviderArtifactSchemaRegistry::from_manifest( + &proof, + [ResolvedProviderSchemaArtifact { + role: SCHEMA_ROLE.to_owned(), + bytes: Arc::<[u8]>::from(SCHEMA_BYTES), + }], + required, + ) + .expect("every routed canonical primary has an owning package schema binding") +} + +fn hex(bytes: &[u8]) -> String { + let mut output = String::with_capacity(bytes.len() * 2); + for byte in bytes { + write!(&mut output, "{byte:02x}").expect("writing hexadecimal to String cannot fail"); + } + output +} + +fn raw_digest(bytes: &[u8]) -> String { + format!("sha256:{}", hex(&Sha256::digest(bytes))) +} + +fn provider_digest(domain: &str, canonical_bytes: &[u8]) -> ProviderDigest { + decode_canonical_cbor(canonical_bytes).expect("artifact is canonical CBOR"); + let mut framed = vec![0x83]; + framed + .extend(encode_canonical_cbor(&text(CORE_DIGEST_FRAME)).expect("digest frame tag encodes")); + framed.extend(encode_canonical_cbor(&text(domain)).expect("digest domain encodes")); + framed.extend_from_slice(canonical_bytes); + ProviderDigest { + algorithm: ProviderDigestAlgorithm::Sha256, + bytes: Sha256::digest(framed).to_vec(), + } +} + +fn rendered_provider_digest(digest: &ProviderDigest) -> String { + assert_eq!(digest.algorithm, ProviderDigestAlgorithm::Sha256); + format!("sha256:{}", hex(&digest.bytes)) +} + +fn bound_artifact(coordinate: &str, domain: &str, bytes: &[u8]) -> ProviderBoundArtifact { + ProviderBoundArtifact { + reference: ProviderResourceRef { + coordinate: coordinate.to_owned(), + digest: provider_digest(domain, bytes), + }, + artifact: ProviderArtifact { + domain: domain.to_owned(), + bytes: bytes.to_vec(), + }, + } +} + +fn artifact_binding(bound: &ProviderBoundArtifact) -> ProviderArtifactBinding { + ProviderArtifactBinding { + reference: bound.reference.clone(), + domain: bound.artifact.domain.clone(), + } +} + +fn text(value: &str) -> CanonicalValue { + CanonicalValue::Text(value.to_owned()) +} + +fn map(entries: impl IntoIterator) -> CanonicalValue { + CanonicalValue::Map( + entries + .into_iter() + .map(|(key, value)| (text(key), value)) + .collect(), + ) +} + +fn lowerability_bytes() -> Vec { + encode_canonical_cbor(&map([ + ("apiVersion", text(LOWERABILITY_DOMAIN)), + ("operationProfile", text("continuum.profile.write/v1")), + ( + "semanticEffects", + CanonicalValue::Array(vec![map([ + ("coordinate", text("target.replace")), + ("writeClass", text("replace")), + ( + "guardKinds", + CanonicalValue::Array(vec![text("precommit-atomic")]), + ), + ( + "obstructionCoordinates", + CanonicalValue::Array(vec![text("rejected")]), + ), + ( + "footprintObligations", + CanonicalValue::Array(vec![text("target.replace.footprint")]), + ), + ( + "costObligations", + CanonicalValue::Array(vec![text("target.replace.cost")]), + ), + ])]), + ), + ( + "requiredWriteClasses", + CanonicalValue::Array(vec![text("replace")]), + ), + ( + "guardKinds", + CanonicalValue::Array(vec![text("precommit-atomic")]), + ), + ("atomicity", text("atomic")), + ("postconditionSupport", CanonicalValue::Bool(true)), + ( + "obstructionCoordinates", + CanonicalValue::Array(vec![text("rejected")]), + ), + ( + "footprintObligations", + CanonicalValue::Array(vec![text("target.replace.footprint")]), + ), + ( + "costObligations", + CanonicalValue::Array(vec![text("target.replace.cost")]), + ), + ("opticContract", text("replace-point")), + ])) + .expect("lowerability facts encode canonically") +} + +fn echo_core() -> CoreModule { + let context = CompilerContext::new() + .with_operation_profile("p.effectful", "continuum.profile.write/v1") + .with_operation_profile_write_classes("p.effectful", [WriteClass::Replace]) + .with_effect_write_class("target.replace", WriteClass::Replace) + .with_budget( + "p.tiny", + CoreBudget { + max_steps: 8, + max_allocated_bytes: 1024, + max_output_bytes: 256, + }, + ); + let module = parse_module(ECHO_SOURCE).expect("Echo source parses"); + compile_to_core(&module, &context).expect("Echo source compiles to Core") +} + +fn semantic_input( + role: &str, + kind: ProviderSemanticInputKind, + coordinate: &str, + domain: &str, + bytes: &[u8], +) -> ProviderSemanticInput { + ProviderSemanticInput { + role: role.to_owned(), + kind, + artifact: bound_artifact(coordinate, domain, bytes), + } +} + +fn semantic_inputs() -> Vec { + let lowerability = lowerability_bytes(); + vec![ + semantic_input( + "authority-facts.echo-dpo", + ProviderSemanticInputKind::AuthorityFacts, + "echo.dpo-authority-facts@1", + AUTHORITY_FACTS_API_VERSION, + TARGET_AUTHORITY_BYTES, + ), + semantic_input( + "authority-facts.echo-lawpack", + ProviderSemanticInputKind::AuthorityFacts, + "echo.dpo-lawpack-authority-facts@1", + AUTHORITY_FACTS_API_VERSION, + LAWPACK_AUTHORITY_BYTES, + ), + semantic_input( + "lawpack.echo-dpo", + ProviderSemanticInputKind::Lawpack, + "echo.dpo-lawpack@1", + PROVIDER_LAWPACK_ARTIFACT_DOMAIN, + LAWPACK_BYTES, + ), + semantic_input( + "lowerability.echo-dpo", + ProviderSemanticInputKind::LowerabilityFacts, + "echo.dpo-lowerability@1", + LOWERABILITY_DOMAIN, + &lowerability, + ), + ] +} + +fn lowering_request( + core: &CoreModule, +) -> (ProviderLoweringInvocationContract, ProviderLoweringRequest) { + let core_bytes = encode_core_module(core).expect("Core module encodes canonically"); + let core_artifact = bound_artifact("a.b@1", CORE_MODULE_DIGEST_DOMAIN, &core_bytes); + let target_profile = bound_artifact( + ECHO_DPO_TARGET_PROFILE, + TARGET_PROFILE_API_VERSION, + TARGET_PROFILE_BYTES, + ); + let semantic_inputs = semantic_inputs(); + let contract = ProviderLoweringInvocationContract { + core: artifact_binding(&core_artifact), + target_profile: artifact_binding(&target_profile), + semantic_inputs: semantic_inputs + .iter() + .map(|input| ProviderSemanticInputBinding { + role: input.role.clone(), + kind: input.kind.clone(), + artifact: artifact_binding(&input.artifact), + }) + .collect(), + }; + let request = ProviderLoweringRequest { + protocol_version: TARGET_PROVIDER_PROTOCOL_VERSION, + core: core_artifact, + target_profile, + semantic_inputs, + requested_outputs: vec![ProviderLoweringOutputRequest { + role: TARGET_IR_ROLE.to_owned(), + kind: ProviderLoweringOutputKind::TargetIr, + domain: TARGET_IR_ARTIFACT_DIGEST_DOMAIN.to_owned(), + }], + limits: response_limits(), + }; + (contract, request) +} + +fn oracle_target_ir(core: &CoreModule, target_profile: ResourceRef) -> Vec { + let facts = TargetIrLoweringFacts { + target_profile, + target_ir_domain: ECHO_SPAN_IR_DOMAIN.to_owned(), + operation_profiles: vec!["continuum.profile.write/v1".to_owned()], + obstruction_coordinates: vec!["rejected".to_owned()], + effect_lowerings: vec![TargetEffectLowering { + effect: "target.replace".to_owned(), + target_intrinsic: "echo.dpo@1.replace".to_owned(), + }], + }; + let report = lower_with_builtin_lowerer( + BuiltinTargetLowerer::EchoDpo, + BuiltinLowererRequest { + core, + facts: &facts, + }, + ) + .expect("Edict's built-in Echo lowerer accepts the exact fixture"); + let artifact = report + .artifact + .expect("the exact fixture lowers to Target IR"); + encode_target_ir_artifact(&artifact).expect("oracle Target IR encodes canonically") +} + +fn verification_request( + core: &CoreModule, + target_ir_bytes: &[u8], +) -> ( + ProviderVerificationInvocationContract, + ProviderVerificationRequest, +) { + let core_bytes = encode_core_module(core).expect("Core module encodes canonically"); + let core_artifact = bound_artifact("a.b@1", CORE_MODULE_DIGEST_DOMAIN, &core_bytes); + let target_profile = bound_artifact( + ECHO_DPO_TARGET_PROFILE, + TARGET_PROFILE_API_VERSION, + TARGET_PROFILE_BYTES, + ); + let target_ir = bound_artifact( + "echo.target-ir@1", + TARGET_IR_ARTIFACT_DIGEST_DOMAIN, + target_ir_bytes, + ); + let semantic_inputs = semantic_inputs(); + let contract = ProviderVerificationInvocationContract { + core: artifact_binding(&core_artifact), + target_profile: artifact_binding(&target_profile), + target_ir: artifact_binding(&target_ir), + semantic_inputs: semantic_inputs + .iter() + .map(|input| ProviderSemanticInputBinding { + role: input.role.clone(), + kind: input.kind.clone(), + artifact: artifact_binding(&input.artifact), + }) + .collect(), + }; + let request = ProviderVerificationRequest { + protocol_version: TARGET_PROVIDER_PROTOCOL_VERSION, + core: core_artifact, + target_profile, + target_ir, + semantic_inputs, + requested_outputs: vec![ProviderVerificationOutputRequest { + role: VERIFIER_REPORT_ROLE.to_owned(), + kind: ProviderVerificationOutputKind::VerifierReport, + domain: VERIFIER_REPORT_DOMAIN.to_owned(), + }], + limits: response_limits(), + }; + (contract, request) +} + +const fn response_limits() -> ProviderResponseLimits { + ProviderResponseLimits { + max_output_count: 8, + max_diagnostic_count: 8, + max_total_response_bytes: 64 * 1024, + } +} + +fn routed_resource<'a>(manifest: &'a TargetProviderManifest, role: &str) -> &'a ResourceRef { + &manifest + .artifacts + .iter() + .find(|artifact| artifact.role == role) + .expect("the package contains every required routed role") + .resource +} + +fn assert_bound_matches_routed( + manifest: &TargetProviderManifest, + role: &str, + bound: &ProviderBoundArtifact, +) { + let routed = routed_resource(manifest, role); + assert_eq!(bound.reference.coordinate, routed.coordinate); + assert_eq!( + Some(rendered_provider_digest(&bound.reference.digest).as_str()), + routed.digest.as_deref() + ); +} + +fn assert_request_semantics_are_package_routed( + manifest: &TargetProviderManifest, + target_profile: &ProviderBoundArtifact, + inputs: &[ProviderSemanticInput], +) { + assert_bound_matches_routed(manifest, "target-profile.echo-dpo", target_profile); + for role in [ + "authority-facts.echo-dpo", + "authority-facts.echo-lawpack", + "lawpack.echo-dpo", + ] { + let input = inputs + .iter() + .find(|input| input.role == role) + .expect("the exact request carries every routed semantic input"); + assert_bound_matches_routed(manifest, role, &input.artifact); + } +} + +fn assert_schema_binding(manifest: &TargetProviderManifest, domain: &str, root: &str) { + let binding = manifest + .schema_bindings + .iter() + .find(|binding| binding.domain == domain) + .expect("every canonical package member has an exact owning schema binding"); + assert_eq!(binding.schema_role, SCHEMA_ROLE); + assert_eq!(binding.format, ProviderSchemaFormat::SelfContainedCddlV1); + assert_eq!(binding.root_rule, root); +} + +fn map_field<'a>(value: &'a CanonicalValue, field: &str) -> Option<&'a CanonicalValue> { + let CanonicalValue::Map(entries) = value else { + return None; + }; + entries.iter().find_map(|(key, value)| match key { + CanonicalValue::Text(key) if key == field => Some(value), + _ => None, + }) +} + +fn map_field_mut<'a>(value: &'a mut CanonicalValue, field: &str) -> Option<&'a mut CanonicalValue> { + let CanonicalValue::Map(entries) = value else { + return None; + }; + entries.iter_mut().find_map(|(key, value)| match key { + CanonicalValue::Text(key) if key == field => Some(value), + _ => None, + }) +} + +fn required_field<'a>( + value: &'a CanonicalValue, + field: &str, +) -> Result<&'a CanonicalValue, PackageIdentityFailure> { + map_field(value, field).ok_or(PackageIdentityFailure::ReferenceMalformed) +} + +fn required_singleton(value: &CanonicalValue) -> Result<&CanonicalValue, PackageIdentityFailure> { + let CanonicalValue::Array(values) = value else { + return Err(PackageIdentityFailure::ReferenceMalformed); + }; + let [value] = values.as_slice() else { + return Err(PackageIdentityFailure::CoordinateClosureMismatch); + }; + Ok(value) +} + +fn typed_digest_bytes(value: &CanonicalValue) -> Result<&[u8], PackageIdentityFailure> { + let CanonicalValue::Array(parts) = value else { + return Err(PackageIdentityFailure::ReferenceMalformed); + }; + let [CanonicalValue::Text(algorithm), CanonicalValue::Bytes(bytes)] = parts.as_slice() else { + return Err(PackageIdentityFailure::ReferenceMalformed); + }; + if algorithm != "sha256" || bytes.len() != 32 { + return Err(PackageIdentityFailure::ReferenceMalformed); + } + Ok(bytes) +} + +fn embedded_resource_ref(value: &CanonicalValue) -> Result<(&str, &[u8]), PackageIdentityFailure> { + let CanonicalValue::Text(coordinate) = required_field(value, "id")? else { + return Err(PackageIdentityFailure::ReferenceMalformed); + }; + let digest = typed_digest_bytes(required_field(value, "digest")?)?; + Ok((coordinate, digest)) +} + +fn insert_owner_reference( + closure: &mut BTreeMap>, + reference: &CanonicalValue, +) -> Result<(), PackageIdentityFailure> { + let (coordinate, digest) = embedded_resource_ref(reference)?; + if let Some(existing) = closure.get(coordinate) { + if existing != digest { + return Err(PackageIdentityFailure::ConflictingOwnerReference); + } + } else { + closure.insert(coordinate.to_owned(), digest.to_vec()); + } + Ok(()) +} + +fn owner_generated_resource_closure( + lawpack: &CanonicalValue, + target_profile: &CanonicalValue, +) -> Result>, PackageIdentityFailure> { + let target_adapters = required_field(lawpack, "targetAdapters")?; + let target_adapter = required_singleton(target_adapters)?; + let lawpack_verifier = required_field(lawpack, "verifier")?; + let mut closure = BTreeMap::new(); + for owner in [ + OwnerResourceReference { + expected_coordinate: "echo.dpo-lawpack.exports@1", + reference: required_field(lawpack, "exports")?, + }, + OwnerResourceReference { + expected_coordinate: "echo.span-ir/v1", + reference: required_field(target_adapter, "acceptedTargetIr")?, + }, + OwnerResourceReference { + expected_coordinate: "echo.dpo-lawpack.adapter.echo-dpo@1", + reference: required_field(target_adapter, "adapter")?, + }, + OwnerResourceReference { + expected_coordinate: "echo.dpo-lawpack.verifier@1", + reference: required_field(lawpack_verifier, "ruleset")?, + }, + OwnerResourceReference { + expected_coordinate: "echo.dpo-lawpack.compatibility@1", + reference: required_field(lawpack, "compatibility")?, + }, + OwnerResourceReference { + expected_coordinate: "echo.dpo.fixtures/v1", + reference: required_field(lawpack, "conformanceFixtureCorpus")?, + }, + OwnerResourceReference { + expected_coordinate: "echo.dpo.intrinsics/v1", + reference: required_field(target_profile, "intrinsics")?, + }, + OwnerResourceReference { + expected_coordinate: "echo.dpo.operation-profiles/v1", + reference: required_field(target_profile, "operationProfiles")?, + }, + OwnerResourceReference { + expected_coordinate: "echo.dpo.footprint/v1", + reference: required_field(target_profile, "footprintAlgebra")?, + }, + OwnerResourceReference { + expected_coordinate: "echo.dpo.cost/v1", + reference: required_field(target_profile, "costAlgebra")?, + }, + OwnerResourceReference { + expected_coordinate: "echo.span-ir/v1", + reference: required_field(target_profile, "targetIr")?, + }, + OwnerResourceReference { + expected_coordinate: "echo.dpo.obstructions/v1", + reference: required_field(target_profile, "obstructionTaxonomy")?, + }, + OwnerResourceReference { + expected_coordinate: "echo.dpo.verifier/v1", + reference: required_field(target_profile, "verifier")?, + }, + OwnerResourceReference { + expected_coordinate: "echo.dpo.lowerer/v1", + reference: required_field(target_profile, "lowerer")?, + }, + OwnerResourceReference { + expected_coordinate: "echo.dpo.bundle/v1", + reference: required_field(target_profile, "bundleProfile")?, + }, + OwnerResourceReference { + expected_coordinate: "echo.dpo.fixtures/v1", + reference: required_field(target_profile, "conformanceFixtureCorpus")?, + }, + ] { + let (coordinate, _) = embedded_resource_ref(owner.reference)?; + if coordinate != owner.expected_coordinate { + return Err(PackageIdentityFailure::FieldReferenceMismatch); + } + insert_owner_reference(&mut closure, owner.reference)?; + } + Ok(closure) +} + +fn validate_generated_resource_identity_closure<'a>( + lawpack: &CanonicalValue, + target_profile: &CanonicalValue, + materials: impl IntoIterator>, +) -> Result<(), PackageIdentityFailure> { + let owner_closure = owner_generated_resource_closure(lawpack, target_profile)?; + let mut material_closure = BTreeMap::new(); + for material in materials { + let digest = provider_digest(material.domain, material.bytes).bytes; + if material_closure + .insert(material.coordinate.to_owned(), digest) + .is_some() + { + return Err(PackageIdentityFailure::DuplicateMaterial); + } + } + if owner_closure.len() != material_closure.len() + || owner_closure + .keys() + .zip(material_closure.keys()) + .any(|(owner, material)| owner != material) + { + return Err(PackageIdentityFailure::CoordinateClosureMismatch); + } + if material_closure + .iter() + .any(|(coordinate, digest)| owner_closure.get(coordinate) != Some(digest)) + { + return Err(PackageIdentityFailure::DigestMismatch); + } + Ok(()) +} + +fn embedded_ref_matches_routed( + manifest: &TargetProviderManifest, + role: &str, + reference: &CanonicalValue, +) -> Result<(), PackageIdentityFailure> { + let (coordinate, digest) = embedded_resource_ref(reference)?; + let routed = routed_resource(manifest, role); + if coordinate != routed.coordinate + || Some(format!("sha256:{}", hex(digest)).as_str()) != routed.digest.as_deref() + { + return Err(PackageIdentityFailure::PrimaryReferenceMismatch); + } + Ok(()) +} + +fn authority_source_matches_routed( + manifest: &TargetProviderManifest, + facts: &CanonicalValue, + expected_kind: &str, + expected_role: &str, +) -> Result<(), PackageIdentityFailure> { + let source = required_field(facts, "source")?; + let CanonicalValue::Text(kind) = required_field(source, "kind")? else { + return Err(PackageIdentityFailure::ReferenceMalformed); + }; + let CanonicalValue::Text(coordinate) = required_field(source, "coordinate")? else { + return Err(PackageIdentityFailure::ReferenceMalformed); + }; + let digest = typed_digest_bytes(required_field(source, "digest")?)?; + let routed = routed_resource(manifest, expected_role); + if kind != expected_kind + || coordinate != &routed.coordinate + || Some(format!("sha256:{}", hex(digest)).as_str()) != routed.digest.as_deref() + { + return Err(PackageIdentityFailure::AuthoritySourceMismatch); + } + Ok(()) +} + +fn validate_adjacent_primary_identity_edges( + manifest: &TargetProviderManifest, + lawpack: &CanonicalValue, + target_profile: &CanonicalValue, + target_authority: &CanonicalValue, + lawpack_authority: &CanonicalValue, +) -> Result<(), PackageIdentityFailure> { + let generated_profiles = required_field(target_profile, "generatedArtifactProfiles")?; + embedded_ref_matches_routed( + manifest, + GENERATED_ARTIFACT_PROFILE_ROLE, + required_singleton(generated_profiles)?, + )?; + let target_adapters = required_field(lawpack, "targetAdapters")?; + let target_adapter = required_singleton(target_adapters)?; + embedded_ref_matches_routed( + manifest, + "target-profile.echo-dpo", + required_field(target_adapter, "acceptedTargetProfile")?, + )?; + authority_source_matches_routed( + manifest, + target_authority, + "targetProfile", + "target-profile.echo-dpo", + )?; + authority_source_matches_routed(manifest, lawpack_authority, "lawpack", "lawpack.echo-dpo") +} + +fn exact_generated_resource_materials() -> impl Iterator> +{ + GENERATED_RESOURCE_FIXTURES + .iter() + .map(|fixture| GeneratedResourceMaterial { + coordinate: fixture.domain, + domain: fixture.domain, + bytes: fixture.bytes, + }) +} + +fn assert_routed_canonical_artifacts( + manifest: &TargetProviderManifest, + registry: &ProviderArtifactSchemaRegistry, +) { + for fixture in ROUTED_CANONICAL_ARTIFACTS { + assert_schema_binding(manifest, fixture.domain, fixture.root); + let value = decode_canonical_cbor(fixture.bytes) + .expect("the routed package artifact is exact canonical CBOR"); + registry + .validate_canonical_value(fixture.domain, &value) + .expect("the routed package artifact satisfies its owning root"); + let expected = rendered_provider_digest(&provider_digest(fixture.domain, fixture.bytes)); + assert_eq!( + routed_resource(manifest, fixture.role).digest.as_deref(), + Some(expected.as_str()) + ); + } +} + +fn assert_generated_resources( + manifest: &TargetProviderManifest, + registry: &ProviderArtifactSchemaRegistry, +) { + for fixture in GENERATED_RESOURCE_FIXTURES { + assert_schema_binding(manifest, fixture.domain, fixture.root); + let value = decode_canonical_cbor(fixture.bytes) + .unwrap_or_else(|error| panic!("{} is not canonical: {error}", fixture.path)); + registry + .validate_canonical_value(fixture.domain, &value) + .unwrap_or_else(|error| { + panic!( + "{} does not satisfy {}: {error:?}", + fixture.path, fixture.root + ); + }); + } +} + +#[test] +fn checked_package_is_ready_for_edict_before_guest_execution() { + let manifest = checked_manifest(); + let proof = bind_target_provider_manifest(&manifest) + .expect("the checked package manifest satisfies the Edict envelope"); + let registry = package_registry(&manifest); + assert_eq!( + manifest.schema_bindings.len(), + EXPECTED_SCHEMA_BINDING_COUNT + ); + assert_eq!(registry.bindings().len(), EXPECTED_SCHEMA_BINDING_COUNT); + assert_routed_canonical_artifacts(&manifest, ®istry); + assert_generated_resources(&manifest, ®istry); + let lawpack = decode_canonical_cbor(LAWPACK_BYTES).expect("lawpack is canonical"); + let target_profile = + decode_canonical_cbor(TARGET_PROFILE_BYTES).expect("target profile is canonical"); + let target_authority = + decode_canonical_cbor(TARGET_AUTHORITY_BYTES).expect("target facts are canonical"); + let lawpack_authority = + decode_canonical_cbor(LAWPACK_AUTHORITY_BYTES).expect("lawpack facts are canonical"); + validate_generated_resource_identity_closure( + &lawpack, + &target_profile, + exact_generated_resource_materials(), + ) + .expect("the exact 14-resource owner union binds every domain-framed digest"); + validate_adjacent_primary_identity_edges( + &manifest, + &lawpack, + &target_profile, + &target_authority, + &lawpack_authority, + ) + .expect("the exact package primary identity graph is closed"); + + let host = ProviderComponentHost::new().expect("the deterministic host configures"); + let lowerer = select_provider_component(&proof, LOWERER_ROLE, ProviderInvocationKind::Lowering) + .expect("the checked package selects its lowerer"); + let lowerer = ResolvedProviderComponent::new(lowerer, Arc::<[u8]>::from(LOWERER_BYTES)); + let _prepared_lowerer = host + .prepare(&lowerer) + .expect("the exact packaged lowerer passes Edict preflight"); + let verifier = + select_provider_component(&proof, VERIFIER_ROLE, ProviderInvocationKind::Verification) + .expect("the checked package selects its verifier"); + let verifier = ResolvedProviderComponent::new(verifier, Arc::<[u8]>::from(VERIFIER_BYTES)); + let _prepared_verifier = host + .prepare(&verifier) + .expect("the exact packaged verifier passes Edict preflight"); + + let core = echo_core(); + let (lowering_contract, lowering_request) = lowering_request(&core); + assert_request_semantics_are_package_routed( + &manifest, + &lowering_request.target_profile, + &lowering_request.semantic_inputs, + ); + let _lowering_proof = + validate_provider_lowering_request(®istry, &lowering_contract, &lowering_request) + .expect("the exact lowerer request has an Edict validation proof"); + + let target_ir = oracle_target_ir( + &core, + routed_resource(&manifest, "target-profile.echo-dpo").clone(), + ); + let (verification_contract, verification_request) = verification_request(&core, &target_ir); + assert_request_semantics_are_package_routed( + &manifest, + &verification_request.target_profile, + &verification_request.semantic_inputs, + ); + let _verification_proof = validate_provider_verification_request( + ®istry, + &verification_contract, + &verification_request, + ) + .expect("the exact verifier request has an Edict validation proof"); +} + +#[test] +fn schema_valid_resource_replacement_cannot_cross_the_exact_owner_digest() { + let manifest = checked_manifest(); + let registry = package_registry(&manifest); + let mut replacement = decode_canonical_cbor( + GENERATED_RESOURCE_FIXTURES + .iter() + .find(|fixture| fixture.domain == "echo.dpo.cost/v1") + .expect("the cost resource fixture exists") + .bytes, + ) + .expect("the exact cost resource is canonical"); + let capabilities = map_field_mut(&mut replacement, "capabilities") + .expect("the cost resource carries capabilities"); + let CanonicalValue::Map(capabilities) = capabilities else { + panic!("cost capabilities must be a map"); + }; + let capability = &mut capabilities + .first_mut() + .expect("the exact cost resource carries one capability") + .1; + *map_field_mut(capability, "costTemplate").expect("the cost capability has a template") = + text("schema-valid-but-different"); + registry + .validate_canonical_value("echo.dpo.cost/v1", &replacement) + .expect("the replacement deliberately preserves the owning CDDL shape"); + let replacement_bytes = + encode_canonical_cbor(&replacement).expect("the replacement encodes canonically"); + let exact_cost_bytes = GENERATED_RESOURCE_FIXTURES + .iter() + .find(|fixture| fixture.domain == "echo.dpo.cost/v1") + .expect("the cost resource fixture exists") + .bytes; + assert_ne!(replacement_bytes, exact_cost_bytes); + + let lawpack = decode_canonical_cbor(LAWPACK_BYTES).expect("lawpack is canonical"); + let target_profile = + decode_canonical_cbor(TARGET_PROFILE_BYTES).expect("target profile is canonical"); + let materials = GENERATED_RESOURCE_FIXTURES + .iter() + .map(|fixture| GeneratedResourceMaterial { + coordinate: fixture.domain, + domain: fixture.domain, + bytes: if fixture.domain == "echo.dpo.cost/v1" { + &replacement_bytes + } else { + fixture.bytes + }, + }); + let error = validate_generated_resource_identity_closure(&lawpack, &target_profile, materials) + .expect_err("schema validity cannot replace the exact owner-bound resource identity"); + assert_eq!(error, PackageIdentityFailure::DigestMismatch); +} + +#[test] +fn schema_valid_lawpack_reference_mismatch_fails_identity_closure() { + let manifest = checked_manifest(); + let registry = declared_package_registry(&manifest); + let mut lawpack = decode_canonical_cbor(LAWPACK_BYTES).expect("lawpack is canonical"); + let exports = map_field_mut(&mut lawpack, "exports").expect("lawpack carries exports"); + let digest = map_field_mut(exports, "digest").expect("exports reference carries a digest"); + let CanonicalValue::Array(parts) = digest else { + panic!("exports digest must be typed"); + }; + let Some(CanonicalValue::Bytes(bytes)) = parts.get_mut(1) else { + panic!("exports digest must carry raw bytes"); + }; + bytes[0] ^= 1; + let changed_bytes = + encode_canonical_cbor(&lawpack).expect("changed lawpack encodes canonically"); + let changed_lawpack = + decode_canonical_cbor(&changed_bytes).expect("changed lawpack remains canonical"); + registry + .validate_canonical_value(PROVIDER_LAWPACK_ARTIFACT_DOMAIN, &changed_lawpack) + .expect("the mismatching reference deliberately preserves the lawpack CDDL shape"); + let target_profile = + decode_canonical_cbor(TARGET_PROFILE_BYTES).expect("target profile is canonical"); + let error = validate_generated_resource_identity_closure( + &changed_lawpack, + &target_profile, + exact_generated_resource_materials(), + ) + .expect_err("schema-valid reference bytes cannot replace the exact resource digest"); + assert_eq!(error, PackageIdentityFailure::DigestMismatch); +} + +#[test] +fn schema_valid_reference_swap_cannot_hide_behind_union_equality() { + let manifest = checked_manifest(); + let registry = declared_package_registry(&manifest); + let mut lawpack = decode_canonical_cbor(LAWPACK_BYTES).expect("lawpack is canonical"); + let exports = map_field(&lawpack, "exports") + .expect("lawpack carries exports") + .clone(); + let compatibility = map_field(&lawpack, "compatibility") + .expect("lawpack carries compatibility") + .clone(); + *map_field_mut(&mut lawpack, "exports").expect("lawpack carries exports") = compatibility; + *map_field_mut(&mut lawpack, "compatibility").expect("lawpack carries compatibility") = exports; + let changed_bytes = + encode_canonical_cbor(&lawpack).expect("swapped lawpack encodes canonically"); + let changed_lawpack = + decode_canonical_cbor(&changed_bytes).expect("swapped lawpack remains canonical"); + registry + .validate_canonical_value(PROVIDER_LAWPACK_ARTIFACT_DOMAIN, &changed_lawpack) + .expect("the field swap deliberately preserves the lawpack CDDL shape"); + let target_profile = + decode_canonical_cbor(TARGET_PROFILE_BYTES).expect("target profile is canonical"); + let error = validate_generated_resource_identity_closure( + &changed_lawpack, + &target_profile, + exact_generated_resource_materials(), + ) + .expect_err("coordinate-union equality cannot erase semantic field ownership"); + assert_eq!(error, PackageIdentityFailure::FieldReferenceMismatch); +} + +#[test] +fn schema_valid_authority_source_mismatch_fails_primary_identity_closure() { + let manifest = checked_manifest(); + let registry = declared_package_registry(&manifest); + let lawpack = decode_canonical_cbor(LAWPACK_BYTES).expect("lawpack is canonical"); + let target_profile = + decode_canonical_cbor(TARGET_PROFILE_BYTES).expect("target profile is canonical"); + let mut target_authority = + decode_canonical_cbor(TARGET_AUTHORITY_BYTES).expect("target facts are canonical"); + let source = + map_field_mut(&mut target_authority, "source").expect("target facts carry a source"); + *map_field_mut(source, "coordinate").expect("authority source carries a coordinate") = + text("echo.dpo@1.schema-valid-mismatch"); + let changed_bytes = encode_canonical_cbor(&target_authority) + .expect("changed authority facts encode canonically"); + let changed_target_authority = + decode_canonical_cbor(&changed_bytes).expect("changed authority facts remain canonical"); + registry + .validate_canonical_value(AUTHORITY_FACTS_API_VERSION, &changed_target_authority) + .expect("the mismatching source deliberately preserves the authority-facts CDDL shape"); + let lawpack_authority = + decode_canonical_cbor(LAWPACK_AUTHORITY_BYTES).expect("lawpack facts are canonical"); + let error = validate_adjacent_primary_identity_edges( + &manifest, + &lawpack, + &target_profile, + &changed_target_authority, + &lawpack_authority, + ) + .expect_err("schema-valid authority facts cannot change their exact subject identity"); + assert_eq!(error, PackageIdentityFailure::AuthoritySourceMismatch); +} + +#[test] +fn malformed_manifest_fails_before_component_selection() { + let mut manifest = checked_manifest(); + manifest.provider_abi = "edict:target-provider@invalid".to_owned(); + let report = bind_target_provider_manifest(&manifest) + .expect_err("an invalid provider ABI cannot produce an Edict manifest proof"); + assert!(report.failures.iter().any(|failure| { + failure.kind == edict_syntax::ProviderManifestValidationFailureKind::InvalidProviderAbi + })); +} + +#[test] +fn malformed_schema_fails_before_component_selection() { + let mut manifest = checked_manifest(); + let malformed_schema: &[u8] = b"this is not CDDL"; + let schema = manifest + .artifacts + .iter_mut() + .find(|artifact| artifact.role == SCHEMA_ROLE) + .expect("the package routes its schema artifact"); + schema.resource.digest = Some(raw_digest(malformed_schema)); + let proof = bind_target_provider_manifest(&manifest) + .expect("the coherently rebound malformed schema remains a typed manifest"); + let required = required_domains(&manifest); + let error = ProviderArtifactSchemaRegistry::from_manifest( + &proof, + [ResolvedProviderSchemaArtifact { + role: SCHEMA_ROLE.to_owned(), + bytes: Arc::<[u8]>::from(malformed_schema), + }], + required, + ) + .expect_err("malformed schema bytes cannot produce an Edict registry"); + assert_eq!( + error.kind(), + ProviderSchemaRegistryFailureKind::SchemaCompileFailed + ); +} + +#[test] +fn malformed_component_fails_during_preflight() { + let mut manifest = checked_manifest(); + let mut malformed_component = LOWERER_BYTES.to_vec(); + malformed_component[0] ^= 1; + let digest = raw_digest(&malformed_component); + let lowerer = manifest + .artifacts + .iter_mut() + .find(|artifact| artifact.role == LOWERER_ROLE) + .expect("the package routes its lowerer component"); + lowerer.resource.digest = Some(digest.clone()); + match &mut lowerer.source { + ProviderArtifactSource::Component { component } => { + component.digest = Some(digest); + } + ProviderArtifactSource::Generated { .. } => panic!("lowerer source must be a component"), + } + let proof = bind_target_provider_manifest(&manifest) + .expect("the coherently rebound component remains a typed manifest"); + let selected = + select_provider_component(&proof, LOWERER_ROLE, ProviderInvocationKind::Lowering) + .expect("the coherently rebound lowerer still selects"); + let resolved = ResolvedProviderComponent::new(selected, Arc::<[u8]>::from(malformed_component)); + let host = ProviderComponentHost::new().expect("the deterministic host configures"); + let error = host + .prepare(&resolved) + .expect_err("malformed component bytes cannot pass Edict preflight"); + assert_eq!(error.kind(), ProviderHostFailureKind::ComponentDecodeFailed); + assert_eq!(error.phase(), ProviderHostPhase::Preflight); +} + +#[test] +fn malformed_artifact_fails_before_component_execution() { + let manifest = checked_manifest(); + let proof = bind_target_provider_manifest(&manifest) + .expect("the checked package manifest satisfies the Edict envelope"); + let required = required_domains(&manifest); + let registry = ProviderArtifactSchemaRegistry::from_manifest( + &proof, + [ResolvedProviderSchemaArtifact { + role: SCHEMA_ROLE.to_owned(), + bytes: Arc::<[u8]>::from(SCHEMA_BYTES), + }], + required, + ) + .expect("the exact manifest bindings produce an Edict registry"); + let core = echo_core(); + let (mut contract, mut request) = lowering_request(&core); + let malformed = + encode_canonical_cbor(&CanonicalValue::Null).expect("null has canonical CBOR bytes"); + let target_profile = bound_artifact( + ECHO_DPO_TARGET_PROFILE, + TARGET_PROFILE_API_VERSION, + &malformed, + ); + contract.target_profile = artifact_binding(&target_profile); + request.target_profile = target_profile; + + let report = validate_provider_lowering_request(®istry, &contract, &request) + .expect_err("schema-invalid target profile cannot produce an invocation proof"); + assert!(report.failures.iter().any(|failure| { + failure.kind == ProviderInvocationValidationFailureKind::ArtifactSchemaMismatch + })); +} diff --git a/xtask/src/provider_lowerer_component.rs b/xtask/src/provider_lowerer_component.rs index a2865174..98ad9d0b 100644 --- a/xtask/src/provider_lowerer_component.rs +++ b/xtask/src/provider_lowerer_component.rs @@ -53,7 +53,7 @@ const PINNED_CARGO_COMMIT: &str = "840b83a10fb0e039a83f4d70ad032892c287570a"; /// Reviewed identity that the portable promotion command is permitted to install. pub(crate) const APPROVED_CHECKED_COMPONENT_SHA256: &str = - "03edee44c6bc70eb998c0c17662a214809746af3bba0740f3407c18a4016309e"; + "d58a7838df9d7e65c72063aafad91df97cb21404be556c93e864234f3f846326"; pub(crate) const CHECKED_COMPONENT_REPOSITORY_PATH: &str = "schemas/edict-provider/components/v1/lowerer.echo-dpo.component.wasm"; @@ -62,7 +62,7 @@ pub(crate) const VERIFIER_CHECKED_COMPONENT_REPOSITORY_PATH: &str = "schemas/edict-provider/components/v1/verifier.echo-dpo.component.wasm"; /// Approved SHA-256 identity of the checked verifier component. pub(crate) const APPROVED_CHECKED_VERIFIER_COMPONENT_SHA256: &str = - "e13eda6e02d5a46d2aecdec0546d53a7bf66f2580f8d5ec06e5d76710716a27b"; + "11fcaf291ffacac62800ab3fc198e29a23a591bcd30ca98828fa10a8ad1f1952"; #[derive(Clone, Copy, Debug, Eq, PartialEq)] struct ProviderComponentSpec {