diff --git a/CHANGELOG.md b/CHANGELOG.md index da5158a..47ce11d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ versions still track specification maturity rather than a released product. ### Changed +- Hardened result-projection admission with immutable emitted artifacts, + bounded recursive decoding, shared compiler input identity, reuse of the + compiler-computed semantic closure, and provider-schema parity for positive + output bounds, flat-record size, and source-path depth. - Rejected many-to-one target obstruction mappings before Target IR emission so failure-coordinate collisions cannot silently discard an obstruction arm. - Hardened standalone application builds around complete lawpack dependency @@ -21,6 +25,17 @@ versions still track specification maturity rather than a released product. ### Added +- Added the compiler-owned `edict.result-projection/v1` artifact for preserving + typed application results across the Echo target boundary. The bounded, + canonical projection names only declared application input and + capability-step result paths; an independent verifier reconstructs the + authored Core result and requires exact Core, Target IR, semantic-closure, + schema, and digest agreement. Target lowering separates admitted projections + from explicit per-intent projection failures without narrowing general Target + IR support. The application build requires one projection, independently + verifies it, and binds the same artifact into both provider closures. The + provider contract pack publishes its CDDL root, and the Hello Echo lawpack + generator owns reviewed projection bytes and identity fixtures. - Added the public `edict` CLI `build` operation for standalone applications. A settings-only JSONL request loads one exact `edict.application/v1` manifest, source, complete lawpack dependency closure, direct target adapter, diff --git a/crates/edict-cli/src/application_build.rs b/crates/edict-cli/src/application_build.rs index 16a87be..8dff855 100644 --- a/crates/edict-cli/src/application_build.rs +++ b/crates/edict-cli/src/application_build.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeSet; +use std::collections::{BTreeMap, BTreeSet}; use std::ffi::OsString; use std::fs::{self, OpenOptions}; use std::io::{ErrorKind, Read, Write}; @@ -15,17 +15,19 @@ use edict_syntax::{ decode_lawpack_bundle, digest_canonical_artifact, encode_canonical_cbor, encode_core_module, encode_target_ir_artifact, lower_to_target_ir, parse_module, prepare_lawpack_compilation, select_provider_component, validate_lawpack_dependency_graph, - validate_provider_lowering_request, validate_provider_verification_request, CanonicalValue, - ProviderArtifact, ProviderArtifactBinding, ProviderArtifactKind, ProviderArtifactRef, - ProviderBoundArtifact, ProviderDigest, ProviderDigestAlgorithm, ProviderInvocationKind, - ProviderLoweringInvocationContract, ProviderLoweringOutputKind, ProviderLoweringOutputRequest, - ProviderLoweringRequest, ProviderResourceRef, ProviderResponseLimits, ProviderSemanticInput, + validate_provider_lowering_request, validate_provider_verification_request, + verify_result_projection, CanonicalValue, ProviderArtifact, ProviderArtifactBinding, + ProviderArtifactKind, ProviderArtifactRef, ProviderBoundArtifact, ProviderDigest, + ProviderDigestAlgorithm, ProviderInvocationKind, ProviderLoweringInvocationContract, + ProviderLoweringOutputKind, ProviderLoweringOutputRequest, ProviderLoweringRequest, + ProviderResourceRef, ProviderResponseLimits, ProviderSemanticInput, ProviderSemanticInputBinding, ProviderSemanticInputKind, ProviderVerificationInvocationContract, ProviderVerificationOutputKind, - ProviderVerificationOutputRequest, ProviderVerificationRequest, TargetLoweringStatus, - TargetProviderManifest, ValidatedLawpackBundle, ValidatedTargetProviderManifest, - CORE_MODULE_DIGEST_DOMAIN, PROVIDER_LAWPACK_ARTIFACT_DOMAIN, TARGET_IR_ARTIFACT_DIGEST_DOMAIN, - TARGET_PROFILE_API_VERSION, TARGET_PROVIDER_PROTOCOL_VERSION, + ProviderVerificationOutputRequest, ProviderVerificationRequest, ResultProjectionArtifact, + TargetLoweringStatus, TargetProviderManifest, ValidatedLawpackBundle, + ValidatedTargetProviderManifest, CORE_MODULE_DIGEST_DOMAIN, PROVIDER_LAWPACK_ARTIFACT_DOMAIN, + RESULT_PROJECTION_DIGEST_DOMAIN, TARGET_IR_ARTIFACT_DIGEST_DOMAIN, TARGET_PROFILE_API_VERSION, + TARGET_PROVIDER_PROTOCOL_VERSION, }; use serde::Deserialize; @@ -37,6 +39,7 @@ const PACKAGE_ROLE: &str = "executable-operation-package.echo"; const PACKAGE_DOMAIN: &str = "echo.operation-package/v1"; const VERIFICATION_REPORT_ROLE: &str = "verifier-report.echo-operation"; const VERIFICATION_REPORT_DOMAIN: &str = "echo.operation-package-verifier-report/v1"; +const RESULT_PROJECTION_ROLE: &str = "07-result-projection"; const MAX_APPLICATION_ARTIFACT_BYTES: u64 = 1024 * 1024; #[derive(Debug)] @@ -88,6 +91,7 @@ struct ProviderInvocationContext<'a> { adapter: &'a edict_syntax::ValidatedLawpackAdapter, source_bytes: &'a [u8], target_ir_bytes: &'a [u8], + result_projection: &'a ResultProjectionArtifact, } #[allow( @@ -297,12 +301,29 @@ pub(crate) fn build_application(config_path: &Path) -> Result<(), ApplicationBui ), )); } + let (result_intent, result_projection) = single_result_projection( + &target_ir_report.result_projections, + &target_ir_report.result_projection_failures, + )?; let target_ir = target_ir_report.artifact.ok_or_else(|| { failure( "TargetLoweringFailed", "target lowering reported success without an artifact", ) })?; + verify_result_projection( + &core, + &target_ir, + result_intent, + result_projection.canonical_bytes(), + result_projection.digest(), + ) + .map_err(|error| { + failure( + "ResultProjectionVerificationFailed", + format!("compiler result projection failed independent verification: {error}"), + ) + })?; let core_bytes = encode_core_module(&core).map_err(|error| { failure( @@ -353,6 +374,7 @@ pub(crate) fn build_application(config_path: &Path) -> Result<(), ApplicationBui adapter: &adapter, source_bytes: &source_artifact_bytes, target_ir_bytes: &target_ir_bytes, + result_projection, }; let package_bytes = invoke_lowerer( &host, @@ -709,6 +731,7 @@ fn invoke_lowerer( invocation.coordinate, invocation.source_bytes, invocation.target_ir_bytes, + invocation.result_projection, )?; let contract = lowering_contract(&core, invocation.target_profile, &semantic_inputs); let request = ProviderLoweringRequest { @@ -812,6 +835,7 @@ fn invoke_verifier( invocation.coordinate, invocation.source_bytes, package_bytes, + invocation.result_projection, )?; let contract = verification_contract( &core, @@ -884,60 +908,64 @@ fn lowering_inputs( coordinate: &str, source_bytes: &[u8], target_ir_bytes: &[u8], + result_projection: &ResultProjectionArtifact, ) -> Result, ApplicationBuildFailure> { let configuration = single_configuration(adapter)?; let adapter_reference = selected_adapter_reference( &loaded.bundle.manifest().target_adapters, adapter.target_profile(), )?; - Ok(vec![ - semantic_input( - "01-lawpack-adapter", - ProviderSemanticInputKind::Auxiliary("lawpack-adapter".to_owned()), - &adapter_reference.id, - ADAPTER_DOMAIN, - &loaded.adapter_bytes, - )?, - semantic_input( - "02-lawpack-exports", - ProviderSemanticInputKind::Auxiliary("lawpack-exports".to_owned()), - &loaded.bundle.manifest().exports.id, - EXPORTS_DOMAIN, - &loaded.exports_bytes, - )?, - semantic_input( - "03-lawpack", - ProviderSemanticInputKind::Lawpack, - &format!( - "{}@{}", - loaded.bundle.manifest().id, - loaded.bundle.manifest().version - ), - PROVIDER_LAWPACK_ARTIFACT_DOMAIN, - &loaded.manifest_bytes, - )?, - semantic_input( - "04-source", - ProviderSemanticInputKind::Auxiliary("edict-source".to_owned()), - coordinate, - SOURCE_DOMAIN, - source_bytes, - )?, - semantic_input( - "05-target-configuration", - ProviderSemanticInputKind::Auxiliary("target-configuration".to_owned()), - &configuration.id, - &configuration.id, - &loaded.configuration_bytes, - )?, - semantic_input( - "06-target-ir", - ProviderSemanticInputKind::Auxiliary("target-ir".to_owned()), - &adapter.target_ir().id, - TARGET_IR_ARTIFACT_DIGEST_DOMAIN, - target_ir_bytes, - )?, - ]) + with_result_projection_input( + vec![ + semantic_input( + "01-lawpack-adapter", + ProviderSemanticInputKind::Auxiliary("lawpack-adapter".to_owned()), + &adapter_reference.id, + ADAPTER_DOMAIN, + &loaded.adapter_bytes, + )?, + semantic_input( + "02-lawpack-exports", + ProviderSemanticInputKind::Auxiliary("lawpack-exports".to_owned()), + &loaded.bundle.manifest().exports.id, + EXPORTS_DOMAIN, + &loaded.exports_bytes, + )?, + semantic_input( + "03-lawpack", + ProviderSemanticInputKind::Lawpack, + &format!( + "{}@{}", + loaded.bundle.manifest().id, + loaded.bundle.manifest().version + ), + PROVIDER_LAWPACK_ARTIFACT_DOMAIN, + &loaded.manifest_bytes, + )?, + semantic_input( + "04-source", + ProviderSemanticInputKind::Auxiliary("edict-source".to_owned()), + coordinate, + SOURCE_DOMAIN, + source_bytes, + )?, + semantic_input( + "05-target-configuration", + ProviderSemanticInputKind::Auxiliary("target-configuration".to_owned()), + &configuration.id, + &configuration.id, + &loaded.configuration_bytes, + )?, + semantic_input( + "06-target-ir", + ProviderSemanticInputKind::Auxiliary("target-ir".to_owned()), + &adapter.target_ir().id, + TARGET_IR_ARTIFACT_DIGEST_DOMAIN, + target_ir_bytes, + )?, + ], + result_projection, + ) } fn verification_inputs( @@ -946,60 +974,64 @@ fn verification_inputs( coordinate: &str, source_bytes: &[u8], package_bytes: &[u8], + result_projection: &ResultProjectionArtifact, ) -> Result, ApplicationBuildFailure> { let configuration = single_configuration(adapter)?; let adapter_reference = selected_adapter_reference( &loaded.bundle.manifest().target_adapters, adapter.target_profile(), )?; - Ok(vec![ - semantic_input( - "01-lawpack-adapter", - ProviderSemanticInputKind::Auxiliary("lawpack-adapter".to_owned()), - &adapter_reference.id, - ADAPTER_DOMAIN, - &loaded.adapter_bytes, - )?, - semantic_input( - "02-executable-operation-package", - ProviderSemanticInputKind::Auxiliary("executable-operation-package".to_owned()), - PACKAGE_ROLE, - PACKAGE_DOMAIN, - package_bytes, - )?, - semantic_input( - "03-lawpack-exports", - ProviderSemanticInputKind::Auxiliary("lawpack-exports".to_owned()), - &loaded.bundle.manifest().exports.id, - EXPORTS_DOMAIN, - &loaded.exports_bytes, - )?, - semantic_input( - "04-lawpack", - ProviderSemanticInputKind::Lawpack, - &format!( - "{}@{}", - loaded.bundle.manifest().id, - loaded.bundle.manifest().version - ), - PROVIDER_LAWPACK_ARTIFACT_DOMAIN, - &loaded.manifest_bytes, - )?, - semantic_input( - "05-source", - ProviderSemanticInputKind::Auxiliary("edict-source".to_owned()), - coordinate, - SOURCE_DOMAIN, - source_bytes, - )?, - semantic_input( - "06-target-configuration", - ProviderSemanticInputKind::Auxiliary("target-configuration".to_owned()), - &configuration.id, - &configuration.id, - &loaded.configuration_bytes, - )?, - ]) + with_result_projection_input( + vec![ + semantic_input( + "01-lawpack-adapter", + ProviderSemanticInputKind::Auxiliary("lawpack-adapter".to_owned()), + &adapter_reference.id, + ADAPTER_DOMAIN, + &loaded.adapter_bytes, + )?, + semantic_input( + "02-executable-operation-package", + ProviderSemanticInputKind::Auxiliary("executable-operation-package".to_owned()), + PACKAGE_ROLE, + PACKAGE_DOMAIN, + package_bytes, + )?, + semantic_input( + "03-lawpack-exports", + ProviderSemanticInputKind::Auxiliary("lawpack-exports".to_owned()), + &loaded.bundle.manifest().exports.id, + EXPORTS_DOMAIN, + &loaded.exports_bytes, + )?, + semantic_input( + "04-lawpack", + ProviderSemanticInputKind::Lawpack, + &format!( + "{}@{}", + loaded.bundle.manifest().id, + loaded.bundle.manifest().version + ), + PROVIDER_LAWPACK_ARTIFACT_DOMAIN, + &loaded.manifest_bytes, + )?, + semantic_input( + "05-source", + ProviderSemanticInputKind::Auxiliary("edict-source".to_owned()), + coordinate, + SOURCE_DOMAIN, + source_bytes, + )?, + semantic_input( + "06-target-configuration", + ProviderSemanticInputKind::Auxiliary("target-configuration".to_owned()), + &configuration.id, + &configuration.id, + &loaded.configuration_bytes, + )?, + ], + result_projection, + ) } fn single_configuration( @@ -1013,6 +1045,26 @@ fn single_configuration( ) } +fn single_result_projection<'a>( + projections: &'a BTreeMap, + failures: &BTreeMap, +) -> Result<(&'a str, &'a ResultProjectionArtifact), ApplicationBuildFailure> { + if !failures.is_empty() { + return Err(failure( + "ResultProjectionUnavailable", + format!("application result projection failed closed: {failures:?}"), + )); + } + let mut projections = projections.iter(); + match (projections.next(), projections.next()) { + (Some((intent, projection)), None) => Ok((intent.as_str(), projection)), + _ => Err(failure( + "ResultProjectionUnavailable", + "the executable-operation build requires exactly one compiler result projection", + )), + } +} + fn single_unique_configuration<'a>( references: impl IntoIterator, ) -> Result<&'a edict_syntax::LawpackResourceRef, ApplicationBuildFailure> { @@ -1057,6 +1109,27 @@ fn semantic_input( }) } +fn with_result_projection_input( + mut inputs: Vec, + projection: &ResultProjectionArtifact, +) -> Result, ApplicationBuildFailure> { + let input = semantic_input( + RESULT_PROJECTION_ROLE, + ProviderSemanticInputKind::Auxiliary("result-projection".to_owned()), + &projection.projection().operation_coordinate, + RESULT_PROJECTION_DIGEST_DOMAIN, + projection.canonical_bytes(), + )?; + if input.artifact.reference.digest.bytes != projection.digest().bytes() { + return Err(failure( + "ResultProjectionDigestMismatch", + "provider input does not preserve the compiler result projection identity", + )); + } + inputs.push(input); + Ok(inputs) +} + fn lowering_contract( core: &ProviderBoundArtifact, target_profile: &ProviderBoundArtifact, @@ -1664,22 +1737,25 @@ fn failure(kind: &'static str, message: impl Into) -> ApplicationBuildFa #[cfg(test)] mod tests { - use std::collections::BTreeSet; + use std::collections::{BTreeMap, BTreeSet}; use std::fs; use std::path::PathBuf; use std::sync::atomic::{AtomicU64, Ordering}; use edict_syntax::{ - LawpackResourceRef, LawpackTargetAdapter, ProviderArtifactKind, ProviderArtifactRef, - ProviderArtifactSource, ProviderSchemaBinding, ProviderSchemaFormat, ResourceRef, - TargetProviderManifest, TARGET_PROVIDER_ABI, TARGET_PROVIDER_MANIFEST_API_VERSION, + compile_to_core, decode_lawpack_adapter, decode_lawpack_bundle, decode_result_projection, + lower_to_target_ir, parse_module, prepare_lawpack_compilation, LawpackResourceRef, + LawpackTargetAdapter, ProviderArtifactKind, ProviderArtifactRef, ProviderArtifactSource, + ProviderSchemaBinding, ProviderSchemaFormat, ResourceRef, ResultProjectionArtifact, + TargetProviderManifest, RESULT_PROJECTION_DIGEST_DOMAIN, TARGET_PROVIDER_ABI, + TARGET_PROVIDER_MANIFEST_API_VERSION, }; use super::{ build_application, canonical_application_root, output_lock_path, provider_schema_artifacts, - read, selected_adapter_reference, single_unique_configuration, - validate_application_manifest, write_outputs, ApplicationLawpack, ApplicationManifest, - ApplicationTarget, + read, selected_adapter_reference, single_result_projection, single_unique_configuration, + validate_application_manifest, with_result_projection_input, write_outputs, + ApplicationLawpack, ApplicationManifest, ApplicationTarget, RESULT_PROJECTION_ROLE, }; const STRESS_SEED: u64 = 0x5eed_1a77_c105_0a11; @@ -1758,6 +1834,75 @@ mod tests { assert_eq!(actual, &configuration); } + #[test] + fn compiler_result_projection_is_bound_into_the_provider_closure() { + let projection = result_projection_artifact(); + let bytes = projection.canonical_bytes().to_vec(); + let input = test_ok( + with_result_projection_input(Vec::new(), &projection), + "bind compiler result projection", + ); + + assert_eq!(input.len(), 1); + assert_eq!(input[0].role, RESULT_PROJECTION_ROLE); + assert_eq!( + input[0].kind, + edict_syntax::ProviderSemanticInputKind::Auxiliary("result-projection".to_owned()) + ); + assert_eq!( + input[0].artifact.reference.coordinate, + "examples.hello_echo@1.createGreeting" + ); + assert_eq!( + input[0].artifact.artifact.domain, + RESULT_PROJECTION_DIGEST_DOMAIN + ); + assert_eq!(input[0].artifact.artifact.bytes, bytes); + } + + #[test] + fn application_build_requires_one_projection_and_no_projection_failures() { + let projection = result_projection_artifact(); + let empty = BTreeMap::new(); + let no_failures = BTreeMap::new(); + assert_eq!( + test_err( + single_result_projection(&empty, &no_failures), + "an application build without a projection must reject", + ) + .kind, + "ResultProjectionUnavailable" + ); + + let multiple = BTreeMap::from([ + ("first".to_owned(), projection.clone()), + ("second".to_owned(), projection.clone()), + ]); + assert_eq!( + test_err( + single_result_projection(&multiple, &no_failures), + "an application build with multiple projections must reject", + ) + .kind, + "ResultProjectionUnavailable" + ); + + let admitted = BTreeMap::from([("createGreeting".to_owned(), projection)]); + let projection_failure = test_err( + decode_result_projection(&[]), + "empty projection bytes must reject", + ); + let failures = BTreeMap::from([("createGreeting".to_owned(), projection_failure)]); + assert_eq!( + test_err( + single_result_projection(&admitted, &failures), + "a recorded projection failure must reject the application build", + ) + .kind, + "ResultProjectionUnavailable" + ); + } + #[test] fn provider_schema_selection_loads_every_bound_role_under_bounded_stress() { let mut manifest = provider_manifest_with_schema_count(32); @@ -1787,6 +1932,38 @@ mod tests { assert_eq!(roles.len(), 32); } + fn result_projection_artifact() -> ResultProjectionArtifact { + let manifest = + include_bytes!("../../../fixtures/lawpack/hello-echo/manifest.cbor").as_slice(); + let exports = + include_bytes!("../../../fixtures/lawpack/hello-echo/exports.cbor").as_slice(); + let adapter = + include_bytes!("../../../fixtures/lawpack/hello-echo/adapter.cbor").as_slice(); + let source = include_str!("../../../fixtures/lawpack/hello-echo/create-greeting.edict"); + let bundle = test_ok( + decode_lawpack_bundle(manifest, exports), + "decode Hello Echo lawpack", + ); + let adapter = test_ok( + decode_lawpack_adapter(&bundle, "echo.dpo@1", adapter), + "decode Hello Echo adapter", + ); + let module = test_ok(parse_module(source), "parse Hello Echo source"); + let preparation = test_ok( + prepare_lawpack_compilation(&module, &bundle, &adapter), + "prepare Hello Echo compilation", + ); + let core = test_ok( + compile_to_core(&module, preparation.compiler_context()), + "compile Hello Echo Core", + ); + let mut report = lower_to_target_ir(&core, preparation.target_ir_facts()); + match report.result_projections.remove("createGreeting") { + Some(projection) => projection, + None => panic!("Hello Echo lowering emits its result projection"), + } + } + #[test] fn failed_pair_publication_preserves_the_previous_package() { let root = temp_tree("pair-publication"); diff --git a/crates/edict-provider-schema/src/contract_pack.rs b/crates/edict-provider-schema/src/contract_pack.rs index c08606a..370480d 100644 --- a/crates/edict-provider-schema/src/contract_pack.rs +++ b/crates/edict-provider-schema/src/contract_pack.rs @@ -12,7 +12,8 @@ use edict_syntax::{ ProviderArtifactSchemaValidationErrorKind, TargetProfileContractResource, TargetProfileContractResourceFailureKind, TargetProfileContractResourceProvenance, AUTHORITY_FACTS_API_VERSION, CORE_MODULE_DIGEST_DOMAIN, PROVIDER_LAWPACK_ARTIFACT_DOMAIN, - TARGET_IR_ARTIFACT_DIGEST_DOMAIN, TARGET_PROFILE_API_VERSION, + RESULT_PROJECTION_CDDL_ROOT, RESULT_PROJECTION_DIGEST_DOMAIN, TARGET_IR_ARTIFACT_DIGEST_DOMAIN, + TARGET_PROFILE_API_VERSION, }; use sha2::{Digest, Sha256}; @@ -32,13 +33,14 @@ const PACK_HEADER: &str = "; SPDX-License-Identifier: Apache-2.0\n\ ; edict-provider-contracts.cddl\n\ ; Generated from Edict-owned ABI fragments. DO NOT EDIT.\n"; -const CONTRACT_BINDINGS: [(&str, &str); 10] = [ +const CONTRACT_BINDINGS: [(&str, &str); 11] = [ ("authority-facts", "authority-facts"), ("core-module", "core-module"), ("lawpack-adapter", "lawpack-adapter"), ("lawpack-exports", "lawpack-exports"), ("lawpack-manifest", "lawpack-manifest"), ("lowering-requirements", "lowering-requirements"), + ("result-projection", RESULT_PROJECTION_CDDL_ROOT), ("target-ir-artifact", TARGET_IR_ARTIFACT_CDDL_ROOT), ("target-profile-intrinsics", "intrinsics-document"), ("target-profile-manifest", "target-profile-manifest"), @@ -48,11 +50,12 @@ const CONTRACT_BINDINGS: [(&str, &str); 10] = [ ), ]; -const DOMAIN_BINDINGS: [(&str, &str); 6] = [ +const DOMAIN_BINDINGS: [(&str, &str); 7] = [ (AUTHORITY_FACTS_API_VERSION, "authority-facts"), (CORE_MODULE_DIGEST_DOMAIN, "core-module"), (PROVIDER_LAWPACK_ARTIFACT_DOMAIN, "lawpack-manifest"), ("edict.lowering-requirements/v1", "lowering-requirements"), + (RESULT_PROJECTION_DIGEST_DOMAIN, RESULT_PROJECTION_CDDL_ROOT), ( TARGET_IR_ARTIFACT_DIGEST_DOMAIN, TARGET_IR_ARTIFACT_CDDL_ROOT, @@ -72,6 +75,7 @@ pub struct ProviderContractPackInput<'a> { pub lawpack_adapter_cddl: &'a [u8], pub target_profile_cddl: &'a [u8], pub authority_facts_cddl: &'a [u8], + pub result_projection_cddl: &'a [u8], pub target_ir_cddl: &'a [u8], pub contract_resources: Vec, } @@ -296,6 +300,7 @@ pub fn assemble_provider_contract_pack( lawpack_adapter_cddl, target_profile_cddl, authority_facts_cddl, + result_projection_cddl, target_ir_cddl, contract_resources, } = input; @@ -306,6 +311,7 @@ pub fn assemble_provider_contract_pack( ("edict-lawpack-adapter.cddl", lawpack_adapter_cddl), ("edict-target-profile.cddl", target_profile_cddl), ("edict-authority-facts.cddl", authority_facts_cddl), + ("edict-result-projection.cddl", result_projection_cddl), ("edict-target-ir.cddl", target_ir_cddl), ]; let mut failures = validate_fragments(&fragments); diff --git a/crates/edict-provider-schema/tests/provider_contract_pack.rs b/crates/edict-provider-schema/tests/provider_contract_pack.rs index de2cec2..45ae88b 100644 --- a/crates/edict-provider-schema/tests/provider_contract_pack.rs +++ b/crates/edict-provider-schema/tests/provider_contract_pack.rs @@ -12,12 +12,13 @@ use edict_provider_schema::{ }; use edict_syntax::{ canonical_target_profile_contract_resources, compile_to_core, decode_canonical_cbor, - digest_target_profile_contract_resource, encode_core_module, encode_target_ir_artifact, - parse_module, CanonicalValue, CompilerContext, CoreBudget, CoreExpr, CoreObstructionReason, - CorePredicate, CoreValue, ProviderArtifactSchemaValidationErrorKind, ResourceRef, - TargetIrArtifact, TargetIrIntent, TargetIrRequireFailure, TargetIrRequirement, - TargetIrSemanticClosure, TargetProfileContractResource, WriteClass, - AUTHORITY_FACTS_API_VERSION, CORE_MODULE_DIGEST_DOMAIN, PROVIDER_LAWPACK_ARTIFACT_DOMAIN, + digest_target_profile_contract_resource, encode_core_module, encode_result_projection, + encode_target_ir_artifact, parse_module, CanonicalValue, CompilerContext, CoreBudget, CoreExpr, + CoreObstructionReason, CorePredicate, CoreValue, ProviderArtifactSchemaValidationErrorKind, + ResourceRef, ResultProjection, ResultProjectionExpr, ResultProjectionSource, TargetIrArtifact, + TargetIrIntent, TargetIrRequireFailure, TargetIrRequirement, TargetIrSemanticClosure, + TargetProfileContractResource, WriteClass, AUTHORITY_FACTS_API_VERSION, + CORE_MODULE_DIGEST_DOMAIN, PROVIDER_LAWPACK_ARTIFACT_DOMAIN, RESULT_PROJECTION_DIGEST_DOMAIN, TARGET_IR_ARTIFACT_DIGEST_DOMAIN, TARGET_PROFILE_API_VERSION, }; use sha2::{Digest, Sha256}; @@ -28,6 +29,8 @@ const LAWPACK_CDDL: &[u8] = include_bytes!("../../../docs/abi/edict-lawpack.cddl const LAWPACK_ADAPTER_CDDL: &[u8] = include_bytes!("../../../docs/abi/edict-lawpack-adapter.cddl"); const TARGET_PROFILE_CDDL: &[u8] = include_bytes!("../../../docs/abi/edict-target-profile.cddl"); const AUTHORITY_FACTS_CDDL: &[u8] = include_bytes!("../../../docs/abi/edict-authority-facts.cddl"); +const RESULT_PROJECTION_CDDL: &[u8] = + include_bytes!("../../../docs/abi/edict-result-projection.cddl"); const TARGET_IR_CDDL: &[u8] = include_bytes!("../../../docs/abi/edict-target-ir.cddl"); const CORE_FIXTURE: &[u8] = include_bytes!("../../../fixtures/core/canonical/bounded-hello.core.cbor"); @@ -40,13 +43,16 @@ const ALTERNATE_TARGET_IR_FIXTURE: &[u8] = include_bytes!("../../../fixtures/target-ir/canonical/gitwarp-append.target-ir.cbor"); const LAWPACK_ADAPTER_FIXTURE: &[u8] = include_bytes!("../../../fixtures/lawpack/hello-echo/adapter.cbor"); +const RESULT_PROJECTION_FIXTURE: &[u8] = + include_bytes!("../../../fixtures/lawpack/hello-echo/create-greeting.result-projection.cbor"); const OPERATION_SOURCE: &str = include_str!("../../../fixtures/lang/operations/explicit-basis-u64.edict"); -const EXPECTED_DOMAIN_BINDINGS: [(&str, &str); 6] = [ +const EXPECTED_DOMAIN_BINDINGS: [(&str, &str); 7] = [ (AUTHORITY_FACTS_API_VERSION, "authority-facts"), (CORE_MODULE_DIGEST_DOMAIN, "core-module"), (PROVIDER_LAWPACK_ARTIFACT_DOMAIN, "lawpack-manifest"), ("edict.lowering-requirements/v1", "lowering-requirements"), + (RESULT_PROJECTION_DIGEST_DOMAIN, "result-projection"), (TARGET_IR_ARTIFACT_DIGEST_DOMAIN, "target-ir-artifact"), (TARGET_PROFILE_API_VERSION, "target-profile-manifest"), ]; @@ -71,8 +77,8 @@ fn contract_pack_is_self_contained_and_repeatable() { PROVIDER_CONTRACT_PACK_COORDINATE ); assert_eq!(forward.manifest().license, PROVIDER_CONTRACT_PACK_LICENSE); - assert_eq!(forward.manifest().contracts.len(), 10); - assert_eq!(forward.manifest().domains.len(), 6); + assert_eq!(forward.manifest().contracts.len(), 11); + assert_eq!(forward.manifest().domains.len(), 7); assert_eq!(forward.manifest().resources.len(), 5); assert!(forward .cddl_bytes() @@ -272,6 +278,97 @@ fn target_ir_root_matches_reference_encoder() { ); } +#[test] +fn result_projection_root_matches_reference_encoder() { + let pack = assemble(canonical_target_profile_contract_resources()); + let projection = + decode_canonical_cbor(RESULT_PROJECTION_FIXTURE).expect("projection fixture is canonical"); + pack.validate_domain(RESULT_PROJECTION_DIGEST_DOMAIN, &projection) + .expect("projection fixture satisfies the compiler-owned root"); + + let mut missing_output_bound = projection; + remove_map_field(&mut missing_output_bound, "maxOutputBytes"); + assert_eq!( + pack.validate_domain(RESULT_PROJECTION_DIGEST_DOMAIN, &missing_output_bound), + Err(ProviderArtifactSchemaValidationErrorKind::SchemaMismatch) + ); +} + +#[test] +fn result_projection_root_enforces_decoder_path_limit() { + let pack = assemble(canonical_target_profile_contract_resources()); + let mut projection = representative_result_projection(); + *projection_path_mut(&mut projection) = array( + (0..edict_syntax::MAX_RESULT_PROJECTION_PATH_SEGMENTS) + .map(|index| text(&format!("segment{index:02}"))) + .collect(), + ); + + pack.validate_domain(RESULT_PROJECTION_DIGEST_DOMAIN, &projection) + .expect("the decoder's exact path-segment limit satisfies the published root"); + + let CanonicalValue::Array(path) = projection_path_mut(&mut projection) else { + panic!("projection path must be an array"); + }; + path.push(text("segment32")); + assert_eq!( + pack.validate_domain(RESULT_PROJECTION_DIGEST_DOMAIN, &projection), + Err(ProviderArtifactSchemaValidationErrorKind::SchemaMismatch), + "the published root must reject one more segment than the decoder permits" + ); +} + +#[test] +fn result_projection_root_enforces_output_and_record_node_limits() { + let pack = assemble(canonical_target_profile_contract_resources()); + + let mut zero_output_bound = representative_result_projection(); + *map_value_mut(&mut zero_output_bound, "maxOutputBytes") = CanonicalValue::Integer(0); + assert_eq!( + pack.validate_domain(RESULT_PROJECTION_DIGEST_DOMAIN, &zero_output_bound), + Err(ProviderArtifactSchemaValidationErrorKind::SchemaMismatch), + "the published root must reject the decoder's zero output bound" + ); + + let mut boundary = representative_result_projection(); + let expression = map_value_mut(&mut boundary, "expression"); + let fields = map_value_mut(expression, "fields"); + *fields = CanonicalValue::Map( + (0..(edict_syntax::MAX_RESULT_PROJECTION_NODES - 1)) + .map(|index| { + ( + text(&format!("field{index:03}")), + map(vec![ + ("kind", text("source")), + ("source", map(vec![("kind", text("applicationInput"))])), + ("path", array(Vec::new())), + ]), + ) + }) + .collect(), + ); + pack.validate_domain(RESULT_PROJECTION_DIGEST_DOMAIN, &boundary) + .expect("the root record plus 255 source nodes satisfies the decoder limit"); + + let fields = map_value_mut(map_value_mut(&mut boundary, "expression"), "fields"); + let CanonicalValue::Map(entries) = fields else { + panic!("projection record fields must be a map"); + }; + entries.push(( + text("field255"), + map(vec![ + ("kind", text("source")), + ("source", map(vec![("kind", text("applicationInput"))])), + ("path", array(Vec::new())), + ]), + )); + assert_eq!( + pack.validate_domain(RESULT_PROJECTION_DIGEST_DOMAIN, &boundary), + Err(ProviderArtifactSchemaValidationErrorKind::SchemaMismatch), + "the published root must reject one more field than the flat record node limit permits" + ); +} + #[test] fn core_root_accepts_reference_encoded_operation_basis() { let context = CompilerContext::new() @@ -518,6 +615,7 @@ fn input_with( lawpack_adapter_cddl: LAWPACK_ADAPTER_CDDL, target_profile_cddl: TARGET_PROFILE_CDDL, authority_facts_cddl: AUTHORITY_FACTS_CDDL, + result_projection_cddl: RESULT_PROJECTION_CDDL, target_ir_cddl, contract_resources: resources, } @@ -571,6 +669,7 @@ fn representative_contract_instances() -> Vec<(&'static str, CanonicalValue)> { ("lawpack-exports", lawpack_exports()), ("lawpack-manifest", lawpack_manifest()), ("lowering-requirements", lowering_requirements()), + ("result-projection", representative_result_projection()), ( "target-ir-artifact", decode_canonical_cbor(TARGET_IR_FIXTURE).expect("Target IR fixture is canonical"), @@ -584,6 +683,26 @@ fn representative_contract_instances() -> Vec<(&'static str, CanonicalValue)> { ] } +fn representative_result_projection() -> CanonicalValue { + let projection = ResultProjection { + api_version: edict_syntax::RESULT_PROJECTION_API_VERSION.to_owned(), + operation_coordinate: "example.application@1.create".to_owned(), + output_type: "example.application@1.Created".to_owned(), + max_output_bytes: 512, + expression: ResultProjectionExpr::Record { + fields: BTreeMap::from([( + "value".to_owned(), + ResultProjectionExpr::Source { + source: ResultProjectionSource::ApplicationInput, + path: vec!["value".to_owned()], + }, + )]), + }, + }; + let bytes = encode_result_projection(&projection).expect("representative projection encodes"); + decode_canonical_cbor(&bytes).expect("representative projection is canonical") +} + fn encoded_target_ir_with_requirements() -> CanonicalValue { encoded_target_ir_with_coordinate("example.target-profile@1") } @@ -809,6 +928,19 @@ fn map_value_mut<'a>(value: &'a mut CanonicalValue, field: &str) -> &'a mut Cano .unwrap_or_else(|| panic!("missing map field {field}")) } +fn projection_path_mut(projection: &mut CanonicalValue) -> &mut CanonicalValue { + let expression = map_value_mut(projection, "expression"); + let fields = map_value_mut(expression, "fields"); + let CanonicalValue::Map(fields) = fields else { + panic!("projection record fields must be a map"); + }; + let source = &mut fields + .first_mut() + .expect("representative projection has one field") + .1; + map_value_mut(source, "path") +} + fn remove_map_field(value: &mut CanonicalValue, field: &str) { let CanonicalValue::Map(entries) = value else { panic!("{field} parent must be a map"); diff --git a/crates/edict-syntax/src/compiler.rs b/crates/edict-syntax/src/compiler.rs index 2938bcf..b8d8feb 100644 --- a/crates/edict-syntax/src/compiler.rs +++ b/crates/edict-syntax/src/compiler.rs @@ -14,7 +14,7 @@ use crate::core_ir::{ parse_core_integer, CompareOp, CoreBlock, CoreBudget, CoreExpr, CoreImport, CoreImportKind, CoreIntent, CoreModule, CoreNode, CoreObstructionArm, CoreObstructionReason, CorePredicate, CoreRequireFailureArm, CoreType, CoreValue, InputConstraint, InputConstraintSource, LocalRef, - ResourceRef, CORE_API_VERSION, + ResourceRef, CORE_API_VERSION, CORE_APPLICATION_INPUT_LOCAL_ID, }; use crate::lowerability::WriteClass; use crate::semantic::validate_surface; @@ -648,7 +648,7 @@ impl<'a> TypeChecker<'a> { let input_shape = self.type_ref_shape(¶m.ty, param.span, None)?; let output_shape = self.type_ref_shape(&source.returns, source.span, None)?; let input_binding = LocalRef { - id: "arg.0".to_owned(), + id: CORE_APPLICATION_INPUT_LOCAL_ID.to_owned(), alpha_name: "$arg0".to_owned(), ty: input_shape.coord.clone(), }; diff --git a/crates/edict-syntax/src/core_ir.rs b/crates/edict-syntax/src/core_ir.rs index dd74ecc..3a458e9 100644 --- a/crates/edict-syntax/src/core_ir.rs +++ b/crates/edict-syntax/src/core_ir.rs @@ -11,6 +11,9 @@ use serde::{Deserialize, Serialize}; /// The Core ABI identifier emitted by this crate. pub const CORE_API_VERSION: &str = "edict.core/v1"; +/// Compiler-owned local identity for the single application intent input. +pub(crate) const CORE_APPLICATION_INPUT_LOCAL_ID: &str = "arg.0"; + /// A lowered in-memory Core module. #[derive(Debug, Clone, PartialEq, Eq)] pub struct CoreModule { diff --git a/crates/edict-syntax/src/lib.rs b/crates/edict-syntax/src/lib.rs index ba3d18b..0d6db55 100644 --- a/crates/edict-syntax/src/lib.rs +++ b/crates/edict-syntax/src/lib.rs @@ -83,6 +83,7 @@ pub mod parser; pub mod provider; pub mod provider_invocation; pub mod provider_lowering; +pub mod result_projection; pub mod semantic; pub mod target_ir; pub mod target_profile; @@ -195,6 +196,15 @@ pub use provider_lowering::{ BuiltinLowererCompatibilityFailureKind, BuiltinLowererRequest, BuiltinLoweringResult, BuiltinTargetLowerer, }; +pub use result_projection::{ + decode_result_projection, digest_result_projection, emit_result_projection, + encode_result_projection, verify_result_projection, ResultProjection, ResultProjectionArtifact, + ResultProjectionExpr, ResultProjectionFailure, ResultProjectionFailureKind, + ResultProjectionSource, VerifiedResultProjection, MAX_RESULT_PROJECTION_ARTIFACT_BYTES, + MAX_RESULT_PROJECTION_NODES, MAX_RESULT_PROJECTION_PATH_SEGMENTS, + MAX_RESULT_PROJECTION_TEXT_BYTES, RESULT_PROJECTION_API_VERSION, RESULT_PROJECTION_CDDL_ROOT, + RESULT_PROJECTION_DIGEST_DOMAIN, +}; pub use semantic::{validate_module, validate_surface, SemanticError, SemanticErrorKind}; pub use target_ir::{ lower_to_target_ir, TargetEffectLowering, TargetIrArtifact, TargetIrIntent, diff --git a/crates/edict-syntax/src/result_projection.rs b/crates/edict-syntax/src/result_projection.rs new file mode 100644 index 0000000..c29b042 --- /dev/null +++ b/crates/edict-syntax/src/result_projection.rs @@ -0,0 +1,1241 @@ +//! Canonical, bounded application-result projections derived from Edict Core. +//! +//! The projection is data. It does not execute application code, call a target, +//! or grant runtime authority. Emitters derive it from exact Core and Target IR; +//! verifiers reconstruct the authored Core result from the claimed projection. + +use std::collections::BTreeMap; +use std::fmt; + +use crate::core_ir::CORE_APPLICATION_INPUT_LOCAL_ID; +use crate::{ + decode_canonical_cbor, digest_canonical_artifact, encode_canonical_cbor, CanonicalValue, + CoreDigest, CoreExpr, CoreIntent, CoreModule, CoreNode, CoreType, LocalRef, TargetIrArtifact, + TargetIrIntent, TargetIrSemanticClosure, TargetIrStep, +}; + +/// Semantic schema identifier for result-projection values. +pub const RESULT_PROJECTION_API_VERSION: &str = "edict.result-projection/v1"; + +/// Domain used to compute result-projection artifact identities. +pub const RESULT_PROJECTION_DIGEST_DOMAIN: &str = "edict.result-projection.artifact/v1"; + +/// CDDL root published for result-projection artifacts. +pub const RESULT_PROJECTION_CDDL_ROOT: &str = "result-projection"; + +/// Maximum expression nodes in one projection artifact. +pub const MAX_RESULT_PROJECTION_NODES: usize = 256; + +/// Maximum field-path segments in one source reference. +pub const MAX_RESULT_PROJECTION_PATH_SEGMENTS: usize = 32; + +/// Maximum UTF-8 bytes in any coordinate, field, step, or path segment. +pub const MAX_RESULT_PROJECTION_TEXT_BYTES: usize = 1_024; + +/// Maximum canonical bytes in one projection artifact. +pub const MAX_RESULT_PROJECTION_ARTIFACT_BYTES: usize = 64 * 1_024; + +/// Maximum recursive type comparisons while validating projection output shape. +const MAX_RESULT_PROJECTION_TYPE_DEPTH: usize = 32; + +/// Untrusted result-projection candidate for one application operation. +/// +/// Callers may construct candidates for encoding and verification. Only +/// [`ResultProjectionArtifact`] binds an accepted candidate to canonical bytes +/// and their domain-framed identity. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ResultProjection { + /// Projection schema identifier. + pub api_version: String, + /// Fully qualified application operation coordinate. + pub operation_coordinate: String, + /// Fully qualified Core output type. + pub output_type: String, + /// Maximum canonical result bytes admitted by the Core budget. + pub max_output_bytes: u64, + /// Closed projection expression derived from the authored Core result. + pub expression: ResultProjectionExpr, +} + +/// Closed projection-expression language. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ResultProjectionExpr { + Record { + fields: BTreeMap, + }, + Source { + source: ResultProjectionSource, + path: Vec, + }, +} + +/// Declared data sources available to a result projection. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ResultProjectionSource { + ApplicationInput, + CapabilityResult { step_id: String }, +} + +/// Emitted canonical bytes and their domain-framed identity. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ResultProjectionArtifact { + projection: ResultProjection, + canonical_bytes: Vec, + digest: CoreDigest, +} + +impl ResultProjectionArtifact { + /// Borrow the validated projection value. + #[must_use] + pub const fn projection(&self) -> &ResultProjection { + &self.projection + } + + /// Borrow the exact canonical bytes bound to the projection. + #[must_use] + pub fn canonical_bytes(&self) -> &[u8] { + &self.canonical_bytes + } + + /// Return the domain-framed artifact identity. + #[must_use] + pub const fn digest(&self) -> CoreDigest { + self.digest + } +} + +/// Projection accepted after independent semantic reconstruction. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct VerifiedResultProjection { + projection: ResultProjection, + digest: CoreDigest, +} + +impl VerifiedResultProjection { + /// Borrow the accepted projection. + #[must_use] + pub const fn projection(&self) -> &ResultProjection { + &self.projection + } + + /// Return the accepted domain-framed identity. + #[must_use] + pub const fn digest(&self) -> CoreDigest { + self.digest + } +} + +/// Stable result-projection rejection categories. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ResultProjectionFailureKind { + MissingIntent, + CoreTargetMismatch, + TargetResultMismatch, + InvalidApplicationInput, + UndeclaredProjectionSource, + UnknownCapabilityStep, + UnsupportedExpression, + OutputShapeMismatch, + InvalidOutputBound, + ProjectionLimitExceeded, + InvalidCanonicalArtifact, + DigestMismatch, +} + +/// Structured result-projection failure. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ResultProjectionFailure { + kind: ResultProjectionFailureKind, + subject: String, +} + +impl ResultProjectionFailure { + /// Return the stable rejection category. + #[must_use] + pub const fn kind(&self) -> ResultProjectionFailureKind { + self.kind + } + + /// Return the rejected semantic subject. + #[must_use] + pub fn subject(&self) -> &str { + &self.subject + } + + fn new(kind: ResultProjectionFailureKind, subject: impl Into) -> Self { + Self { + kind, + subject: subject.into(), + } + } +} + +impl fmt::Display for ResultProjectionFailure { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "{:?}: {}", self.kind, self.subject) + } +} + +impl std::error::Error for ResultProjectionFailure {} + +/// Derive and emit one canonical projection artifact. +/// +/// # Errors +/// +/// Returns a structured failure when Core, Target IR, result sources, output +/// shape, or representation bounds do not admit the closed projection subset. +pub fn emit_result_projection( + core: &CoreModule, + target_ir: &TargetIrArtifact, + intent_name: &str, +) -> Result { + let expected_semantic_closure = + crate::target_ir::semantic_closure_for_core(core).map_err(|error| { + failure( + ResultProjectionFailureKind::CoreTargetMismatch, + format!("{error:?}"), + ) + })?; + let expected_semantic_closure = expected_semantic_closure.as_ref().ok_or_else(|| { + failure( + ResultProjectionFailureKind::CoreTargetMismatch, + format!("{intent_name}.semanticClosure"), + ) + })?; + emit_result_projection_with_closure(core, target_ir, intent_name, expected_semantic_closure) +} + +pub(crate) fn emit_result_projection_with_closure( + core: &CoreModule, + target_ir: &TargetIrArtifact, + intent_name: &str, + expected_semantic_closure: &TargetIrSemanticClosure, +) -> Result { + let semantics = + ProjectionSemantics::new(core, target_ir, intent_name, expected_semantic_closure)?; + let expression = project_core_expression(&semantics, &semantics.core_intent.body.result)?; + validate_output_shape(&semantics, &expression, &semantics.core_intent.output)?; + let projection = ResultProjection { + api_version: RESULT_PROJECTION_API_VERSION.to_owned(), + operation_coordinate: format!("{}.{}", core.coordinate, intent_name), + output_type: semantics.core_intent.output.clone(), + max_output_bytes: semantics + .core_intent + .core_evaluation_budget + .max_output_bytes, + expression, + }; + let canonical_bytes = encode_result_projection(&projection)?; + let digest = digest_canonical_artifact(RESULT_PROJECTION_DIGEST_DOMAIN, &canonical_bytes) + .map_err(|error| { + failure( + ResultProjectionFailureKind::InvalidCanonicalArtifact, + error.to_string(), + ) + })?; + Ok(ResultProjectionArtifact { + projection, + canonical_bytes, + digest, + }) +} + +/// Independently verify claimed projection bytes against exact Core and Target IR. +/// +/// # Errors +/// +/// Returns a structured failure for malformed or non-canonical bytes, digest +/// mismatch, unsupported semantics, or failed reverse reconstruction. +pub fn verify_result_projection( + core: &CoreModule, + target_ir: &TargetIrArtifact, + intent_name: &str, + canonical_bytes: &[u8], + claimed_digest: CoreDigest, +) -> Result { + let projection = decode_result_projection(canonical_bytes)?; + let digest = digest_canonical_artifact(RESULT_PROJECTION_DIGEST_DOMAIN, canonical_bytes) + .map_err(|error| { + failure( + ResultProjectionFailureKind::InvalidCanonicalArtifact, + error.to_string(), + ) + })?; + if digest != claimed_digest { + return Err(failure( + ResultProjectionFailureKind::DigestMismatch, + "claimed projection digest", + )); + } + + let expected_semantic_closure = + crate::target_ir::semantic_closure_for_core(core).map_err(|error| { + failure( + ResultProjectionFailureKind::CoreTargetMismatch, + format!("{error:?}"), + ) + })?; + let expected_semantic_closure = expected_semantic_closure.as_ref().ok_or_else(|| { + failure( + ResultProjectionFailureKind::CoreTargetMismatch, + format!("{intent_name}.semanticClosure"), + ) + })?; + let semantics = + ProjectionSemantics::new(core, target_ir, intent_name, expected_semantic_closure)?; + if projection.api_version != RESULT_PROJECTION_API_VERSION + || projection.operation_coordinate != format!("{}.{}", core.coordinate, intent_name) + || projection.output_type != semantics.core_intent.output + || projection.max_output_bytes + != semantics + .core_intent + .core_evaluation_budget + .max_output_bytes + { + return Err(failure( + ResultProjectionFailureKind::CoreTargetMismatch, + "projection envelope", + )); + } + let reconstructed = reconstruct_core_expression(&semantics, &projection.expression)?; + if reconstructed != semantics.core_intent.body.result + || reconstructed != semantics.target_intent.result + { + return Err(failure( + ResultProjectionFailureKind::TargetResultMismatch, + intent_name, + )); + } + validate_output_shape( + &semantics, + &projection.expression, + &semantics.core_intent.output, + )?; + Ok(VerifiedResultProjection { projection, digest }) +} + +/// Encode one projection using `edict.canonical-cbor/v1`. +/// +/// # Errors +/// +/// Returns a structured failure when the projection violates a representation +/// bound or cannot be represented canonically. +pub fn encode_result_projection( + projection: &ResultProjection, +) -> Result, ResultProjectionFailure> { + validate_projection_bounds(projection)?; + let bytes = encode_canonical_cbor(&projection_value(projection)).map_err(|error| { + failure( + ResultProjectionFailureKind::InvalidCanonicalArtifact, + error.to_string(), + ) + })?; + if bytes.len() > MAX_RESULT_PROJECTION_ARTIFACT_BYTES { + return Err(limit_failure("artifact bytes")); + } + Ok(bytes) +} + +/// Decode and validate one canonical projection value. +/// +/// # Errors +/// +/// Returns a structured failure for malformed, non-canonical, incomplete, +/// unsupported, or unbounded projection bytes. +pub fn decode_result_projection(bytes: &[u8]) -> Result { + if bytes.len() > MAX_RESULT_PROJECTION_ARTIFACT_BYTES { + return Err(limit_failure("artifact bytes")); + } + let value = decode_canonical_cbor(bytes).map_err(|error| { + failure( + ResultProjectionFailureKind::InvalidCanonicalArtifact, + error.to_string(), + ) + })?; + let projection = projection_from_value(value)?; + if encode_result_projection(&projection)? != bytes { + return Err(failure( + ResultProjectionFailureKind::InvalidCanonicalArtifact, + "projection bytes do not reproduce exactly", + )); + } + Ok(projection) +} + +/// Compute the domain-framed identity of one projection. +/// +/// # Errors +/// +/// Returns a structured failure when canonical encoding fails. +pub fn digest_result_projection( + projection: &ResultProjection, +) -> Result { + let bytes = encode_result_projection(projection)?; + digest_canonical_artifact(RESULT_PROJECTION_DIGEST_DOMAIN, &bytes).map_err(|error| { + failure( + ResultProjectionFailureKind::InvalidCanonicalArtifact, + error.to_string(), + ) + }) +} + +struct ProjectionSemantics<'a> { + core: &'a CoreModule, + core_intent: &'a CoreIntent, + target_intent: &'a TargetIrIntent, + input: LocalRef, + capability_by_local: BTreeMap, + capability_by_step: BTreeMap, +} + +impl<'a> ProjectionSemantics<'a> { + fn new( + core: &'a CoreModule, + target_ir: &'a TargetIrArtifact, + intent_name: &str, + expected_semantic_closure: &TargetIrSemanticClosure, + ) -> Result { + let (core_intent, target_intent) = + resolve_projection_intents(core, target_ir, intent_name)?; + validate_projection_basis( + core, + target_ir, + core_intent, + target_intent, + intent_name, + expected_semantic_closure, + )?; + let input = resolve_application_input(core_intent, intent_name)?; + let (capability_by_local, capability_by_step) = + resolve_capability_sources(core_intent, target_intent, intent_name)?; + + Ok(Self { + core, + core_intent, + target_intent, + input, + capability_by_local, + capability_by_step, + }) + } +} + +fn resolve_projection_intents<'a>( + core: &'a CoreModule, + target_ir: &'a TargetIrArtifact, + intent_name: &str, +) -> Result<(&'a CoreIntent, &'a TargetIrIntent), ResultProjectionFailure> { + let core_intent = core.intents.get(intent_name).ok_or_else(|| { + failure( + ResultProjectionFailureKind::MissingIntent, + format!("Core intent {intent_name}"), + ) + })?; + let target_intent = target_ir.intents.get(intent_name).ok_or_else(|| { + failure( + ResultProjectionFailureKind::MissingIntent, + format!("Target IR intent {intent_name}"), + ) + })?; + Ok((core_intent, target_intent)) +} + +fn validate_projection_basis( + core: &CoreModule, + target_ir: &TargetIrArtifact, + core_intent: &CoreIntent, + target_intent: &TargetIrIntent, + intent_name: &str, + expected_semantic_closure: &TargetIrSemanticClosure, +) -> Result<(), ResultProjectionFailure> { + if target_ir.source_core_coordinate != core.coordinate + || core_intent.required_operation_profile != target_intent.operation_profile + || core_intent.core_evaluation_budget != target_intent.core_evaluation_budget + { + return Err(failure( + ResultProjectionFailureKind::CoreTargetMismatch, + intent_name, + )); + } + if target_ir.semantic_closure.as_ref() != Some(expected_semantic_closure) { + return Err(failure( + ResultProjectionFailureKind::CoreTargetMismatch, + format!("{intent_name}.semanticClosure"), + )); + } + if core_intent.body.result != target_intent.result { + return Err(failure( + ResultProjectionFailureKind::TargetResultMismatch, + intent_name, + )); + } + if core_intent.core_evaluation_budget.max_output_bytes == 0 { + return Err(failure( + ResultProjectionFailureKind::InvalidOutputBound, + intent_name, + )); + } + Ok(()) +} + +fn resolve_application_input( + core_intent: &CoreIntent, + intent_name: &str, +) -> Result { + let mut matching_inputs = core_intent.body.locals.iter().filter(|local| { + local.id == CORE_APPLICATION_INPUT_LOCAL_ID && local.ty == core_intent.input + }); + let input = matching_inputs.next().cloned().ok_or_else(|| { + failure( + ResultProjectionFailureKind::InvalidApplicationInput, + intent_name, + ) + })?; + if matching_inputs.next().is_some() { + return Err(failure( + ResultProjectionFailureKind::InvalidApplicationInput, + intent_name, + )); + } + Ok(input) +} + +type CapabilityByLocal = BTreeMap; +type CapabilityByStep = BTreeMap; + +fn resolve_capability_sources( + core_intent: &CoreIntent, + target_intent: &TargetIrIntent, + intent_name: &str, +) -> Result<(CapabilityByLocal, CapabilityByStep), ResultProjectionFailure> { + let core_effects = core_intent + .body + .nodes + .iter() + .filter_map(|node| match node { + CoreNode::Effect { + binding, + effect, + input, + .. + } => Some((binding, effect, input)), + CoreNode::Let { .. } | CoreNode::Require { .. } => None, + }) + .collect::>(); + if core_effects.len() != target_intent.steps.len() { + return Err(failure( + ResultProjectionFailureKind::CoreTargetMismatch, + format!("{intent_name}.steps"), + )); + } + + let mut capability_by_local = BTreeMap::new(); + let mut capability_by_step = BTreeMap::new(); + for step in &target_intent.steps { + validate_step_matches_core(step, &core_effects, intent_name)?; + if capability_by_local + .insert( + step.binding.id.clone(), + (step.id.clone(), step.binding.clone()), + ) + .is_some() + || capability_by_step + .insert(step.id.clone(), step.binding.clone()) + .is_some() + { + return Err(failure( + ResultProjectionFailureKind::CoreTargetMismatch, + format!("{intent_name}.steps"), + )); + } + } + Ok((capability_by_local, capability_by_step)) +} + +fn validate_step_matches_core( + step: &TargetIrStep, + core_effects: &[(&LocalRef, &String, &CoreExpr)], + intent_name: &str, +) -> Result<(), ResultProjectionFailure> { + let matches = core_effects + .iter() + .filter(|(binding, effect, input)| { + **binding == step.binding && **effect == step.effect && **input == step.input + }) + .count(); + if matches == 1 { + Ok(()) + } else { + Err(failure( + ResultProjectionFailureKind::CoreTargetMismatch, + format!("{intent_name}.{}", step.id), + )) + } +} + +fn project_core_expression( + semantics: &ProjectionSemantics<'_>, + expression: &CoreExpr, +) -> Result { + match expression { + CoreExpr::Record { fields } => Ok(ResultProjectionExpr::Record { + fields: fields + .iter() + .map(|(name, field)| Ok((name.clone(), project_core_expression(semantics, field)?))) + .collect::, ResultProjectionFailure>>()?, + }), + CoreExpr::Local { .. } | CoreExpr::Field { .. } => { + let (reference, path) = source_path(expression)?; + let source = if reference == &semantics.input { + ResultProjectionSource::ApplicationInput + } else if let Some((step_id, binding)) = + semantics.capability_by_local.get(&reference.id) + { + if binding != reference { + return Err(failure( + ResultProjectionFailureKind::UndeclaredProjectionSource, + &reference.id, + )); + } + ResultProjectionSource::CapabilityResult { + step_id: step_id.clone(), + } + } else { + return Err(failure( + ResultProjectionFailureKind::UndeclaredProjectionSource, + &reference.id, + )); + }; + Ok(ResultProjectionExpr::Source { source, path }) + } + CoreExpr::Const(_) | CoreExpr::Call { .. } => Err(failure( + ResultProjectionFailureKind::UnsupportedExpression, + "result expression", + )), + } +} + +fn source_path(expression: &CoreExpr) -> Result<(&LocalRef, Vec), ResultProjectionFailure> { + let mut current = expression; + let mut path = Vec::new(); + while let CoreExpr::Field { base, field } = current { + path.push(field.clone()); + current = base; + } + path.reverse(); + match current { + CoreExpr::Local { reference } => Ok((reference, path)), + CoreExpr::Const(_) | CoreExpr::Record { .. } | CoreExpr::Call { .. } => Err(failure( + ResultProjectionFailureKind::UnsupportedExpression, + "projection source", + )), + CoreExpr::Field { .. } => unreachable!("field roots are consumed by the loop"), + } +} + +fn reconstruct_core_expression( + semantics: &ProjectionSemantics<'_>, + expression: &ResultProjectionExpr, +) -> Result { + match expression { + ResultProjectionExpr::Record { fields } => Ok(CoreExpr::Record { + fields: fields + .iter() + .map(|(name, field)| { + Ok((name.clone(), reconstruct_core_expression(semantics, field)?)) + }) + .collect::, ResultProjectionFailure>>()?, + }), + ResultProjectionExpr::Source { source, path } => { + let reference = match source { + ResultProjectionSource::ApplicationInput => semantics.input.clone(), + ResultProjectionSource::CapabilityResult { step_id } => semantics + .capability_by_step + .get(step_id) + .cloned() + .ok_or_else(|| { + failure(ResultProjectionFailureKind::UnknownCapabilityStep, step_id) + })?, + }; + let mut expression = CoreExpr::Local { reference }; + for field in path { + expression = CoreExpr::Field { + base: Box::new(expression), + field: field.clone(), + }; + } + Ok(expression) + } + } +} + +fn validate_output_shape( + semantics: &ProjectionSemantics<'_>, + expression: &ResultProjectionExpr, + expected_type: &str, +) -> Result<(), ResultProjectionFailure> { + match expression { + ResultProjectionExpr::Record { fields } => { + let CoreType::Record { + fields: expected_fields, + } = resolve_type(semantics.core, expected_type).ok_or_else(|| { + failure( + ResultProjectionFailureKind::OutputShapeMismatch, + expected_type, + ) + })? + else { + return Err(failure( + ResultProjectionFailureKind::OutputShapeMismatch, + expected_type, + )); + }; + if fields.keys().ne(expected_fields.keys()) { + return Err(failure( + ResultProjectionFailureKind::OutputShapeMismatch, + expected_type, + )); + } + for (field, expected_field_type) in expected_fields { + validate_output_shape( + semantics, + fields.get(field).expect("equal field keys"), + expected_field_type, + )?; + } + Ok(()) + } + ResultProjectionExpr::Source { source, path } => { + let source_type = source_type(semantics, source, path)?; + if types_are_compatible(semantics.core, source_type, expected_type, 0) { + Ok(()) + } else { + Err(failure( + ResultProjectionFailureKind::OutputShapeMismatch, + expected_type, + )) + } + } + } +} + +fn source_type<'a>( + semantics: &'a ProjectionSemantics<'_>, + source: &ResultProjectionSource, + path: &[String], +) -> Result<&'a str, ResultProjectionFailure> { + let mut coordinate = match source { + ResultProjectionSource::ApplicationInput => semantics.input.ty.as_str(), + ResultProjectionSource::CapabilityResult { step_id } => semantics + .capability_by_step + .get(step_id) + .map(|binding| binding.ty.as_str()) + .ok_or_else(|| failure(ResultProjectionFailureKind::UnknownCapabilityStep, step_id))?, + }; + for field in path { + let CoreType::Record { fields } = resolve_type(semantics.core, coordinate) + .ok_or_else(|| failure(ResultProjectionFailureKind::OutputShapeMismatch, coordinate))? + else { + return Err(failure( + ResultProjectionFailureKind::OutputShapeMismatch, + coordinate, + )); + }; + coordinate = fields.get(field).map(String::as_str).ok_or_else(|| { + failure( + ResultProjectionFailureKind::OutputShapeMismatch, + format!("{coordinate}.{field}"), + ) + })?; + } + Ok(coordinate) +} + +fn resolve_type<'a>(core: &'a CoreModule, coordinate: &str) -> Option<&'a CoreType> { + core.types.get(coordinate).or_else(|| { + coordinate + .strip_prefix(core.coordinate.as_str()) + .and_then(|relative| relative.strip_prefix('.')) + .and_then(|relative| core.types.get(relative)) + }) +} + +fn types_are_compatible(core: &CoreModule, left: &str, right: &str, depth: usize) -> bool { + if depth > MAX_RESULT_PROJECTION_TYPE_DEPTH { + return false; + } + let (Some(left), Some(right)) = (resolve_type(core, left), resolve_type(core, right)) else { + return false; + }; + match (left, right) { + (CoreType::Bool, CoreType::Bool) => true, + (CoreType::Int { width: left }, CoreType::Int { width: right }) => left == right, + ( + CoreType::String { + max: left_max, + canonical: left_canonical, + }, + CoreType::String { + max: right_max, + canonical: right_canonical, + }, + ) => left_max == right_max && left_canonical == right_canonical, + (CoreType::Bytes { max: left }, CoreType::Bytes { max: right }) => left == right, + ( + CoreType::Record { + fields: left_fields, + }, + CoreType::Record { + fields: right_fields, + }, + ) => { + left_fields.keys().eq(right_fields.keys()) + && left_fields.iter().all(|(field, left_type)| { + types_are_compatible( + core, + left_type, + right_fields.get(field).expect("equal field keys"), + depth + 1, + ) + }) + } + (CoreType::Variant { cases: left }, CoreType::Variant { cases: right }) => { + left.keys().eq(right.keys()) + && left.iter().all(|(case, left_payload)| { + match ( + left_payload, + right.get(case).expect("equal variant case keys"), + ) { + (None, None) => true, + (Some(left), Some(right)) => { + types_are_compatible(core, left, right, depth + 1) + } + (None, Some(_)) | (Some(_), None) => false, + } + }) + } + (CoreType::Option { item: left }, CoreType::Option { item: right }) + | (CoreType::CapabilityRef { item: left }, CoreType::CapabilityRef { item: right }) => { + types_are_compatible(core, left, right, depth + 1) + } + ( + CoreType::List { + item: left, + max: left_max, + }, + CoreType::List { + item: right, + max: right_max, + }, + ) => left_max == right_max && types_are_compatible(core, left, right, depth + 1), + ( + CoreType::Map { + key: left_key, + value: left_value, + max: left_max, + }, + CoreType::Map { + key: right_key, + value: right_value, + max: right_max, + }, + ) => { + left_max == right_max + && types_are_compatible(core, left_key, right_key, depth + 1) + && types_are_compatible(core, left_value, right_value, depth + 1) + } + _ => false, + } +} + +fn validate_projection_bounds( + projection: &ResultProjection, +) -> Result<(), ResultProjectionFailure> { + if projection.api_version != RESULT_PROJECTION_API_VERSION { + return Err(failure( + ResultProjectionFailureKind::InvalidCanonicalArtifact, + "projection schema", + )); + } + validate_text(&projection.operation_coordinate, "operation coordinate")?; + validate_text(&projection.output_type, "output type")?; + if projection.max_output_bytes == 0 { + return Err(failure( + ResultProjectionFailureKind::InvalidOutputBound, + "max output bytes", + )); + } + let nodes = validate_expression_bounds(&projection.expression)?; + if nodes > MAX_RESULT_PROJECTION_NODES { + return Err(limit_failure("expression nodes")); + } + Ok(()) +} + +fn validate_expression_bounds( + expression: &ResultProjectionExpr, +) -> Result { + match expression { + ResultProjectionExpr::Record { fields } => { + let mut nodes = 1usize; + for (field, value) in fields { + validate_text(field, "record field")?; + nodes = nodes + .checked_add(validate_expression_bounds(value)?) + .ok_or_else(|| limit_failure("expression nodes"))?; + if nodes > MAX_RESULT_PROJECTION_NODES { + return Err(limit_failure("expression nodes")); + } + } + Ok(nodes) + } + ResultProjectionExpr::Source { source, path } => { + if let ResultProjectionSource::CapabilityResult { step_id } = source { + validate_text(step_id, "capability step")?; + } + if path.len() > MAX_RESULT_PROJECTION_PATH_SEGMENTS { + return Err(limit_failure("source path")); + } + for field in path { + validate_text(field, "source path field")?; + } + Ok(1) + } + } +} + +fn validate_text(value: &str, subject: &str) -> Result<(), ResultProjectionFailure> { + if value.is_empty() { + return Err(failure( + ResultProjectionFailureKind::InvalidCanonicalArtifact, + subject, + )); + } + if value.len() > MAX_RESULT_PROJECTION_TEXT_BYTES { + return Err(limit_failure(subject)); + } + Ok(()) +} + +fn projection_value(projection: &ResultProjection) -> CanonicalValue { + map([ + ("schema", text(&projection.api_version)), + ( + "operationCoordinate", + text(&projection.operation_coordinate), + ), + ("outputType", text(&projection.output_type)), + ( + "maxOutputBytes", + CanonicalValue::Integer(i128::from(projection.max_output_bytes)), + ), + ("expression", expression_value(&projection.expression)), + ]) +} + +fn expression_value(expression: &ResultProjectionExpr) -> CanonicalValue { + match expression { + ResultProjectionExpr::Record { fields } => map([ + ("kind", text("record")), + ( + "fields", + CanonicalValue::Map( + fields + .iter() + .map(|(field, value)| (text(field), expression_value(value))) + .collect(), + ), + ), + ]), + ResultProjectionExpr::Source { source, path } => map([ + ("kind", text("source")), + ("source", source_value(source)), + ( + "path", + CanonicalValue::Array(path.iter().map(|field| text(field)).collect()), + ), + ]), + } +} + +fn source_value(source: &ResultProjectionSource) -> CanonicalValue { + match source { + ResultProjectionSource::ApplicationInput => map([("kind", text("applicationInput"))]), + ResultProjectionSource::CapabilityResult { step_id } => map([ + ("kind", text("capabilityResult")), + ("stepId", text(step_id)), + ]), + } +} + +fn projection_from_value( + value: CanonicalValue, +) -> Result { + let mut fields = exact_map( + value, + &[ + "schema", + "operationCoordinate", + "outputType", + "maxOutputBytes", + "expression", + ], + "result projection", + )?; + let api_version = take_text(&mut fields, "schema", "result projection")?; + let operation_coordinate = take_text(&mut fields, "operationCoordinate", "result projection")?; + let output_type = take_text(&mut fields, "outputType", "result projection")?; + let max_output_bytes = take_u64(&mut fields, "maxOutputBytes", "result projection")?; + let expression = expression_from_value( + fields.remove("expression").expect("exact projection field"), + MAX_RESULT_PROJECTION_NODES, + )?; + Ok(ResultProjection { + api_version, + operation_coordinate, + output_type, + max_output_bytes, + expression, + }) +} + +fn expression_from_value( + value: CanonicalValue, + remaining_depth: usize, +) -> Result { + if remaining_depth == 0 { + return Err(limit_failure("expression depth")); + } + let kind = map_text_field(&value, "kind", "projection expression")?; + match kind.as_str() { + "record" => { + let mut fields = exact_map(value, &["kind", "fields"], "record expression")?; + require_text(&mut fields, "kind", "record", "record expression")?; + let CanonicalValue::Map(entries) = fields.remove("fields").expect("exact record field") + else { + return Err(invalid_artifact("record expression fields")); + }; + let mut projected = BTreeMap::new(); + for (field, value) in entries { + let CanonicalValue::Text(field) = field else { + return Err(invalid_artifact("record expression field name")); + }; + if projected + .insert(field, expression_from_value(value, remaining_depth - 1)?) + .is_some() + { + return Err(invalid_artifact("duplicate record expression field")); + } + } + Ok(ResultProjectionExpr::Record { fields: projected }) + } + "source" => { + let mut fields = exact_map(value, &["kind", "source", "path"], "source expression")?; + require_text(&mut fields, "kind", "source", "source expression")?; + let source = source_from_value(fields.remove("source").expect("exact source field"))?; + let CanonicalValue::Array(path) = + fields.remove("path").expect("exact source path field") + else { + return Err(invalid_artifact("source expression path")); + }; + let path = path + .into_iter() + .map(|field| match field { + CanonicalValue::Text(field) => Ok(field), + _ => Err(invalid_artifact("source expression path field")), + }) + .collect::, _>>()?; + Ok(ResultProjectionExpr::Source { source, path }) + } + _ => Err(invalid_artifact("projection expression kind")), + } +} + +fn source_from_value( + value: CanonicalValue, +) -> Result { + let kind = map_text_field(&value, "kind", "projection source")?; + match kind.as_str() { + "applicationInput" => { + let mut fields = exact_map(value, &["kind"], "application input source")?; + require_text( + &mut fields, + "kind", + "applicationInput", + "application input source", + )?; + Ok(ResultProjectionSource::ApplicationInput) + } + "capabilityResult" => { + let mut fields = exact_map(value, &["kind", "stepId"], "capability result source")?; + require_text( + &mut fields, + "kind", + "capabilityResult", + "capability result source", + )?; + Ok(ResultProjectionSource::CapabilityResult { + step_id: take_text(&mut fields, "stepId", "capability result source")?, + }) + } + _ => Err(invalid_artifact("projection source kind")), + } +} + +fn exact_map( + value: CanonicalValue, + expected: &[&str], + subject: &str, +) -> Result, ResultProjectionFailure> { + let CanonicalValue::Map(entries) = value else { + return Err(invalid_artifact(subject)); + }; + let mut fields = BTreeMap::new(); + for (key, value) in entries { + let CanonicalValue::Text(key) = key else { + return Err(invalid_artifact(subject)); + }; + if fields.insert(key, value).is_some() { + return Err(invalid_artifact(subject)); + } + } + if fields.len() != expected.len() || expected.iter().any(|field| !fields.contains_key(*field)) { + return Err(invalid_artifact(subject)); + } + Ok(fields) +} + +fn map_text_field( + value: &CanonicalValue, + field: &str, + subject: &str, +) -> Result { + let CanonicalValue::Map(entries) = value else { + return Err(invalid_artifact(subject)); + }; + entries + .iter() + .find_map(|(key, value)| match (key, value) { + (CanonicalValue::Text(key), CanonicalValue::Text(value)) if key == field => { + Some(value.clone()) + } + _ => None, + }) + .ok_or_else(|| invalid_artifact(subject)) +} + +fn take_text( + fields: &mut BTreeMap, + field: &str, + subject: &str, +) -> Result { + match fields.remove(field) { + Some(CanonicalValue::Text(value)) => Ok(value), + _ => Err(invalid_artifact(subject)), + } +} + +fn take_u64( + fields: &mut BTreeMap, + field: &str, + subject: &str, +) -> Result { + match fields.remove(field) { + Some(CanonicalValue::Integer(value)) => { + u64::try_from(value).map_err(|_| invalid_artifact(subject)) + } + _ => Err(invalid_artifact(subject)), + } +} + +fn require_text( + fields: &mut BTreeMap, + field: &str, + expected: &str, + subject: &str, +) -> Result<(), ResultProjectionFailure> { + if take_text(fields, field, subject)? == expected { + Ok(()) + } else { + Err(invalid_artifact(subject)) + } +} + +fn map(entries: [(&str, CanonicalValue); N]) -> CanonicalValue { + CanonicalValue::Map( + entries + .into_iter() + .map(|(key, value)| (text(key), value)) + .collect(), + ) +} + +fn text(value: &str) -> CanonicalValue { + CanonicalValue::Text(value.to_owned()) +} + +fn invalid_artifact(subject: impl Into) -> ResultProjectionFailure { + failure( + ResultProjectionFailureKind::InvalidCanonicalArtifact, + subject, + ) +} + +fn limit_failure(subject: impl Into) -> ResultProjectionFailure { + failure( + ResultProjectionFailureKind::ProjectionLimitExceeded, + subject, + ) +} + +fn failure( + kind: ResultProjectionFailureKind, + subject: impl Into, +) -> ResultProjectionFailure { + ResultProjectionFailure::new(kind, subject) +} + +#[cfg(test)] +mod tests { + use super::{ + expression_from_value, map, text, CanonicalValue, ResultProjectionFailureKind, + MAX_RESULT_PROJECTION_NODES, + }; + + fn nested_record_expression(record_count: usize) -> CanonicalValue { + let mut expression = map([ + ("kind", text("source")), + ("source", map([("kind", text("applicationInput"))])), + ("path", CanonicalValue::Array(Vec::new())), + ]); + for index in 0..record_count { + expression = map([ + ("kind", text("record")), + ( + "fields", + CanonicalValue::Map(vec![(text(&format!("field{index:03}")), expression)]), + ), + ]); + } + expression + } + + #[test] + fn expression_parser_refuses_recursion_beyond_the_node_budget() { + expression_from_value( + nested_record_expression(MAX_RESULT_PROJECTION_NODES - 1), + MAX_RESULT_PROJECTION_NODES, + ) + .expect("the exact expression-node boundary parses"); + + let failure = expression_from_value( + nested_record_expression(MAX_RESULT_PROJECTION_NODES), + MAX_RESULT_PROJECTION_NODES, + ) + .expect_err("one recursive expression node beyond the budget must reject"); + assert_eq!( + failure.kind(), + ResultProjectionFailureKind::ProjectionLimitExceeded + ); + } +} diff --git a/crates/edict-syntax/src/target_ir.rs b/crates/edict-syntax/src/target_ir.rs index 417aff8..65e59fd 100644 --- a/crates/edict-syntax/src/target_ir.rs +++ b/crates/edict-syntax/src/target_ir.rs @@ -13,6 +13,8 @@ use crate::core_ir::{ }; use crate::digest_core_module; use crate::lowerability::{LowerabilityEffectStatus, LowerabilityReport, LowerabilityStatus}; +use crate::result_projection::emit_result_projection_with_closure; +use crate::{ResultProjectionArtifact, ResultProjectionFailure}; pub const ECHO_DPO_TARGET_PROFILE: &str = "echo.dpo@1"; pub const ECHO_SPAN_IR_DOMAIN: &str = "echo.span-ir/v1"; @@ -171,8 +173,17 @@ pub struct TargetLoweringFailure { #[derive(Debug, Clone, PartialEq, Eq)] pub struct TargetLoweringReport { + /// Overall Target IR lowering status. pub status: TargetLoweringStatus, + /// Target IR artifact when general target lowering succeeds. pub artifact: Option, + /// Compiler-owned result projection per supported intent. This map may be + /// empty even when `status` is `Lowered`; callers own admission cardinality. + pub result_projections: BTreeMap, + /// Per-intent result-projection refusals. This map may be non-empty while + /// `status` is `Lowered` and general `failures` remains empty. + pub result_projection_failures: BTreeMap, + /// General target-lowering failures. pub failures: Vec, } @@ -278,15 +289,24 @@ pub fn lower_to_target_ir( } if failures.is_empty() { + let artifact = TargetIrArtifact { + domain: facts.target_ir_domain.clone(), + target_profile: facts.target_profile.clone(), + source_core_coordinate: core.coordinate.clone(), + semantic_closure, + intents, + }; + let (result_projections, result_projection_failures) = + if let Some(expected_semantic_closure) = artifact.semantic_closure.as_ref() { + lower_result_projections(core, &artifact, expected_semantic_closure) + } else { + (BTreeMap::new(), BTreeMap::new()) + }; TargetLoweringReport { status: TargetLoweringStatus::Lowered, - artifact: Some(TargetIrArtifact { - domain: facts.target_ir_domain.clone(), - target_profile: facts.target_profile.clone(), - source_core_coordinate: core.coordinate.clone(), - semantic_closure, - intents, - }), + artifact: Some(artifact), + result_projections, + result_projection_failures, failures, } } else { @@ -294,6 +314,34 @@ pub fn lower_to_target_ir( } } +fn lower_result_projections( + core: &CoreModule, + target_ir: &TargetIrArtifact, + expected_semantic_closure: &TargetIrSemanticClosure, +) -> ( + BTreeMap, + BTreeMap, +) { + let mut projections = BTreeMap::new(); + let mut failures = BTreeMap::new(); + for intent_name in target_ir.intents.keys() { + match emit_result_projection_with_closure( + core, + target_ir, + intent_name, + expected_semantic_closure, + ) { + Ok(projection) => { + projections.insert(intent_name.clone(), projection); + } + Err(failure) => { + failures.insert(intent_name.clone(), failure); + } + } + } + (projections, failures) +} + pub(crate) fn semantic_closure_for_core( core: &CoreModule, ) -> Result, TargetLoweringFailure> { @@ -759,6 +807,8 @@ fn unsupported(failures: Vec) -> TargetLoweringReport { TargetLoweringReport { status: TargetLoweringStatus::Unsupported, artifact: None, + result_projections: BTreeMap::new(), + result_projection_failures: BTreeMap::new(), failures, } } diff --git a/crates/edict-syntax/tests/result_projection.rs b/crates/edict-syntax/tests/result_projection.rs new file mode 100644 index 0000000..bf9f15c --- /dev/null +++ b/crates/edict-syntax/tests/result_projection.rs @@ -0,0 +1,619 @@ +//! Compiler-owned result-projection contract and independent verification. + +use std::collections::BTreeMap; + +use edict_syntax::{ + decode_canonical_cbor, decode_lawpack_adapter, decode_lawpack_bundle, decode_result_projection, + digest_core_module, digest_result_projection, emit_result_projection, encode_canonical_cbor, + encode_result_projection, lower_to_target_ir, parse_module, prepare_lawpack_compilation, + verify_result_projection, CanonicalValue, CoreExpr, CoreModule, LocalRef, ResultProjection, + ResultProjectionExpr, ResultProjectionFailureKind, ResultProjectionSource, TargetIrArtifact, + TargetIrLoweringFacts, TargetLoweringReport, TargetLoweringStatus, + MAX_RESULT_PROJECTION_ARTIFACT_BYTES, MAX_RESULT_PROJECTION_NODES, + MAX_RESULT_PROJECTION_PATH_SEGMENTS, MAX_RESULT_PROJECTION_TEXT_BYTES, + RESULT_PROJECTION_API_VERSION, +}; + +const MANIFEST_BYTES: &[u8] = include_bytes!("../../../fixtures/lawpack/hello-echo/manifest.cbor"); +const EXPORTS_BYTES: &[u8] = include_bytes!("../../../fixtures/lawpack/hello-echo/exports.cbor"); +const ADAPTER_BYTES: &[u8] = include_bytes!("../../../fixtures/lawpack/hello-echo/adapter.cbor"); +const SOURCE: &str = include_str!("../../../fixtures/lawpack/hello-echo/create-greeting.edict"); +const PROJECTION_BYTES: &[u8] = + include_bytes!("../../../fixtures/lawpack/hello-echo/create-greeting.result-projection.cbor"); +const PROJECTION_DIGEST: &str = + include_str!("../../../fixtures/lawpack/hello-echo/create-greeting.result-projection.sha256"); +const PROPERTY_SEED: u64 = 0x1730_5eed_cafe_babe; + +fn hello_echo_core_and_facts() -> (CoreModule, TargetIrLoweringFacts) { + let bundle = + decode_lawpack_bundle(MANIFEST_BYTES, EXPORTS_BYTES).expect("load Hello Echo lawpack"); + let adapter = decode_lawpack_adapter(&bundle, "echo.dpo@1", ADAPTER_BYTES) + .expect("load Hello Echo adapter"); + let module = parse_module(SOURCE).expect("parse Hello Echo source"); + let preparation = prepare_lawpack_compilation(&module, &bundle, &adapter) + .expect("prepare Hello Echo compilation"); + let core = edict_syntax::compile_to_core(&module, preparation.compiler_context()) + .expect("compile Hello Echo Core"); + (core, preparation.target_ir_facts().clone()) +} + +fn hello_echo_lowering() -> (CoreModule, TargetLoweringReport) { + let (core, facts) = hello_echo_core_and_facts(); + let target = lower_to_target_ir(&core, &facts); + assert_eq!(target.status, TargetLoweringStatus::Lowered); + (core, target) +} + +fn hello_echo() -> (CoreModule, TargetIrArtifact) { + let (core, target) = hello_echo_lowering(); + (core, target.artifact.expect("lower Hello Echo Target IR")) +} + +fn expected_expression() -> ResultProjectionExpr { + ResultProjectionExpr::Record { + fields: BTreeMap::from([ + ( + "key".to_owned(), + ResultProjectionExpr::Source { + source: ResultProjectionSource::CapabilityResult { + step_id: "createGreeting.step.0".to_owned(), + }, + path: vec!["key".to_owned()], + }, + ), + ( + "message".to_owned(), + ResultProjectionExpr::Source { + source: ResultProjectionSource::ApplicationInput, + path: vec!["message".to_owned()], + }, + ), + ]), + } +} + +fn projection_with_fields(count: usize) -> ResultProjection { + let fields = (0..count) + .map(|index| { + ( + format!("field{index:04}"), + ResultProjectionExpr::Source { + source: ResultProjectionSource::ApplicationInput, + path: vec![format!("source{index:04}")], + }, + ) + }) + .collect(); + ResultProjection { + api_version: RESULT_PROJECTION_API_VERSION.to_owned(), + operation_coordinate: "examples.boundary@1.project".to_owned(), + output_type: "examples.boundary@1.Output".to_owned(), + max_output_bytes: 65_536, + expression: ResultProjectionExpr::Record { fields }, + } +} + +fn mutate_results(core: &mut CoreModule, target: &mut TargetIrArtifact, expression: CoreExpr) { + core.intents + .get_mut("createGreeting") + .expect("Core intent") + .body + .result = expression.clone(); + target + .intents + .get_mut("createGreeting") + .expect("Target IR intent") + .result = expression; + repin_target_core(core, target); +} + +fn repin_target_core(core: &CoreModule, target: &mut TargetIrArtifact) { + target + .semantic_closure + .as_mut() + .expect("semantic closure") + .source_core + .digest = Some( + digest_core_module(core) + .expect("digest mutated Core") + .to_review_string(), + ); +} + +fn remove_canonical_field(value: &mut CanonicalValue, field: &str) { + let CanonicalValue::Map(entries) = value else { + panic!("canonical projection root is a map"); + }; + entries.retain(|(key, _)| key != &CanonicalValue::Text(field.to_owned())); +} + +fn canonical_field_mut<'a>(value: &'a mut CanonicalValue, field: &str) -> &'a mut CanonicalValue { + let CanonicalValue::Map(entries) = value else { + panic!("canonical projection root is a map"); + }; + entries + .iter_mut() + .find_map(|(key, value)| (key == &CanonicalValue::Text(field.to_owned())).then_some(value)) + .unwrap_or_else(|| panic!("missing canonical field {field}")) +} + +#[test] +fn exact_core_and_target_ir_emit_the_typed_hello_echo_projection() { + let (core, target) = hello_echo(); + let artifact = emit_result_projection(&core, &target, "createGreeting") + .expect("emit exact result projection"); + + assert_eq!( + artifact.projection().api_version, + RESULT_PROJECTION_API_VERSION + ); + assert_eq!( + artifact.projection().operation_coordinate, + "examples.hello_echo@1.createGreeting" + ); + assert_eq!( + artifact.projection().output_type, + "examples.hello_echo@1.GreetingCreated" + ); + assert_eq!(artifact.projection().max_output_bytes, 512); + assert_eq!(artifact.projection().expression, expected_expression()); + assert_eq!( + decode_result_projection(artifact.canonical_bytes()).expect("decode emitted projection"), + *artifact.projection() + ); + assert_eq!( + digest_result_projection(artifact.projection()).expect("digest projection"), + artifact.digest() + ); + assert_eq!(artifact.canonical_bytes(), PROJECTION_BYTES); + assert_eq!( + artifact.digest().to_review_string(), + PROJECTION_DIGEST.trim() + ); +} + +#[test] +fn echo_target_lowering_emits_the_verified_result_projection() { + let (core, report) = hello_echo_lowering(); + let artifact = report.artifact.as_ref().expect("lowered Target IR"); + let projection = report + .result_projections + .get("createGreeting") + .expect("target lowerer emits the application result projection"); + + assert_eq!(projection.canonical_bytes(), PROJECTION_BYTES); + assert_eq!( + projection.digest().to_review_string(), + PROJECTION_DIGEST.trim() + ); + verify_result_projection( + &core, + artifact, + "createGreeting", + projection.canonical_bytes(), + projection.digest(), + ) + .expect("independent verifier reconstructs the lowerer output"); +} + +#[test] +fn target_lowering_exposes_an_unsupported_result_projection_without_claiming_one() { + let (mut core, facts) = hello_echo_core_and_facts(); + core.intents + .get_mut("createGreeting") + .expect("Core intent") + .body + .result = CoreExpr::Call { + callee: "examples.hidden@1.callback".to_owned(), + type_args: Vec::new(), + args: Vec::new(), + }; + + let report = lower_to_target_ir(&core, &facts); + + assert_eq!(report.status, TargetLoweringStatus::Lowered); + assert!(report.artifact.is_some()); + assert!(report.result_projections.is_empty()); + assert!(report.failures.is_empty()); + assert_eq!( + report.result_projection_failures["createGreeting"].kind(), + ResultProjectionFailureKind::UnsupportedExpression + ); +} + +#[test] +fn independent_verifier_reconstructs_the_authored_core_result() { + let (core, target) = hello_echo(); + let artifact = + emit_result_projection(&core, &target, "createGreeting").expect("emit projection"); + let verified = verify_result_projection( + &core, + &target, + "createGreeting", + artifact.canonical_bytes(), + artifact.digest(), + ) + .expect("independent verifier accepts projection"); + + assert_eq!(verified.projection(), artifact.projection()); + assert_eq!(verified.digest(), artifact.digest()); +} + +#[test] +fn mutated_target_result_fails_closed() { + let (core, mut target) = hello_echo(); + target + .intents + .get_mut("createGreeting") + .expect("Target IR intent") + .result = CoreExpr::Local { + reference: LocalRef { + id: "arg.0".to_owned(), + alpha_name: "$arg0".to_owned(), + ty: "examples.hello_echo@1.CreateGreetingInput".to_owned(), + }, + }; + + let failure = emit_result_projection(&core, &target, "createGreeting") + .expect_err("mutated Target IR result must reject"); + assert_eq!( + failure.kind(), + ResultProjectionFailureKind::TargetResultMismatch + ); +} + +#[test] +fn mutated_target_core_closure_fails_closed() { + let (core, mut target) = hello_echo(); + target + .semantic_closure + .as_mut() + .expect("semantic closure") + .source_core + .digest = Some(format!("sha256:{}", "0".repeat(64))); + + let failure = emit_result_projection(&core, &target, "createGreeting") + .expect_err("mutated source Core identity must reject"); + assert_eq!( + failure.kind(), + ResultProjectionFailureKind::CoreTargetMismatch + ); +} + +#[test] +fn mutated_target_lawpack_closure_fails_closed() { + let (core, mut target) = hello_echo(); + target + .semantic_closure + .as_mut() + .expect("semantic closure") + .lawpacks[0] + .digest = Some(format!("sha256:{}", "0".repeat(64))); + + let failure = emit_result_projection(&core, &target, "createGreeting") + .expect_err("mutated lawpack closure must reject"); + assert_eq!( + failure.kind(), + ResultProjectionFailureKind::CoreTargetMismatch + ); +} + +#[test] +fn undeclared_locals_and_unsupported_calls_fail_closed() { + let (mut core, mut target) = hello_echo(); + mutate_results( + &mut core, + &mut target, + CoreExpr::Local { + reference: LocalRef { + id: "local.999".to_owned(), + alpha_name: "$999".to_owned(), + ty: "examples.hello_echo@1.GreetingCreated".to_owned(), + }, + }, + ); + let undeclared = emit_result_projection(&core, &target, "createGreeting") + .expect_err("undeclared local must reject"); + assert_eq!( + undeclared.kind(), + ResultProjectionFailureKind::UndeclaredProjectionSource + ); + + let (mut core, mut target) = hello_echo(); + mutate_results( + &mut core, + &mut target, + CoreExpr::Call { + callee: "examples.hidden@1.callback".to_owned(), + type_args: Vec::new(), + args: Vec::new(), + }, + ); + let call = emit_result_projection(&core, &target, "createGreeting") + .expect_err("call expression must reject"); + assert_eq!( + call.kind(), + ResultProjectionFailureKind::UnsupportedExpression + ); +} + +#[test] +fn incomplete_output_and_zero_output_bound_fail_closed() { + let (mut core, mut target) = hello_echo(); + let key = core.intents["createGreeting"].body.result.clone(); + let CoreExpr::Record { mut fields } = key else { + panic!("Hello Echo result is a record"); + }; + fields.remove("message"); + mutate_results(&mut core, &mut target, CoreExpr::Record { fields }); + let incomplete = emit_result_projection(&core, &target, "createGreeting") + .expect_err("incomplete output must reject"); + assert_eq!( + incomplete.kind(), + ResultProjectionFailureKind::OutputShapeMismatch + ); + + let (mut core, mut target) = hello_echo(); + core.intents + .get_mut("createGreeting") + .expect("Core intent") + .core_evaluation_budget + .max_output_bytes = 0; + target + .intents + .get_mut("createGreeting") + .expect("Target intent") + .core_evaluation_budget + .max_output_bytes = 0; + repin_target_core(&core, &mut target); + let unbounded = emit_result_projection(&core, &target, "createGreeting") + .expect_err("zero output bound must reject"); + assert_eq!( + unbounded.kind(), + ResultProjectionFailureKind::InvalidOutputBound + ); +} + +#[test] +fn canonical_bytes_and_digest_are_independently_enforced() { + let (core, target) = hello_echo(); + let artifact = + emit_result_projection(&core, &target, "createGreeting").expect("emit projection"); + + let mut malformed = artifact.canonical_bytes().to_vec(); + malformed.push(0); + let malformed_failure = verify_result_projection( + &core, + &target, + "createGreeting", + &malformed, + artifact.digest(), + ) + .expect_err("trailing data must reject"); + assert_eq!( + malformed_failure.kind(), + ResultProjectionFailureKind::InvalidCanonicalArtifact + ); + + let different = digest_result_projection(&projection_with_fields(1)) + .expect("digest different canonical projection"); + let digest_failure = verify_result_projection( + &core, + &target, + "createGreeting", + artifact.canonical_bytes(), + different, + ) + .expect_err("substituted digest must reject"); + assert_eq!( + digest_failure.kind(), + ResultProjectionFailureKind::DigestMismatch + ); +} + +#[test] +fn projection_node_limit_accepts_the_boundary_and_rejects_the_next_node() { + let boundary = projection_with_fields(MAX_RESULT_PROJECTION_NODES - 1); + encode_result_projection(&boundary).expect("exact node boundary encodes"); + + let oversized = projection_with_fields(MAX_RESULT_PROJECTION_NODES); + let failure = + encode_result_projection(&oversized).expect_err("one node beyond boundary must reject"); + assert_eq!( + failure.kind(), + ResultProjectionFailureKind::ProjectionLimitExceeded + ); +} + +#[test] +fn path_text_artifact_and_structure_bounds_fail_closed() { + let mut path_projection = projection_with_fields(1); + path_projection.expression = ResultProjectionExpr::Source { + source: ResultProjectionSource::ApplicationInput, + path: vec!["field".to_owned(); MAX_RESULT_PROJECTION_PATH_SEGMENTS + 1], + }; + assert_eq!( + encode_result_projection(&path_projection) + .expect_err("overlong source path must reject") + .kind(), + ResultProjectionFailureKind::ProjectionLimitExceeded + ); + + let mut text_projection = projection_with_fields(1); + text_projection.operation_coordinate = "x".repeat(MAX_RESULT_PROJECTION_TEXT_BYTES + 1); + assert_eq!( + encode_result_projection(&text_projection) + .expect_err("overlong coordinate must reject") + .kind(), + ResultProjectionFailureKind::ProjectionLimitExceeded + ); + + assert_eq!( + decode_result_projection(&vec![0; MAX_RESULT_PROJECTION_ARTIFACT_BYTES + 1]) + .expect_err("oversized artifact must reject before decoding") + .kind(), + ResultProjectionFailureKind::ProjectionLimitExceeded + ); + + let bytes = + encode_result_projection(&projection_with_fields(1)).expect("encode complete projection"); + let mut incomplete = decode_canonical_cbor(&bytes).expect("decode projection value"); + remove_canonical_field(&mut incomplete, "outputType"); + let incomplete_bytes = + encode_canonical_cbor(&incomplete).expect("encode incomplete canonical value"); + assert_eq!( + decode_result_projection(&incomplete_bytes) + .expect_err("incomplete projection must reject") + .kind(), + ResultProjectionFailureKind::InvalidCanonicalArtifact + ); +} + +#[test] +fn hostile_decoded_values_fail_closed_before_semantic_admission() { + let mut empty_text = projection_with_fields(1); + empty_text.operation_coordinate.clear(); + assert_eq!( + encode_result_projection(&empty_text) + .expect_err("empty required text must reject") + .kind(), + ResultProjectionFailureKind::InvalidCanonicalArtifact + ); + + let bytes = + encode_result_projection(&projection_with_fields(1)).expect("encode bounded projection"); + let mut zero_output_bound = decode_canonical_cbor(&bytes).expect("decode projection value"); + *canonical_field_mut(&mut zero_output_bound, "maxOutputBytes") = CanonicalValue::Integer(0); + let zero_output_bytes = + encode_canonical_cbor(&zero_output_bound).expect("encode hostile canonical value"); + assert_eq!( + decode_result_projection(&zero_output_bytes) + .expect_err("decoded zero output bound must reject") + .kind(), + ResultProjectionFailureKind::InvalidOutputBound + ); +} + +#[test] +fn unknown_steps_and_invalid_application_input_bindings_fail_closed() { + let (core, target) = hello_echo(); + let artifact = + emit_result_projection(&core, &target, "createGreeting").expect("emit projection"); + let mut unknown_step = artifact.projection().clone(); + let ResultProjectionExpr::Record { fields } = &mut unknown_step.expression else { + panic!("Hello Echo projection is a record"); + }; + let ResultProjectionExpr::Source { source, .. } = + fields.get_mut("key").expect("Hello Echo key projection") + else { + panic!("Hello Echo key projection is a source"); + }; + *source = ResultProjectionSource::CapabilityResult { + step_id: "createGreeting.step.missing".to_owned(), + }; + let unknown_step_bytes = + encode_result_projection(&unknown_step).expect("encode hostile step projection"); + let unknown_step_digest = + digest_result_projection(&unknown_step).expect("digest hostile step projection"); + assert_eq!( + verify_result_projection( + &core, + &target, + "createGreeting", + &unknown_step_bytes, + unknown_step_digest, + ) + .expect_err("unknown capability step must reject") + .kind(), + ResultProjectionFailureKind::UnknownCapabilityStep + ); + + let (mut missing_core, mut missing_target) = hello_echo(); + missing_core + .intents + .get_mut("createGreeting") + .expect("Core intent") + .body + .locals + .retain(|local| local.id != "arg.0"); + repin_target_core(&missing_core, &mut missing_target); + assert_eq!( + emit_result_projection(&missing_core, &missing_target, "createGreeting") + .expect_err("missing application input binding must reject") + .kind(), + ResultProjectionFailureKind::InvalidApplicationInput + ); + + let (mut duplicate_core, mut duplicate_target) = hello_echo(); + let duplicate_input = duplicate_core.intents["createGreeting"].body.locals[0].clone(); + duplicate_core + .intents + .get_mut("createGreeting") + .expect("Core intent") + .body + .locals + .push(duplicate_input); + repin_target_core(&duplicate_core, &mut duplicate_target); + assert_eq!( + emit_result_projection(&duplicate_core, &duplicate_target, "createGreeting") + .expect_err("duplicate application input binding must reject") + .kind(), + ResultProjectionFailureKind::InvalidApplicationInput + ); +} + +#[test] +fn canonical_maps_are_insertion_order_independent_for_fixed_seed_cases() { + let entries = (0..32) + .map(|index| { + ( + CanonicalValue::Text(format!("field{index:04}")), + CanonicalValue::Integer(i128::from(index)), + ) + }) + .collect::>(); + let expected = encode_canonical_cbor(&CanonicalValue::Map(entries.clone())) + .expect("encode ordered canonical map"); + let mut state = PROPERTY_SEED; + + for _case in 0..64 { + let mut indexes = (0..32).collect::>(); + for index in (1..indexes.len()).rev() { + state ^= state << 13; + state ^= state >> 7; + state ^= state << 17; + let selected = usize::try_from(state % u64::try_from(index + 1).expect("small index")) + .expect("selected index"); + indexes.swap(index, selected); + } + let candidate = CanonicalValue::Map( + indexes + .into_iter() + .map(|index| entries[index].clone()) + .collect(), + ); + assert_eq!( + encode_canonical_cbor(&candidate).expect("encode property candidate"), + expected + ); + } +} + +#[test] +fn repeated_emit_and_verify_is_stable_under_bounded_stress() { + let (core, target) = hello_echo(); + let first = emit_result_projection(&core, &target, "createGreeting").expect("emit projection"); + for _attempt in 0..128 { + let next = + emit_result_projection(&core, &target, "createGreeting").expect("repeat emission"); + assert_eq!(next, first); + verify_result_projection( + &core, + &target, + "createGreeting", + next.canonical_bytes(), + next.digest(), + ) + .expect("repeat verification"); + } +} diff --git a/docs/abi/edict-result-projection.cddl b/docs/abi/edict-result-projection.cddl new file mode 100644 index 0000000..ba17e9f --- /dev/null +++ b/docs/abi/edict-result-projection.cddl @@ -0,0 +1,37 @@ +; SPDX-License-Identifier: Apache-2.0 +; edict-result-projection.cddl +; Canonical schema for compiler-owned application-result projections. +; Authoritative byte encoding: edict.canonical-cbor/v1. + +result-projection = { + schema: "edict.result-projection/v1", + operationCoordinate: bounded-projection-text, + outputType: bounded-projection-text, + maxOutputBytes: uint .gt 0, + expression: result-projection-expr, +} + +result-projection-expr = result-projection-record / result-projection-source + +result-projection-record = { + kind: "record", + ; The root record counts as one of the Rust decoder's 256 expression nodes. + ; Nested aggregate node count remains an authoritative decoder check. + fields: { 0*255 bounded-projection-text => result-projection-expr }, +} + +result-projection-source = { + kind: "source", + source: result-projection-source-kind, + ; Matches MAX_RESULT_PROJECTION_PATH_SEGMENTS in edict-syntax. + path: [0*32 bounded-projection-text], +} + +result-projection-source-kind = + { kind: "applicationInput" } / + { + kind: "capabilityResult", + stepId: bounded-projection-text, + } + +bounded-projection-text = tstr .size (1..1024) diff --git a/docs/topics/README.md b/docs/topics/README.md index e5a8d2b..ebb99e1 100644 --- a/docs/topics/README.md +++ b/docs/topics/README.md @@ -65,6 +65,9 @@ cargo xtask verify validation, explicit in-process compatibility adapters for the current built-in target lowerers, the external provider WIT contract, and pure invocation request/result validation with host-authored output identity. +- [Result Projections](./result-projections/README.md): compiler-owned, + canonical, bounded application-result assembly from declared input and + capability-result sources with independent reverse verification. - [Release Process](./release-process/README.md): tag-triggered GitHub Release publication contract, release runbook, and verification matrix. - [Review Process](./review-process/README.md): pull request review-bot fallback diff --git a/docs/topics/cli/README.md b/docs/topics/cli/README.md index da53c03..8f91e74 100644 --- a/docs/topics/cli/README.md +++ b/docs/topics/cli/README.md @@ -73,6 +73,15 @@ Edict does not re-encode either artifact and does not execute the package. Concurrent writers are excluded, and replacement preserves the previous pair if either output cannot be published. +For the singleton executable-operation route, target lowering emits +compiler-authored `edict.result-projection/v1` artifacts or explicit per-intent +projection failures. The application build then requires exactly one artifact, +independently verifies it against exact Core and Target IR, and binds its +canonical bytes plus `edict.result-projection.artifact/v1` identity into both +provider semantic-input closures. Providers that implement the previous +six-input closure refuse before publication; Echo #698 owns package inclusion +and runtime consumption of the new seventh input. + Provider diagnostics are fail-closed on this first public build route: any provider-authored diagnostic rejects publication, independent of its severity. The terminal status `checked` count is `1` after a successful build because the diff --git a/docs/topics/result-projections/README.md b/docs/topics/result-projections/README.md new file mode 100644 index 0000000..49f70c1 --- /dev/null +++ b/docs/topics/result-projections/README.md @@ -0,0 +1,152 @@ +# Result Projections + +Status: current HEAD contract. + +This shelf describes Edict's compiler-owned representation of an application +operation result. A result projection preserves how the authored result is +assembled from declared application input and capability-step results without +executing the operation or introducing host-authored application semantics. +[RESULT-PROJ-REQ-001] [RESULT-PROJ-REQ-007] + +## Current Contract + +`edict_syntax::emit_result_projection` accepts one exact Core module, its +matching Target IR artifact, and an intent name. The emitter requires: + +- the intent to exist in both artifacts; +- the Target IR semantic closure to equal the complete Core-derived closure, + including the exact Core coordinate, canonical Core digest, and every + digest-locked lawpack; +- the Core and Target IR result expressions, operation profile, and evaluation + budget to agree; +- the application input to be the declared `arg.0` local with the intent input + type; and +- every capability-result source to identify exactly one Target IR step whose + binding, effect coordinate, and input expression match the corresponding + Core effect node. + +No projection is emitted after any of these checks fails. +[RESULT-PROJ-REQ-001] [RESULT-PROJ-REQ-005] + +`lower_to_target_ir` invokes that emitter for every intent in an artifact with +an explicit semantic closure and returns the artifacts in +`TargetLoweringReport::result_projections`, keyed by intent name. An intent +outside the first closed projection subset appears instead in +`result_projection_failures`; no projection is claimed for that intent. +General Target IR lowering remains available because not every target consumer +requires an executable application result. The public application-build path +does require exactly one admitted projection and rejects any projection +failure. Legacy Target IR without an explicit Core/lawpack semantic closure +remains byte-compatible and claims neither projection nor projection failure. +[RESULT-PROJ-REQ-001] [RESULT-PROJ-REQ-006] + +The closed `edict.result-projection/v1` expression language contains only: + +- records with canonically ordered field names; +- the declared application input; +- capability results addressed by Target IR step identity; and +- bounded field paths rooted at one of those declared sources. + +Constants, calls, undeclared locals, unbound capability steps, and other Core +expression forms are outside this version and fail with stable +`ResultProjectionFailureKind` values. Output records must reconstruct the +declared Core output type exactly; missing, extra, or type-incompatible fields +reject before artifact identity exists. [RESULT-PROJ-REQ-002] +[RESULT-PROJ-REQ-006] + +Each projection carries: + +- schema identity `edict.result-projection/v1`; +- the Core module and intent operation coordinate; +- the declared Core output type; +- the authored `maxOutputBytes` evaluation bound; and +- the closed projection expression. + +The representation limits one artifact to 256 expression nodes, 32 path +segments per source, 1,024 UTF-8 bytes per coordinate or field string, and +65,536 canonical bytes. A zero output bound and every one-over-limit value +reject before admission. [RESULT-PROJ-REQ-004] + +`ResultProjection` is an untrusted candidate value so callers can submit +hostile values to the encoder and verifier. A `ResultProjectionArtifact` can +only be constructed by the compiler-owned emitter and exposes its accepted +projection, canonical bytes, and identity through read-only accessors. Safe +callers therefore cannot detach the artifact bytes from their digest after +emission. [RESULT-PROJ-REQ-003] [RESULT-PROJ-REQ-006] + +`encode_result_projection` emits `edict.canonical-cbor/v1` bytes. +`digest_result_projection` frames those exact bytes under +`edict.result-projection.artifact/v1`. The generated provider contract pack +publishes the `result-projection` CDDL root and the matching artifact-domain +binding. The root mirrors positive output bounds, bounded text, the 32-segment +path ceiling, and the maximum 255 children of a flat root record. Aggregate +recursive node count and encoded artifact bytes remain authoritative decoder +checks because CDDL cannot express either whole-value total. The reviewed Hello +Echo bytes and digest under +`fixtures/lawpack/hello-echo/` are reproduced only by +`cargo xtask lawpack-goldens`. [RESULT-PROJ-REQ-003] + +## Independent Verification + +`verify_result_projection` does not call the emitter or trust the claimed +digest. It: + +1. decodes the claimed bytes through the canonical CBOR decoder; +2. validates the closed projection shape and representation bounds; +3. reproduces the exact canonical bytes; +4. recomputes the domain-framed identity; +5. independently reconstructs and compares the complete Core-derived semantic + closure; +6. independently rebuilds the Core-to-Target step/source correspondence; +7. reconstructs a Core result expression from the projection; and +8. requires that reconstruction to equal both the authored Core result and the + matching Target IR result. + +Only then does the API return `VerifiedResultProjection`, whose fields are +available through read-only accessors. [RESULT-PROJ-REQ-005] +[RESULT-PROJ-REQ-006] + +The public application-build path requires exactly one compiler-emitted +projection for its current singleton executable-operation slice, runs the +independent verifier over that artifact, and adds the same canonical bytes and +domain-framed identity to both the provider lowerer and provider verifier +semantic-input closures under the `result-projection` auxiliary kind. A +provider package that does not declare and accept that input refuses rather +than receiving a projection reconstructed by the host. [RESULT-PROJ-REQ-001] +[RESULT-PROJ-REQ-003] [RESULT-PROJ-REQ-005] + +```text +Edict source + lawpack closure + -> Core result + -> matching Target IR result + -> bounded canonical projection + -> independent reverse reconstruction + -> verified projection +``` + +## Authority Boundary + +The projection is data, not an execution callback. Emission and verification +perform no filesystem, network, process, model, runtime, or application-host +operation. The representation names application and capability data already +declared by Core and Target IR; it does not grant authority to obtain those +values. [RESULT-PROJ-REQ-007] + +Echo #698 owns generic runtime evaluation, durable result binding, recovery, +package inclusion, and exposure of the projected value. Until that producer +change lands, the previous six-input Echo provider package is incompatible with +the new seven-input application-build closure. Hello Echo #18 owns the external +application proof. Neither runtime responsibility is implemented in this Edict +contract. + +## Deferred + +- Runtime evaluation of a verified projection. +- Durable binding of evaluated result bytes to an Echo Action, Tick, outcome, + Receipt, or recovery record. +- Additional expression forms beyond records and bounded source field paths. +- External effects, model invocation, native callbacks, or application-specific + reconstruction. + +The executable verification matrix is tracked in +[test-plan.md](./test-plan.md). diff --git a/docs/topics/result-projections/test-plan.md b/docs/topics/result-projections/test-plan.md new file mode 100644 index 0000000..5a70128 --- /dev/null +++ b/docs/topics/result-projections/test-plan.md @@ -0,0 +1,77 @@ +# Result Projections Test Plan + +## Scope + +This shelf owns the compiler-authored, runtime-neutral representation of an +application operation result. It covers derivation from exact Core and Target +IR, canonical bytes and digest identity, source authority, boundedness, and +independent reverse reconstruction. Runtime evaluation and durable result +binding belong to Echo. + +## Requirements + +| ID | Status | Requirement | Source | +| --- | --- | --- | --- | +| RESULT-PROJ-REQ-001 | implemented | The compiler derives one operation result projection from exact Core and the matching Target IR result without host-authored application semantics. | issue #173 | +| RESULT-PROJ-REQ-002 | implemented | Projection leaves may name only the declared application input or a declared capability-step result, with explicit bounded field paths. | issue #173 | +| RESULT-PROJ-REQ-003 | implemented | `edict.result-projection/v1` has canonical CBOR bytes, a published CDDL root, and a domain-framed `edict.result-projection.artifact/v1` identity. | issue #173 | +| RESULT-PROJ-REQ-004 | implemented | Projection node count, path depth, text size, artifact bytes, and output bytes are bounded before the artifact can be admitted. | issue #173 | +| RESULT-PROJ-REQ-005 | implemented | Independent verification decodes the claimed bytes, recomputes their identity, reconstructs the authored Core result from declared sources, and requires exact Core and Target IR agreement. | issue #173 | +| RESULT-PROJ-REQ-006 | implemented | Mutated, incomplete, unsupported, undeclared-source, digest-substituted, malformed, and over-bound projections fail with stable error kinds. | issue #173 | +| RESULT-PROJ-REQ-007 | implemented | The representation remains target-neutral and contains no runtime callback, external effect, model call, or application-specific production behavior. | issue #173 | + +## Fixtures + +| Fixture | Purpose | Oracle | +| --- | --- | --- | +| `fixtures/lawpack/hello-echo/create-greeting.edict` | Real application source with one application-input field and one capability-result field in its result. | Compilation and Target IR lowering derive the reviewed projection without native Hello Echo code. | +| `fixtures/lawpack/hello-echo/create-greeting.result-projection.cbor` | Reviewed canonical projection bytes. | `cargo xtask lawpack-goldens --check` reproduces exact bytes through the compiler-owned emitter. | +| `fixtures/lawpack/hello-echo/create-greeting.result-projection.sha256` | Reviewed domain-framed projection identity. | The emitter and independent verifier reproduce the same identity. | +| `docs/abi/edict-result-projection.cddl` | Edict-owned canonical schema. | The provider contract pack validates a reference projection and rejects structural mutation. | + +## Test Cases + +| ID | Status | Category | Requirement | Oracle | Evidence | Fixtures | Notes | +| --- | --- | --- | --- | --- | --- | --- | --- | +| RESULT-PROJ-TP-001 | implemented | Golden path | RESULT-PROJ-REQ-001, RESULT-PROJ-REQ-002 | Exact Hello Echo Core and Target IR emit the expected typed record projection. | `exact_core_and_target_ir_emit_the_typed_hello_echo_projection` | fixtures/lawpack/hello-echo/create-greeting.edict, fixtures/lawpack/hello-echo/create-greeting.result-projection.cbor | The test enters through real source and lawpack compilation. | +| RESULT-PROJ-TP-002 | implemented | Independent verification | RESULT-PROJ-REQ-005 | The verifier reconstructs the exact authored Core result from projection sources and accepts the emitted identity. | `independent_verifier_reconstructs_the_authored_core_result` | crates/edict-syntax/tests/result_projection.rs | Verification runs in the reverse direction from projection to Core expression. | +| RESULT-PROJ-TP-003 | implemented | Mutation rejection | RESULT-PROJ-REQ-005, RESULT-PROJ-REQ-006 | A Target IR result that differs from its source Core result fails closed. | `mutated_target_result_fails_closed` | crates/edict-syntax/tests/result_projection.rs | Prevents target-owned result authorship. | +| RESULT-PROJ-TP-004 | implemented | Authority rejection | RESULT-PROJ-REQ-002, RESULT-PROJ-REQ-006, RESULT-PROJ-REQ-007 | Undeclared locals and call expressions fail with stable error kinds. | `undeclared_locals_and_unsupported_calls_fail_closed` | crates/edict-syntax/tests/result_projection.rs | No callback or general expression VM enters the artifact. | +| RESULT-PROJ-TP-005 | implemented | Shape and bound rejection | RESULT-PROJ-REQ-004, RESULT-PROJ-REQ-006 | Incomplete output records and a zero output-byte ceiling fail closed. | `incomplete_output_and_zero_output_bound_fail_closed` | crates/edict-syntax/tests/result_projection.rs | Output shape is checked against declared Core types. | +| RESULT-PROJ-TP-006 | implemented | Canonical identity | RESULT-PROJ-REQ-003, RESULT-PROJ-REQ-006 | Trailing bytes and a substituted digest fail independently. | `canonical_bytes_and_digest_are_independently_enforced` | fixtures/lawpack/hello-echo/create-greeting.result-projection.cbor, fixtures/lawpack/hello-echo/create-greeting.result-projection.sha256 | Canonicality and identity are separate gates. | +| RESULT-PROJ-TP-007 | implemented | Boundary | RESULT-PROJ-REQ-004 | The exact node limit encodes and the next node rejects. | `projection_node_limit_accepts_the_boundary_and_rejects_the_next_node` | crates/edict-syntax/tests/result_projection.rs | The root record counts as one node. | +| RESULT-PROJ-TP-008 | implemented | Property | RESULT-PROJ-REQ-003 | Sixty-four fixed-seed permutations of one canonical map produce identical canonical bytes. | `canonical_maps_are_insertion_order_independent_for_fixed_seed_cases` | crates/edict-syntax/tests/result_projection.rs | Fixed seed: `0x17305eedcafebabe`; the test preserves insertion order until it reaches the canonical encoder. | +| RESULT-PROJ-TP-009 | implemented | Stress | RESULT-PROJ-REQ-001, RESULT-PROJ-REQ-005 | 128 bounded emit/verify repetitions reproduce identical artifacts. | `repeated_emit_and_verify_is_stable_under_bounded_stress` | crates/edict-syntax/tests/result_projection.rs | Bounded deterministic stress, not a benchmark. | +| RESULT-PROJ-TP-010 | implemented | Schema fidelity | RESULT-PROJ-REQ-003 | The provider contract pack publishes the result-projection root and domain. | `result_projection_root_matches_reference_encoder` | docs/abi/edict-result-projection.cddl, fixtures/provider-contracts/v1/edict-provider-contracts.cddl | Echo #698 can consume only this admitted representation. | +| RESULT-PROJ-TP-011 | implemented | Closure binding | RESULT-PROJ-REQ-001, RESULT-PROJ-REQ-005, RESULT-PROJ-REQ-006 | Substituting the Target IR semantic-closure Core identity rejects before projection emission. | `mutated_target_core_closure_fails_closed` | crates/edict-syntax/tests/result_projection.rs | Matching result syntax cannot detach the projection from the exact compiler input. | +| RESULT-PROJ-TP-012 | implemented | Bound matrix | RESULT-PROJ-REQ-004, RESULT-PROJ-REQ-006 | One-over-limit path depth, UTF-8 text, artifact bytes, and structurally incompatible output types reject with stable kinds. | `path_text_artifact_and_structure_bounds_fail_closed` | crates/edict-syntax/tests/result_projection.rs | Complements the exact node-limit boundary. | +| RESULT-PROJ-TP-013 | implemented | Target emission | RESULT-PROJ-REQ-001, RESULT-PROJ-REQ-005 | Echo target lowering returns the compiler-produced projection and the independent verifier reconstructs that exact output. | `echo_target_lowering_emits_the_verified_result_projection` | crates/edict-syntax/tests/result_projection.rs | Prevents callers from reauthoring or dropping the projection between Core and the provider boundary. | +| RESULT-PROJ-TP-014 | implemented | Provider closure | RESULT-PROJ-REQ-001, RESULT-PROJ-REQ-003 | The application build binds the compiler-emitted canonical projection and digest into the provider semantic-input closure. | `compiler_result_projection_is_bound_into_the_provider_closure` | crates/edict-cli/src/application_build.rs | Echo #698 consumes this Edict-owned representation rather than inventing one inside the runtime. | +| RESULT-PROJ-TP-015 | implemented | Target refusal | RESULT-PROJ-REQ-006, RESULT-PROJ-REQ-007 | A result outside the closed projection language produces no projection claim and records the stable per-intent projection failure while preserving general Target IR. | `target_lowering_exposes_an_unsupported_result_projection_without_claiming_one` | crates/edict-syntax/tests/result_projection.rs | The application build treats any recorded projection failure as terminal; non-application Target IR consumers remain compatible. | +| RESULT-PROJ-TP-016 | implemented | Closure binding | RESULT-PROJ-REQ-001, RESULT-PROJ-REQ-005, RESULT-PROJ-REQ-006 | Substituting a lawpack digest in the Target IR semantic closure rejects even when the source Core identity and result expression are unchanged. | `mutated_target_lawpack_closure_fails_closed` | crates/edict-syntax/tests/result_projection.rs | Independent verification reconstructs the complete Core-derived semantic closure, not only its source-Core member. | +| RESULT-PROJ-TP-017 | implemented | Schema parity | RESULT-PROJ-REQ-003, RESULT-PROJ-REQ-004, RESULT-PROJ-REQ-006 | The provider contract root accepts exactly 32 projection path segments and rejects the 33rd. | `result_projection_root_enforces_decoder_path_limit` | crates/edict-provider-schema/tests/provider_contract_pack.rs | Provider admission must not accept an artifact that the authoritative Rust decoder rejects. | +| RESULT-PROJ-TP-018 | implemented | Application cardinality | RESULT-PROJ-REQ-001, RESULT-PROJ-REQ-006 | The public application build rejects zero projections, multiple projections, and any projection-failure entry alongside an otherwise valid artifact. | `application_build_requires_one_projection_and_no_projection_failures` | crates/edict-cli/src/application_build.rs | The singleton provider route does not guess which compiler result contract to bind. | +| RESULT-PROJ-TP-019 | implemented | Hostile decoding | RESULT-PROJ-REQ-004, RESULT-PROJ-REQ-006 | Empty required text and a decoded zero output-byte ceiling fail with stable categories before semantic admission. | `hostile_decoded_values_fail_closed_before_semantic_admission` | crates/edict-syntax/tests/result_projection.rs | Covers hostile canonical values independently of the compiler emitter. | +| RESULT-PROJ-TP-020 | implemented | Source resolution | RESULT-PROJ-REQ-002, RESULT-PROJ-REQ-005, RESULT-PROJ-REQ-006 | Unknown capability steps plus missing or duplicate application-input bindings fail closed. | `unknown_steps_and_invalid_application_input_bindings_fail_closed` | crates/edict-syntax/tests/result_projection.rs | The verifier and emitter accept only the exact compiler-owned local/step closure. | +| RESULT-PROJ-TP-021 | implemented | Schema bounds | RESULT-PROJ-REQ-003, RESULT-PROJ-REQ-004, RESULT-PROJ-REQ-006 | The provider root rejects a zero output ceiling and a flat record whose root plus fields exceeds the 256-node decoder limit. | `result_projection_root_enforces_output_and_record_node_limits` | crates/edict-provider-schema/tests/provider_contract_pack.rs | Aggregate recursive node and encoded-byte limits remain authoritative decoder checks because CDDL cannot express those totals. | +| RESULT-PROJ-TP-022 | implemented | Parser recursion | RESULT-PROJ-REQ-004, RESULT-PROJ-REQ-006 | The projection parser accepts the exact expression-node recursion boundary and rejects the next recursive node before descending into it. | `expression_parser_refuses_recursion_beyond_the_node_budget` | crates/edict-syntax/src/result_projection.rs | The parser carries an explicit remaining-node budget independently of canonical-CBOR nesting limits. | + +## Determinism Obligations + +- Projection derivation performs no I/O and consumes only supplied Core and + Target IR values. +- Record fields are canonicalized by key. +- Step references use Target IR step identities, not source variable names. +- Verification never trusts a claimed digest or a host-authored result. +- Exact replay of the same Core and Target IR produces identical bytes and + identity. +- The lowerer and verifier receive byte-identical projection inputs bound to + the compiler-authored identity. + +## Non-Goals + +- Runtime projection evaluation. +- Echo package or verification-report changes. +- General expression evaluation. +- External effects or model execution. +- Application-specific callbacks. diff --git a/docs/topics/target-ir/README.md b/docs/topics/target-ir/README.md index 720773f..e01e5d6 100644 --- a/docs/topics/target-ir/README.md +++ b/docs/topics/target-ir/README.md @@ -41,6 +41,19 @@ compatibility paths produce identical Target IR artifacts, canonical bytes, and digests. This adapter does not resolve provider manifests, load components, or define general target plugin dispatch. [TIR-REQ-013] +For Target IR with an explicit Core/lawpack semantic closure, +`TargetLoweringReport` also carries one compiler-owned result-projection +artifact per supported intent and a separate per-intent failure map for results +outside the first closed projection language. This does not make projection +support a precondition for general Target IR consumers. The public +application-build route separately requires exactly one admitted projection and +fails closed on any projection failure. Legacy Target IR that intentionally +omits semantic closure retains empty projection and failure maps plus +byte-identical Target IR encoding. The projection artifact is outside the +Target IR digest preimage and has its own canonical schema and domain-framed +identity; its contract is defined by the +[Result Projections topic](../result-projections/README.md). + `TargetIrLoweringFacts::from_lowerability_report` derives the effect-to-intrinsic lowering table from accepted native lowerability reports. The derived facts use the target-profile coordinate and operation profile from diff --git a/fixtures/lawpack/hello-echo/README.md b/fixtures/lawpack/hello-echo/README.md index ae01977..df1735b 100644 --- a/fixtures/lawpack/hello-echo/README.md +++ b/fixtures/lawpack/hello-echo/README.md @@ -25,6 +25,11 @@ a fake transport, or a handwritten Echo executable package. artifact lowered from that exact Core module. - `create-greeting.target-ir.sha256` is its `edict.target-ir.artifact/v1` domain-framed identity. +- `create-greeting.result-projection.cbor` is the canonical + `edict.result-projection/v1` assembly of the authored success value from + declared application input and the capability-step result. +- `create-greeting.result-projection.sha256` is its + `edict.result-projection.artifact/v1` domain-framed identity. Regenerate only through: @@ -41,7 +46,8 @@ cargo xtask lawpack-goldens --check The loader validates the manifest, exports, and exact direct adapter. The compiler derives Core and `echo.span-ir/v1` facts from that closure without a handwritten compiler context. The golden command compiles and lowers the source -before reproducing the reviewed Core and Target IR bytes and identities. This -fixture does not yet emit an Echo executable package or execute an Echo Action. -Edict corroborates the target-configuration reference but deliberately leaves -its Echo-specific semantics to the Echo-owned target provider. +before reproducing the reviewed Core, Target IR, and result-projection bytes +and identities. This fixture does not yet bind the projected result during +Echo execution. Edict corroborates the target-configuration reference but +deliberately leaves its Echo-specific semantics and runtime evaluation to the +Echo-owned target provider. diff --git a/fixtures/lawpack/hello-echo/create-greeting.result-projection.cbor b/fixtures/lawpack/hello-echo/create-greeting.result-projection.cbor new file mode 100644 index 0000000..981cba6 Binary files /dev/null and b/fixtures/lawpack/hello-echo/create-greeting.result-projection.cbor differ diff --git a/fixtures/lawpack/hello-echo/create-greeting.result-projection.sha256 b/fixtures/lawpack/hello-echo/create-greeting.result-projection.sha256 new file mode 100644 index 0000000..d55e408 --- /dev/null +++ b/fixtures/lawpack/hello-echo/create-greeting.result-projection.sha256 @@ -0,0 +1 @@ +sha256:73c936f15b5fa12c0a3632288e516ae7a9f3858c62b638e801e163dd7ff5625f diff --git a/fixtures/provider-contracts/v1/edict-provider-contracts.cddl b/fixtures/provider-contracts/v1/edict-provider-contracts.cddl index 5b6fe74..5df2ec6 100644 --- a/fixtures/provider-contracts/v1/edict-provider-contracts.cddl +++ b/fixtures/provider-contracts/v1/edict-provider-contracts.cddl @@ -789,6 +789,45 @@ authority-budget-fact = { authority-write-class = "none" / "read" / "create" / "ensure" / "append" / "replace" / "delete" / "custom" +; --- edict-result-projection.cddl --- +; SPDX-License-Identifier: Apache-2.0 +; edict-result-projection.cddl +; Canonical schema for compiler-owned application-result projections. +; Authoritative byte encoding: edict.canonical-cbor/v1. + +result-projection = { + schema: "edict.result-projection/v1", + operationCoordinate: bounded-projection-text, + outputType: bounded-projection-text, + maxOutputBytes: uint .gt 0, + expression: result-projection-expr, +} + +result-projection-expr = result-projection-record / result-projection-source + +result-projection-record = { + kind: "record", + ; The root record counts as one of the Rust decoder's 256 expression nodes. + ; Nested aggregate node count remains an authoritative decoder check. + fields: { 0*255 bounded-projection-text => result-projection-expr }, +} + +result-projection-source = { + kind: "source", + source: result-projection-source-kind, + ; Matches MAX_RESULT_PROJECTION_PATH_SEGMENTS in edict-syntax. + path: [0*32 bounded-projection-text], +} + +result-projection-source-kind = + { kind: "applicationInput" } / + { + kind: "capabilityResult", + stepId: bounded-projection-text, + } + +bounded-projection-text = tstr .size (1..1024) + ; --- edict-target-ir.cddl --- ; SPDX-License-Identifier: Apache-2.0 ; edict-target-ir.cddl diff --git a/fixtures/provider-contracts/v1/manifest.json b/fixtures/provider-contracts/v1/manifest.json index 440dec8..0106222 100644 --- a/fixtures/provider-contracts/v1/manifest.json +++ b/fixtures/provider-contracts/v1/manifest.json @@ -3,8 +3,8 @@ "coordinate": "edict.provider-contract-pack.cddl@1", "license": "Apache-2.0", "schema": { - "bytesHex": "3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d70726f76696465722d636f6e7472616374732e6364646c0a3b2047656e6572617465642066726f6d2045646963742d6f776e65642041424920667261676d656e74732e20444f204e4f5420454449542e0a0a3b202d2d2d2065646963742d636f6d6d6f6e2e6364646c202d2d2d0a3b2065646963742d636f6d6d6f6e2e6364646c0a3b20536861726564204344444c20747970657320666f722074686520456469637420414249732c20646566696e6564204f4e4345206865726520736f20746865792063616e6e6f742064726966740a3b202845444943542d4142492d4e4f4455502d303031292e2054776f2067726f7570733a0a3b2020202d207265736f757263652d7265662c207368613235362d6469676573742c206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c0a3b2020202020636f72652d747970652d7265663a20617373656d626c656420776974682065646963742d7461726765742d70726f66696c652e6364646c20616e640a3b202020202065646963742d6c61777061636b2e6364646c20627920746865206275696c643b2074686f736520736368656d617320646f206e6f74207265646566696e65207468656d2e0a3b2020202d206f7065726174696f6e2d70726f66696c652c206f707469632d74656d706c6174652c2061706572747572652d726571756972656d656e7420616e6420746865697220726566733a0a3b2020202020636f6e73756d65642062792074686520436f72652f6f70746963206c61796572202865646963742d636f72652e6364646c2920616e64207265666572656e636564206279207468650a3b20202020206c616e67756167652f7461726765742d70726f66696c652073706563732e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a0a3b2041206e6f726d617469766520737562636f6d706f6e656e74207265666572656e636564206279206964656e7469747920706c7573206469676573742e204d616e696665737473206e657665720a3b20656d626564207468656972206f776e2073656c662d64696765737420696e20746865697220707265696d616765202845444943542d434f52452d53454c46484153482d303031292e0a7265736f757263652d726566203d207b2069643a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20446967657374732061726520617574686f726974617469766520617320747970656420627974652076616c7565732c206e657665722068657820737472696e67732e20526576696577204a534f4e0a3b2072656e64657273207468697320617320227368613235363a3c3634206c6f77657263617365206865783e22202845444943542d4449474553542d574952452d303031292e0a7368613235362d646967657374203d205b20616c676f726974686d3a2022736861323536222c2062797465733a2062737472202e73697a65203332205d0a0a3b2041206e616d6564206c6f772d6c6576656c206661696c75726520616e206566666563742063616e2072616973652e2054686520736f75726365206f62737472756374696f6e206d61700a3b2062696e64732069742028627920636f6f7264696e6174652920616e6420636f6e73747275637473206120747970656420646f6d61696e206f62737472756374696f6e2066726f6d206974730a3b207061796c6f6164202845444943542d4142492d4641494c5552452d4e414d45442d303031292e0a3b20416e20656666656374277320606566666563744661696c7572657360206c697374204d555354206861766520756e697175652060636f6f7264696e61746560733a2073696e6365207468650a3b206f62737472756374696f6e206d6170206973206b6579656420627920636f6f7264696e6174652c2074776f206661696c757265732073686172696e67206120636f6f7264696e61746520286576656e0a3b207769746820646966666572656e7420617574686f72697479436c6173732f7061796c6f61645479706529206d616b652065786861757374697665206d617070696e6720616e642062696e6465720a3b20747970696e6720616d626967756f757320616e64206172652072656a6563746564202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a3b0a3b2045666665637473206361727279207468656972206661696c757265732061732061206d617020607b206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d600a3b202873656520746865207461726765742f6c61777061636b2065666665637420736368656d6173292e20546865206661696c75726520636f6f7264696e61746520697320746865206d61700a3b204b45592c20736f206974206973206e6f7420726570656174656420696e2074686520626f647920616e642063616e6e6f74206469736167726565207769746820746865206b65792e0a6566666563742d6661696c7572652d626f6479203d207b0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164547970653a20636f72652d747970652d7265662c202020202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b2041206661696c75726520636f6f7264696e617465206d7573742062652061206261726520456469637420606964656e746020286c65747465722f756e64657273636f7265207468656e0a3b206c6574746572732f6469676974732f756e64657273636f7265732920414e44206d757374206e6f742062652061207265736572766564206b6579776f72642028652e672e2060656c7365602c0a3b20606261736973602c20607768657265602c206072657175697265602c2060666f72602c2060696660292e2054686520736f75726365206f62737472756374696f6e2d6d6170204c4853206f6e6c790a3b20616363657074732061206e6f6e2d6b6579776f726420606964656e74602c20736f20612068797068656e2f646f742f6b6579776f726420636f6f7264696e61746520776f756c642062650a3b204142492d76616c69642079657420696d706f737369626c6520746f206d617020657868617573746976656c7920696e20736f757263652e20546865207265676578206361707475726573207468650a3b206c65786963616c2073686170653b206b6579776f7264206578636c7573696f6e20697320616e206164646974696f6e616c2076616c69646174696f6e2072756c650a3b202845444943542d4142492d4641494c5552452d4944454e542d303031292e0a6661696c7572652d6964656e74203d2074737472202e72656765787020225b412d5a612d7a5f5d5b412d5a612d7a302d395f5d2a220a0a6566666563742d6b696e64203d20227265616422202f202263726561746522202f2022656e7375726522202f20227265706c61636522202f202264656c65746522202f0a202020202020202020202020202022617070656e6422202f202272656475636522202f202273656d616e7469632e656d697422202f2022637573746f6d220a0a617574686f726974792d636c617373203d2022646f6d61696e4d61707061626c6522202f20227061727469636970616e744f776e656422202f2022696e746567726974794661756c7422202f0a202020202020202020202020202020202020227265736f757263654661756c7422202f2022696e7465726e616c4661756c74220a0a636f72652d747970652d726566203d20747374722020203b2063616e6f6e6963616c20436f7265207479706520636f6f7264696e6174650a0a3b20416e206f7065726174696f6e2070726f66696c6520737570706c69657320746865206f707469632074656d706c617465206120436f726520696e74656e74207265736f6c766573206974730a3b206f707469634b696e642f626f756e646172794b696e642f737570706f7274506f6c6963792f6c6f7373446973706f736974696f6e2066726f6d2e205461726765742070726f66696c657320616e640a3b206c61777061636b73207075626c6973682074686573652061732061206d617020607b20636f6f7264696e617465203d3e206f7065726174696f6e2d70726f66696c65207d602c20736f207468650a3b20636f6f7264696e61746520697320746865204b45592c206e6f7420612076616c7565206669656c64202845444943542d4f505449432d54454d504c4154452d4f574e45522d3030312c0a3b2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65203d207b0a20206f7074696354656d706c6174653a206f707469632d74656d706c6174652c0a20206566666563745072656469636174653a20747374722c20202020202020202020203b20636f6f7264696e617465206f6620746865206f7065726174696f6e2d6d6f6465207072656469636174650a7d0a0a6f707469632d74656d706c617465203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a2020737570706f7274506f6c6963793a20747374722c202020202020202020202020203b2063616e6f6e6963616c20737570706f72742d706f6c69637920636f6f7264696e6174650a20206c6f7373446973706f736974696f6e3a20747374722c20202020202020202020203b2063616e6f6e6963616c206c6f73732d646973706f736974696f6e20636f6f7264696e6174650a20203f20626173697354656d706c6174653a20747374722c20202020202020202020203b206f7074696f6e616c206469676573742d6c6f636b65642062617369732074656d706c61746520636f6f72640a20203b2074686520617065727475726520726571756972656d656e7420746869732074656d706c61746520737570706c6965732e205265717569726564207768656e207468652074656d706c6174650a20203b2069732074686520736f75726365206f66206120436f7265206f707469632773206170657274757265526571756972656d656e742028692e652e2074686520696e74656e7420686173206e6f0a20203b20736f757263652060666f6f747072696e74203c3d202e2e2e60292c2073696e6365206170657274757265526571756972656d656e74206973206d616e6461746f727920696e20436f72650a20203b202845444943542d4f505449432d41504552545552452d5245462d303031292e0a20203f206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a7d0a0a3b206170657274757265526571756972656d656e742069732061207479706564207265666572656e63652c206e65766572206120667265652d666f726d20737472696e672e2041207265766965770a3b2072656e646572696e67206d61792073686f772069747320636f6f7264696e617465202845444943542d4f505449432d41504552545552452d5245462d303031292e0a61706572747572652d726571756972656d656e74203d20666f6f747072696e742d6365696c696e672d726566202f2061627374726163742d666f6f747072696e742d6f626c69676174696f6e2d7265660a666f6f747072696e742d6365696c696e672d726566203d207b206b696e643a2022666f6f747072696e744365696c696e67222c207265663a2074737472207d0a61627374726163742d666f6f747072696e742d6f626c69676174696f6e2d726566203d207b206b696e643a20226162737472616374466f6f747072696e744f626c69676174696f6e222c207265663a2074737472207d0a0a3b202d2d2d2065646963742d636f72652e6364646c202d2d2d0a3b2065646963742d636f72652e6364646c0a3b204e6f726d617469766520736368656d6120666f722074686520456469637420436f72652076312073656d616e746963206d6f64656c2e0a3b0a3b2053636f706520626f756e646172793a20746869732066696c6520646566696e657320436f7265206d65616e696e6720616e6420736368656d61207368617065206f6e6c792e20497420646f65730a3b206e6f7420646566696e6520612063616e6f6e6963616c20656e636f6465722c20436f7265206d6f64756c652068617368206669656c64732c20686173682066697874757265732c207461726765740a3b206c6f776572696e672c2061646d697373696f6e2062756e646c65732c206f72207461726765742d6f776e65642049522e0a0a636f72652d6d6f64756c65203d207b0a202061706956657273696f6e3a202265646963742e636f72652f7631222c0a2020636f6f7264696e6174653a20747374722c0a2020696d706f7274733a205b2a20636f72652d696d706f72745d2c0a202074797065733a207b202a2074737472203d3e20636f72652d74797065207d2c0a2020696e74656e74733a207b202b2074737472203d3e20636f72652d696e74656e74207d2c0a20207265717569726564436f72654361706162696c69746965733a205b2a20747374725d2c0a7d0a0a636f72652d696d706f7274203d207b0a20206b696e643a20226c61777061636b22202f202274617267657422202f2022636f7265222c0a20207265663a207265736f757263652d7265662c0a20203f20616c6961733a20747374722c0a7d0a0a3b202d2d2d207479706573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d74797065203d20636f72652d7363616c61722d74797065202f20636f72652d7265636f72642d74797065202f20636f72652d76617269616e742d74797065202f0a202020202020202020202020636f72652d6f7074696f6e2d74797065202f20636f72652d6c6973742d74797065202f20636f72652d6d61702d74797065202f0a202020202020202020202020636f72652d6361706162696c6974792d7265662d747970650a0a636f72652d7363616c61722d74797065203d20636f72652d626f6f6c2d74797065202f20636f72652d696e742d74797065202f20636f72652d737472696e672d74797065202f0a20202020202020202020202020202020202020636f72652d62797465732d74797065202f20636f72652d756e69742d747970650a0a636f72652d626f6f6c2d74797065203d207b206b696e643a2022426f6f6c22207d0a636f72652d756e69742d74797065203d207b206b696e643a2022556e697422207d0a636f72652d696e742d74797065203d207b0a20206b696e643a202249363422202f202255363422202f202249333222202f202255333222202f202249313622202f202255313622202f2022493822202f20225538222c0a7d0a636f72652d737472696e672d74797065203d207b0a20206b696e643a2022537472696e67222c0a20206d61783a2075696e742c0a202063616e6f6e6963616c3a2022756e69636f64652d7363616c61722d6e666322202f20227261772d75746638222c0a7d0a636f72652d62797465732d74797065203d207b0a20206b696e643a20224279746573222c0a20206d61783a2075696e742c0a7d0a636f72652d7265636f72642d74797065203d207b0a20206b696e643a20225265636f7264222c0a20206669656c64733a207b202a2074737472203d3e20636f72652d747970652d726566207d2c0a7d0a636f72652d76617269616e742d74797065203d207b0a20206b696e643a202256617269616e74222c0a202063617365733a207b202b2074737472203d3e2076617269616e742d636173652d626f6479207d2c0a7d0a76617269616e742d636173652d626f6479203d207b0a20203f207061796c6f61643a20636f72652d747970652d7265662c0a7d0a636f72652d6f7074696f6e2d74797065203d207b0a20206b696e643a20224f7074696f6e222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a636f72652d6c6973742d74797065203d207b0a20206b696e643a20224c697374222c0a20206974656d3a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6d61702d74797065203d207b0a20206b696e643a20224d6170222c0a20206b65793a20636f72652d747970652d7265662c0a202076616c75653a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6361706162696c6974792d7265662d74797065203d207b0a20206b696e643a20224361706162696c697479526566222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a0a3b20636f72652d747970652d72656620697320646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d612e0a0a3b202d2d2d207265666572656e63657320616e642076616c756573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a3b204c6f63616c206964656e7469747920697320616c7068612d737461626c652e20606964602069732074686520636f6d70696c65722d6f776e6564206c6f63616c20636f6f7264696e6174653b0a3b2060616c7068614e616d656020697320746865206e6f726d616c697a65642068756d616e2f6465627567206e616d652e20536f757263652062696e646572207370656c6c696e67206973206e6f740a3b206964656e746974792e0a6c6f63616c2d726566203d207b0a202069643a20747374722c0a2020616c7068614e616d653a20747374722c0a2020747970653a20636f72652d747970652d7265662c0a7d0a0a636f72652d76616c7565203d20636f72652d6e756c6c2d76616c7565202f20636f72652d626f6f6c2d76616c7565202f20636f72652d696e742d76616c7565202f0a20202020202020202020202020636f72652d737472696e672d76616c7565202f20636f72652d62797465732d76616c7565202f20636f72652d7265636f72642d76616c7565202f0a20202020202020202020202020636f72652d76617269616e742d76616c7565202f20636f72652d6c6973742d76616c7565202f20636f72652d6d61702d76616c7565202f0a20202020202020202020202020636f72652d6361706162696c6974792d76616c75650a0a636f72652d6e756c6c2d76616c7565203d207b206b696e643a20226e756c6c22207d0a636f72652d626f6f6c2d76616c7565203d207b206b696e643a2022626f6f6c222c2076616c75653a20626f6f6c207d0a636f72652d696e742d76616c7565203d207b206b696e643a2022696e74222c2077696474683a20747374722c2076616c75653a20696e74207d0a636f72652d737472696e672d76616c7565203d207b206b696e643a2022737472696e67222c2076616c75653a2074737472207d0a636f72652d62797465732d76616c7565203d207b206b696e643a20226279746573222c2076616c75653a2062737472207d0a636f72652d7265636f72642d76616c7565203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d76616c7565207d207d0a636f72652d76617269616e742d76616c7565203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d76616c75652c0a7d0a636f72652d6c6973742d76616c7565203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d76616c75655d207d0a636f72652d6d61702d76616c7565203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d76616c75652c2076616c75653a20636f72652d76616c75655d5d207d0a636f72652d6361706162696c6974792d76616c7565203d207b0a20206b696e643a20226361706162696c697479222c0a2020726563656970743a207368613235362d6469676573742c0a7d0a0a3b2045646963742d617574686f72656420707572652068656c70657273207573652061207075726520436f72652066756e6374696f6e20626f64792e2054686520626f64792063616e2062696e640a3b20707572652065787072657373696f6e7320616e642072657475726e20616e2065787072657373696f6e2c206275742069742063616e6e6f7420636f6e7461696e20436f7265206566666563742c0a3b2067756172642c206272616e63682c206c6f6f702c206d617463682d6e6f64652c206f722070726f6f662d6f626c69676174696f6e206e6f6465732e0a636f72652d666e2d626f6479203d207b0a2020706172616d733a205b2a206c6f63616c2d7265665d2c0a2020626f64793a20636f72652d707572652d626c6f636b2c0a7d0a0a636f72652d707572652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a202062696e64696e67733a205b2a20707572652d6c65742d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a707572652d6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a0a3b202d2d2d2065787072657373696f6e7320616e642070726564696361746573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d65787072203d206c6f63616c2d65787072202f20636f6e73742d65787072202f207265636f72642d65787072202f206669656c642d65787072202f0a20202020202020202020202076617269616e742d65787072202f206d617463682d65787072202f2063616c6c2d65787072202f206c6973742d65787072202f206d61702d65787072202f0a20202020202020202020202069662d657870720a0a6c6f63616c2d65787072203d207b206b696e643a20226c6f63616c222c207265663a206c6f63616c2d726566207d0a636f6e73742d65787072203d207b206b696e643a2022636f6e7374222c2076616c75653a20636f72652d76616c7565207d0a7265636f72642d65787072203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d65787072207d207d0a6669656c642d65787072203d207b206b696e643a20226669656c64222c20626173653a20636f72652d657870722c206669656c643a2074737472207d0a76617269616e742d65787072203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d657870722c0a7d0a6d617463682d65787072203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d61726d5d2c0a7d0a6d617463682d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d657870722c0a7d0a63616c6c2d65787072203d207b0a20206b696e643a202263616c6c222c0a202063616c6c65653a20747374722c0a202074797065417267733a205b2a20636f72652d747970652d7265665d2c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6c6973742d65787072203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d657870725d207d0a6d61702d65787072203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d657870722c2076616c75653a20636f72652d657870725d5d207d0a69662d65787072203d207b0a20206b696e643a20226966222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d657870722c0a2020656c73653a20636f72652d657870722c0a7d0a0a636f72652d707265646963617465203d20747275652d707265646963617465202f2066616c73652d707265646963617465202f206e6f742d707265646963617465202f0a2020202020202020202020202020202020616c6c2d707265646963617465202f20616e792d707265646963617465202f20636f6d706172652d707265646963617465202f0a202020202020202020202020202020202063616c6c2d707265646963617465202f206f62737472756374696f6e2d7072656469636174650a0a747275652d707265646963617465203d207b206b696e643a20227472756522207d0a66616c73652d707265646963617465203d207b206b696e643a202266616c736522207d0a6e6f742d707265646963617465203d207b206b696e643a20226e6f74222c2076616c75653a20636f72652d707265646963617465207d0a616c6c2d707265646963617465203d207b206b696e643a2022616c6c222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a616e792d707265646963617465203d207b206b696e643a2022616e79222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a636f6d706172652d707265646963617465203d207b0a20206b696e643a2022636f6d70617265222c0a20206f703a20223d3d22202f2022213d22202f20223c22202f20223c3d22202f20223e22202f20223e3d222c0a20206c6566743a20636f72652d657870722c0a202072696768743a20636f72652d657870722c0a7d0a63616c6c2d707265646963617465203d207b0a20206b696e643a202263616c6c222c0a20207072656469636174653a20747374722c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6f62737472756374696f6e2d707265646963617465203d207b0a20206b696e643a20226f62737472756374696f6e222c0a2020636f6f7264696e6174653a206661696c7572652d6964656e742c0a20207061796c6f61643a20636f72652d657870722c0a7d0a0a696e7075742d636f6e73747261696e74203d207b0a2020636f6f7264696e6174653a20747374722c0a2020736f757263653a2022776865726522202f2022636f6d70696c6572222c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a3b202d2d2d20696e74656e74732c20626c6f636b732c20616e64206e6f646573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d696e74656e74203d207b0a2020696e7075743a20636f72652d747970652d7265662c0a20206f75747075743a20636f72652d747970652d7265662c0a202072657175697265644f7065726174696f6e50726f66696c653a20747374722c0a20203f2062617369733a20636f72652d657870722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020626f64793a20636f72652d626c6f636b2c0a20203f206f707469633a20636f72652d6f707469632c0a7d0a0a636f72652d627564676574203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a636f72652d6f70746963203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a20206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a2020737570706f7274506f6c6963793a20747374722c0a20206c6f7373446973706f736974696f6e3a20747374722c0a7d0a0a636f72652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a20206e6f6465733a205b2a20636f72652d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a636f72652d6e6f6465203d206c65742d6e6f6465202f20726571756972652d6e6f6465202f206566666563742d6e6f6465202f2067756172642d6e6f6465202f206272616e63682d6e6f6465202f0a202020202020202020202020666f722d6e6f6465202f206d617463682d6e6f6465202f2070726f6f662d6f626c69676174696f6e2d6e6f64650a0a6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a726571756972652d6e6f6465203d207b0a20206b696e643a202272657175697265222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a726571756972652d6661696c7572652d61726d203d207465726d696e616c2d726571756972652d6661696c757265202f0a20202020202020202020202020202020202020202020636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c7572650a7465726d696e616c2d726571756972652d6661696c757265203d207b0a20206b696e643a20227465726d696e616c222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c757265203d207b0a20206b696e643a2022636f6e74696e75654f627374727563746564222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a6f62737472756374696f6e2d726561736f6e203d207b0a2020726561736f6e4b696e643a20747374722c0a20207061796c6f61643a207b202a2074737472203d3e20636f72652d65787072207d2c0a7d0a6566666563742d6e6f6465203d207b0a20206b696e643a2022656666656374222c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4d61703a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a6f62737472756374696f6e2d61726d203d207b0a202062696e6465723a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a67756172642d6e6f6465203d207b0a20206b696e643a20226775617264222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f62737472756374696f6e3a20636f72652d657870722c0a7d0a6272616e63682d6e6f6465203d207b0a20206b696e643a20226272616e6368222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d626c6f636b2c0a2020656c73653a20636f72652d626c6f636b2c0a7d0a666f722d6e6f6465203d207b0a20206b696e643a2022666f72222c0a202062696e6465723a206c6f63616c2d7265662c0a2020697465723a20636f72652d657870722c0a2020626f756e643a20636f72652d626f756e642c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a6d617463682d6e6f6465203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d626c6f636b2d61726d5d2c0a7d0a6d617463682d626c6f636b2d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a70726f6f662d6f626c69676174696f6e2d6e6f6465203d207b0a20206b696e643a202270726f6f66222c0a2020636f6f7264696e6174653a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a636f72652d626f756e64203d206c69746572616c2d626f756e64202f20636f6f7264696e6174652d626f756e640a6c69746572616c2d626f756e64203d207b206b696e643a20226c69746572616c222c2076616c75653a2075696e74207d0a636f6f7264696e6174652d626f756e64203d207b206b696e643a2022636f6f7264696e617465222c207265663a2074737472207d0a0a3b20536861726564207265736f757263652d7265662c207368613235362d6469676573742c206661696c7572652d6964656e742c2061706572747572652d726571756972656d656e742c20616e640a3b20636f72652d747970652d7265662061726520646566696e6564206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c2e0a0a3b202d2d2d2065646963742d6c61777061636b2e6364646c202d2d2d0a3b2065646963742d6c61777061636b2e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374206c61777061636b206d616e696665737420616e64206578706f727420737572666163652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e204a534f4e20696e207468652070726f73652073706563730a3b2069732061207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a6c61777061636b2d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e6c61777061636b2f7631222c0a202069643a20747374722c0a202076657273696f6e3a20747374722c0a20206163636570746564436f72654162693a205b2b20747374725d2c0a2020646570656e64656e636965733a205b2a206c61777061636b2d6465705d2c202020202020202020203b20616379636c69632c206469676573742d6c6f636b6564202845444943542d4c41575041434b2d4441472d303031290a20206578706f7274733a207265736f757263652d7265662c0a20203f2074617267657441646170746572733a205b2b207461726765742d616461707465725d2c2020203b207265717569726564206f6e6c7920696620616e792072756e74696d6520656666656374206578697374730a20203f2068656c706572436f6d706f6e656e743a2065786563757461626c652d636f6d706f6e656e742c203b2065786563757461626c652068656c70657273206361727279207468656972206f776e2073616e64626f782b6675656c0a202076657269666965723a2076657269666965722c202020202020202020202020202020202020202020203b20636c61737369666965643a206465636c61726174697665206f722065786563757461626c650a2020636f6d7061746962696c6974793a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b2041207665726966696572206973206569746865722061206465636c617261746976652072756c6573657420286e6f2072756e74696d6529206f7220616e2065786563757461626c650a3b20636f6d706f6e656e742e20416e2065786563757461626c65207665726966696572204d55535420636172727920697473206f776e2073616e64626f7820616e64206675656c206d6f64656c2c0a3b20736f2074686520736368656d6120656e666f726365732074686174206e6f2065786563757461626c6520636f6d706f6e656e74206973206c65667420756e626f756e6465640a3b202845444943542d4142492d56455249464945522d424f554e442d303031292e0a7665726966696572203d206465636c617261746976652d7665726966696572202f2065786563757461626c652d76657269666965720a6465636c617261746976652d7665726966696572203d207b20636c6173733a20226465636c61726174697665222c2072756c657365743a207265736f757263652d726566207d0a65786563757461626c652d7665726966696572203d207b0a2020636c6173733a202265786563757461626c65222c0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a3b20416e792065786563757461626c6520636f6d706f6e656e7420697320626f756e64656420627920697473206f776e2073616e64626f78202b206675656c206d6f64656c2e0a65786563757461626c652d636f6d706f6e656e74203d207b0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a6c61777061636b2d646570203d207b2069643a20747374722c2076657273696f6e3a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20416461707465722073656c656374696f6e206b65797320534f4c454c59206f666620746865206469676573742d6c6f636b65642060616363657074656454617267657450726f66696c65600a3b20286974732060696460206973207468652070726f66696c652069643b206974732060646967657374602070696e73207468652065786163742070726f66696c652f76657273696f6e292e2054686572650a3b20617265206e6f20696e646570656e64656e7420646973706c617920737472696e6773207468617420636f756c64206469736167726565207769746820746865206c6f636b2c20736f20610a3b207265736f6c7665722063616e6e6f742062696e6420616e206164617074657220746f206f6e6520746172676574207768696c6520746865206c6f636b2070726f76657320616e6f746865720a3b202845444943542d4c41575041434b2d414441505445522d54415247455449522d303031292e0a7461726765742d61646170746572203d207b0a2020616363657074656454617267657450726f66696c653a207265736f757263652d7265662c202020203b206469676573742d6c6f636b65642c20617574686f72697461746976652073656c6563746f720a2020616363657074656454617267657449723a207265736f757263652d7265662c2020202020202020203b206469676573742d6c6f636b65640a2020616461707465723a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d206578706f72742073757266616365202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a6c61777061636b2d6578706f727473203d207b0a202074797065733a205b2a206578706f727465642d747970655d2c0a2020636f6e7374616e74733a205b2a206578706f727465642d636f6e7374616e745d2c0a20207075726546756e6374696f6e733a205b2a20707572652d66756e6374696f6e5d2c0a2020656666656374733a205b2a2073656d616e7469632d6566666563745d2c0a20206f62737472756374696f6e733a205b2a206f62737472756374696f6e2d6465665d2c0a20203b206b65796564206279206f7065726174696f6e2d70726f66696c6520636f6f7264696e61746520e2869220756e697175656e65737320656e666f726365640a20203b202845444943542d4142492d4f5050524f46494c452d554e495155452d303031290a20203b206f7065726174696f6e2d70726f66696c65207265636f7264732074686973206c61777061636b206578706f72747320286f707469632074656d706c6174657320746861740a20203b2060696d706c656d656e7473602f6070726f66696c656020636c6175736573207265736f6c766520616761696e7374292e206f7065726174696f6e2d70726f66696c652069730a20203b20646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c20203b206b6579656420627920636f6f7264696e6174650a7d0a0a6578706f727465642d7479706520202020203d207b20636f6f7264696e6174653a20747374722c20646566696e6974696f6e3a20636f72652d747970652d726566207d0a6578706f727465642d636f6e7374616e74203d207b20636f6f7264696e6174653a20747374722c20747970653a20636f72652d747970652d7265662c2076616c75653a20616e79207d0a0a3b204120707572652068656c7065722069732061206469736372696d696e6174656420756e696f6e2062792060736f75726365602c20736f2074686520736368656d6120697473656c660a3b2067756172616e7465657320616e20696d706c656d656e746174696f6e20657869737473202845444943542d4c41575041434b2d505552452d494d504c2d303031293a0a3b2020202d20226564696374223a20617574686f72656420696e2045646963742f436f72653b2074686520436f726520626f6479206973206361727269656420696e6c696e6520286861736865640a3b20202020207769746820746865206578706f72742073757266616365292e2054686520736368656d61207265717569726573207468652060626f647960206669656c642e0a3b2020202d2022636f6d706f6e656e74223a20696d706c656d656e746564206f7574736964652045646963743b2063617272696573206e6f20696e6c696e6520626f647920616e6420696e73746561640a3b20202020206361727269657320697473206f776e206469676573742d6c6f636b65642060696d706c656d656e746174696f6e60202873616e64626f78202b206675656c292e20497420646f65730a3b20202020206e6f7420646570656e64206f6e20746865206f7074696f6e616c206d616e69666573742d6c6576656c2068656c706572436f6d706f6e656e742e0a707572652d66756e6374696f6e203d2065646963742d707572652d66756e6374696f6e202f20636f6d706f6e656e742d707572652d66756e6374696f6e0a0a707572652d66756e6374696f6e2d636f6d6d6f6e203d20280a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020706172616d6574657254797065733a205b2a20636f72652d747970652d7265665d2c2020202020203b20616c6c20626f756e6465640a202072657475726e547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020636f737454656d706c6174653a20747374722c0a202064657465726d696e69736d436c6173733a2022746f74616c22202f2022746f74616c2d776974682d74797065642d646961676e6f73746963222c0a290a0a65646963742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a20226564696374222c0a2020626f64793a20636f72652d666e2d626f64792c2020202020202020202020202020202020202020203b20696e6c696e652c20686173682d7369676e69666963616e740a7d0a0a636f6d706f6e656e742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a2022636f6d706f6e656e74222c0a20203b20746865206469676573742d6c6f636b656420636f6d706f6e656e7420696d706c656d656e74696e6720746869732068656c7065722e2052657175697265642061742074686520736368656d610a20203b206c6576656c20736f206120636f6d706f6e656e742068656c7065722063616e206e657665722076616c696461746520776974686f7574206120686173682d626f756e642c0a20203b2073616e64626f782b6675656c2d64657363726962656420696d706c656d656e746174696f6e202845444943542d4c41575041434b2d505552452d494d504c2d303031292e0a2020696d706c656d656e746174696f6e3a2065786563757461626c652d636f6d706f6e656e742c0a7d0a0a3b20636f72652d666e2d626f647920697320646566696e65642062792065646963742d636f72652e6364646c20616e6420617373656d626c656420776974682074686973206c61777061636b0a3b20736368656d612e2049742069732061207075726520436f72652066756e6374696f6e20626f64792c206e6f7420616e206566666563742d63617061626c6520636f72652d626c6f636b2e0a0a73656d616e7469632d656666656374203d207b0a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020696e707574547970653a20636f72652d747970652d7265662c2020202020202020202020202020203b20626f756e6465640a20206f7574707574547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020657865637574696f6e436c6173733a202270726f6f664f6e6c7922202f202272756e74696d65222c2020203b206f7274686f676f6e616c20746f207772697465436c6173730a20206566666563744b696e6448696e743a206566666563742d6b696e642c0a2020666f6f747072696e744f626c69676174696f6e3a20747374722c0a2020636f73744f626c69676174696f6e3a20747374722c0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c20203b206b6579656420627920636f6f7264696e6174653b20756e697175650a20206775617264537570706f72743a20626f6f6c2c0a7d0a0a6f62737472756374696f6e2d646566203d207b0a2020636f6f7264696e6174653a20747374722c0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164536368656d613a20636f72652d747970652d7265662c20202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d6c61777061636b2d616461707465722e6364646c202d2d2d0a3b2065646963742d6c61777061636b2d616461707465722e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f72206f6e6520646972656374206465636c61726174697665206c61777061636b2074617267657420616461707465722e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b2054686520656e636c6f73696e67206c61777061636b206d616e69666573742073656c6563747320746865206578616374207461726765742070726f66696c652c207461726765742049522c0a3b20616e642061646170746572207265736f75726365206469676573742e2054686f7365206964656e74697469657320617265206e6f7420726570656174656420686572652e0a0a6c61777061636b2d61646170746572203d207b0a202061706956657273696f6e3a202265646963742e6c61777061636b2d616461707465722f7631222c0a2020636c6173733a20226465636c61726174697665222c0a20206f7065726174696f6e50726f66696c65733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6f7065726174696f6e2d70726f66696c650a20207d2c0a2020656666656374496d706c656d656e746174696f6e733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6566666563740a20207d2c0a2020627564676574733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6275646765740a20207d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206c61777061636b206f7065726174696f6e2d70726f66696c6520636f6f7264696e617465732e0a6c61777061636b2d616461707465722d6f7065726174696f6e2d70726f66696c65203d207b0a2020636f72653a20747374722c0a202073656d616e746963456666656374733a205b2b20747374725d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206c61777061636b2073656d616e7469632d65666665637420636f6f7264696e617465732e20466f6f747072696e742c20636f73742c20616e640a3b206661696c757265206669656c6473206d7573742065786163746c792064697363686172676520746865206d61746368696e67206578706f72746564206566666563742e0a6c61777061636b2d616461707465722d656666656374203d207b0a2020746172676574496e7472696e7369633a20747374722c0a2020746172676574436f6e66696775726174696f6e3a207265736f757263652d7265662c0a20207772697465436c6173733a206c61777061636b2d616461707465722d77726974652d636c6173732c0a2020666f6f747072696e744f626c69676174696f6e3a20747374722c0a2020636f73744f626c69676174696f6e3a20747374722c0a20206661696c7572654d617070696e67733a207b202a206661696c7572652d6964656e74203d3e2074737472207d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206578706f7274656420636f73742d6f626c69676174696f6e20636f6f7264696e617465732e0a6c61777061636b2d616461707465722d627564676574203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a6c61777061636b2d616461707465722d77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f0a20202020202020202020202020202020202020202020202020202020202022617070656e6422202f20227265706c61636522202f202264656c65746522202f2022637573746f6d220a0a3b206661696c7572652d6964656e7420697320646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c2e0a0a3b202d2d2d2065646963742d7461726765742d70726f66696c652e6364646c202d2d2d0a3b2065646963742d7461726765742d70726f66696c652e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374207461726765742070726f66696c65206d616e69666573742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76310a3b202873656520535045435f636f6e74696e75756d2d636f6e74726163742d62756e646c652d76312e6d64292e204a534f4e20696e207468652070726f736520737065637320697320610a3b207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d613b2074686973204344444c206973207468652073696e676c6520736f757263650a3b206f66207472757468202845444943542d4142492d4e4f4455502d303031292e0a0a7461726765742d70726f66696c652d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652f7631222c0a202069643a20747374722c202020202020202020202020202020202020202020202020203b20652e672e20226563686f2e64706f220a202076657273696f6e3a20747374722c20202020202020202020202020202020202020203b20652e672e202231220a20206163636570746564436f72654162693a205b2b20747374725d2c20202020202020203b20652e672e205b2265646963742e636f72652f7631225d0a0a2020696e7472696e736963733a207265736f757263652d7265662c0a2020696e7472696e7369634e616d6573706163653a20747374722c0a20203b207075626c697368657320746869732070726f66696c652773206f7065726174696f6e2d70726f66696c65207265636f72647320286f707469632074656d706c6174657320746861740a20203b206070726f66696c65602f60696d706c656d656e74736020636c6175736573207265736f6c766520616761696e7374292e205265666572656e63657320616e0a20203b206f7065726174696f6e2d70726f66696c65732d646f63756d656e74202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207265736f757263652d7265662c0a2020666f6f747072696e74416c67656272613a207265736f757263652d7265662c0a2020636f7374416c67656272613a207265736f757263652d7265662c0a202074617267657449723a207265736f757263652d7265662c0a20206f62737472756374696f6e5461786f6e6f6d793a207265736f757263652d7265662c0a202076657269666965723a207265736f757263652d7265662c0a20206c6f77657265723a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a0a20203b206669656c647320746865206c616e67756167652073706563207265717569726573206f662065766572792070726f66696c650a202062756e646c6550726f66696c653a207265736f757263652d7265662c0a202067656e657261746564417274696661637450726f66696c65733a205b2a207265736f757263652d7265665d2c0a202063616e6f6e6963616c456e636f64696e6752756c65733a207265736f757263652d7265662c0a20203b20412070726f66696c65207468617420616363657074732074686520646972656374206465636c61726174697665206c61777061636b2d6164617074657220414249206e616d65732069740a20203b2065786163746c79206f6e63652e2050726f66696c6573207468617420646f206e6f7420636f6e73756d65206c61777061636b206164617074657273206c6561766520746869730a20203b206f7074696f6e616c20736c6f7420616273656e74206f7220656d7074792e0a20203f2061636365707465644c61777061636b416461707465724162693a205b5d202f205b2265646963742e6c61777061636b2d616461707465722f7631225d2c0a2020646961676e6f737469634162693a207265736f757263652d7265662c0a0a20203b206170706c69636174696f6e20646f637472696e650a20206170706c69636174696f6e4d6f64656c3a202261746f6d6963222c0a202072656164436f6e73697374656e63793a20226170706c69636174696f6e2d736e617073686f7422202f20747374722c0a202067756172644576616c756174696f6e3a2022707265636f6d6d69742d61746f6d696322202f20747374722c0a20206f62737472756374696f6e526f6c6c6261636b3a20226e6f2d76697369626c652d6566666563747322202f20747374722c0a20206d756c74695461726765743a20626f6f6c2c0a20203b207768657468657220746865207461726765742063616e206576616c7561746520707265636f6d6d697420706f7374636f6e646974696f6e20286067756172616e746565602920636865636b730a20203b20696e73696465207468652061746f6d6963206170706c69636174696f6e20756e6974202845444943542d5441524745542d504f5354434f4e442d303031290a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a0a202064657465726d696e6973746963457865637574696f6e3a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d6120627920746865206275696c640a3b202845444943542d4142492d4e4f4455502d303031292e205468657920617265206e6f74207265646566696e656420686572652e0a0a3b202d2d2d20696e7472696e736963207369676e6174757265202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e696665737427732060696e7472696e7369637360207265736f757263652d726566206973207468650a3b20696e7472696e7369632d7369676e617475726520636f7270757320646f63756d656e742062656c6f772e20497473206c61796f757420697320666978656420736f2074776f0a3b20696e646570656e64656e742070726f66696c65732076616c69646174652f686173682074686520636f72707573206964656e746963616c6c790a3b202845444943542d4142492d494e5452494e534943532d444f432d303031292e0a0a3b20696e7472696e736963732069732061204d4150206b6579656420627920636f6f7264696e6174652c20736f2074686520736368656d6120697473656c6620656e666f726365730a3b20636f6f7264696e61746520756e697175656e6573732e20412070726f766964657220726563656976657320746865207265736f6c76656420636f7270757320617320610a3b206469676573742d626f756e642073656d616e74696320696e70757420616e64207265736f6c76657320636f6f7264696e617465732077697468696e20746861742061727469666163742e0a3b2045616368206d6170206b6579204d55535420657175616c20697473207265636f726427732060636f6f7264696e61746560206669656c640a3b202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e696e7472696e736963732f7631222c0a2020696e7472696e736963733a207b202a2074737472203d3e20696e7472696e736963207d2c0a7d0a0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e6966657374277320606f7065726174696f6e50726f66696c657360207265736f757263652d7265662e0a3b206f7065726174696f6e2d70726f66696c65202f206f707469632d74656d706c6174652061726520646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c2e204b657965642062790a3b20636f6f7264696e61746520736f207265736f6c7574696f6e2063616e2774207069636b206265747765656e2074776f2073616d652d636f6f7264696e6174652070726f66696c65730a3b202845444943542d4142492d4f5050524f46494c452d534c4f542d3030312c2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e6f7065726174696f6e2d70726f66696c65732f7631222c0a202070726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c0a7d0a0a3b2041207479706564207072652d6c6f776572696e67207175657374696f6e20746861742063616e2062652070726f706f73656420627920576174736f6e206f7220616e206167656e7420616e640a3b20636865636b65642062792074686520636f6d70696c65722e2049742069732063616e6f6e6963616c2d43424f5220656e636f64656420756e6465720a3b206065646963742e6c6f776572696e672d726571756972656d656e74732f7631603b2074686520636f6d70696c657220636865636b7320746869732061727469666163742c206e6f74207468650a3b2070726f736520746861742070726f64756365642069742e0a6c6f776572696e672d726571756972656d656e7473203d207b0a202061706956657273696f6e3a202265646963742e6c6f776572696e672d726571756972656d656e74732f7631222c0a20206f7065726174696f6e50726f66696c653a20747374722c0a202073656d616e746963456666656374733a205b2a2073656d616e7469632d6566666563742d726571756972656d656e745d2c0a202072657175697265645772697465436c61737365733a205b2a2077726974652d636c6173735d2c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a202061746f6d69636974793a2061746f6d69636974792d726571756972656d656e742c0a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a20206f70746963436f6e74726163743a20747374722c0a7d0a0a73656d616e7469632d6566666563742d726571756972656d656e74203d207b0a2020636f6f7264696e6174653a20747374722c0a20207772697465436c6173733a2077726974652d636c6173732c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a7d0a0a77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a2020202020202020202020202020227265706c61636522202f202264656c65746522202f20747374720a67756172642d6b696e64203d2022707265636f6d6d69742d61746f6d696322202f20747374720a61746f6d69636974792d726571756972656d656e74203d202261746f6d696322202f20747374720a0a3b20412067656e75696e6520756e696f6e3a207075726520636f6e7374727563746f7273206361727279206e6f20656666656374206b696e64206f72206661696c757265733b206566666563740a3b20696e7472696e73696373206d757374202845444943542d5441524745542d494e5452494e5349432d434c4153532d303031292e2054686520736368656d6120656e666f7263657320746869732c0a3b206e6f74206120636f6d6d656e742e0a0a3b2054686520696e7472696e736963277320636f6f7264696e6174652069732074686520696e7472696e73696373206d6170204b45592c206e6f7420612076616c7565206669656c642c20736f207468650a3b206b657920616e6420636f6f7264696e6174652063616e206e65766572206469736167726565202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963203d20707572652d696e7472696e736963202f206566666563742d696e7472696e7369630a0a707572652d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a202270757265222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206775617264537570706f72743a2066616c73652c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20226e6f6e65222c0a7d0a0a6566666563742d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a2022656666656374222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206566666563744b696e643a206566666563742d6b696e642c0a20203b206d6170206b65796564206279206661696c75726520636f6f7264696e61746520286661696c7572652d6964656e74293b20746865206661696c75726520636f6f7264696e6174652069730a20203b20746865206b65792c206e6f7420612076616c7565206669656c642c20736f20756e697175656e657373206973207374727563747572616c0a20203b202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c0a20206775617264537570706f72743a20626f6f6c2c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f20227265706c61636522202f0a20202020202020202020202020202264656c65746522202f2022637573746f6d222c0a202063616e5061727469636970617465496e41746f6d696347756172643a20626f6f6c2c0a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d617574686f726974792d66616374732e6364646c202d2d2d0a3b2065646963742d617574686f726974792d66616374732e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f722074686520666972737420636f6d70696c65722d636f6e7465787420617574686f726974792d666163747320646f63756d656e742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20736f20736f757263652e6469676573742075736573207468650a3b20736861726564207368613235362d6469676573742074797065642076616c75652e204a534f4e2069732061207265766965772f696e7075742072656e646572696e673a206974730a3b20607368613235363a3c3634206865783e6020736f75726365206469676573742069732070726f6a656374656420746f205b60736861323536602c203332207261772062797465735d206f6e0a3b2074686520776972652c20616e64206974732066616374206172726179732070726f6a65637420746f2074686520636f6f7264696e6174652d6b65796564206d6170732062656c6f772e0a0a617574686f726974792d6661637473203d207b0a202061706956657273696f6e3a202265646963742e617574686f726974792d66616374732f7631222c0a2020736f757263653a20617574686f726974792d666163742d736f757263652c0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e20617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374207d2c0a20206566666563745772697465436c61737365733a207b202a2074737472203d3e20617574686f726974792d77726974652d636c617373207d2c0a2020627564676574733a207b202a2074737472203d3e20617574686f726974792d6275646765742d66616374207d2c0a7d0a0a617574686f726974792d666163742d736f75726365203d207b0a20206b696e643a20226c61777061636b22202f202274617267657450726f66696c65222c0a2020636f6f7264696e6174653a20747374722c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a3b20546865206d6170206b65792069732074686520736f75726365206f7065726174696f6e2d70726f66696c6520636f6f7264696e6174652e204974206973206e6f7420726570656174656420696e0a3b207468652076616c75652c20736f2061206b657920616e6420656d62656464656420636f6f7264696e6174652063616e6e6f742064697361677265652e20416c6c6f7765642077726974650a3b20636c61737365732061726520612063616e6f6e6963616c206d61702d7365743a2074686520636c6173732069732074686520756e69717565206b657920616e64206e756c6c206973207468650a3b20756e6974206d61726b65722e2043616e6f6e6963616c2043424f52206669786573206b6579206f7264657220776974686f75742061207365636f6e64206f72646572696e672072756c652e0a617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374203d207b0a2020636f72653a20747374722c0a2020616c6c6f7765645772697465436c61737365733a207b202a20617574686f726974792d77726974652d636c617373203d3e206e756c6c207d2c0a7d0a0a3b20546865206566666563745772697465436c6173736573206d6170206b6579206973207468652073656d616e7469632065666665637420636f6f7264696e6174652e2054686520627564676574730a3b206d6170206b65792069732074686520736f757263652062756467657420636f6f7264696e6174652e2043616e6f6e6963616c2043424f52206d61702d6b657920756e697175656e6573730a3b206d616b6573206475706c6963617465206661637420636f6f7264696e61746573207374727563747572616c6c7920756e726570726573656e7461626c652e0a617574686f726974792d6275646765742d66616374203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a3b20417574686f726974794661637473446f63756d656e7420763120696e74656e74696f6e616c6c792061636365707473206f6e6c792074686520777269746520636c6173736573207468650a3b2063757272656e7420636f6d70696c6572206d6f64656c2063616e20636f6e73756d652e2060637573746f6d602069732074686520736f6c6520763120637573746f6d207370656c6c696e673b0a3b20617262697472617279207461726765742d70726f66696c6520657874656e73696f6e20737472696e677320646f206e6f7420656e746572207468697320636f6d70696c657220706174682e0a617574686f726974792d77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a202020202020202020202020202020202020202020202020227265706c61636522202f202264656c65746522202f2022637573746f6d220a0a3b202d2d2d2065646963742d7461726765742d69722e6364646c202d2d2d0a3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d7461726765742d69722e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f72207468652045646963742d6f776e65642054617267657420495220617274696661637420656e76656c6f70652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20616e642065646963742d636f72652e6364646c2e2049740a3b2064656c696265726174656c792072657573657320436f72652065787072657373696f6e732c20707265646963617465732c20627564676574732c206c6f63616c207265666572656e6365732c0a3b206f62737472756374696f6e20726561736f6e732c20616e64206f62737472756374696f6e2061726d7320736f2074686520736368656d61206d617463686573207468652076616c75650a3b20656d6974746564206279207468652063616e6f6e6963616c2054617267657420495220656e636f64657220726174686572207468616e20726573746174696e672074686f73652074797065732e0a3b2049742064657363726962657320746865207374727563747572616c207368617065206f662076616c6964206c6f776572696e672d70726f6475636564206172746966616374732e205468650a3b206c6f776572696e6720616e6420656e636f64657220636f6e7472616374732073657061726174656c7920656e666f7263652073656d616e746963206964656e7469666965722072756c65730a3b20616e642063616e6f6e6963616c206f72646572696e672f64656475706c69636174696f6e20666f72207365742d6c696b652076616c7565732e0a0a3b2054617267657420495220656e636f64696e672072656a6563747320616e20656d707479207461726765742d70726f66696c6520636f6f7264696e617465206265666f72652062797465730a3b2065786973742c20736f207468697320726f6f74207469676874656e732074686520736861726564207374727563747572616c207265736f757263652d726566206163636f7264696e676c792e0a7461726765742d69722d7265736f757263652d726566203d207b0a202069643a2074737472202e7265676578702022283f73292e2b222c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a7461726765742d69722d6172746966616374203d207461726765742d69722d636c6f7365642d6172746966616374202f207461726765742d69722d6c65676163792d61727469666163740a0a7461726765742d69722d61727469666163742d636f6d6d6f6e203d20280a20206b696e643a202274617267657449724172746966616374222c0a2020646f6d61696e3a20747374722c0a202074617267657450726f66696c653a207461726765742d69722d7265736f757263652d7265662c0a2020736f75726365436f7265436f6f7264696e6174653a2074737472202e7265676578702022283f73292e2b222c0a290a0a7461726765742d69722d636c6f7365642d6172746966616374203d207b0a20207461726765742d69722d61727469666163742d636f6d6d6f6e2c0a202073656d616e746963436c6f737572653a207461726765742d69722d73656d616e7469632d636c6f737572652c0a2020696e74656e74733a207b202a2074737472203d3e207461726765742d69722d696e74656e74207d2c0a7d0a0a7461726765742d69722d6c65676163792d6172746966616374203d207b0a20207461726765742d69722d61727469666163742d636f6d6d6f6e2c0a2020696e74656e74733a207b202a2074737472203d3e207461726765742d69722d6c65676163792d696e74656e74207d2c0a7d0a0a7461726765742d69722d73656d616e7469632d636c6f73757265203d207b0a2020736f75726365436f72653a207461726765742d69722d7265736f757263652d7265662c0a20206c61777061636b733a205b2a207461726765742d69722d7265736f757263652d7265665d2c0a7d0a0a7461726765742d69722d696e74656e74203d207b0a20207461726765742d69722d696e74656e742d636f6d6d6f6e2c0a20203f2062617369733a20636f72652d657870722c0a7d0a0a7461726765742d69722d6c65676163792d696e74656e74203d207b0a20207461726765742d69722d696e74656e742d636f6d6d6f6e2c0a7d0a0a7461726765742d69722d696e74656e742d636f6d6d6f6e203d20280a20206f7065726174696f6e50726f66696c653a20747374722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020726571756972656d656e74733a205b2a207461726765742d69722d726571756972656d656e745d2c0a202073746570733a205b2a207461726765742d69722d737465705d2c0a2020726573756c743a20636f72652d657870722c0a290a0a7461726765742d69722d726571756972656d656e74203d207b0a202069643a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a0a7461726765742d69722d73746570203d207b0a202069643a20747374722c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020746172676574496e7472696e7369633a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4661696c757265733a205b2a206661696c7572652d6964656e745d2c0a20206f62737472756374696f6e41726d733a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a", - "rawSha256": "b01990008a21dfe34e24c5306c562d71cd9afb83d1355a5677a7b44d8efe5ea5" + "bytesHex": "3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d70726f76696465722d636f6e7472616374732e6364646c0a3b2047656e6572617465642066726f6d2045646963742d6f776e65642041424920667261676d656e74732e20444f204e4f5420454449542e0a0a3b202d2d2d2065646963742d636f6d6d6f6e2e6364646c202d2d2d0a3b2065646963742d636f6d6d6f6e2e6364646c0a3b20536861726564204344444c20747970657320666f722074686520456469637420414249732c20646566696e6564204f4e4345206865726520736f20746865792063616e6e6f742064726966740a3b202845444943542d4142492d4e4f4455502d303031292e2054776f2067726f7570733a0a3b2020202d207265736f757263652d7265662c207368613235362d6469676573742c206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c0a3b2020202020636f72652d747970652d7265663a20617373656d626c656420776974682065646963742d7461726765742d70726f66696c652e6364646c20616e640a3b202020202065646963742d6c61777061636b2e6364646c20627920746865206275696c643b2074686f736520736368656d617320646f206e6f74207265646566696e65207468656d2e0a3b2020202d206f7065726174696f6e2d70726f66696c652c206f707469632d74656d706c6174652c2061706572747572652d726571756972656d656e7420616e6420746865697220726566733a0a3b2020202020636f6e73756d65642062792074686520436f72652f6f70746963206c61796572202865646963742d636f72652e6364646c2920616e64207265666572656e636564206279207468650a3b20202020206c616e67756167652f7461726765742d70726f66696c652073706563732e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a0a3b2041206e6f726d617469766520737562636f6d706f6e656e74207265666572656e636564206279206964656e7469747920706c7573206469676573742e204d616e696665737473206e657665720a3b20656d626564207468656972206f776e2073656c662d64696765737420696e20746865697220707265696d616765202845444943542d434f52452d53454c46484153482d303031292e0a7265736f757263652d726566203d207b2069643a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20446967657374732061726520617574686f726974617469766520617320747970656420627974652076616c7565732c206e657665722068657820737472696e67732e20526576696577204a534f4e0a3b2072656e64657273207468697320617320227368613235363a3c3634206c6f77657263617365206865783e22202845444943542d4449474553542d574952452d303031292e0a7368613235362d646967657374203d205b20616c676f726974686d3a2022736861323536222c2062797465733a2062737472202e73697a65203332205d0a0a3b2041206e616d6564206c6f772d6c6576656c206661696c75726520616e206566666563742063616e2072616973652e2054686520736f75726365206f62737472756374696f6e206d61700a3b2062696e64732069742028627920636f6f7264696e6174652920616e6420636f6e73747275637473206120747970656420646f6d61696e206f62737472756374696f6e2066726f6d206974730a3b207061796c6f6164202845444943542d4142492d4641494c5552452d4e414d45442d303031292e0a3b20416e20656666656374277320606566666563744661696c7572657360206c697374204d555354206861766520756e697175652060636f6f7264696e61746560733a2073696e6365207468650a3b206f62737472756374696f6e206d6170206973206b6579656420627920636f6f7264696e6174652c2074776f206661696c757265732073686172696e67206120636f6f7264696e61746520286576656e0a3b207769746820646966666572656e7420617574686f72697479436c6173732f7061796c6f61645479706529206d616b652065786861757374697665206d617070696e6720616e642062696e6465720a3b20747970696e6720616d626967756f757320616e64206172652072656a6563746564202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a3b0a3b2045666665637473206361727279207468656972206661696c757265732061732061206d617020607b206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d600a3b202873656520746865207461726765742f6c61777061636b2065666665637420736368656d6173292e20546865206661696c75726520636f6f7264696e61746520697320746865206d61700a3b204b45592c20736f206974206973206e6f7420726570656174656420696e2074686520626f647920616e642063616e6e6f74206469736167726565207769746820746865206b65792e0a6566666563742d6661696c7572652d626f6479203d207b0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164547970653a20636f72652d747970652d7265662c202020202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b2041206661696c75726520636f6f7264696e617465206d7573742062652061206261726520456469637420606964656e746020286c65747465722f756e64657273636f7265207468656e0a3b206c6574746572732f6469676974732f756e64657273636f7265732920414e44206d757374206e6f742062652061207265736572766564206b6579776f72642028652e672e2060656c7365602c0a3b20606261736973602c20607768657265602c206072657175697265602c2060666f72602c2060696660292e2054686520736f75726365206f62737472756374696f6e2d6d6170204c4853206f6e6c790a3b20616363657074732061206e6f6e2d6b6579776f726420606964656e74602c20736f20612068797068656e2f646f742f6b6579776f726420636f6f7264696e61746520776f756c642062650a3b204142492d76616c69642079657420696d706f737369626c6520746f206d617020657868617573746976656c7920696e20736f757263652e20546865207265676578206361707475726573207468650a3b206c65786963616c2073686170653b206b6579776f7264206578636c7573696f6e20697320616e206164646974696f6e616c2076616c69646174696f6e2072756c650a3b202845444943542d4142492d4641494c5552452d4944454e542d303031292e0a6661696c7572652d6964656e74203d2074737472202e72656765787020225b412d5a612d7a5f5d5b412d5a612d7a302d395f5d2a220a0a6566666563742d6b696e64203d20227265616422202f202263726561746522202f2022656e7375726522202f20227265706c61636522202f202264656c65746522202f0a202020202020202020202020202022617070656e6422202f202272656475636522202f202273656d616e7469632e656d697422202f2022637573746f6d220a0a617574686f726974792d636c617373203d2022646f6d61696e4d61707061626c6522202f20227061727469636970616e744f776e656422202f2022696e746567726974794661756c7422202f0a202020202020202020202020202020202020227265736f757263654661756c7422202f2022696e7465726e616c4661756c74220a0a636f72652d747970652d726566203d20747374722020203b2063616e6f6e6963616c20436f7265207479706520636f6f7264696e6174650a0a3b20416e206f7065726174696f6e2070726f66696c6520737570706c69657320746865206f707469632074656d706c617465206120436f726520696e74656e74207265736f6c766573206974730a3b206f707469634b696e642f626f756e646172794b696e642f737570706f7274506f6c6963792f6c6f7373446973706f736974696f6e2066726f6d2e205461726765742070726f66696c657320616e640a3b206c61777061636b73207075626c6973682074686573652061732061206d617020607b20636f6f7264696e617465203d3e206f7065726174696f6e2d70726f66696c65207d602c20736f207468650a3b20636f6f7264696e61746520697320746865204b45592c206e6f7420612076616c7565206669656c64202845444943542d4f505449432d54454d504c4154452d4f574e45522d3030312c0a3b2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65203d207b0a20206f7074696354656d706c6174653a206f707469632d74656d706c6174652c0a20206566666563745072656469636174653a20747374722c20202020202020202020203b20636f6f7264696e617465206f6620746865206f7065726174696f6e2d6d6f6465207072656469636174650a7d0a0a6f707469632d74656d706c617465203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a2020737570706f7274506f6c6963793a20747374722c202020202020202020202020203b2063616e6f6e6963616c20737570706f72742d706f6c69637920636f6f7264696e6174650a20206c6f7373446973706f736974696f6e3a20747374722c20202020202020202020203b2063616e6f6e6963616c206c6f73732d646973706f736974696f6e20636f6f7264696e6174650a20203f20626173697354656d706c6174653a20747374722c20202020202020202020203b206f7074696f6e616c206469676573742d6c6f636b65642062617369732074656d706c61746520636f6f72640a20203b2074686520617065727475726520726571756972656d656e7420746869732074656d706c61746520737570706c6965732e205265717569726564207768656e207468652074656d706c6174650a20203b2069732074686520736f75726365206f66206120436f7265206f707469632773206170657274757265526571756972656d656e742028692e652e2074686520696e74656e7420686173206e6f0a20203b20736f757263652060666f6f747072696e74203c3d202e2e2e60292c2073696e6365206170657274757265526571756972656d656e74206973206d616e6461746f727920696e20436f72650a20203b202845444943542d4f505449432d41504552545552452d5245462d303031292e0a20203f206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a7d0a0a3b206170657274757265526571756972656d656e742069732061207479706564207265666572656e63652c206e65766572206120667265652d666f726d20737472696e672e2041207265766965770a3b2072656e646572696e67206d61792073686f772069747320636f6f7264696e617465202845444943542d4f505449432d41504552545552452d5245462d303031292e0a61706572747572652d726571756972656d656e74203d20666f6f747072696e742d6365696c696e672d726566202f2061627374726163742d666f6f747072696e742d6f626c69676174696f6e2d7265660a666f6f747072696e742d6365696c696e672d726566203d207b206b696e643a2022666f6f747072696e744365696c696e67222c207265663a2074737472207d0a61627374726163742d666f6f747072696e742d6f626c69676174696f6e2d726566203d207b206b696e643a20226162737472616374466f6f747072696e744f626c69676174696f6e222c207265663a2074737472207d0a0a3b202d2d2d2065646963742d636f72652e6364646c202d2d2d0a3b2065646963742d636f72652e6364646c0a3b204e6f726d617469766520736368656d6120666f722074686520456469637420436f72652076312073656d616e746963206d6f64656c2e0a3b0a3b2053636f706520626f756e646172793a20746869732066696c6520646566696e657320436f7265206d65616e696e6720616e6420736368656d61207368617065206f6e6c792e20497420646f65730a3b206e6f7420646566696e6520612063616e6f6e6963616c20656e636f6465722c20436f7265206d6f64756c652068617368206669656c64732c20686173682066697874757265732c207461726765740a3b206c6f776572696e672c2061646d697373696f6e2062756e646c65732c206f72207461726765742d6f776e65642049522e0a0a636f72652d6d6f64756c65203d207b0a202061706956657273696f6e3a202265646963742e636f72652f7631222c0a2020636f6f7264696e6174653a20747374722c0a2020696d706f7274733a205b2a20636f72652d696d706f72745d2c0a202074797065733a207b202a2074737472203d3e20636f72652d74797065207d2c0a2020696e74656e74733a207b202b2074737472203d3e20636f72652d696e74656e74207d2c0a20207265717569726564436f72654361706162696c69746965733a205b2a20747374725d2c0a7d0a0a636f72652d696d706f7274203d207b0a20206b696e643a20226c61777061636b22202f202274617267657422202f2022636f7265222c0a20207265663a207265736f757263652d7265662c0a20203f20616c6961733a20747374722c0a7d0a0a3b202d2d2d207479706573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d74797065203d20636f72652d7363616c61722d74797065202f20636f72652d7265636f72642d74797065202f20636f72652d76617269616e742d74797065202f0a202020202020202020202020636f72652d6f7074696f6e2d74797065202f20636f72652d6c6973742d74797065202f20636f72652d6d61702d74797065202f0a202020202020202020202020636f72652d6361706162696c6974792d7265662d747970650a0a636f72652d7363616c61722d74797065203d20636f72652d626f6f6c2d74797065202f20636f72652d696e742d74797065202f20636f72652d737472696e672d74797065202f0a20202020202020202020202020202020202020636f72652d62797465732d74797065202f20636f72652d756e69742d747970650a0a636f72652d626f6f6c2d74797065203d207b206b696e643a2022426f6f6c22207d0a636f72652d756e69742d74797065203d207b206b696e643a2022556e697422207d0a636f72652d696e742d74797065203d207b0a20206b696e643a202249363422202f202255363422202f202249333222202f202255333222202f202249313622202f202255313622202f2022493822202f20225538222c0a7d0a636f72652d737472696e672d74797065203d207b0a20206b696e643a2022537472696e67222c0a20206d61783a2075696e742c0a202063616e6f6e6963616c3a2022756e69636f64652d7363616c61722d6e666322202f20227261772d75746638222c0a7d0a636f72652d62797465732d74797065203d207b0a20206b696e643a20224279746573222c0a20206d61783a2075696e742c0a7d0a636f72652d7265636f72642d74797065203d207b0a20206b696e643a20225265636f7264222c0a20206669656c64733a207b202a2074737472203d3e20636f72652d747970652d726566207d2c0a7d0a636f72652d76617269616e742d74797065203d207b0a20206b696e643a202256617269616e74222c0a202063617365733a207b202b2074737472203d3e2076617269616e742d636173652d626f6479207d2c0a7d0a76617269616e742d636173652d626f6479203d207b0a20203f207061796c6f61643a20636f72652d747970652d7265662c0a7d0a636f72652d6f7074696f6e2d74797065203d207b0a20206b696e643a20224f7074696f6e222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a636f72652d6c6973742d74797065203d207b0a20206b696e643a20224c697374222c0a20206974656d3a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6d61702d74797065203d207b0a20206b696e643a20224d6170222c0a20206b65793a20636f72652d747970652d7265662c0a202076616c75653a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6361706162696c6974792d7265662d74797065203d207b0a20206b696e643a20224361706162696c697479526566222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a0a3b20636f72652d747970652d72656620697320646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d612e0a0a3b202d2d2d207265666572656e63657320616e642076616c756573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a3b204c6f63616c206964656e7469747920697320616c7068612d737461626c652e20606964602069732074686520636f6d70696c65722d6f776e6564206c6f63616c20636f6f7264696e6174653b0a3b2060616c7068614e616d656020697320746865206e6f726d616c697a65642068756d616e2f6465627567206e616d652e20536f757263652062696e646572207370656c6c696e67206973206e6f740a3b206964656e746974792e0a6c6f63616c2d726566203d207b0a202069643a20747374722c0a2020616c7068614e616d653a20747374722c0a2020747970653a20636f72652d747970652d7265662c0a7d0a0a636f72652d76616c7565203d20636f72652d6e756c6c2d76616c7565202f20636f72652d626f6f6c2d76616c7565202f20636f72652d696e742d76616c7565202f0a20202020202020202020202020636f72652d737472696e672d76616c7565202f20636f72652d62797465732d76616c7565202f20636f72652d7265636f72642d76616c7565202f0a20202020202020202020202020636f72652d76617269616e742d76616c7565202f20636f72652d6c6973742d76616c7565202f20636f72652d6d61702d76616c7565202f0a20202020202020202020202020636f72652d6361706162696c6974792d76616c75650a0a636f72652d6e756c6c2d76616c7565203d207b206b696e643a20226e756c6c22207d0a636f72652d626f6f6c2d76616c7565203d207b206b696e643a2022626f6f6c222c2076616c75653a20626f6f6c207d0a636f72652d696e742d76616c7565203d207b206b696e643a2022696e74222c2077696474683a20747374722c2076616c75653a20696e74207d0a636f72652d737472696e672d76616c7565203d207b206b696e643a2022737472696e67222c2076616c75653a2074737472207d0a636f72652d62797465732d76616c7565203d207b206b696e643a20226279746573222c2076616c75653a2062737472207d0a636f72652d7265636f72642d76616c7565203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d76616c7565207d207d0a636f72652d76617269616e742d76616c7565203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d76616c75652c0a7d0a636f72652d6c6973742d76616c7565203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d76616c75655d207d0a636f72652d6d61702d76616c7565203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d76616c75652c2076616c75653a20636f72652d76616c75655d5d207d0a636f72652d6361706162696c6974792d76616c7565203d207b0a20206b696e643a20226361706162696c697479222c0a2020726563656970743a207368613235362d6469676573742c0a7d0a0a3b2045646963742d617574686f72656420707572652068656c70657273207573652061207075726520436f72652066756e6374696f6e20626f64792e2054686520626f64792063616e2062696e640a3b20707572652065787072657373696f6e7320616e642072657475726e20616e2065787072657373696f6e2c206275742069742063616e6e6f7420636f6e7461696e20436f7265206566666563742c0a3b2067756172642c206272616e63682c206c6f6f702c206d617463682d6e6f64652c206f722070726f6f662d6f626c69676174696f6e206e6f6465732e0a636f72652d666e2d626f6479203d207b0a2020706172616d733a205b2a206c6f63616c2d7265665d2c0a2020626f64793a20636f72652d707572652d626c6f636b2c0a7d0a0a636f72652d707572652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a202062696e64696e67733a205b2a20707572652d6c65742d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a707572652d6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a0a3b202d2d2d2065787072657373696f6e7320616e642070726564696361746573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d65787072203d206c6f63616c2d65787072202f20636f6e73742d65787072202f207265636f72642d65787072202f206669656c642d65787072202f0a20202020202020202020202076617269616e742d65787072202f206d617463682d65787072202f2063616c6c2d65787072202f206c6973742d65787072202f206d61702d65787072202f0a20202020202020202020202069662d657870720a0a6c6f63616c2d65787072203d207b206b696e643a20226c6f63616c222c207265663a206c6f63616c2d726566207d0a636f6e73742d65787072203d207b206b696e643a2022636f6e7374222c2076616c75653a20636f72652d76616c7565207d0a7265636f72642d65787072203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d65787072207d207d0a6669656c642d65787072203d207b206b696e643a20226669656c64222c20626173653a20636f72652d657870722c206669656c643a2074737472207d0a76617269616e742d65787072203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d657870722c0a7d0a6d617463682d65787072203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d61726d5d2c0a7d0a6d617463682d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d657870722c0a7d0a63616c6c2d65787072203d207b0a20206b696e643a202263616c6c222c0a202063616c6c65653a20747374722c0a202074797065417267733a205b2a20636f72652d747970652d7265665d2c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6c6973742d65787072203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d657870725d207d0a6d61702d65787072203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d657870722c2076616c75653a20636f72652d657870725d5d207d0a69662d65787072203d207b0a20206b696e643a20226966222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d657870722c0a2020656c73653a20636f72652d657870722c0a7d0a0a636f72652d707265646963617465203d20747275652d707265646963617465202f2066616c73652d707265646963617465202f206e6f742d707265646963617465202f0a2020202020202020202020202020202020616c6c2d707265646963617465202f20616e792d707265646963617465202f20636f6d706172652d707265646963617465202f0a202020202020202020202020202020202063616c6c2d707265646963617465202f206f62737472756374696f6e2d7072656469636174650a0a747275652d707265646963617465203d207b206b696e643a20227472756522207d0a66616c73652d707265646963617465203d207b206b696e643a202266616c736522207d0a6e6f742d707265646963617465203d207b206b696e643a20226e6f74222c2076616c75653a20636f72652d707265646963617465207d0a616c6c2d707265646963617465203d207b206b696e643a2022616c6c222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a616e792d707265646963617465203d207b206b696e643a2022616e79222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a636f6d706172652d707265646963617465203d207b0a20206b696e643a2022636f6d70617265222c0a20206f703a20223d3d22202f2022213d22202f20223c22202f20223c3d22202f20223e22202f20223e3d222c0a20206c6566743a20636f72652d657870722c0a202072696768743a20636f72652d657870722c0a7d0a63616c6c2d707265646963617465203d207b0a20206b696e643a202263616c6c222c0a20207072656469636174653a20747374722c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6f62737472756374696f6e2d707265646963617465203d207b0a20206b696e643a20226f62737472756374696f6e222c0a2020636f6f7264696e6174653a206661696c7572652d6964656e742c0a20207061796c6f61643a20636f72652d657870722c0a7d0a0a696e7075742d636f6e73747261696e74203d207b0a2020636f6f7264696e6174653a20747374722c0a2020736f757263653a2022776865726522202f2022636f6d70696c6572222c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a3b202d2d2d20696e74656e74732c20626c6f636b732c20616e64206e6f646573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d696e74656e74203d207b0a2020696e7075743a20636f72652d747970652d7265662c0a20206f75747075743a20636f72652d747970652d7265662c0a202072657175697265644f7065726174696f6e50726f66696c653a20747374722c0a20203f2062617369733a20636f72652d657870722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020626f64793a20636f72652d626c6f636b2c0a20203f206f707469633a20636f72652d6f707469632c0a7d0a0a636f72652d627564676574203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a636f72652d6f70746963203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a20206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a2020737570706f7274506f6c6963793a20747374722c0a20206c6f7373446973706f736974696f6e3a20747374722c0a7d0a0a636f72652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a20206e6f6465733a205b2a20636f72652d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a636f72652d6e6f6465203d206c65742d6e6f6465202f20726571756972652d6e6f6465202f206566666563742d6e6f6465202f2067756172642d6e6f6465202f206272616e63682d6e6f6465202f0a202020202020202020202020666f722d6e6f6465202f206d617463682d6e6f6465202f2070726f6f662d6f626c69676174696f6e2d6e6f64650a0a6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a726571756972652d6e6f6465203d207b0a20206b696e643a202272657175697265222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a726571756972652d6661696c7572652d61726d203d207465726d696e616c2d726571756972652d6661696c757265202f0a20202020202020202020202020202020202020202020636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c7572650a7465726d696e616c2d726571756972652d6661696c757265203d207b0a20206b696e643a20227465726d696e616c222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c757265203d207b0a20206b696e643a2022636f6e74696e75654f627374727563746564222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a6f62737472756374696f6e2d726561736f6e203d207b0a2020726561736f6e4b696e643a20747374722c0a20207061796c6f61643a207b202a2074737472203d3e20636f72652d65787072207d2c0a7d0a6566666563742d6e6f6465203d207b0a20206b696e643a2022656666656374222c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4d61703a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a6f62737472756374696f6e2d61726d203d207b0a202062696e6465723a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a67756172642d6e6f6465203d207b0a20206b696e643a20226775617264222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f62737472756374696f6e3a20636f72652d657870722c0a7d0a6272616e63682d6e6f6465203d207b0a20206b696e643a20226272616e6368222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d626c6f636b2c0a2020656c73653a20636f72652d626c6f636b2c0a7d0a666f722d6e6f6465203d207b0a20206b696e643a2022666f72222c0a202062696e6465723a206c6f63616c2d7265662c0a2020697465723a20636f72652d657870722c0a2020626f756e643a20636f72652d626f756e642c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a6d617463682d6e6f6465203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d626c6f636b2d61726d5d2c0a7d0a6d617463682d626c6f636b2d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a70726f6f662d6f626c69676174696f6e2d6e6f6465203d207b0a20206b696e643a202270726f6f66222c0a2020636f6f7264696e6174653a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a636f72652d626f756e64203d206c69746572616c2d626f756e64202f20636f6f7264696e6174652d626f756e640a6c69746572616c2d626f756e64203d207b206b696e643a20226c69746572616c222c2076616c75653a2075696e74207d0a636f6f7264696e6174652d626f756e64203d207b206b696e643a2022636f6f7264696e617465222c207265663a2074737472207d0a0a3b20536861726564207265736f757263652d7265662c207368613235362d6469676573742c206661696c7572652d6964656e742c2061706572747572652d726571756972656d656e742c20616e640a3b20636f72652d747970652d7265662061726520646566696e6564206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c2e0a0a3b202d2d2d2065646963742d6c61777061636b2e6364646c202d2d2d0a3b2065646963742d6c61777061636b2e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374206c61777061636b206d616e696665737420616e64206578706f727420737572666163652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e204a534f4e20696e207468652070726f73652073706563730a3b2069732061207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a6c61777061636b2d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e6c61777061636b2f7631222c0a202069643a20747374722c0a202076657273696f6e3a20747374722c0a20206163636570746564436f72654162693a205b2b20747374725d2c0a2020646570656e64656e636965733a205b2a206c61777061636b2d6465705d2c202020202020202020203b20616379636c69632c206469676573742d6c6f636b6564202845444943542d4c41575041434b2d4441472d303031290a20206578706f7274733a207265736f757263652d7265662c0a20203f2074617267657441646170746572733a205b2b207461726765742d616461707465725d2c2020203b207265717569726564206f6e6c7920696620616e792072756e74696d6520656666656374206578697374730a20203f2068656c706572436f6d706f6e656e743a2065786563757461626c652d636f6d706f6e656e742c203b2065786563757461626c652068656c70657273206361727279207468656972206f776e2073616e64626f782b6675656c0a202076657269666965723a2076657269666965722c202020202020202020202020202020202020202020203b20636c61737369666965643a206465636c61726174697665206f722065786563757461626c650a2020636f6d7061746962696c6974793a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b2041207665726966696572206973206569746865722061206465636c617261746976652072756c6573657420286e6f2072756e74696d6529206f7220616e2065786563757461626c650a3b20636f6d706f6e656e742e20416e2065786563757461626c65207665726966696572204d55535420636172727920697473206f776e2073616e64626f7820616e64206675656c206d6f64656c2c0a3b20736f2074686520736368656d6120656e666f726365732074686174206e6f2065786563757461626c6520636f6d706f6e656e74206973206c65667420756e626f756e6465640a3b202845444943542d4142492d56455249464945522d424f554e442d303031292e0a7665726966696572203d206465636c617261746976652d7665726966696572202f2065786563757461626c652d76657269666965720a6465636c617261746976652d7665726966696572203d207b20636c6173733a20226465636c61726174697665222c2072756c657365743a207265736f757263652d726566207d0a65786563757461626c652d7665726966696572203d207b0a2020636c6173733a202265786563757461626c65222c0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a3b20416e792065786563757461626c6520636f6d706f6e656e7420697320626f756e64656420627920697473206f776e2073616e64626f78202b206675656c206d6f64656c2e0a65786563757461626c652d636f6d706f6e656e74203d207b0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a6c61777061636b2d646570203d207b2069643a20747374722c2076657273696f6e3a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20416461707465722073656c656374696f6e206b65797320534f4c454c59206f666620746865206469676573742d6c6f636b65642060616363657074656454617267657450726f66696c65600a3b20286974732060696460206973207468652070726f66696c652069643b206974732060646967657374602070696e73207468652065786163742070726f66696c652f76657273696f6e292e2054686572650a3b20617265206e6f20696e646570656e64656e7420646973706c617920737472696e6773207468617420636f756c64206469736167726565207769746820746865206c6f636b2c20736f20610a3b207265736f6c7665722063616e6e6f742062696e6420616e206164617074657220746f206f6e6520746172676574207768696c6520746865206c6f636b2070726f76657320616e6f746865720a3b202845444943542d4c41575041434b2d414441505445522d54415247455449522d303031292e0a7461726765742d61646170746572203d207b0a2020616363657074656454617267657450726f66696c653a207265736f757263652d7265662c202020203b206469676573742d6c6f636b65642c20617574686f72697461746976652073656c6563746f720a2020616363657074656454617267657449723a207265736f757263652d7265662c2020202020202020203b206469676573742d6c6f636b65640a2020616461707465723a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d206578706f72742073757266616365202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a6c61777061636b2d6578706f727473203d207b0a202074797065733a205b2a206578706f727465642d747970655d2c0a2020636f6e7374616e74733a205b2a206578706f727465642d636f6e7374616e745d2c0a20207075726546756e6374696f6e733a205b2a20707572652d66756e6374696f6e5d2c0a2020656666656374733a205b2a2073656d616e7469632d6566666563745d2c0a20206f62737472756374696f6e733a205b2a206f62737472756374696f6e2d6465665d2c0a20203b206b65796564206279206f7065726174696f6e2d70726f66696c6520636f6f7264696e61746520e2869220756e697175656e65737320656e666f726365640a20203b202845444943542d4142492d4f5050524f46494c452d554e495155452d303031290a20203b206f7065726174696f6e2d70726f66696c65207265636f7264732074686973206c61777061636b206578706f72747320286f707469632074656d706c6174657320746861740a20203b2060696d706c656d656e7473602f6070726f66696c656020636c6175736573207265736f6c766520616761696e7374292e206f7065726174696f6e2d70726f66696c652069730a20203b20646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c20203b206b6579656420627920636f6f7264696e6174650a7d0a0a6578706f727465642d7479706520202020203d207b20636f6f7264696e6174653a20747374722c20646566696e6974696f6e3a20636f72652d747970652d726566207d0a6578706f727465642d636f6e7374616e74203d207b20636f6f7264696e6174653a20747374722c20747970653a20636f72652d747970652d7265662c2076616c75653a20616e79207d0a0a3b204120707572652068656c7065722069732061206469736372696d696e6174656420756e696f6e2062792060736f75726365602c20736f2074686520736368656d6120697473656c660a3b2067756172616e7465657320616e20696d706c656d656e746174696f6e20657869737473202845444943542d4c41575041434b2d505552452d494d504c2d303031293a0a3b2020202d20226564696374223a20617574686f72656420696e2045646963742f436f72653b2074686520436f726520626f6479206973206361727269656420696e6c696e6520286861736865640a3b20202020207769746820746865206578706f72742073757266616365292e2054686520736368656d61207265717569726573207468652060626f647960206669656c642e0a3b2020202d2022636f6d706f6e656e74223a20696d706c656d656e746564206f7574736964652045646963743b2063617272696573206e6f20696e6c696e6520626f647920616e6420696e73746561640a3b20202020206361727269657320697473206f776e206469676573742d6c6f636b65642060696d706c656d656e746174696f6e60202873616e64626f78202b206675656c292e20497420646f65730a3b20202020206e6f7420646570656e64206f6e20746865206f7074696f6e616c206d616e69666573742d6c6576656c2068656c706572436f6d706f6e656e742e0a707572652d66756e6374696f6e203d2065646963742d707572652d66756e6374696f6e202f20636f6d706f6e656e742d707572652d66756e6374696f6e0a0a707572652d66756e6374696f6e2d636f6d6d6f6e203d20280a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020706172616d6574657254797065733a205b2a20636f72652d747970652d7265665d2c2020202020203b20616c6c20626f756e6465640a202072657475726e547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020636f737454656d706c6174653a20747374722c0a202064657465726d696e69736d436c6173733a2022746f74616c22202f2022746f74616c2d776974682d74797065642d646961676e6f73746963222c0a290a0a65646963742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a20226564696374222c0a2020626f64793a20636f72652d666e2d626f64792c2020202020202020202020202020202020202020203b20696e6c696e652c20686173682d7369676e69666963616e740a7d0a0a636f6d706f6e656e742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a2022636f6d706f6e656e74222c0a20203b20746865206469676573742d6c6f636b656420636f6d706f6e656e7420696d706c656d656e74696e6720746869732068656c7065722e2052657175697265642061742074686520736368656d610a20203b206c6576656c20736f206120636f6d706f6e656e742068656c7065722063616e206e657665722076616c696461746520776974686f7574206120686173682d626f756e642c0a20203b2073616e64626f782b6675656c2d64657363726962656420696d706c656d656e746174696f6e202845444943542d4c41575041434b2d505552452d494d504c2d303031292e0a2020696d706c656d656e746174696f6e3a2065786563757461626c652d636f6d706f6e656e742c0a7d0a0a3b20636f72652d666e2d626f647920697320646566696e65642062792065646963742d636f72652e6364646c20616e6420617373656d626c656420776974682074686973206c61777061636b0a3b20736368656d612e2049742069732061207075726520436f72652066756e6374696f6e20626f64792c206e6f7420616e206566666563742d63617061626c6520636f72652d626c6f636b2e0a0a73656d616e7469632d656666656374203d207b0a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020696e707574547970653a20636f72652d747970652d7265662c2020202020202020202020202020203b20626f756e6465640a20206f7574707574547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020657865637574696f6e436c6173733a202270726f6f664f6e6c7922202f202272756e74696d65222c2020203b206f7274686f676f6e616c20746f207772697465436c6173730a20206566666563744b696e6448696e743a206566666563742d6b696e642c0a2020666f6f747072696e744f626c69676174696f6e3a20747374722c0a2020636f73744f626c69676174696f6e3a20747374722c0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c20203b206b6579656420627920636f6f7264696e6174653b20756e697175650a20206775617264537570706f72743a20626f6f6c2c0a7d0a0a6f62737472756374696f6e2d646566203d207b0a2020636f6f7264696e6174653a20747374722c0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164536368656d613a20636f72652d747970652d7265662c20202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d6c61777061636b2d616461707465722e6364646c202d2d2d0a3b2065646963742d6c61777061636b2d616461707465722e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f72206f6e6520646972656374206465636c61726174697665206c61777061636b2074617267657420616461707465722e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b2054686520656e636c6f73696e67206c61777061636b206d616e69666573742073656c6563747320746865206578616374207461726765742070726f66696c652c207461726765742049522c0a3b20616e642061646170746572207265736f75726365206469676573742e2054686f7365206964656e74697469657320617265206e6f7420726570656174656420686572652e0a0a6c61777061636b2d61646170746572203d207b0a202061706956657273696f6e3a202265646963742e6c61777061636b2d616461707465722f7631222c0a2020636c6173733a20226465636c61726174697665222c0a20206f7065726174696f6e50726f66696c65733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6f7065726174696f6e2d70726f66696c650a20207d2c0a2020656666656374496d706c656d656e746174696f6e733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6566666563740a20207d2c0a2020627564676574733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6275646765740a20207d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206c61777061636b206f7065726174696f6e2d70726f66696c6520636f6f7264696e617465732e0a6c61777061636b2d616461707465722d6f7065726174696f6e2d70726f66696c65203d207b0a2020636f72653a20747374722c0a202073656d616e746963456666656374733a205b2b20747374725d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206c61777061636b2073656d616e7469632d65666665637420636f6f7264696e617465732e20466f6f747072696e742c20636f73742c20616e640a3b206661696c757265206669656c6473206d7573742065786163746c792064697363686172676520746865206d61746368696e67206578706f72746564206566666563742e0a6c61777061636b2d616461707465722d656666656374203d207b0a2020746172676574496e7472696e7369633a20747374722c0a2020746172676574436f6e66696775726174696f6e3a207265736f757263652d7265662c0a20207772697465436c6173733a206c61777061636b2d616461707465722d77726974652d636c6173732c0a2020666f6f747072696e744f626c69676174696f6e3a20747374722c0a2020636f73744f626c69676174696f6e3a20747374722c0a20206661696c7572654d617070696e67733a207b202a206661696c7572652d6964656e74203d3e2074737472207d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206578706f7274656420636f73742d6f626c69676174696f6e20636f6f7264696e617465732e0a6c61777061636b2d616461707465722d627564676574203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a6c61777061636b2d616461707465722d77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f0a20202020202020202020202020202020202020202020202020202020202022617070656e6422202f20227265706c61636522202f202264656c65746522202f2022637573746f6d220a0a3b206661696c7572652d6964656e7420697320646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c2e0a0a3b202d2d2d2065646963742d7461726765742d70726f66696c652e6364646c202d2d2d0a3b2065646963742d7461726765742d70726f66696c652e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374207461726765742070726f66696c65206d616e69666573742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76310a3b202873656520535045435f636f6e74696e75756d2d636f6e74726163742d62756e646c652d76312e6d64292e204a534f4e20696e207468652070726f736520737065637320697320610a3b207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d613b2074686973204344444c206973207468652073696e676c6520736f757263650a3b206f66207472757468202845444943542d4142492d4e4f4455502d303031292e0a0a7461726765742d70726f66696c652d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652f7631222c0a202069643a20747374722c202020202020202020202020202020202020202020202020203b20652e672e20226563686f2e64706f220a202076657273696f6e3a20747374722c20202020202020202020202020202020202020203b20652e672e202231220a20206163636570746564436f72654162693a205b2b20747374725d2c20202020202020203b20652e672e205b2265646963742e636f72652f7631225d0a0a2020696e7472696e736963733a207265736f757263652d7265662c0a2020696e7472696e7369634e616d6573706163653a20747374722c0a20203b207075626c697368657320746869732070726f66696c652773206f7065726174696f6e2d70726f66696c65207265636f72647320286f707469632074656d706c6174657320746861740a20203b206070726f66696c65602f60696d706c656d656e74736020636c6175736573207265736f6c766520616761696e7374292e205265666572656e63657320616e0a20203b206f7065726174696f6e2d70726f66696c65732d646f63756d656e74202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207265736f757263652d7265662c0a2020666f6f747072696e74416c67656272613a207265736f757263652d7265662c0a2020636f7374416c67656272613a207265736f757263652d7265662c0a202074617267657449723a207265736f757263652d7265662c0a20206f62737472756374696f6e5461786f6e6f6d793a207265736f757263652d7265662c0a202076657269666965723a207265736f757263652d7265662c0a20206c6f77657265723a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a0a20203b206669656c647320746865206c616e67756167652073706563207265717569726573206f662065766572792070726f66696c650a202062756e646c6550726f66696c653a207265736f757263652d7265662c0a202067656e657261746564417274696661637450726f66696c65733a205b2a207265736f757263652d7265665d2c0a202063616e6f6e6963616c456e636f64696e6752756c65733a207265736f757263652d7265662c0a20203b20412070726f66696c65207468617420616363657074732074686520646972656374206465636c61726174697665206c61777061636b2d6164617074657220414249206e616d65732069740a20203b2065786163746c79206f6e63652e2050726f66696c6573207468617420646f206e6f7420636f6e73756d65206c61777061636b206164617074657273206c6561766520746869730a20203b206f7074696f6e616c20736c6f7420616273656e74206f7220656d7074792e0a20203f2061636365707465644c61777061636b416461707465724162693a205b5d202f205b2265646963742e6c61777061636b2d616461707465722f7631225d2c0a2020646961676e6f737469634162693a207265736f757263652d7265662c0a0a20203b206170706c69636174696f6e20646f637472696e650a20206170706c69636174696f6e4d6f64656c3a202261746f6d6963222c0a202072656164436f6e73697374656e63793a20226170706c69636174696f6e2d736e617073686f7422202f20747374722c0a202067756172644576616c756174696f6e3a2022707265636f6d6d69742d61746f6d696322202f20747374722c0a20206f62737472756374696f6e526f6c6c6261636b3a20226e6f2d76697369626c652d6566666563747322202f20747374722c0a20206d756c74695461726765743a20626f6f6c2c0a20203b207768657468657220746865207461726765742063616e206576616c7561746520707265636f6d6d697420706f7374636f6e646974696f6e20286067756172616e746565602920636865636b730a20203b20696e73696465207468652061746f6d6963206170706c69636174696f6e20756e6974202845444943542d5441524745542d504f5354434f4e442d303031290a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a0a202064657465726d696e6973746963457865637574696f6e3a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d6120627920746865206275696c640a3b202845444943542d4142492d4e4f4455502d303031292e205468657920617265206e6f74207265646566696e656420686572652e0a0a3b202d2d2d20696e7472696e736963207369676e6174757265202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e696665737427732060696e7472696e7369637360207265736f757263652d726566206973207468650a3b20696e7472696e7369632d7369676e617475726520636f7270757320646f63756d656e742062656c6f772e20497473206c61796f757420697320666978656420736f2074776f0a3b20696e646570656e64656e742070726f66696c65732076616c69646174652f686173682074686520636f72707573206964656e746963616c6c790a3b202845444943542d4142492d494e5452494e534943532d444f432d303031292e0a0a3b20696e7472696e736963732069732061204d4150206b6579656420627920636f6f7264696e6174652c20736f2074686520736368656d6120697473656c6620656e666f726365730a3b20636f6f7264696e61746520756e697175656e6573732e20412070726f766964657220726563656976657320746865207265736f6c76656420636f7270757320617320610a3b206469676573742d626f756e642073656d616e74696320696e70757420616e64207265736f6c76657320636f6f7264696e617465732077697468696e20746861742061727469666163742e0a3b2045616368206d6170206b6579204d55535420657175616c20697473207265636f726427732060636f6f7264696e61746560206669656c640a3b202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e696e7472696e736963732f7631222c0a2020696e7472696e736963733a207b202a2074737472203d3e20696e7472696e736963207d2c0a7d0a0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e6966657374277320606f7065726174696f6e50726f66696c657360207265736f757263652d7265662e0a3b206f7065726174696f6e2d70726f66696c65202f206f707469632d74656d706c6174652061726520646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c2e204b657965642062790a3b20636f6f7264696e61746520736f207265736f6c7574696f6e2063616e2774207069636b206265747765656e2074776f2073616d652d636f6f7264696e6174652070726f66696c65730a3b202845444943542d4142492d4f5050524f46494c452d534c4f542d3030312c2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e6f7065726174696f6e2d70726f66696c65732f7631222c0a202070726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c0a7d0a0a3b2041207479706564207072652d6c6f776572696e67207175657374696f6e20746861742063616e2062652070726f706f73656420627920576174736f6e206f7220616e206167656e7420616e640a3b20636865636b65642062792074686520636f6d70696c65722e2049742069732063616e6f6e6963616c2d43424f5220656e636f64656420756e6465720a3b206065646963742e6c6f776572696e672d726571756972656d656e74732f7631603b2074686520636f6d70696c657220636865636b7320746869732061727469666163742c206e6f74207468650a3b2070726f736520746861742070726f64756365642069742e0a6c6f776572696e672d726571756972656d656e7473203d207b0a202061706956657273696f6e3a202265646963742e6c6f776572696e672d726571756972656d656e74732f7631222c0a20206f7065726174696f6e50726f66696c653a20747374722c0a202073656d616e746963456666656374733a205b2a2073656d616e7469632d6566666563742d726571756972656d656e745d2c0a202072657175697265645772697465436c61737365733a205b2a2077726974652d636c6173735d2c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a202061746f6d69636974793a2061746f6d69636974792d726571756972656d656e742c0a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a20206f70746963436f6e74726163743a20747374722c0a7d0a0a73656d616e7469632d6566666563742d726571756972656d656e74203d207b0a2020636f6f7264696e6174653a20747374722c0a20207772697465436c6173733a2077726974652d636c6173732c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a7d0a0a77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a2020202020202020202020202020227265706c61636522202f202264656c65746522202f20747374720a67756172642d6b696e64203d2022707265636f6d6d69742d61746f6d696322202f20747374720a61746f6d69636974792d726571756972656d656e74203d202261746f6d696322202f20747374720a0a3b20412067656e75696e6520756e696f6e3a207075726520636f6e7374727563746f7273206361727279206e6f20656666656374206b696e64206f72206661696c757265733b206566666563740a3b20696e7472696e73696373206d757374202845444943542d5441524745542d494e5452494e5349432d434c4153532d303031292e2054686520736368656d6120656e666f7263657320746869732c0a3b206e6f74206120636f6d6d656e742e0a0a3b2054686520696e7472696e736963277320636f6f7264696e6174652069732074686520696e7472696e73696373206d6170204b45592c206e6f7420612076616c7565206669656c642c20736f207468650a3b206b657920616e6420636f6f7264696e6174652063616e206e65766572206469736167726565202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963203d20707572652d696e7472696e736963202f206566666563742d696e7472696e7369630a0a707572652d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a202270757265222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206775617264537570706f72743a2066616c73652c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20226e6f6e65222c0a7d0a0a6566666563742d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a2022656666656374222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206566666563744b696e643a206566666563742d6b696e642c0a20203b206d6170206b65796564206279206661696c75726520636f6f7264696e61746520286661696c7572652d6964656e74293b20746865206661696c75726520636f6f7264696e6174652069730a20203b20746865206b65792c206e6f7420612076616c7565206669656c642c20736f20756e697175656e657373206973207374727563747572616c0a20203b202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c0a20206775617264537570706f72743a20626f6f6c2c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f20227265706c61636522202f0a20202020202020202020202020202264656c65746522202f2022637573746f6d222c0a202063616e5061727469636970617465496e41746f6d696347756172643a20626f6f6c2c0a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d617574686f726974792d66616374732e6364646c202d2d2d0a3b2065646963742d617574686f726974792d66616374732e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f722074686520666972737420636f6d70696c65722d636f6e7465787420617574686f726974792d666163747320646f63756d656e742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20736f20736f757263652e6469676573742075736573207468650a3b20736861726564207368613235362d6469676573742074797065642076616c75652e204a534f4e2069732061207265766965772f696e7075742072656e646572696e673a206974730a3b20607368613235363a3c3634206865783e6020736f75726365206469676573742069732070726f6a656374656420746f205b60736861323536602c203332207261772062797465735d206f6e0a3b2074686520776972652c20616e64206974732066616374206172726179732070726f6a65637420746f2074686520636f6f7264696e6174652d6b65796564206d6170732062656c6f772e0a0a617574686f726974792d6661637473203d207b0a202061706956657273696f6e3a202265646963742e617574686f726974792d66616374732f7631222c0a2020736f757263653a20617574686f726974792d666163742d736f757263652c0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e20617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374207d2c0a20206566666563745772697465436c61737365733a207b202a2074737472203d3e20617574686f726974792d77726974652d636c617373207d2c0a2020627564676574733a207b202a2074737472203d3e20617574686f726974792d6275646765742d66616374207d2c0a7d0a0a617574686f726974792d666163742d736f75726365203d207b0a20206b696e643a20226c61777061636b22202f202274617267657450726f66696c65222c0a2020636f6f7264696e6174653a20747374722c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a3b20546865206d6170206b65792069732074686520736f75726365206f7065726174696f6e2d70726f66696c6520636f6f7264696e6174652e204974206973206e6f7420726570656174656420696e0a3b207468652076616c75652c20736f2061206b657920616e6420656d62656464656420636f6f7264696e6174652063616e6e6f742064697361677265652e20416c6c6f7765642077726974650a3b20636c61737365732061726520612063616e6f6e6963616c206d61702d7365743a2074686520636c6173732069732074686520756e69717565206b657920616e64206e756c6c206973207468650a3b20756e6974206d61726b65722e2043616e6f6e6963616c2043424f52206669786573206b6579206f7264657220776974686f75742061207365636f6e64206f72646572696e672072756c652e0a617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374203d207b0a2020636f72653a20747374722c0a2020616c6c6f7765645772697465436c61737365733a207b202a20617574686f726974792d77726974652d636c617373203d3e206e756c6c207d2c0a7d0a0a3b20546865206566666563745772697465436c6173736573206d6170206b6579206973207468652073656d616e7469632065666665637420636f6f7264696e6174652e2054686520627564676574730a3b206d6170206b65792069732074686520736f757263652062756467657420636f6f7264696e6174652e2043616e6f6e6963616c2043424f52206d61702d6b657920756e697175656e6573730a3b206d616b6573206475706c6963617465206661637420636f6f7264696e61746573207374727563747572616c6c7920756e726570726573656e7461626c652e0a617574686f726974792d6275646765742d66616374203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a3b20417574686f726974794661637473446f63756d656e7420763120696e74656e74696f6e616c6c792061636365707473206f6e6c792074686520777269746520636c6173736573207468650a3b2063757272656e7420636f6d70696c6572206d6f64656c2063616e20636f6e73756d652e2060637573746f6d602069732074686520736f6c6520763120637573746f6d207370656c6c696e673b0a3b20617262697472617279207461726765742d70726f66696c6520657874656e73696f6e20737472696e677320646f206e6f7420656e746572207468697320636f6d70696c657220706174682e0a617574686f726974792d77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a202020202020202020202020202020202020202020202020227265706c61636522202f202264656c65746522202f2022637573746f6d220a0a3b202d2d2d2065646963742d726573756c742d70726f6a656374696f6e2e6364646c202d2d2d0a3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d726573756c742d70726f6a656374696f6e2e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220636f6d70696c65722d6f776e6564206170706c69636174696f6e2d726573756c742070726f6a656374696f6e732e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a0a726573756c742d70726f6a656374696f6e203d207b0a2020736368656d613a202265646963742e726573756c742d70726f6a656374696f6e2f7631222c0a20206f7065726174696f6e436f6f7264696e6174653a20626f756e6465642d70726f6a656374696f6e2d746578742c0a20206f7574707574547970653a20626f756e6465642d70726f6a656374696f6e2d746578742c0a20206d61784f757470757442797465733a2075696e74202e677420302c0a202065787072657373696f6e3a20726573756c742d70726f6a656374696f6e2d657870722c0a7d0a0a726573756c742d70726f6a656374696f6e2d65787072203d20726573756c742d70726f6a656374696f6e2d7265636f7264202f20726573756c742d70726f6a656374696f6e2d736f757263650a0a726573756c742d70726f6a656374696f6e2d7265636f7264203d207b0a20206b696e643a20227265636f7264222c0a20203b2054686520726f6f74207265636f726420636f756e7473206173206f6e65206f66207468652052757374206465636f6465722773203235362065787072657373696f6e206e6f6465732e0a20203b204e657374656420616767726567617465206e6f646520636f756e742072656d61696e7320616e20617574686f7269746174697665206465636f64657220636865636b2e0a20206669656c64733a207b20302a32353520626f756e6465642d70726f6a656374696f6e2d74657874203d3e20726573756c742d70726f6a656374696f6e2d65787072207d2c0a7d0a0a726573756c742d70726f6a656374696f6e2d736f75726365203d207b0a20206b696e643a2022736f75726365222c0a2020736f757263653a20726573756c742d70726f6a656374696f6e2d736f757263652d6b696e642c0a20203b204d617463686573204d41585f524553554c545f50524f4a454354494f4e5f504154485f5345474d454e545320696e2065646963742d73796e7461782e0a2020706174683a205b302a333220626f756e6465642d70726f6a656374696f6e2d746578745d2c0a7d0a0a726573756c742d70726f6a656374696f6e2d736f757263652d6b696e64203d0a20207b206b696e643a20226170706c69636174696f6e496e70757422207d202f0a20207b0a202020206b696e643a20226361706162696c697479526573756c74222c0a202020207374657049643a20626f756e6465642d70726f6a656374696f6e2d746578742c0a20207d0a0a626f756e6465642d70726f6a656374696f6e2d74657874203d2074737472202e73697a652028312e2e31303234290a0a3b202d2d2d2065646963742d7461726765742d69722e6364646c202d2d2d0a3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d7461726765742d69722e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f72207468652045646963742d6f776e65642054617267657420495220617274696661637420656e76656c6f70652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20616e642065646963742d636f72652e6364646c2e2049740a3b2064656c696265726174656c792072657573657320436f72652065787072657373696f6e732c20707265646963617465732c20627564676574732c206c6f63616c207265666572656e6365732c0a3b206f62737472756374696f6e20726561736f6e732c20616e64206f62737472756374696f6e2061726d7320736f2074686520736368656d61206d617463686573207468652076616c75650a3b20656d6974746564206279207468652063616e6f6e6963616c2054617267657420495220656e636f64657220726174686572207468616e20726573746174696e672074686f73652074797065732e0a3b2049742064657363726962657320746865207374727563747572616c207368617065206f662076616c6964206c6f776572696e672d70726f6475636564206172746966616374732e205468650a3b206c6f776572696e6720616e6420656e636f64657220636f6e7472616374732073657061726174656c7920656e666f7263652073656d616e746963206964656e7469666965722072756c65730a3b20616e642063616e6f6e6963616c206f72646572696e672f64656475706c69636174696f6e20666f72207365742d6c696b652076616c7565732e0a0a3b2054617267657420495220656e636f64696e672072656a6563747320616e20656d707479207461726765742d70726f66696c6520636f6f7264696e617465206265666f72652062797465730a3b2065786973742c20736f207468697320726f6f74207469676874656e732074686520736861726564207374727563747572616c207265736f757263652d726566206163636f7264696e676c792e0a7461726765742d69722d7265736f757263652d726566203d207b0a202069643a2074737472202e7265676578702022283f73292e2b222c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a7461726765742d69722d6172746966616374203d207461726765742d69722d636c6f7365642d6172746966616374202f207461726765742d69722d6c65676163792d61727469666163740a0a7461726765742d69722d61727469666163742d636f6d6d6f6e203d20280a20206b696e643a202274617267657449724172746966616374222c0a2020646f6d61696e3a20747374722c0a202074617267657450726f66696c653a207461726765742d69722d7265736f757263652d7265662c0a2020736f75726365436f7265436f6f7264696e6174653a2074737472202e7265676578702022283f73292e2b222c0a290a0a7461726765742d69722d636c6f7365642d6172746966616374203d207b0a20207461726765742d69722d61727469666163742d636f6d6d6f6e2c0a202073656d616e746963436c6f737572653a207461726765742d69722d73656d616e7469632d636c6f737572652c0a2020696e74656e74733a207b202a2074737472203d3e207461726765742d69722d696e74656e74207d2c0a7d0a0a7461726765742d69722d6c65676163792d6172746966616374203d207b0a20207461726765742d69722d61727469666163742d636f6d6d6f6e2c0a2020696e74656e74733a207b202a2074737472203d3e207461726765742d69722d6c65676163792d696e74656e74207d2c0a7d0a0a7461726765742d69722d73656d616e7469632d636c6f73757265203d207b0a2020736f75726365436f72653a207461726765742d69722d7265736f757263652d7265662c0a20206c61777061636b733a205b2a207461726765742d69722d7265736f757263652d7265665d2c0a7d0a0a7461726765742d69722d696e74656e74203d207b0a20207461726765742d69722d696e74656e742d636f6d6d6f6e2c0a20203f2062617369733a20636f72652d657870722c0a7d0a0a7461726765742d69722d6c65676163792d696e74656e74203d207b0a20207461726765742d69722d696e74656e742d636f6d6d6f6e2c0a7d0a0a7461726765742d69722d696e74656e742d636f6d6d6f6e203d20280a20206f7065726174696f6e50726f66696c653a20747374722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020726571756972656d656e74733a205b2a207461726765742d69722d726571756972656d656e745d2c0a202073746570733a205b2a207461726765742d69722d737465705d2c0a2020726573756c743a20636f72652d657870722c0a290a0a7461726765742d69722d726571756972656d656e74203d207b0a202069643a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a0a7461726765742d69722d73746570203d207b0a202069643a20747374722c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020746172676574496e7472696e7369633a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4661696c757265733a205b2a206661696c7572652d6964656e745d2c0a20206f62737472756374696f6e41726d733a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a", + "rawSha256": "ff5405708185e0bcf33dac263000fcb772c79e21145ad69da7cad0692e1a9552" }, "contracts": [ {"contract": "authority-facts", "rootRule": "authority-facts"}, @@ -13,6 +13,7 @@ {"contract": "lawpack-exports", "rootRule": "lawpack-exports"}, {"contract": "lawpack-manifest", "rootRule": "lawpack-manifest"}, {"contract": "lowering-requirements", "rootRule": "lowering-requirements"}, + {"contract": "result-projection", "rootRule": "result-projection"}, {"contract": "target-ir-artifact", "rootRule": "target-ir-artifact"}, {"contract": "target-profile-intrinsics", "rootRule": "intrinsics-document"}, {"contract": "target-profile-manifest", "rootRule": "target-profile-manifest"}, @@ -23,6 +24,7 @@ {"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.result-projection.artifact/v1", "rootRule": "result-projection"}, {"domain": "edict.target-ir.artifact/v1", "rootRule": "target-ir-artifact"}, {"domain": "edict.target-profile/v1", "rootRule": "target-profile-manifest"} ], diff --git a/xtask/src/lawpack_goldens.rs b/xtask/src/lawpack_goldens.rs index 483dbdc..4dfd677 100644 --- a/xtask/src/lawpack_goldens.rs +++ b/xtask/src/lawpack_goldens.rs @@ -32,6 +32,10 @@ const CREATE_GREETING_TARGET_IR_CBOR: &str = "fixtures/lawpack/hello-echo/create-greeting.target-ir.cbor"; const CREATE_GREETING_TARGET_IR_DIGEST: &str = "fixtures/lawpack/hello-echo/create-greeting.target-ir.sha256"; +const CREATE_GREETING_RESULT_PROJECTION_CBOR: &str = + "fixtures/lawpack/hello-echo/create-greeting.result-projection.cbor"; +const CREATE_GREETING_RESULT_PROJECTION_DIGEST: &str = + "fixtures/lawpack/hello-echo/create-greeting.result-projection.sha256"; const ADAPTER_COORDINATE: &str = "hello.echo.echo-dpo-adapter/v1"; const TARGET_CONFIGURATION_COORDINATE: &str = "hello.echo.echo-operation-configuration/v1"; const ECHO_TARGET_PROFILE_DIGEST: [u8; 32] = [ @@ -455,6 +459,13 @@ fn hello_echo_golden_artifacts(root: &Path) -> Result target_ir_report.status )); } + let result_projection = target_ir_report + .result_projections + .get("createGreeting") + .cloned() + .ok_or_else(|| { + "lower Hello Echo Target IR: lowered report omitted result projection".to_owned() + })?; let target_ir = target_ir_report .artifact .ok_or_else(|| "lower Hello Echo Target IR: lowered report omitted artifact".to_owned())?; @@ -466,6 +477,7 @@ fn hello_echo_golden_artifacts(root: &Path) -> Result .map_err(|error| format!("digest Hello Echo Target IR: {error}"))? .to_review_string() ); + let result_projection_digest = format!("{}\n", result_projection.digest().to_review_string()); let manifest_digest = format!("{}\n", bundle.manifest_digest_review_string()); let exports_digest = format!("{}\n", bundle.manifest().exports.digest_review_string()); let adapter_digest = format!("{}\n", sha256_review_string(&adapter_digest)); @@ -491,6 +503,14 @@ fn hello_echo_golden_artifacts(root: &Path) -> Result CREATE_GREETING_TARGET_IR_DIGEST, target_ir_digest.into_bytes(), ), + ( + CREATE_GREETING_RESULT_PROJECTION_CBOR, + result_projection.canonical_bytes().to_vec(), + ), + ( + CREATE_GREETING_RESULT_PROJECTION_DIGEST, + result_projection_digest.into_bytes(), + ), ]) } diff --git a/xtask/src/provider_contract_pack.rs b/xtask/src/provider_contract_pack.rs index 74be7b9..cf60d89 100644 --- a/xtask/src/provider_contract_pack.rs +++ b/xtask/src/provider_contract_pack.rs @@ -12,6 +12,7 @@ const LAWPACK_CDDL: &str = "docs/abi/edict-lawpack.cddl"; const LAWPACK_ADAPTER_CDDL: &str = "docs/abi/edict-lawpack-adapter.cddl"; const TARGET_PROFILE_CDDL: &str = "docs/abi/edict-target-profile.cddl"; const AUTHORITY_FACTS_CDDL: &str = "docs/abi/edict-authority-facts.cddl"; +const RESULT_PROJECTION_CDDL: &str = "docs/abi/edict-result-projection.cddl"; const TARGET_IR_CDDL: &str = "docs/abi/edict-target-ir.cddl"; pub(crate) const CONTRACT_PACK_CDDL: &str = @@ -34,6 +35,7 @@ pub(crate) fn provider_contract_pack( let lawpack_adapter_cddl = read(root, LAWPACK_ADAPTER_CDDL)?; let target_profile_cddl = read(root, TARGET_PROFILE_CDDL)?; let authority_facts_cddl = read(root, AUTHORITY_FACTS_CDDL)?; + let result_projection_cddl = read(root, RESULT_PROJECTION_CDDL)?; let target_ir_cddl = read(root, TARGET_IR_CDDL)?; let pack = assemble_provider_contract_pack(ProviderContractPackInput { common_cddl: &common_cddl, @@ -42,6 +44,7 @@ pub(crate) fn provider_contract_pack( lawpack_adapter_cddl: &lawpack_adapter_cddl, target_profile_cddl: &target_profile_cddl, authority_facts_cddl: &authority_facts_cddl, + result_projection_cddl: &result_projection_cddl, target_ir_cddl: &target_ir_cddl, contract_resources: canonical_target_profile_contract_resources(), }) diff --git a/xtask/src/tests.rs b/xtask/src/tests.rs index 0343a80..4a5b1c5 100644 --- a/xtask/src/tests.rs +++ b/xtask/src/tests.rs @@ -141,6 +141,7 @@ fn assert_provider_contract_manifest_metadata(manifest: &Value) { ("lawpack-exports", "lawpack-exports"), ("lawpack-manifest", "lawpack-manifest"), ("lowering-requirements", "lowering-requirements"), + ("result-projection", "result-projection"), ("target-ir-artifact", "target-ir-artifact"), ("target-profile-intrinsics", "intrinsics-document"), ("target-profile-manifest", "target-profile-manifest"), @@ -173,6 +174,10 @@ fn assert_provider_contract_manifest_metadata(manifest: &Value) { (CORE_MODULE_DIGEST_DOMAIN, "core-module"), (PROVIDER_LAWPACK_ARTIFACT_DOMAIN, "lawpack-manifest"), ("edict.lowering-requirements/v1", "lowering-requirements"), + ( + edict_syntax::RESULT_PROJECTION_DIGEST_DOMAIN, + "result-projection" + ), (TARGET_IR_ARTIFACT_DIGEST_DOMAIN, "target-ir-artifact"), (TARGET_PROFILE_API_VERSION, "target-profile-manifest"), ] @@ -275,6 +280,7 @@ fn provider_contract_pack_check_rejects_drift_without_rewriting() { "docs/abi/edict-lawpack-adapter.cddl", "docs/abi/edict-target-profile.cddl", "docs/abi/edict-authority-facts.cddl", + "docs/abi/edict-result-projection.cddl", "docs/abi/edict-target-ir.cddl", ] { let destination = root.join(relative);