From bc17a9cf869796bd7cb75c5f305129175ddf88e6 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 29 Jul 2026 17:02:10 -0700 Subject: [PATCH 01/16] test: define external request artifact publication --- crates/edict-cli/src/application_build.rs | 198 +++++++++++++++++++++- 1 file changed, 196 insertions(+), 2 deletions(-) diff --git a/crates/edict-cli/src/application_build.rs b/crates/edict-cli/src/application_build.rs index 8dff855..0628b61 100644 --- a/crates/edict-cli/src/application_build.rs +++ b/crates/edict-cli/src/application_build.rs @@ -48,10 +48,20 @@ pub(crate) struct ApplicationBuildFailure { pub(crate) message: String, } +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +enum ApplicationBuildKind { + #[default] + ExecutableOperation, + ExternalAction, +} + #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct ApplicationManifest { schema: String, + #[serde(default)] + build_kind: ApplicationBuildKind, coordinate: String, sources: Vec, lawpacks: Vec, @@ -1416,6 +1426,17 @@ fn write_outputs( }) } +fn write_external_action_outputs( + _directory: &Path, + _core: &[u8], + _target_ir: &[u8], +) -> Result<(), ApplicationBuildFailure> { + Err(failure( + "ExternalActionBuildUnavailable", + "the public application build cannot publish external-action artifacts", + )) +} + fn output_lock_path(directory: &Path) -> PathBuf { let mut name = OsString::from("."); name.push( @@ -1754,8 +1775,9 @@ mod tests { use super::{ build_application, canonical_application_root, output_lock_path, provider_schema_artifacts, 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, + validate_application_manifest, with_result_projection_input, write_external_action_outputs, + write_outputs, ApplicationBuildKind, ApplicationLawpack, ApplicationManifest, + ApplicationTarget, RESULT_PROJECTION_ROLE, }; const STRESS_SEED: u64 = 0x5eed_1a77_c105_0a11; @@ -1771,6 +1793,34 @@ mod tests { ); } + #[test] + fn application_manifest_accepts_an_explicit_external_action_build_kind() { + let manifest = serde_json::json!({ + "schema": "edict.application/v1", + "buildKind": "externalAction", + "coordinate": "examples.workspace_observer@1", + "sources": ["src/workspace_observer.edict"], + "lawpacks": [{ + "manifest": "vendor/workspace/manifest.cbor", + "exports": "vendor/workspace/exports.cbor", + "adapter": "vendor/workspace/adapter.cbor", + "targetConfiguration": "vendor/workspace/target-configuration.cbor" + }], + "target": { + "profile": "echo.dpo@1", + "providerPackage": ".build/echo-provider" + }, + "outputDirectory": ".build/application" + }); + + let decoded = test_ok( + serde_json::from_value::(manifest), + "decode external-action application manifest", + ); + + assert_eq!(decoded.build_kind, ApplicationBuildKind::ExternalAction); + } + #[test] fn relative_application_config_uses_the_current_directory_as_root() { let actual = test_ok( @@ -1991,6 +2041,149 @@ mod tests { test_ok(fs::remove_dir_all(root), "remove test-owned temp tree"); } + #[test] + fn external_action_publication_writes_the_exact_compiler_pair() { + let root = temp_tree("external-action-pair"); + let core = include_bytes!("../../../fixtures/core/canonical/workspace-snapshot.core.cbor"); + let target_ir = include_bytes!( + "../../../fixtures/target-ir/canonical/workspace-snapshot.target-ir.cbor" + ); + + test_ok( + write_external_action_outputs(&root, core, target_ir), + "publish compiler-owned external-action artifacts", + ); + + assert_eq!( + test_ok(fs::read(root.join("core.cbor")), "read published Core"), + core + ); + assert_eq!( + test_ok( + fs::read(root.join("target-ir.cbor")), + "read published Target IR", + ), + target_ir + ); + assert!(!root.join("executable-operation-package.cbor").exists()); + assert!(!root.join("verification-report.cbor").exists()); + test_ok(fs::remove_dir_all(root), "remove external-action pair"); + } + + #[test] + fn external_action_publication_removes_stale_executable_outputs() { + let root = temp_tree("external-action-stale"); + test_ok( + fs::write( + root.join("executable-operation-package.cbor"), + b"stale-package", + ), + "write stale package", + ); + test_ok( + fs::write(root.join("verification-report.cbor"), b"stale-report"), + "write stale report", + ); + + test_ok( + write_external_action_outputs(&root, b"core", b"target"), + "publish external-action pair over stale executable outputs", + ); + + assert!(!root.join("executable-operation-package.cbor").exists()); + assert!(!root.join("verification-report.cbor").exists()); + assert_eq!( + test_ok(fs::read(root.join("core.cbor")), "read Core"), + b"core" + ); + assert_eq!( + test_ok(fs::read(root.join("target-ir.cbor")), "read Target IR"), + b"target" + ); + test_ok(fs::remove_dir_all(root), "remove stale-output tree"); + } + + #[test] + fn failed_external_action_pair_publication_preserves_previous_core() { + let root = temp_tree("external-action-rollback"); + let core_path = root.join("core.cbor"); + let target_path = root.join("target-ir.cbor"); + test_ok(fs::write(&core_path, b"previous-core"), "write prior Core"); + test_ok( + fs::create_dir(&target_path), + "create conflicting Target IR directory", + ); + + let failure = test_err( + write_external_action_outputs(&root, b"new-core", b"new-target"), + "a non-file target must reject the pair", + ); + + assert_eq!(failure.kind, "ApplicationOutputWriteFailed"); + assert_eq!( + test_ok(fs::read(&core_path), "read preserved Core"), + b"previous-core" + ); + test_ok(fs::remove_dir_all(root), "remove rollback tree"); + } + + #[test] + fn external_action_pair_publication_is_deterministic_for_a_fixed_seed_corpus() { + let root = temp_tree("external-action-property"); + let mut state = STRESS_SEED; + for ordinal in 0_u8..16 { + state ^= state << 13; + state ^= state >> 7; + state ^= state << 17; + let core = state.to_le_bytes(); + let target = [ordinal; 8]; + + test_ok( + write_external_action_outputs(&root, &core, &target), + "publish fixed-seed pair", + ); + assert_eq!( + test_ok(fs::read(root.join("core.cbor")), "read property Core"), + core + ); + assert_eq!( + test_ok( + fs::read(root.join("target-ir.cbor")), + "read property Target IR", + ), + target + ); + } + test_ok(fs::remove_dir_all(root), "remove property tree"); + } + + #[test] + fn external_action_pair_publication_remains_bounded_under_stress() { + let root = temp_tree("external-action-stress"); + for ordinal in 0_u8..64 { + let core = vec![ordinal; 1024]; + let target = vec![ordinal.wrapping_add(1); 1024]; + test_ok( + write_external_action_outputs(&root, &core, &target), + "publish bounded stress pair", + ); + } + + assert_eq!( + test_ok(fs::metadata(root.join("core.cbor")), "measure stress Core",).len(), + 1024 + ); + assert_eq!( + test_ok( + fs::metadata(root.join("target-ir.cbor")), + "measure stress Target IR", + ) + .len(), + 1024 + ); + test_ok(fs::remove_dir_all(root), "remove stress tree"); + } + #[test] fn application_artifact_reads_are_bounded_before_allocation() { let root = temp_tree("bounded-read"); @@ -2077,6 +2270,7 @@ mod tests { fn application_manifest(lawpack_count: usize) -> ApplicationManifest { ApplicationManifest { schema: "edict.application/v1".to_owned(), + build_kind: ApplicationBuildKind::ExecutableOperation, coordinate: "examples.test@1.operation".to_owned(), sources: vec![PathBuf::from("operation.edict")], lawpacks: (0..lawpack_count) From 9a99f56e3f22e8eb77abc069b510a84d6da86932 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 29 Jul 2026 17:14:10 -0700 Subject: [PATCH 02/16] test: define request-only lawpack profiles --- crates/edict-syntax/tests/lawpack.rs | 140 +++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/crates/edict-syntax/tests/lawpack.rs b/crates/edict-syntax/tests/lawpack.rs index 3d54302..629febd 100644 --- a/crates/edict-syntax/tests/lawpack.rs +++ b/crates/edict-syntax/tests/lawpack.rs @@ -272,6 +272,105 @@ fn lawpack_adapter_requires_complete_exported_effect_coverage() { ); } +#[test] +fn request_only_profile_supplies_budget_without_callable_effect_authority() { + let mut exports = hello_echo_exports(); + array_mut(field_mut(&mut exports, "effects")).clear(); + let mut adapter = decode_canonical_cbor(ADAPTER_BYTES).expect("decode canonical adapter"); + map_mut(field_mut(&mut adapter, "effectImplementations")).clear(); + let profile = first_map_value_mut(field_mut(&mut adapter, "operationProfiles")); + array_mut(field_mut(profile, "semanticEffects")).clear(); + insert_field( + profile, + "budgetObligation", + text("hello.echo@1.smallCreateBudget"), + ); + let (bundle, adapter) = bundle_and_adapter(&exports, &adapter); + let source = format!( + r#"package examples.workspace_observer@1; + +use lawpack hello.echo@1 digest "{}" as hello; +use capability workspace.snapshot.observe@1 + digest "sha256:{}" + as snapshot; + +type ObserveInput = {{ + payload: Bytes, + scope: Bytes, + basis: Bytes, + maxSettlementBytes: U64, + maxAttempts: U32, +}}; + +intent observe(input: ObserveInput) + returns ExternalActionRequest> + profile hello.createGreeting + basis input.basis + budget <= hello.smallCreateBudget +{{ + request pending: ExternalActionRequest> = + snapshot(input.payload) + input schema workspace.snapshot.input@1 digest "sha256:{}" + settlement schema workspace.snapshot.settlement@1 digest "sha256:{}" + authority input.scope + basis input.basis + budget maxSettlementBytes input.maxSettlementBytes maxAttempts input.maxAttempts + reconcile workspace.snapshot.reconcile@1 digest "sha256:{}"; + return pending; +}} +"#, + bundle.manifest_digest_review_string(), + "a".repeat(64), + "b".repeat(64), + "c".repeat(64), + "d".repeat(64), + ); + let module = parse_module(&source).expect("parse request-only application"); + let preparation = prepare_lawpack_compilation(&module, &bundle, &adapter) + .expect("prepare request-only application"); + let core = compile_to_core(&module, preparation.compiler_context()) + .expect("compile request-only application"); + let report = lower_to_target_ir(&core, preparation.target_ir_facts()); + let intent = report + .artifact + .expect("request-only Target IR") + .intents + .remove("observe") + .expect("observe intent"); + + assert_eq!(intent.external_action_requests.len(), 1); + assert!(intent.steps.is_empty()); + assert!( + adapter.effects().is_empty(), + "request-only profile must not grant target-call authority" + ); +} + +#[test] +fn request_only_profile_requires_an_exact_budget_obligation() { + let mut exports = hello_echo_exports(); + array_mut(field_mut(&mut exports, "effects")).clear(); + let mut adapter = decode_canonical_cbor(ADAPTER_BYTES).expect("decode canonical adapter"); + map_mut(field_mut(&mut adapter, "effectImplementations")).clear(); + let profile = first_map_value_mut(field_mut(&mut adapter, "operationProfiles")); + array_mut(field_mut(profile, "semanticEffects")).clear(); + insert_field( + profile, + "budgetObligation", + text("hello.echo@1.missingBudget"), + ); + let bundle = bundle_with_exports_and_adapter(&exports, &adapter); + let bytes = encode_canonical_cbor(&adapter).expect("encode request-only adapter"); + + let failures = decode_lawpack_adapter(&bundle, "echo.dpo@1", &bytes) + .expect_err("unknown request-only budget must reject"); + + assert_eq!( + adapter_failure_kinds(&failures), + vec![LawpackAdapterFailureKind::MissingBudget] + ); +} + #[test] fn lawpack_adapter_corroborates_footprint_cost_and_failure_obligations() { for (field, replacement, expected) in [ @@ -953,6 +1052,47 @@ fn bundle_with_adapter(adapter: &CanonicalValue) -> ValidatedLawpackBundle { decode_lawpack_bundle(&manifest_bytes, EXPORTS_BYTES).expect("load rebound lawpack") } +fn bundle_with_exports_and_adapter( + exports: &CanonicalValue, + adapter: &CanonicalValue, +) -> ValidatedLawpackBundle { + let exports_bytes = encode_canonical_cbor(exports).expect("encode rebound exports"); + let mut manifest = decode_canonical_cbor(MANIFEST_BYTES).expect("decode canonical manifest"); + replace_field( + field_mut(&mut manifest, "exports"), + "digest", + CanonicalValue::Array(vec![ + text("sha256"), + CanonicalValue::Bytes(digest_value(EXPORTS_COORDINATE, exports).to_vec()), + ]), + ); + let descriptor = first_array_item_mut(field_mut(&mut manifest, "targetAdapters")); + replace_field( + descriptor, + "adapter", + resource_ref( + ADAPTER_COORDINATE, + digest_value(ADAPTER_COORDINATE, adapter), + ), + ); + let manifest_bytes = encode_canonical_cbor(&manifest).expect("encode rebound manifest"); + decode_lawpack_bundle(&manifest_bytes, &exports_bytes).expect("load rebound lawpack") +} + +fn bundle_and_adapter( + exports: &CanonicalValue, + adapter: &CanonicalValue, +) -> ( + ValidatedLawpackBundle, + edict_syntax::ValidatedLawpackAdapter, +) { + let bundle = bundle_with_exports_and_adapter(exports, adapter); + let adapter_bytes = encode_canonical_cbor(adapter).expect("encode rebound adapter"); + let validated = decode_lawpack_adapter(&bundle, "echo.dpo@1", &adapter_bytes) + .expect("decode rebound adapter"); + (bundle, validated) +} + fn insert_field(value: &mut CanonicalValue, field: &str, replacement: CanonicalValue) { map_mut(value).push((text(field), replacement)); } From 2ac93754f4ee112df2834601d9dda52cc710c78a Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 29 Jul 2026 17:15:55 -0700 Subject: [PATCH 03/16] test: require request profile target configuration --- crates/edict-syntax/tests/lawpack.rs | 37 ++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/crates/edict-syntax/tests/lawpack.rs b/crates/edict-syntax/tests/lawpack.rs index 629febd..ae5048f 100644 --- a/crates/edict-syntax/tests/lawpack.rs +++ b/crates/edict-syntax/tests/lawpack.rs @@ -277,6 +277,11 @@ fn request_only_profile_supplies_budget_without_callable_effect_authority() { let mut exports = hello_echo_exports(); array_mut(field_mut(&mut exports, "effects")).clear(); let mut adapter = decode_canonical_cbor(ADAPTER_BYTES).expect("decode canonical adapter"); + let target_configuration = field_mut( + first_map_value_mut(field_mut(&mut adapter, "effectImplementations")), + "targetConfiguration", + ) + .clone(); map_mut(field_mut(&mut adapter, "effectImplementations")).clear(); let profile = first_map_value_mut(field_mut(&mut adapter, "operationProfiles")); array_mut(field_mut(profile, "semanticEffects")).clear(); @@ -285,6 +290,7 @@ fn request_only_profile_supplies_budget_without_callable_effect_authority() { "budgetObligation", text("hello.echo@1.smallCreateBudget"), ); + insert_field(profile, "targetConfiguration", target_configuration); let (bundle, adapter) = bundle_and_adapter(&exports, &adapter); let source = format!( r#"package examples.workspace_observer@1; @@ -351,6 +357,11 @@ fn request_only_profile_requires_an_exact_budget_obligation() { let mut exports = hello_echo_exports(); array_mut(field_mut(&mut exports, "effects")).clear(); let mut adapter = decode_canonical_cbor(ADAPTER_BYTES).expect("decode canonical adapter"); + let target_configuration = field_mut( + first_map_value_mut(field_mut(&mut adapter, "effectImplementations")), + "targetConfiguration", + ) + .clone(); map_mut(field_mut(&mut adapter, "effectImplementations")).clear(); let profile = first_map_value_mut(field_mut(&mut adapter, "operationProfiles")); array_mut(field_mut(profile, "semanticEffects")).clear(); @@ -359,6 +370,7 @@ fn request_only_profile_requires_an_exact_budget_obligation() { "budgetObligation", text("hello.echo@1.missingBudget"), ); + insert_field(profile, "targetConfiguration", target_configuration); let bundle = bundle_with_exports_and_adapter(&exports, &adapter); let bytes = encode_canonical_cbor(&adapter).expect("encode request-only adapter"); @@ -371,6 +383,31 @@ fn request_only_profile_requires_an_exact_budget_obligation() { ); } +#[test] +fn request_only_profile_requires_an_exact_target_configuration() { + let mut exports = hello_echo_exports(); + array_mut(field_mut(&mut exports, "effects")).clear(); + let mut adapter = decode_canonical_cbor(ADAPTER_BYTES).expect("decode canonical adapter"); + map_mut(field_mut(&mut adapter, "effectImplementations")).clear(); + let profile = first_map_value_mut(field_mut(&mut adapter, "operationProfiles")); + array_mut(field_mut(profile, "semanticEffects")).clear(); + insert_field( + profile, + "budgetObligation", + text("hello.echo@1.smallCreateBudget"), + ); + let bundle = bundle_with_exports_and_adapter(&exports, &adapter); + let bytes = encode_canonical_cbor(&adapter).expect("encode request-only adapter"); + + let failures = decode_lawpack_adapter(&bundle, "echo.dpo@1", &bytes) + .expect_err("unconfigured request-only profile must reject"); + + assert_eq!( + adapter_failure_kinds(&failures), + vec![LawpackAdapterFailureKind::InvalidTargetConfiguration] + ); +} + #[test] fn lawpack_adapter_corroborates_footprint_cost_and_failure_obligations() { for (field, replacement, expected) in [ From cf32e08c32c31949a12e6ef3a61c9ca623dc070d Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 29 Jul 2026 17:24:35 -0700 Subject: [PATCH 04/16] feat: publish external request application artifacts --- crates/edict-cli/src/application_build.rs | 819 ++++++++++++++---- crates/edict-syntax/src/lawpack_adapter.rs | 55 +- .../lawpack/workspace-snapshot/adapter.cbor | Bin 0 -> 472 bytes .../lawpack/workspace-snapshot/adapter.sha256 | 1 + .../lawpack/workspace-snapshot/exports.cbor | 1 + .../lawpack/workspace-snapshot/exports.sha256 | 1 + .../lawpack/workspace-snapshot/manifest.cbor | Bin 0 -> 826 bytes .../workspace-snapshot/manifest.sha256 | 1 + .../observe-workspace.core.cbor | Bin 0 -> 2764 bytes .../observe-workspace.core.sha256 | 1 + .../observe-workspace.edict | 34 + .../observe-workspace.target-ir.cbor | Bin 0 -> 2226 bytes .../observe-workspace.target-ir.sha256 | 1 + .../request-profile-configuration.cbor | 1 + .../request-profile-configuration.sha256 | 1 + .../primary/target-profile.echo-dpo.cbor | Bin 0 -> 1787 bytes xtask/src/lawpack_goldens.rs | 320 ++++++- 17 files changed, 1074 insertions(+), 162 deletions(-) create mode 100644 fixtures/lawpack/workspace-snapshot/adapter.cbor create mode 100644 fixtures/lawpack/workspace-snapshot/adapter.sha256 create mode 100644 fixtures/lawpack/workspace-snapshot/exports.cbor create mode 100644 fixtures/lawpack/workspace-snapshot/exports.sha256 create mode 100644 fixtures/lawpack/workspace-snapshot/manifest.cbor create mode 100644 fixtures/lawpack/workspace-snapshot/manifest.sha256 create mode 100644 fixtures/lawpack/workspace-snapshot/observe-workspace.core.cbor create mode 100644 fixtures/lawpack/workspace-snapshot/observe-workspace.core.sha256 create mode 100644 fixtures/lawpack/workspace-snapshot/observe-workspace.edict create mode 100644 fixtures/lawpack/workspace-snapshot/observe-workspace.target-ir.cbor create mode 100644 fixtures/lawpack/workspace-snapshot/observe-workspace.target-ir.sha256 create mode 100644 fixtures/lawpack/workspace-snapshot/request-profile-configuration.cbor create mode 100644 fixtures/lawpack/workspace-snapshot/request-profile-configuration.sha256 create mode 100644 fixtures/providers/echo-target-profile/generated/primary/target-profile.echo-dpo.cbor diff --git a/crates/edict-cli/src/application_build.rs b/crates/edict-cli/src/application_build.rs index 0628b61..9bb94ef 100644 --- a/crates/edict-cli/src/application_build.rs +++ b/crates/edict-cli/src/application_build.rs @@ -24,7 +24,7 @@ use edict_syntax::{ ProviderSemanticInputBinding, ProviderSemanticInputKind, ProviderVerificationInvocationContract, ProviderVerificationOutputKind, ProviderVerificationOutputRequest, ProviderVerificationRequest, ResultProjectionArtifact, - TargetLoweringStatus, TargetProviderManifest, ValidatedLawpackBundle, + TargetIrArtifact, 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, @@ -228,36 +228,6 @@ pub(crate) fn build_application(config_path: &Path) -> Result<(), ApplicationBui )?; require_manifest_identity(target_profile_artifact, &target_profile)?; - let schema_artifacts = provider_schema_artifacts(&provider_manifest)?; - let resolved_schema_artifacts = schema_artifacts - .into_iter() - .map(|schema_artifact| { - Ok(ResolvedProviderSchemaArtifact { - role: schema_artifact.role.clone(), - bytes: Arc::<[u8]>::from(read_provider_artifact( - &provider_root, - schema_artifact, - ProviderArtifactKind::ArtifactSchema, - )?), - }) - }) - .collect::, ApplicationBuildFailure>>()?; - let required_domains = provider_manifest - .schema_bindings - .iter() - .map(|binding| binding.domain.as_str()); - let registry = ProviderArtifactSchemaRegistry::from_manifest( - &provider_proof, - resolved_schema_artifacts, - required_domains, - ) - .map_err(|error| { - failure( - "InvalidProviderPackage", - format!("provider artifact-schema registry failed: {error}"), - ) - })?; - let adapter = decode_lawpack_adapter( &loaded.bundle, &config.target.profile, @@ -311,16 +281,36 @@ 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", ) })?; + + let core_bytes = encode_core_module(&core).map_err(|error| { + failure( + "ApplicationEncodingFailed", + format!("Core canonical encoding failed: {error}"), + ) + })?; + let target_ir_bytes = encode_target_ir_artifact(&target_ir).map_err(|error| { + failure( + "ApplicationEncodingFailed", + format!("Target IR canonical encoding failed: {error}"), + ) + })?; + + if config.build_kind == ApplicationBuildKind::ExternalAction { + validate_external_action_artifacts(&target_ir, &loaded_lawpacks)?; + let output_directory = prepare_output_directory(&root, &config.output_directory)?; + return write_external_action_outputs(&output_directory, &core_bytes, &target_ir_bytes); + } + + let (result_intent, result_projection) = single_result_projection( + &target_ir_report.result_projections, + &target_ir_report.result_projection_failures, + )?; verify_result_projection( &core, &target_ir, @@ -335,18 +325,36 @@ pub(crate) fn build_application(config_path: &Path) -> Result<(), ApplicationBui ) })?; - let core_bytes = encode_core_module(&core).map_err(|error| { - failure( - "ApplicationEncodingFailed", - format!("Core canonical encoding failed: {error}"), - ) - })?; - let target_ir_bytes = encode_target_ir_artifact(&target_ir).map_err(|error| { + let schema_artifacts = provider_schema_artifacts(&provider_manifest)?; + let resolved_schema_artifacts = schema_artifacts + .into_iter() + .map(|schema_artifact| { + Ok(ResolvedProviderSchemaArtifact { + role: schema_artifact.role.clone(), + bytes: Arc::<[u8]>::from(read_provider_artifact( + &provider_root, + schema_artifact, + ProviderArtifactKind::ArtifactSchema, + )?), + }) + }) + .collect::, ApplicationBuildFailure>>()?; + let required_domains = provider_manifest + .schema_bindings + .iter() + .map(|binding| binding.domain.as_str()); + let registry = ProviderArtifactSchemaRegistry::from_manifest( + &provider_proof, + resolved_schema_artifacts, + required_domains, + ) + .map_err(|error| { failure( - "ApplicationEncodingFailed", - format!("Target IR canonical encoding failed: {error}"), + "InvalidProviderPackage", + format!("provider artifact-schema registry failed: {error}"), ) })?; + let source_artifact_bytes = encode_canonical_cbor(&CanonicalValue::Bytes(source_bytes)) .map_err(|error| { failure( @@ -409,6 +417,73 @@ pub(crate) fn build_application(config_path: &Path) -> Result<(), ApplicationBui write_outputs(&output_directory, &package_bytes, &report_bytes) } +fn validate_external_action_artifacts( + target_ir: &TargetIrArtifact, + loaded_lawpacks: &[LoadedLawpack], +) -> Result<(), ApplicationBuildFailure> { + let request_count = target_ir + .intents + .values() + .map(|intent| intent.external_action_requests.len()) + .sum::(); + if request_count == 0 { + return Err(failure( + "ExternalActionRequestUnavailable", + "external-action application build requires at least one typed request", + )); + } + if target_ir + .intents + .values() + .any(|intent| !intent.steps.is_empty()) + { + return Err(failure( + "MixedApplicationExecution", + "external-action application build cannot mix requests with callable target steps", + )); + } + let capability_manifests = loaded_lawpacks + .iter() + .map(|loaded| { + ( + loaded.bundle.manifest().id.as_str(), + loaded.bundle.manifest().version.as_str(), + loaded.bundle.manifest_digest_review_string(), + ) + }) + .collect::>(); + let substituted = target_ir.intents.values().find_map(|intent| { + intent + .external_action_requests + .iter() + .map(|request| &request.operation) + .find(|operation| { + !capability_manifests.iter().any(|(id, version, digest)| { + let Some((operation_id, operation_version)) = + operation.coordinate.rsplit_once('@') + else { + return false; + }; + operation.digest.as_deref() == Some(digest.as_str()) + && operation_version == *version + && operation_id + .strip_prefix(&format!("{id}.")) + .is_some_and(|suffix| !suffix.is_empty()) + }) + }) + }); + if let Some(operation) = substituted { + return Err(failure( + "ExternalActionCapabilityClosureMismatch", + format!( + "request operation `{}` is not bound to one exact application lawpack manifest", + operation.coordinate + ), + )); + } + Ok(()) +} + fn validate_application_manifest( config: &ApplicationManifest, ) -> Result<(), ApplicationBuildFailure> { @@ -691,7 +766,13 @@ fn validate_target_configuration_binding( adapter .effects() .values() - .map(|effect| &effect.target_configuration), + .map(|effect| &effect.target_configuration) + .chain( + adapter + .operation_profiles() + .values() + .filter_map(|profile| profile.target_configuration.as_ref()), + ), )?; let digest = provider_digest(&reference.id, bytes)?; if reference.digest_review_string() != rendered_digest(&digest) { @@ -1084,7 +1165,7 @@ fn single_unique_configuration<'a>( (Some(reference), None) => Ok(reference), _ => Err(failure( "InvalidLawpackAdapter", - "the executable-operation adapter currently requires exactly one target configuration", + "the application adapter requires exactly one target configuration", )), } } @@ -1270,14 +1351,94 @@ fn require_accepted_report(bytes: &[u8]) -> Result<(), ApplicationBuildFailure> Ok(()) } +fn write_outputs( + directory: &Path, + package: &[u8], + report: &[u8], +) -> Result<(), ApplicationBuildFailure> { + write_application_output_pair( + directory, + &[ + ApplicationOutput { + file_name: "executable-operation-package.cbor", + transaction_name: "new-package.cbor", + backup_name: "previous-package.cbor", + bytes: package, + }, + ApplicationOutput { + file_name: "verification-report.cbor", + transaction_name: "new-report.cbor", + backup_name: "previous-report.cbor", + bytes: report, + }, + ], + &[ + ObsoleteApplicationOutput { + file_name: "core.cbor", + backup_name: "previous-core.cbor", + }, + ObsoleteApplicationOutput { + file_name: "target-ir.cbor", + backup_name: "previous-target-ir.cbor", + }, + ], + ) +} + +fn write_external_action_outputs( + directory: &Path, + core: &[u8], + target_ir: &[u8], +) -> Result<(), ApplicationBuildFailure> { + write_application_output_pair( + directory, + &[ + ApplicationOutput { + file_name: "core.cbor", + transaction_name: "new-core.cbor", + backup_name: "previous-core.cbor", + bytes: core, + }, + ApplicationOutput { + file_name: "target-ir.cbor", + transaction_name: "new-target-ir.cbor", + backup_name: "previous-target-ir.cbor", + bytes: target_ir, + }, + ], + &[ + ObsoleteApplicationOutput { + file_name: "executable-operation-package.cbor", + backup_name: "previous-package.cbor", + }, + ObsoleteApplicationOutput { + file_name: "verification-report.cbor", + backup_name: "previous-report.cbor", + }, + ], + ) +} + +struct ApplicationOutput<'a> { + file_name: &'static str, + transaction_name: &'static str, + backup_name: &'static str, + bytes: &'a [u8], +} + +struct ObsoleteApplicationOutput { + file_name: &'static str, + backup_name: &'static str, +} + #[allow( clippy::too_many_lines, reason = "paired output publication keeps every rollback transition explicit" )] -fn write_outputs( +fn write_application_output_pair( directory: &Path, - package: &[u8], - report: &[u8], + outputs: &[ApplicationOutput<'_>; 2], + obsolete: &[ObsoleteApplicationOutput; 2], ) -> Result<(), ApplicationBuildFailure> { fs::create_dir_all(directory).map_err(|error| { failure( @@ -1315,104 +1476,76 @@ fn write_outputs( ) })?; - let package_path = directory.join("executable-operation-package.cbor"); - let report_path = directory.join("verification-report.cbor"); - let package_existed = validate_output_target(&package_path)?; - let report_existed = validate_output_target(&report_path)?; - let transaction = create_output_transaction(directory)?; - let package_temp = transaction.join("new-package.cbor"); - let report_temp = transaction.join("new-report.cbor"); - let package_backup = transaction.join("previous-package.cbor"); - let report_backup = transaction.join("previous-report.cbor"); - - if let Err(error) = write_synced(&package_temp, package) { - let _ = fs::remove_dir_all(&transaction); - return Err(failure( - "ApplicationOutputWriteFailed", - format!("failed to stage `{}`: {error}", package_path.display()), - )); + let mut destinations = Vec::with_capacity(4); + for output in outputs { + let destination = directory.join(output.file_name); + let existed = validate_output_target(&destination)?; + destinations.push((destination, output.backup_name, existed)); } - if let Err(error) = write_synced(&report_temp, report) { - let _ = fs::remove_dir_all(&transaction); - return Err(failure( - "ApplicationOutputWriteFailed", - format!("failed to stage `{}`: {error}", report_path.display()), - )); + for output in obsolete { + let destination = directory.join(output.file_name); + let existed = validate_output_target(&destination)?; + destinations.push((destination, output.backup_name, existed)); } - if package_existed { - if let Err(error) = fs::rename(&package_path, &package_backup) { + let transaction = create_output_transaction(directory)?; + let mut recoveries = destinations + .into_iter() + .map(|(destination, backup_name, existed)| OutputRecovery { + destination, + backup: transaction.join(backup_name), + existed, + published: false, + }) + .collect::>(); + + let staged = outputs + .iter() + .map(|output| transaction.join(output.transaction_name)) + .collect::>(); + for (output, staged_path) in outputs.iter().zip(&staged) { + if let Err(error) = write_synced(staged_path, output.bytes) { let _ = fs::remove_dir_all(&transaction); return Err(failure( "ApplicationOutputWriteFailed", format!( - "failed to preserve previous output `{}`: {error}", - package_path.display() + "failed to stage `{}`: {error}", + directory.join(output.file_name).display() ), )); } } - if report_existed { - if let Err(error) = fs::rename(&report_path, &report_backup) { - let rollback = restore_output(&package_backup, &package_path, package_existed); + + for index in 0..recoveries.len() { + if !recoveries[index].existed { + continue; + } + if let Err(error) = fs::rename(&recoveries[index].destination, &recoveries[index].backup) { + let rollback = restore_previous_outputs(&recoveries[..index]); if rollback.is_ok() { let _ = fs::remove_dir_all(&transaction); } return Err(output_publication_failure( - &report_path, + &recoveries[index].destination, &error, rollback.err(), )); } } - if let Err(error) = fs::rename(&report_temp, &report_path) { - let rollback = restore_previous_outputs(&[ - OutputRecovery { - destination: &package_path, - backup: &package_backup, - existed: package_existed, - published: false, - }, - OutputRecovery { - destination: &report_path, - backup: &report_backup, - existed: report_existed, - published: false, - }, - ]); - if rollback.is_ok() { - let _ = fs::remove_dir_all(&transaction); - } - return Err(output_publication_failure( - &report_path, - &error, - rollback.err(), - )); - } - if let Err(error) = fs::rename(&package_temp, &package_path) { - let rollback = restore_previous_outputs(&[ - OutputRecovery { - destination: &package_path, - backup: &package_backup, - existed: package_existed, - published: false, - }, - OutputRecovery { - destination: &report_path, - backup: &report_backup, - existed: report_existed, - published: true, - }, - ]); - if rollback.is_ok() { - let _ = fs::remove_dir_all(&transaction); + for index in [1_usize, 0] { + if let Err(error) = fs::rename(&staged[index], &recoveries[index].destination) { + let rollback = restore_previous_outputs(&recoveries); + if rollback.is_ok() { + let _ = fs::remove_dir_all(&transaction); + } + return Err(output_publication_failure( + &recoveries[index].destination, + &error, + rollback.err(), + )); } - return Err(output_publication_failure( - &package_path, - &error, - rollback.err(), - )); + recoveries[index].published = true; } fs::remove_dir_all(&transaction).map_err(|error| { @@ -1426,17 +1559,6 @@ fn write_outputs( }) } -fn write_external_action_outputs( - _directory: &Path, - _core: &[u8], - _target_ir: &[u8], -) -> Result<(), ApplicationBuildFailure> { - Err(failure( - "ExternalActionBuildUnavailable", - "the public application build cannot publish external-action artifacts", - )) -} - fn output_lock_path(directory: &Path) -> PathBuf { let mut name = OsString::from("."); name.push( @@ -1516,17 +1638,17 @@ fn create_output_transaction(directory: &Path) -> Result { - destination: &'a Path, - backup: &'a Path, +struct OutputRecovery { + destination: PathBuf, + backup: PathBuf, existed: bool, published: bool, } -fn restore_previous_outputs(outputs: &[OutputRecovery<'_>; 2]) -> Result<(), String> { +fn restore_previous_outputs(outputs: &[OutputRecovery]) -> Result<(), String> { let mut failures = Vec::new(); for output in outputs.iter().filter(|output| output.published) { - if let Err(error) = fs::remove_file(output.destination) { + if let Err(error) = fs::remove_file(&output.destination) { failures.push(format!( "failed to remove partial output `{}`: {error}", output.destination.display() @@ -1534,7 +1656,7 @@ fn restore_previous_outputs(outputs: &[OutputRecovery<'_>; 2]) -> Result<(), Str } } for output in outputs { - if let Err(error) = restore_output(output.backup, output.destination, output.existed) { + if let Err(error) = restore_output(&output.backup, &output.destination, output.existed) { failures.push(error); } } @@ -1768,16 +1890,17 @@ mod tests { 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, + TargetIrArtifact, TargetLoweringReport, 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_result_projection, single_unique_configuration, - validate_application_manifest, with_result_projection_input, write_external_action_outputs, - write_outputs, ApplicationBuildKind, ApplicationLawpack, ApplicationManifest, - ApplicationTarget, RESULT_PROJECTION_ROLE, + validate_application_manifest, validate_external_action_artifacts, + with_result_projection_input, write_external_action_outputs, write_outputs, + ApplicationBuildKind, ApplicationLawpack, ApplicationManifest, ApplicationTarget, + LoadedLawpack, RESULT_PROJECTION_ROLE, }; const STRESS_SEED: u64 = 0x5eed_1a77_c105_0a11; @@ -1821,6 +1944,164 @@ mod tests { assert_eq!(decoded.build_kind, ApplicationBuildKind::ExternalAction); } + #[test] + fn external_action_build_accepts_request_only_target_ir() { + let closure = [external_action_loaded_lawpack()]; + test_ok( + validate_external_action_artifacts(&external_action_target_ir(), &closure), + "request-only Target IR is publishable", + ); + } + + #[test] + fn external_action_build_requires_a_typed_request() { + let failure = test_err( + validate_external_action_artifacts(&hello_echo_target_ir(), &[]), + "callable-only Target IR must not publish as external-action artifacts", + ); + + assert_eq!(failure.kind, "ExternalActionRequestUnavailable"); + } + + #[test] + fn external_action_build_rejects_mixed_callable_execution() { + let closure = [external_action_loaded_lawpack()]; + let mut external = external_action_target_ir(); + let Some(callable) = hello_echo_target_ir() + .intents + .get("createGreeting") + .and_then(|intent| intent.steps.first()) + .cloned() + else { + panic!("Hello Echo Target IR has one callable step"); + }; + let Some(observe) = external.intents.get_mut("observe") else { + panic!("workspace observer intent exists"); + }; + observe.steps.push(callable); + + let failure = test_err( + validate_external_action_artifacts(&external, &closure), + "mixed callable/request execution must reject", + ); + + assert_eq!(failure.kind, "MixedApplicationExecution"); + } + + #[test] + fn external_action_build_rejects_a_substituted_capability_manifest() { + let closure = [external_action_loaded_lawpack()]; + let mut external = external_action_target_ir(); + let Some(observe) = external.intents.get_mut("observe") else { + panic!("workspace observer intent exists"); + }; + let operation = &mut observe.external_action_requests[0].operation; + operation.digest = Some(format!("sha256:{}", "0".repeat(64))); + + let failure = test_err( + validate_external_action_artifacts(&external, &closure), + "substituted capability manifest must reject", + ); + + assert_eq!(failure.kind, "ExternalActionCapabilityClosureMismatch"); + } + + #[test] + fn public_external_action_build_emits_exact_compiler_artifacts() { + let root = temp_tree("public-external-action"); + let config_path = write_external_action_application(&root); + let output = root.join(".build/application"); + test_ok(fs::create_dir_all(&output), "create stale output directory"); + test_ok( + fs::write( + output.join("executable-operation-package.cbor"), + b"stale-package", + ), + "write stale package", + ); + test_ok( + fs::write(output.join("verification-report.cbor"), b"stale-report"), + "write stale report", + ); + + test_ok( + build_application(&config_path), + "build public external-action application", + ); + let first_core = test_ok(fs::read(output.join("core.cbor")), "read first Core"); + let first_target = test_ok( + fs::read(output.join("target-ir.cbor")), + "read first Target IR", + ); + + assert_eq!( + first_core, + include_bytes!( + "../../../fixtures/lawpack/workspace-snapshot/observe-workspace.core.cbor" + ) + ); + assert_eq!( + first_target, + include_bytes!( + "../../../fixtures/lawpack/workspace-snapshot/observe-workspace.target-ir.cbor" + ) + ); + assert!(!output.join("executable-operation-package.cbor").exists()); + assert!(!output.join("verification-report.cbor").exists()); + + test_ok( + build_application(&config_path), + "rerun public external-action application build", + ); + assert_eq!( + test_ok(fs::read(output.join("core.cbor")), "read rerun Core"), + first_core + ); + assert_eq!( + test_ok( + fs::read(output.join("target-ir.cbor")), + "read rerun Target IR", + ), + first_target + ); + test_ok(fs::remove_dir_all(root), "remove public build tree"); + } + + #[test] + fn public_external_action_build_rejects_capability_substitution() { + let root = temp_tree("public-external-action-substitution"); + let config_path = write_external_action_application(&root); + let source_path = root.join("src/observe-workspace.edict"); + let source = test_ok(fs::read_to_string(&source_path), "read application source"); + let manifest_digest = + include_str!("../../../fixtures/lawpack/workspace-snapshot/manifest.sha256").trim(); + let capability_import = + format!("use capability workspace.snapshot.observe@1 digest \"{manifest_digest}\""); + let substituted = source.replacen( + &capability_import, + &format!( + "use capability workspace.snapshot.observe@1 digest \"sha256:{}\"", + "0".repeat(64) + ), + 1, + ); + assert_ne!(substituted, source, "capability import fixture must mutate"); + test_ok( + fs::write(&source_path, substituted), + "write substituted application source", + ); + + let failure = test_err( + build_application(&config_path), + "substituted capability closure must reject", + ); + + assert_eq!(failure.kind, "ExternalActionCapabilityClosureMismatch"); + assert!(!root.join(".build/application/core.cbor").exists()); + assert!(!root.join(".build/application/target-ir.cbor").exists()); + test_ok(fs::remove_dir_all(root), "remove substituted build tree"); + } + #[test] fn relative_application_config_uses_the_current_directory_as_root() { let actual = test_ok( @@ -1983,6 +2264,21 @@ mod tests { } fn result_projection_artifact() -> ResultProjectionArtifact { + let mut report = hello_echo_target_ir_report(); + match report.result_projections.remove("createGreeting") { + Some(projection) => projection, + None => panic!("Hello Echo lowering emits its result projection"), + } + } + + fn hello_echo_target_ir() -> TargetIrArtifact { + match hello_echo_target_ir_report().artifact { + Some(artifact) => artifact, + None => panic!("Hello Echo lowering emits Target IR"), + } + } + + fn hello_echo_target_ir_report() -> TargetLoweringReport { let manifest = include_bytes!("../../../fixtures/lawpack/hello-echo/manifest.cbor").as_slice(); let exports = @@ -2007,13 +2303,198 @@ mod tests { 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"), + lower_to_target_ir(&core, preparation.target_ir_facts()) + } + + fn external_action_target_ir() -> TargetIrArtifact { + let loaded = external_action_loaded_lawpack(); + let source = + include_str!("../../../fixtures/lawpack/workspace-snapshot/observe-workspace.edict"); + let module = test_ok(parse_module(source), "parse workspace observation source"); + let adapter = test_ok( + decode_lawpack_adapter(&loaded.bundle, "echo.dpo@1", &loaded.adapter_bytes), + "decode workspace observation adapter", + ); + let preparation = test_ok( + prepare_lawpack_compilation(&module, &loaded.bundle, &adapter), + "prepare workspace observation compilation", + ); + let core = test_ok( + compile_to_core(&module, preparation.compiler_context()), + "compile workspace observation Core", + ); + match lower_to_target_ir(&core, preparation.target_ir_facts()).artifact { + Some(artifact) => artifact, + None => panic!("workspace observation lowering emits Target IR"), } } + fn external_action_loaded_lawpack() -> LoadedLawpack { + let manifest_bytes = + include_bytes!("../../../fixtures/lawpack/workspace-snapshot/manifest.cbor").to_vec(); + let exports_bytes = + include_bytes!("../../../fixtures/lawpack/workspace-snapshot/exports.cbor").to_vec(); + let adapter_bytes = + include_bytes!("../../../fixtures/lawpack/workspace-snapshot/adapter.cbor").to_vec(); + let configuration_bytes = include_bytes!( + "../../../fixtures/lawpack/workspace-snapshot/request-profile-configuration.cbor" + ) + .to_vec(); + let bundle = test_ok( + decode_lawpack_bundle(&manifest_bytes, &exports_bytes), + "decode workspace observation lawpack", + ); + LoadedLawpack { + manifest_bytes, + exports_bytes, + adapter_bytes, + configuration_bytes, + bundle, + } + } + + #[allow( + clippy::too_many_lines, + reason = "the public-build fixture keeps its complete file closure visible" + )] + fn write_external_action_application(root: &std::path::Path) -> PathBuf { + let source_directory = root.join("src"); + let lawpack_directory = root.join("vendor/workspace-snapshot"); + let provider_directory = root.join("provider"); + let provider_generated = provider_directory.join("generated/primary"); + for directory in [&source_directory, &lawpack_directory, &provider_generated] { + test_ok( + fs::create_dir_all(directory), + "create application fixture tree", + ); + } + + for (path, bytes) in [ + ( + source_directory.join("observe-workspace.edict"), + include_bytes!( + "../../../fixtures/lawpack/workspace-snapshot/observe-workspace.edict" + ) + .as_slice(), + ), + ( + lawpack_directory.join("manifest.cbor"), + include_bytes!("../../../fixtures/lawpack/workspace-snapshot/manifest.cbor") + .as_slice(), + ), + ( + lawpack_directory.join("exports.cbor"), + include_bytes!("../../../fixtures/lawpack/workspace-snapshot/exports.cbor") + .as_slice(), + ), + ( + lawpack_directory.join("adapter.cbor"), + include_bytes!("../../../fixtures/lawpack/workspace-snapshot/adapter.cbor") + .as_slice(), + ), + ( + lawpack_directory.join("request-profile-configuration.cbor"), + include_bytes!( + "../../../fixtures/lawpack/workspace-snapshot/request-profile-configuration.cbor" + ) + .as_slice(), + ), + ( + provider_generated.join("target-profile.echo-dpo.cbor"), + include_bytes!( + "../../../fixtures/providers/echo-target-profile/generated/primary/target-profile.echo-dpo.cbor" + ) + .as_slice(), + ), + ] { + test_ok(fs::write(path, bytes), "write application fixture"); + } + + let provider_manifest = serde_json::json!({ + "apiVersion": TARGET_PROVIDER_MANIFEST_API_VERSION, + "providerAbi": TARGET_PROVIDER_ABI, + "provider": resource_json("echo.edict-provider@1", '1'), + "artifacts": [ + { + "role": "target-profile.echo-dpo", + "artifactKind": "targetProfile", + "resource": { + "coordinate": "echo.dpo@1", + "digest": "sha256:2e2494121aecf5e6a2d920f5fb85408825d394765fad41484c416397c920fb04" + }, + "source": { + "kind": "generated", + "semanticSource": resource_json("echo.semantic-schema@1", '2'), + "generator": resource_json("echo-wesley-gen.provider-artifact-generator@1", '3') + } + }, + { + "role": "schema.echo-provider-artifacts", + "artifactKind": "artifactSchema", + "resource": resource_json("echo.provider-artifacts.cddl@1", '4'), + "source": { + "kind": "generated", + "semanticSource": resource_json("echo.semantic-schema@1", '2'), + "generator": resource_json("echo-wesley-gen.provider-artifact-generator@1", '3') + } + } + ], + "schemaBindings": [{ + "domain": "echo.generated-artifact/v1", + "schemaRole": "schema.echo-provider-artifacts", + "format": "selfContainedCddlV1", + "rootRule": "generated-artifact" + }] + }); + test_ok( + fs::write( + provider_directory.join("provider-manifest.echo.json"), + test_ok( + serde_json::to_vec_pretty(&provider_manifest), + "encode provider manifest", + ), + ), + "write provider manifest", + ); + + let application = serde_json::json!({ + "schema": "edict.application/v1", + "buildKind": "externalAction", + "coordinate": "examples.workspace_observer@1", + "sources": ["src/observe-workspace.edict"], + "lawpacks": [{ + "manifest": "vendor/workspace-snapshot/manifest.cbor", + "exports": "vendor/workspace-snapshot/exports.cbor", + "adapter": "vendor/workspace-snapshot/adapter.cbor", + "targetConfiguration": "vendor/workspace-snapshot/request-profile-configuration.cbor" + }], + "target": { + "profile": "echo.dpo@1", + "providerPackage": "provider" + }, + "outputDirectory": ".build/application" + }); + let config_path = root.join("edict.application.json"); + test_ok( + fs::write( + &config_path, + test_ok( + serde_json::to_vec_pretty(&application), + "encode application manifest", + ), + ), + "write application manifest", + ); + config_path + } + + fn resource_json(coordinate: &str, digest: char) -> serde_json::Value { + serde_json::json!({ + "coordinate": coordinate, + "digest": format!("sha256:{}", digest.to_string().repeat(64)) + }) + } + #[test] fn failed_pair_publication_preserves_the_previous_package() { let root = temp_tree("pair-publication"); @@ -2041,6 +2522,42 @@ mod tests { test_ok(fs::remove_dir_all(root), "remove test-owned temp tree"); } + #[test] + fn executable_publication_removes_stale_external_action_outputs() { + let root = temp_tree("executable-stale"); + test_ok( + fs::write(root.join("core.cbor"), b"stale-core"), + "write stale Core", + ); + test_ok( + fs::write(root.join("target-ir.cbor"), b"stale-target-ir"), + "write stale Target IR", + ); + + test_ok( + write_outputs(&root, b"package", b"report"), + "publish executable pair over stale external-action outputs", + ); + + assert!(!root.join("core.cbor").exists()); + assert!(!root.join("target-ir.cbor").exists()); + assert_eq!( + test_ok( + fs::read(root.join("executable-operation-package.cbor")), + "read package", + ), + b"package" + ); + assert_eq!( + test_ok( + fs::read(root.join("verification-report.cbor")), + "read report", + ), + b"report" + ); + test_ok(fs::remove_dir_all(root), "remove executable output tree"); + } + #[test] fn external_action_publication_writes_the_exact_compiler_pair() { let root = temp_tree("external-action-pair"); diff --git a/crates/edict-syntax/src/lawpack_adapter.rs b/crates/edict-syntax/src/lawpack_adapter.rs index 8e4e6cb..f82e8d8 100644 --- a/crates/edict-syntax/src/lawpack_adapter.rs +++ b/crates/edict-syntax/src/lawpack_adapter.rs @@ -65,6 +65,8 @@ pub struct LawpackAdapterFailure { pub struct LawpackAdapterOperationProfile { pub core: String, pub semantic_effects: Vec, + pub budget_obligation: Option, + pub target_configuration: Option, } /// One semantic effect discharged by a direct adapter. @@ -306,18 +308,37 @@ fn parse_operation_profiles( let mut profiles = BTreeMap::new(); for (coordinate, value) in values { let path = format!("adapter.operationProfiles.{coordinate}"); - let fields = closed_map(value, &path, &["core", "semanticEffects"])?; + let fields = closed_map( + value, + &path, + &[ + "core", + "semanticEffects", + "budgetObligation", + "targetConfiguration", + ], + )?; let core = required_nonempty_text(&fields, "core", &path)?; let semantic_effects = text_array( required(&fields, "semanticEffects", &path)?, &format!("{path}.semanticEffects"), - true, + false, )?; + let budget_obligation = fields + .get("budgetObligation") + .map(|value| nonempty_text(value, &format!("{path}.budgetObligation"))) + .transpose()?; + let target_configuration = fields + .get("targetConfiguration") + .map(|value| parse_resource_ref(value, &format!("{path}.targetConfiguration"))) + .transpose()?; profiles.insert( coordinate, LawpackAdapterOperationProfile { core, semantic_effects, + budget_obligation, + target_configuration, }, ); } @@ -447,15 +468,24 @@ fn validate_adapter_closure( validate_effect(coordinate, exported, effect, &intrinsic_prefix)?; required_budgets.insert(effect.cost_obligation.as_str()); } - exact_keys( - required_budgets, - budgets.keys().map(String::as_str), - LawpackAdapterFailureKind::MissingBudget, - LawpackAdapterFailureKind::UnknownBudget, - "adapter.budgets", - )?; for (coordinate, profile) in operation_profiles { + if let Some(budget) = &profile.budget_obligation { + required_budgets.insert(budget); + } else if profile.semantic_effects.is_empty() { + return Err(one(failure( + LawpackAdapterFailureKind::MissingBudget, + format!("adapter.operationProfiles.{coordinate}.budgetObligation"), + "request-only profile budget obligation", + ))); + } + if profile.semantic_effects.is_empty() && profile.target_configuration.is_none() { + return Err(one(failure( + LawpackAdapterFailureKind::InvalidTargetConfiguration, + format!("adapter.operationProfiles.{coordinate}.targetConfiguration"), + "request-only profile target configuration", + ))); + } let mut seen = BTreeSet::new(); for effect in &profile.semantic_effects { if !seen.insert(effect) { @@ -474,6 +504,13 @@ fn validate_adapter_closure( } } } + exact_keys( + required_budgets, + budgets.keys().map(String::as_str), + LawpackAdapterFailureKind::MissingBudget, + LawpackAdapterFailureKind::UnknownBudget, + "adapter.budgets", + )?; Ok(()) } diff --git a/fixtures/lawpack/workspace-snapshot/adapter.cbor b/fixtures/lawpack/workspace-snapshot/adapter.cbor new file mode 100644 index 0000000000000000000000000000000000000000..9be4ea568b602b56269af505deb809989a3fa8b5 GIT binary patch literal 472 zcmb8ry-Gtd6bJB7T$~Gj4N?#nu~#bSO@EgwuL`!x^TfEbUdJYB%haRE%PE?6NphqMp? zS&0RiVGy?zq^4vhm+0jrmIH$!8)#TgN@_uBUP@|Sa%O6ALvC_@ZUN9)NtrpBC6!<& zDPwUGiYja_Y9#>jN)n6GQ%f9E5(`Rz4r^STo(N%rtyIEdWomLpzAjV|HXBW6r&g?L zih5Gjem=XLy)HKUP07a}Oeu+5%DAe-_*rs@GxM4oPD z5jIm9!o}(`zVghxbB?3hPUc$q#hO^zLl%3t>UO%#6Dj(mUy5pKKv8~LW=<-|=q#|& zDFyism|KcWb+_;9Oz>rF^?IKR;VqTJ*b=TKze`T+~*)(MDeP1Ic2Q;D=IG`I}PPl3cH%soLk-1?$Aa&R9i2-#w zkuadl!3me{fn26B(;A7xwHvUtLZ*NQj&geU3@z}SR1ql9No2Z)eGbCwGw=Ulk2uXf z6o|y2dhz&kY6Fr^)2o7OqX_P*ddJ{N4(@D|C~@>$ar;86Vvj`zq#hf}alBu16`g{{ zb3?gY&7Myp%O?Z0{dhQ+OGmByS+1>h%, + scope: Bytes, + basis: Bytes, + maxSettlementBytes: U64, + maxAttempts: U32, +}; + +intent observe(input: ObserveInput) + returns ExternalActionRequest> + profile workspace.observeRequest + basis input.basis + budget <= workspace.tinyObservationBudget +{ + request pending: ExternalActionRequest> = + snapshot(input.payload) + input schema workspace.snapshot.input@1 + digest "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + settlement schema workspace.snapshot.settlement@1 + digest "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" + authority input.scope + basis input.basis + budget + maxSettlementBytes input.maxSettlementBytes + maxAttempts input.maxAttempts + reconcile workspace.snapshot.reconcile@1 + digest "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"; + return pending; +} diff --git a/fixtures/lawpack/workspace-snapshot/observe-workspace.target-ir.cbor b/fixtures/lawpack/workspace-snapshot/observe-workspace.target-ir.cbor new file mode 100644 index 0000000000000000000000000000000000000000..103ff0d60d29eae6c99d173272a2779ffd29d430 GIT binary patch literal 2226 zcmc&#&r4KM6dt6FD=R@r%M1)8@THcnOdO>jiezL((8BJ#cix+;_uYHv-uuRxRaz7+ zY!OI5cI8C0slD_M7}X-$RMa*If)?RIBG#+=5goU^L^(#=Y03MH%+-G z4N(D@5tUYk`ouN9CqhEG05>ZfEeY?W>Uh38pxgjA`e`6C8dM%$K!#|lOCJ8fqN~6! zWvB-j&FS(?Q9|Ju6i6r;XlF*KX$~_Y>aYnd|3?)ov$$1h2v}1%WOAVNC z2`j()6qpC)r^N+rKg(-2(Kj$qV-mF6SR1*p_#{;j#!Ade8dDz@TpNXo#XCZRA~D7VC49=D(CP|q zD37Ahk>xE!Ctj!Etcdu=6$(!035)Ehp~%Bpl(NWYB2Mo>U5exTcw`XBQf*GvrBXTE zOS71r0k5*XCzZ;t=b5%?UMy#+=U!o5auwm%b9cISxqMc;oo}8%k|Gnt!}6neip&s- z)?K&w->h|q_FZke@%GW>`yZN~ zX3jR97^?qv?f1gHKhgD$uRVLu99jL*kV8+zS(|XP5E2`{IU&6LCL#FiZ5Ahy$qGeH zal*wUcD{_DV_dGQ?G)=PYx-0P}5|rE4V9UP!e@d*<2}IJQW%b1y7kf)&$_SxnjM&yykn&}m=T-~4ms`{L)e zm9^W0caMHt$WOjZpBqcN58t${?GYM#2VLJCWn#2P=>wp! literal 0 HcmV?d00001 diff --git a/fixtures/lawpack/workspace-snapshot/observe-workspace.target-ir.sha256 b/fixtures/lawpack/workspace-snapshot/observe-workspace.target-ir.sha256 new file mode 100644 index 0000000..cb22456 --- /dev/null +++ b/fixtures/lawpack/workspace-snapshot/observe-workspace.target-ir.sha256 @@ -0,0 +1 @@ +sha256:29cce7912efa7d2923b0c5631339bbac680c042b837ee1f4666aca2d746d3bfe diff --git a/fixtures/lawpack/workspace-snapshot/request-profile-configuration.cbor b/fixtures/lawpack/workspace-snapshot/request-profile-configuration.cbor new file mode 100644 index 0000000..708f189 --- /dev/null +++ b/fixtures/lawpack/workspace-snapshot/request-profile-configuration.cbor @@ -0,0 +1 @@ +¤ioperationxworkspace.snapshot.observe@1japiVersionx%workspace.snapshot.request-profile/v1jbasisClassnworkspace-rootnauthorityClassfscoped \ No newline at end of file diff --git a/fixtures/lawpack/workspace-snapshot/request-profile-configuration.sha256 b/fixtures/lawpack/workspace-snapshot/request-profile-configuration.sha256 new file mode 100644 index 0000000..d9953d9 --- /dev/null +++ b/fixtures/lawpack/workspace-snapshot/request-profile-configuration.sha256 @@ -0,0 +1 @@ +sha256:177eafc6b7207b98c209c6ed00ab13477846d516569f2cdffc3c135756a5bfef diff --git a/fixtures/providers/echo-target-profile/generated/primary/target-profile.echo-dpo.cbor b/fixtures/providers/echo-target-profile/generated/primary/target-profile.echo-dpo.cbor new file mode 100644 index 0000000000000000000000000000000000000000..9fa43529b1021dfd8e3a33256c832d4eee6f4a47 GIT binary patch literal 1787 zcmZuydr%cs7{}7VB1Hoq6q18wW-W$ULYYYj+N6NMAbAx%_w3od2X^5OpFeq!>{|KjlRuSWFlwPV**8B9@WC$|S=BroZ@7{=~T*Q*K;4o!K9M zZ|Cvfamtf@7w4Cbi!JX=A9?fCN5hWP$Oh0vH}P43#Yn`EB@_cg6(CV{q9Khrfj;C~ z=-={KA@%0GX1>1jMy82g-KvxLkF6G zsS|G4;<4*I{f}(B&^f$hcI^3L|C(hJ+LKxuh4<5UM$@~~JRBEG*rv}y^n+C=$d2km zAJWa2&wIiXj<(GFDPvL1)s`LSGWBY;Jg;bn{3n)7q%0&NQY-~jTLG))1Qw=f%|kvr zc2>=5Nsho>>t3JvLC+|5x$&fSYFELP>EVSy$9XpO0WEF%f=-Eq6~r+MvBoIY3=9ox zpf^O~e%)#E-9NG-lSpRy?y#3D`+8nkSc)PeHw6~1 z9>TR6PKfjIoCE%@<}sc1BN{5_f$iZFh1$qC_`?uoa`Typ?7c;*sW6-yu&+;bo0_Pg zESrWkfQAOxEvFHm4^3+R@n-Z5<#1Px6n}g8gB!XN)9Ox@R6Ow3(jC{gey55U$Qs`d z!xT5RP?@d&{kq|VJLMkTS2k%(a#_dl1<(9)sG;k5ce1?y6}74RpB_(7bQ1v(BAsKU z5{fwTh5=h4f8=;XHUSmWyg)Gl<0;j3PKKx|W&&VD#nv&v-{mG53~6u`1G=tY7&t5# z8qf_7VfvtAAU~4*vMv$cN*kYD>zVThSKmp}f7>|eZ&qLZ z$>{4X-ABvgr1SaeLOBz;pB4>XIt<`(3qF zf7R|=yLH5@%i9^v04n156{5o6h&hP{aT|=9(2_2jD2f}vBB~L=iw*2nkboo! zL1qj-mJ57DBkC&4V&C{6RKg%2$2~KbMAfZWk@J1&`mB{<-rnu4U-pbXIyEPyhYjJ(U+Ng^&)>ZW)S6^hES40d^6>B^p7^e9UG_4f|M>upqb#nTP z45fHs!Cv93k)DW4->zraoWyDXHuzWzAQH=gs72vvawQ9AZ9lOmpG-3;)! z|LK$LmZN-I_D-02Fyq>ht=YFmUCo(x^1WxfkFI|@{^yD(_M}g|^LVN+CU{R4fMRZP zQHluot)bS9;VuRK1&ZAffZgI$d@(Z8tP~s?D1!SH$FANRD~*TxCaG_2{K Result<(), String> { let artifacts = hello_echo_golden_artifacts(root)? .into_iter() - .chain(causal_cell_golden_artifacts()?); + .chain(causal_cell_golden_artifacts()?) + .chain(workspace_snapshot_golden_artifacts()?); for (path, bytes) in artifacts { match mode { LawpackGoldenMode::Check => { @@ -82,7 +110,7 @@ pub(crate) fn lawpack_goldens(root: &Path, mode: LawpackGoldenMode) -> Result<() } println!( - "lawpack-goldens: {FIXTURE_ROOT} and {CAUSAL_CELL_FIXTURE_ROOT} {}", + "lawpack-goldens: {FIXTURE_ROOT}, {CAUSAL_CELL_FIXTURE_ROOT}, and {WORKSPACE_SNAPSHOT_FIXTURE_ROOT} {}", match mode { LawpackGoldenMode::Check => "checked", LawpackGoldenMode::Write => "written", @@ -91,6 +119,294 @@ pub(crate) fn lawpack_goldens(root: &Path, mode: LawpackGoldenMode) -> Result<() Ok(()) } +fn workspace_snapshot_golden_artifacts() -> Result)>, String> { + let exports_value = workspace_snapshot_exports(); + let exports_bytes = encode_canonical_cbor(&exports_value) + .map_err(|error| format!("encode workspace snapshot exports: {error}"))?; + let exports_digest = digest_value(WORKSPACE_SNAPSHOT_EXPORTS_COORDINATE, &exports_value)?; + + let configuration_value = workspace_snapshot_target_configuration(); + let configuration_bytes = encode_canonical_cbor(&configuration_value) + .map_err(|error| format!("encode workspace snapshot target configuration: {error}"))?; + let configuration_digest = digest_value( + WORKSPACE_SNAPSHOT_CONFIGURATION_COORDINATE, + &configuration_value, + )?; + + let adapter_value = workspace_snapshot_adapter(configuration_digest); + let adapter_bytes = encode_canonical_cbor(&adapter_value) + .map_err(|error| format!("encode workspace snapshot adapter: {error}"))?; + let adapter_digest = digest_value(WORKSPACE_SNAPSHOT_ADAPTER_COORDINATE, &adapter_value)?; + + let manifest_value = workspace_snapshot_manifest(exports_digest, adapter_digest); + let manifest_bytes = encode_canonical_cbor(&manifest_value) + .map_err(|error| format!("encode workspace snapshot manifest: {error}"))?; + let bundle = decode_lawpack_bundle(&manifest_bytes, &exports_bytes) + .map_err(|failures| format!("validate workspace snapshot lawpack: {failures:?}"))?; + let adapter = decode_lawpack_adapter(&bundle, "echo.dpo@1", &adapter_bytes) + .map_err(|failures| format!("validate workspace snapshot adapter: {failures:?}"))?; + + let source = workspace_snapshot_application_source(&bundle.manifest_digest_review_string()); + let module = parse_module(&source) + .map_err(|error| format!("parse workspace snapshot application: {error:?}"))?; + let preparation = prepare_lawpack_compilation(&module, &bundle, &adapter) + .map_err(|failures| format!("prepare workspace snapshot application: {failures:?}"))?; + let core = compile_to_core(&module, preparation.compiler_context()) + .map_err(|error| format!("compile workspace snapshot application: {error:?}"))?; + let core_bytes = encode_core_module(&core) + .map_err(|error| format!("encode workspace snapshot Core: {error}"))?; + let core_digest = digest_core_module(&core) + .map_err(|error| format!("digest workspace snapshot Core: {error}"))? + .to_review_string(); + let target_ir_report = lower_to_target_ir(&core, preparation.target_ir_facts()); + if target_ir_report.status != TargetLoweringStatus::Lowered { + return Err(format!( + "lower workspace snapshot Target IR: expected lowered status, got {:?}", + target_ir_report.status + )); + } + let target_ir = target_ir_report.artifact.ok_or_else(|| { + "lower workspace snapshot Target IR: lowered report omitted artifact".to_owned() + })?; + let request_count = target_ir + .intents + .values() + .map(|intent| intent.external_action_requests.len()) + .sum::(); + if request_count != 1 + || target_ir + .intents + .values() + .any(|intent| !intent.steps.is_empty()) + { + return Err( + "workspace snapshot application must lower to one request and zero callable steps" + .to_owned(), + ); + } + let target_ir_bytes = encode_target_ir_artifact(&target_ir) + .map_err(|error| format!("encode workspace snapshot Target IR: {error}"))?; + let target_ir_digest = digest_target_ir_artifact(&target_ir) + .map_err(|error| format!("digest workspace snapshot Target IR: {error}"))? + .to_review_string(); + + Ok(vec![ + (WORKSPACE_SNAPSHOT_MANIFEST_CBOR, manifest_bytes), + ( + WORKSPACE_SNAPSHOT_MANIFEST_DIGEST, + format!("{}\n", bundle.manifest_digest_review_string()).into_bytes(), + ), + (WORKSPACE_SNAPSHOT_EXPORTS_CBOR, exports_bytes), + ( + WORKSPACE_SNAPSHOT_EXPORTS_DIGEST, + format!("{}\n", bundle.manifest().exports.digest_review_string()).into_bytes(), + ), + (WORKSPACE_SNAPSHOT_ADAPTER_CBOR, adapter_bytes), + ( + WORKSPACE_SNAPSHOT_ADAPTER_DIGEST, + format!("{}\n", sha256_review_string(&adapter_digest)).into_bytes(), + ), + (WORKSPACE_SNAPSHOT_CONFIGURATION_CBOR, configuration_bytes), + ( + WORKSPACE_SNAPSHOT_CONFIGURATION_DIGEST, + format!("{}\n", sha256_review_string(&configuration_digest)).into_bytes(), + ), + (WORKSPACE_SNAPSHOT_SOURCE, source.into_bytes()), + (WORKSPACE_SNAPSHOT_CORE_CBOR, core_bytes), + ( + WORKSPACE_SNAPSHOT_CORE_DIGEST, + format!("{core_digest}\n").into_bytes(), + ), + (WORKSPACE_SNAPSHOT_TARGET_IR_CBOR, target_ir_bytes), + ( + WORKSPACE_SNAPSHOT_TARGET_IR_DIGEST, + format!("{target_ir_digest}\n").into_bytes(), + ), + ]) +} + +fn workspace_snapshot_manifest( + exports_digest: [u8; 32], + adapter_digest: [u8; 32], +) -> CanonicalValue { + map([ + ("apiVersion", text("edict.lawpack/v1")), + ("id", text("workspace.snapshot")), + ("version", text("1")), + ( + "acceptedCoreAbi", + CanonicalValue::Array(vec![text("edict.core/v1")]), + ), + ("dependencies", CanonicalValue::Array(Vec::new())), + ( + "exports", + resource_ref(WORKSPACE_SNAPSHOT_EXPORTS_COORDINATE, exports_digest), + ), + ( + "targetAdapters", + CanonicalValue::Array(vec![map([ + ( + "acceptedTargetProfile", + resource_ref("echo.dpo@1", ECHO_TARGET_PROFILE_DIGEST), + ), + ( + "acceptedTargetIr", + resource_ref("echo.span-ir/v1", ECHO_TARGET_IR_DIGEST), + ), + ( + "adapter", + resource_ref(WORKSPACE_SNAPSHOT_ADAPTER_COORDINATE, adapter_digest), + ), + ])]), + ), + ( + "verifier", + map([ + ("class", text("declarative")), + ( + "ruleset", + resource_ref("workspace.snapshot.verifier-rules/v1", [0x84; 32]), + ), + ]), + ), + ( + "compatibility", + resource_ref("workspace.snapshot.compatibility/v1", [0x85; 32]), + ), + ( + "conformanceFixtureCorpus", + resource_ref("workspace.snapshot.fixtures/v1", [0x86; 32]), + ), + ]) +} + +fn workspace_snapshot_adapter(configuration_digest: [u8; 32]) -> CanonicalValue { + map([ + ("apiVersion", text("edict.lawpack-adapter/v1")), + ("class", text("declarative")), + ( + "operationProfiles", + map([( + "workspace.snapshot@1.observeRequest", + map([ + ("core", text("continuum.profile.read-only/v1")), + ("semanticEffects", CanonicalValue::Array(Vec::new())), + ( + "budgetObligation", + text("workspace.snapshot@1.tinyObservationBudget"), + ), + ( + "targetConfiguration", + resource_ref( + WORKSPACE_SNAPSHOT_CONFIGURATION_COORDINATE, + configuration_digest, + ), + ), + ]), + )]), + ), + ("effectImplementations", CanonicalValue::Map(Vec::new())), + ( + "budgets", + map([( + "workspace.snapshot@1.tinyObservationBudget", + map([ + ("maxSteps", CanonicalValue::Integer(512)), + ("maxAllocatedBytes", CanonicalValue::Integer(256 * 1024)), + ("maxOutputBytes", CanonicalValue::Integer(128 * 1024)), + ]), + )]), + ), + ]) +} + +fn workspace_snapshot_target_configuration() -> CanonicalValue { + map([ + ("apiVersion", text("workspace.snapshot.request-profile/v1")), + ("operation", text("workspace.snapshot.observe@1")), + ("authorityClass", text("scoped")), + ("basisClass", text("workspace-root")), + ]) +} + +fn workspace_snapshot_exports() -> CanonicalValue { + map([ + ("types", CanonicalValue::Array(Vec::new())), + ("constants", CanonicalValue::Array(Vec::new())), + ("pureFunctions", CanonicalValue::Array(Vec::new())), + ("effects", CanonicalValue::Array(Vec::new())), + ("obstructions", CanonicalValue::Array(Vec::new())), + ( + "operationProfiles", + map([( + "workspace.snapshot@1.observeRequest", + map([ + ( + "opticTemplate", + map([ + ("opticKind", text("revelation")), + ("boundaryKind", text("projection")), + ("supportPolicy", text("workspace.snapshot@1.requestOnly")), + ("lossDisposition", text("workspace.snapshot@1.lossless")), + ( + "apertureRequirement", + map([ + ("kind", text("abstractFootprintObligation")), + ("ref", text("workspace.snapshot@1.authorityScope")), + ]), + ), + ]), + ), + ( + "effectPredicate", + text("workspace.snapshot@1.externalObservation"), + ), + ]), + )]), + ), + ]) +} + +fn workspace_snapshot_application_source(manifest_digest: &str) -> String { + format!( + r#"package examples.workspace_observer@1; + +use lawpack workspace.snapshot@1 digest "{manifest_digest}" as workspace; +use capability workspace.snapshot.observe@1 digest "{manifest_digest}" as snapshot; + +type ObserveInput = {{ + payload: Bytes, + scope: Bytes, + basis: Bytes, + maxSettlementBytes: U64, + maxAttempts: U32, +}}; + +intent observe(input: ObserveInput) + returns ExternalActionRequest> + profile workspace.observeRequest + basis input.basis + budget <= workspace.tinyObservationBudget +{{ + request pending: ExternalActionRequest> = + snapshot(input.payload) + input schema workspace.snapshot.input@1 + digest "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + settlement schema workspace.snapshot.settlement@1 + digest "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" + authority input.scope + basis input.basis + budget + maxSettlementBytes input.maxSettlementBytes + maxAttempts input.maxAttempts + reconcile workspace.snapshot.reconcile@1 + digest "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"; + return pending; +}} +"# + ) +} + fn causal_cell_golden_artifacts() -> Result)>, String> { let exports_value = causal_cell_exports(); let exports_bytes = encode_canonical_cbor(&exports_value) From 3a95595a8de5176a1d9216575a6d35b47bca2d4c Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 29 Jul 2026 17:29:22 -0700 Subject: [PATCH 05/16] docs: define external request application builds --- README.md | 3 ++ docs/REQUIREMENTS.md | 1 + docs/abi/edict-lawpack-adapter.cddl | 17 +++++-- docs/topics/README.md | 2 +- docs/topics/cli/README.md | 50 ++++++++++++++++--- docs/topics/cli/test-plan.md | 14 ++++-- .../topics/external-action-requests/README.md | 17 +++++++ .../external-action-requests/test-plan.md | 6 +++ docs/topics/lawpacks/README.md | 23 ++++++--- docs/topics/lawpacks/test-plan.md | 5 +- fixtures/lawpack/workspace-snapshot/README.md | 35 +++++++++++++ .../v1/edict-provider-contracts.cddl | 17 +++++-- fixtures/provider-contracts/v1/manifest.json | 4 +- .../providers/echo-target-profile/README.md | 21 ++++++++ 14 files changed, 185 insertions(+), 30 deletions(-) create mode 100644 fixtures/lawpack/workspace-snapshot/README.md create mode 100644 fixtures/providers/echo-target-profile/README.md diff --git a/README.md b/README.md index 87f6d5d..94ccc40 100644 --- a/README.md +++ b/README.md @@ -578,6 +578,9 @@ What exists today: - Typed external-action request construction: digest-locked operation families, schemas, scope, basis, budgets, and reconciliation law lower as non-callable Core and Target IR data with exact capability closure +- Public request-only application builds: exact source, lawpack, declarative + adapter, target configuration, and target-profile closure publish canonical + Core and Target IR without provider-component invocation or external I/O - Reference `edict.canonical-cbor/v1` Core encoder and canonical byte validation path for the current in-memory Core module model - Reviewed Core golden bytes and exact `edict.core.module/v1` digest fixture for diff --git a/docs/REQUIREMENTS.md b/docs/REQUIREMENTS.md index 7b321b6..66261f8 100644 --- a/docs/REQUIREMENTS.md +++ b/docs/REQUIREMENTS.md @@ -68,6 +68,7 @@ but owned by a follow-up issue; no fixtures until its dependency lands). | EDICT-LANG-PROFILE-001 | `edict.language/v1` vs `edict.implementation/minimal-v1` capability flags | Language | `lang/profile/minimal` | `lang/profile/undeclared-capability` | spec | | EDICT-LANG-CAPREF-001 | `CapabilityRef` carries receipt digest only; inert until admitted | Language | `lang/capref/inert` | `lang/capref/ambient-authority` | spec | | EDICT-LANG-EXTERNAL-REQUEST-001 | External boundary crossings are typed deterministic request values; exact operation capability closure is required and no ambient world authority enters Edict or the provider seam | Language/Core/Target | `fixtures/lang/external-actions/workspace-snapshot.edict`; `fixtures/core/canonical/workspace-snapshot.core.cbor`; `fixtures/core/canonical/workspace-snapshot.core.sha256`; `fixtures/target-ir/canonical/workspace-snapshot.target-ir.cbor`; `fixtures/target-ir/canonical/workspace-snapshot.target-ir.sha256`; `workspace_observation_request_compiles_as_non_callable_data` | `undeclared_or_floating_operation_families_fail_closed`; `ambient_operation_families_are_not_requestable`; `request_resource_coordinates_must_be_nonempty`; `duplicate_target_request_ids_reject_before_identity` | impl | +| EDICT-CLI-EXTERNAL-REQUEST-BUILD-001 | An explicit application build publishes exact compiler-owned Core and Target IR for a request-only capability closure without performing the action or granting callable target authority | CLI/Lawpack/Core/Target | `public_external_action_build_emits_exact_compiler_artifacts`; `request_only_profile_supplies_budget_without_callable_effect_authority`; `fixtures/lawpack/workspace-snapshot/` | `public_external_action_build_rejects_capability_substitution`; `external_action_build_requires_a_typed_request`; `external_action_build_rejects_mixed_callable_execution`; `failed_external_action_pair_publication_preserves_previous_core` | impl | | EDICT-LANG-READONLY-001 | Read-only inferred; executionClass (proofOnly/runtime) orthogonal to writeClass; runtime read is allowed | Language/Lawpack | `lang/readonly/runtime-read` | `lang/readonly/hidden-append` | spec | | EDICT-OPTIC-SOURCE-001 | Each optic field has one deterministic source (basis clause / profile template / coordinate / footprint) | Language | `optic/source/basis-clause` | `optic/source/freeform-support` | spec | | EDICT-DIGEST-WIRE-001 | Canonical digest is typed `[algorithm, bytes]`, never a hex string; hex only in review JSON | Bundle/ABI | `abi/digest/typed-pair` | `abi/digest/hex-string` | spec | diff --git a/docs/abi/edict-lawpack-adapter.cddl b/docs/abi/edict-lawpack-adapter.cddl index c949bbe..8e887c2 100644 --- a/docs/abi/edict-lawpack-adapter.cddl +++ b/docs/abi/edict-lawpack-adapter.cddl @@ -20,10 +20,19 @@ lawpack-adapter = { } ; Keys are canonical lawpack operation-profile coordinates. -lawpack-adapter-operation-profile = { - core: tstr, - semanticEffects: [+ tstr], -} +lawpack-adapter-operation-profile = + { + core: tstr, + semanticEffects: [+ tstr], + ? budgetObligation: tstr, + ? targetConfiguration: resource-ref, + } / + { + core: tstr, + semanticEffects: [], + budgetObligation: tstr, + targetConfiguration: resource-ref, + } ; Keys are canonical lawpack semantic-effect coordinates. Footprint, cost, and ; failure fields must exactly discharge the matching exported effect. diff --git a/docs/topics/README.md b/docs/topics/README.md index 1a8ce88..dd1bafe 100644 --- a/docs/topics/README.md +++ b/docs/topics/README.md @@ -58,7 +58,7 @@ cargo xtask verify - [Fixtures](./fixtures/README.md): shared executable fixture corpus and reviewed Core golden artifact contract. - [Lawpacks](./lawpacks/README.md): lawpack import, direct-adapter, bundle - reference, and deferred manifest-validation boundary. + reference, request-only profile, and canonical manifest-validation boundary. - [Lowerability](./lowerability/README.md): typed v1 lowering requirements, target-profile facts, and direct-only support classification. - [Obstruction Strands](./obstruction-strands/README.md): current terminal and diff --git a/docs/topics/cli/README.md b/docs/topics/cli/README.md index 8f91e74..b7c2d69 100644 --- a/docs/topics/cli/README.md +++ b/docs/topics/cli/README.md @@ -26,12 +26,12 @@ The implemented operations are `build`, `check`, and `project`. A `build` request contains one settings record and no compiler-input records. Its `application` field points to an `edict.application/v1` JSON manifest. The manifest names one exact Edict source, its complete lawpack closure, the -selected target profile and provider package, and the output directory. The -current executable-operation route accepts exactly one source and a non-empty -ordered lawpack closure whose first entry is the root. It validates the complete -supplied dependency graph, compiles and lowers the source through the root -lawpack's declarative target adapter, and resolves the selected provider only -from its checked package manifest. +selected target profile and provider package, and the output directory. Both +application routes accept exactly one source and a non-empty ordered lawpack +closure whose first entry is the root. They validate the complete supplied +dependency graph, compile and lower the source through the root lawpack's +declarative target adapter, and resolve the selected target profile only from +its checked provider-package manifest. ```json {"schema":"edict.compiler.settings/v1","type":"compilerSettings","operation":"build","application":"edict.application.json"} @@ -61,7 +61,43 @@ has this shape: } ``` -The build invokes the provider's checked lowerer component and its structurally +`buildKind` defaults to `executableOperation`. Setting it to `externalAction` +selects the request-only route explicitly: + +```json +{ + "schema": "edict.application/v1", + "buildKind": "externalAction", + "coordinate": "examples.workspace_observer@1", + "sources": ["src/observe-workspace.edict"], + "lawpacks": [{ + "manifest": "vendor/workspace-snapshot/manifest.cbor", + "exports": "vendor/workspace-snapshot/exports.cbor", + "adapter": "vendor/workspace-snapshot/adapter.cbor", + "targetConfiguration": "vendor/workspace-snapshot/request-profile-configuration.cbor" + }], + "target": { + "profile": "echo.dpo@1", + "providerPackage": ".build/echo-provider" + }, + "outputDirectory": ".build/application" +} +``` + +The request-only route requires at least one compiler-emitted external-action +request, rejects any callable Target IR step, and requires every request +operation to be bound to one exact supplied lawpack manifest. It invokes no +provider component. The owning canonical encoders publish: + +- `core.cbor`; +- `target-ir.cbor`. + +The pair is deterministic and transactionally replaces any previous +application output pair. Switching build kinds removes stale outputs from the +other route. Runtime admission, request execution, settlement, recovery, and +replay remain outside Edict. [CLI-REQ-016] + +The executable-operation build invokes the provider's checked lowerer component and its structurally separate verifier component through the capability-denied provider host. Only an accepted verification result reaches the output directory. The current Echo target writes the exact provider-emitted bytes as: diff --git a/docs/topics/cli/test-plan.md b/docs/topics/cli/test-plan.md index 422bb38..d294ec2 100644 --- a/docs/topics/cli/test-plan.md +++ b/docs/topics/cli/test-plan.md @@ -42,7 +42,8 @@ Out of scope: | CLI-REQ-012 | implemented | The checked-in CLI golden corpus can be regenerated by `cargo xtask cli-goldens --write` and checked by `cargo xtask cli-goldens --check`; `cargo xtask verify` runs the check mode. | xtask/src/goldens.rs, xtask/src/main.rs | | CLI-REQ-013 | implemented | The `project` operation accepts dirty editor source from `source` input records and emits structured syntax, diagnostics, Core, Target IR, digest, and status records without requiring a source file on disk. | crates/edict-cli/tests/jsonl_cli.rs | | CLI-REQ-014 | implemented | Compiler-level projection failures are emitted as structured projection records and diagnostics, not process-level CLI failures. | crates/edict-cli/tests/jsonl_cli.rs | -| CLI-REQ-015 | planned | The `build` operation loads one `edict.application/v1` request, validates the exact Edict source and complete lawpack/adapter closure, invokes the selected target provider lowerer and independent verifier through the bounded provider host, and writes only the accepted canonical package and verification-report bytes. | crates/edict-cli/src/application_build.rs | +| CLI-REQ-015 | implemented | The executable-operation `build` route loads one `edict.application/v1` request, validates the exact Edict source and complete lawpack/adapter closure, invokes the selected target provider lowerer and independent verifier through the bounded provider host, and writes only the accepted canonical package and verification-report bytes. | crates/edict-cli/src/application_build.rs, external Hello Echo build witness | +| CLI-REQ-016 | implemented | The explicit external-action `build` route validates a request-only source and complete capability/adapter/target-profile closure, invokes no provider component, and atomically publishes the owning encoders' exact Core and Target IR bytes while clearing stale executable outputs. | issue #176, crates/edict-cli/src/application_build.rs | ## Fixtures @@ -51,6 +52,8 @@ Out of scope: | crates/edict-cli/tests/jsonl_cli.rs | Runtime-created JSONL requests and source files for CLI behavior. | Tests parse every stdout and stderr line as JSON objects and assert stable fields. | | docs/schemas/edict.compiler-settings.v1.schema.json | Stable JSON Schema for compiler settings records. | The schema contract test validates the schema identifier, required fields, supported operation values, deterministic input-expansion settings, and optional root confinement. | | fixtures/lawpack/hello-echo/README.md | Canonical Hello Echo Target IR witness and digest sidecar corpus. | `cargo xtask lawpack-goldens --check` recompiles the digest-pinned Edict source through the generated causal-cell closure and rejects byte drift. | +| fixtures/lawpack/workspace-snapshot/README.md | Canonical request-only public-build closure. | The public application build reproduces the owner-generated Core and Target IR bytes without provider-component invocation. | +| fixtures/providers/echo-target-profile/README.md | Exact Echo-owned target profile consumed by the request-only build test. | The public build corroborates the profile's domain-framed identity against the provider manifest and lawpack adapter. | | docs/schemas/edict.compiler-input.v1.schema.json | Stable JSON Schema for compiler input records. | The schema contract test validates the identifier, required fields, supported input kinds, and per-kind variant fields. | | docs/schemas/edict.cli-check-result.v1.schema.json | Stable JSON Schema for success result records. | The schema contract test validates the identifier, required fields, and pinned `command`, `type`, and `status` values. | | docs/schemas/edict.cli-diagnostic.v1.schema.json | Stable JSON Schema for diagnostic records. | The schema contract test validates the identifier, required fields, supported commands and stages, and optional span, line, and message fields. | @@ -100,6 +103,8 @@ Out of scope: | CLI-TP-027 | implemented | Build request dispatch | CLI-REQ-015 | A `build` settings record is accepted without compiler input records and reaches application-config loading; a missing application config fails as a structured `ApplicationConfigReadFailed` build diagnostic rather than as invalid settings or missing compiler input. | build_accepts_application_request_without_compiler_input_records | crates/edict-cli/tests/jsonl_cli.rs | Covers the public request shape and dispatch boundary only. | | CLI-TP-028 | planned | Verified application build | CLI-REQ-015 | A repository-owned provider-component fixture builds one exact source and complete lawpack closure, independently accepts the package, and publishes the canonical package/report pair. | - | - | The standalone external Hello Echo build is a green integration witness; this planned case makes the full crossing reproducible inside Edict's own automated suite. | | CLI-TP-029 | implemented | External request projection | CLI-REQ-013 | A `project` request carrying exact external-action source emits Core and Target IR review data with the capability resource, complete request payload, explicit awaiting-settlement posture, capability semantic closure, and zero callable target steps. | project_exposes_external_requests_as_non_callable_review_data | crates/edict-cli/tests/jsonl_cli.rs | Review JSON exposes compiler data only; the CLI performs no external action. | +| CLI-TP-030 | implemented | External-action build | CLI-REQ-016 | An explicit request-only application manifest loads exact source, lawpack, adapter, configuration, and Echo target-profile artifacts; emits exact checked Core and Target IR bytes; removes stale executable outputs; and reruns deterministically. | public_external_action_build_emits_exact_compiler_artifacts | crates/edict-cli/src/application_build.rs, fixtures/lawpack/workspace-snapshot/, fixtures/providers/echo-target-profile/ | The provider component host is not invoked. | +| CLI-TP-031 | implemented | External-action refusal | CLI-REQ-016 | Missing requests, callable-step mixtures, substituted capability manifests, and failed pair replacement refuse before a passing publication witness. | external_action_build_requires_a_typed_request, external_action_build_rejects_mixed_callable_execution, public_external_action_build_rejects_capability_substitution, failed_external_action_pair_publication_preserves_previous_core | crates/edict-cli/src/application_build.rs | Stable failure kinds distinguish closure, execution-class, and output failures. | ## Determinism Obligations @@ -118,6 +123,7 @@ Out of scope: execution workflows yet. - No JSON Schema validation engine is embedded in the CLI; the schema is the stable contract artifact for callers. -- The full successful provider-component application build has an external - Hello Echo integration witness but not yet a repository-owned automated - fixture (CLI-TP-028). +- The full successful executable provider-component application build has an + external Hello Echo integration witness but not yet a repository-owned + automated fixture (CLI-TP-028). The request-only build is repository-owned + and automated (CLI-TP-030). diff --git a/docs/topics/external-action-requests/README.md b/docs/topics/external-action-requests/README.md index 03ae27a..5cb771b 100644 --- a/docs/topics/external-action-requests/README.md +++ b/docs/topics/external-action-requests/README.md @@ -64,6 +64,23 @@ Equivalent source and compiler facts produce byte-identical Core and Target IR. Every operation, schema, input, scope, basis, budget, or reconciliation change participates in canonical identity. [EXTREQ-REQ-005] +## Public Application Build + +An `edict.application/v1` manifest selects the request-only route with +`"buildKind": "externalAction"`. The build loads the exact source, complete +lawpack dependency set, declarative adapter, request-profile configuration, and +provider-owned target profile. It then: + +1. compiles and lowers through the real lawpack closure; +2. requires at least one typed request and zero callable Target IR steps; +3. binds each request operation to an exact supplied lawpack manifest digest; +4. writes the owning encoders' exact `core.cbor` and `target-ir.cbor` bytes. + +Publication is a locked pair replacement. A failure restores the previous pair, +and a successful request build removes stale executable-operation package and +verification-report outputs. The route does not invoke a provider component or +perform an external action. [EXTREQ-REQ-009] + ## Authority Boundary Request authority is not performance authority: diff --git a/docs/topics/external-action-requests/test-plan.md b/docs/topics/external-action-requests/test-plan.md index 96863f6..90cdcab 100644 --- a/docs/topics/external-action-requests/test-plan.md +++ b/docs/topics/external-action-requests/test-plan.md @@ -36,6 +36,7 @@ Out of scope: | EXTREQ-REQ-006 | implemented | Runtime-valued authority scope, basis, and budget expressions survive compilation for Echo admission; Edict performs no external action while compiling or lowering them. | issue #172 | | EXTREQ-REQ-007 | implemented | The request-family allowlist contains only the domain-specific `workspace` root; raw filesystem, process, network, Git, GitHub, model, shell, case-variant, abbreviation, and unregistered roots are outside the requestable capability vocabulary. | issue #172 | | EXTREQ-REQ-008 | implemented | Request construction remains bounded under a fixed-seed mutation corpus and a 64-request stress module. | issue #172 | +| EXTREQ-REQ-009 | implemented | The public application build explicitly selects request-only publication, validates the exact source/lawpack/adapter/target-profile closure, rejects zero requests, callable-step mixtures, and substituted capability manifests, and atomically publishes exact canonical Core and Target IR bytes without invoking a provider component. | issue #176 | ## Fixtures @@ -44,6 +45,7 @@ Out of scope: | In-test `workspace.snapshot.observe@1` source | First bounded read-only external request. | Public parser, compiler, canonical encoders, and Target IR lowerer preserve the exact request contract. | | Fixed seed `0x4558_5452_4551_0001` | Determinism and mutation corpus. | Repeated compilation is byte-identical and distinct capability identities produce distinct Core identities. | | 64-request generated module | Bounded stress case. | All 64 requests survive Core and Target IR without becoming callable steps. | +| `fixtures/lawpack/workspace-snapshot/` | Exact public-build capability closure. | The owning generator reproduces manifest, exports, request-only adapter, target configuration, source, Core, and Target IR; the public build reproduces the checked compiler bytes. | ## Cases @@ -63,6 +65,10 @@ Out of scope: | EXTREQ-TP-012 | implemented | Canonical identity guard | EXTREQ-REQ-004 | Canonical Core encoding rejects empty request schema or reconciliation coordinates, and canonical Target IR encoding rejects duplicate request ids within one intent. | request_resource_coordinates_must_be_nonempty, duplicate_target_request_ids_reject_before_identity | crates/edict-syntax/tests/external_action_requests.rs | Waiting and settlement identity cannot be ambiguous or anonymous. | | EXTREQ-TP-013 | implemented | Tooling guard | EXTREQ-REQ-001 | A non-call request operation has its own stable parser kind, and `request` is highlighted as a keyword. | non_call_request_operation_has_a_request_specific_parse_kind, request_statement_introducer_is_highlighted_as_a_keyword | crates/edict-syntax/tests/external_action_requests.rs, crates/edict-syntax/tests/highlighting.rs | Request syntax remains distinct from semantic effect syntax. | | EXTREQ-TP-014 | implemented | Golden artifact | EXTREQ-REQ-002, EXTREQ-REQ-003, EXTREQ-REQ-004, EXTREQ-REQ-005 | The checked workspace-snapshot source reproduces exact compiler-owned Core and Target IR canonical bytes and domain-framed digests. | core_goldens_match_executable_encoder, target_ir_goldens_match_executable_encoder | fixtures/lang/external-actions/workspace-snapshot.edict, fixtures/core/canonical/workspace-snapshot.core.cbor, fixtures/core/canonical/workspace-snapshot.core.sha256, fixtures/target-ir/canonical/workspace-snapshot.target-ir.cbor, fixtures/target-ir/canonical/workspace-snapshot.target-ir.sha256 | Generated only through the owning xtask commands. | +| EXTREQ-TP-015 | implemented | Public build | EXTREQ-REQ-009 | A real `edict.application/v1` request loads the generated workspace closure and exact Echo target profile, publishes checked canonical Core and Target IR bytes, removes stale executable outputs, and reruns byte-identically. | public_external_action_build_emits_exact_compiler_artifacts | crates/edict-cli/src/application_build.rs, fixtures/lawpack/workspace-snapshot/, fixtures/providers/echo-target-profile/ | Provider components are outside the request-only route. | +| EXTREQ-TP-016 | implemented | Closure refusal | EXTREQ-REQ-004, EXTREQ-REQ-009 | A request operation whose digest no longer equals its owning supplied capability manifest is rejected before output publication. | external_action_build_rejects_a_substituted_capability_manifest, public_external_action_build_rejects_capability_substitution | crates/edict-cli/src/application_build.rs | Internal Core closure remains necessary but is not sufficient for public application authority. | +| EXTREQ-TP-017 | implemented | Execution-class refusal | EXTREQ-REQ-003, EXTREQ-REQ-009 | The request-only build rejects zero requests and any artifact mixing external requests with callable Target IR steps. | external_action_build_requires_a_typed_request, external_action_build_rejects_mixed_callable_execution | crates/edict-cli/src/application_build.rs | The first host route has one execution class. | +| EXTREQ-TP-018 | implemented | Publication transaction | EXTREQ-REQ-009 | Paired request artifacts are deterministic under fixed-seed and stress corpora; stale executable outputs are removed; publication failure preserves the prior request pair. | external_action_pair_publication_is_deterministic_for_a_fixed_seed_corpus, external_action_pair_publication_remains_bounded_under_stress, external_action_publication_removes_stale_executable_outputs, failed_external_action_pair_publication_preserves_previous_core | crates/edict-cli/src/application_build.rs | Output ownership is symmetric across build kinds. | ## Determinism Obligations diff --git a/docs/topics/lawpacks/README.md b/docs/topics/lawpacks/README.md index d5c5325..5ec0e7b 100644 --- a/docs/topics/lawpacks/README.md +++ b/docs/topics/lawpacks/README.md @@ -41,12 +41,15 @@ The current executable Rust surfaces touching lawpacks are: - canonical manifest/export loading through `ValidatedLawpackBundle`; - complete dependency-set validation with exact manifest-digest edges; - canonical direct-adapter loading with exact target selection, adapter digest - corroboration, complete profile/effect/budget coverage, and typed target - configuration resource references; + corroboration, complete callable profile/effect/budget coverage, and + request-only profiles whose exact budget and target configuration confer no + callable effect authority; - compiler and Target IR fact derivation from the exact module/lawpack/adapter closure; - reproducible canonical Core and Target IR artifacts for the standalone Hello Echo crossing; +- reproducible request-only workspace-snapshot closure and public application + build artifacts with one external request and zero callable Target IR steps; - authority-facts loading for budget and effect write-class facts whose source identity is a digest-locked lawpack reference; - target-profile validation for the exact `edict.lawpack-adapter/v1` ABI; @@ -75,15 +78,21 @@ The current executable Rust surfaces touching lawpacks are: - `decode_lawpack_adapter` accepts only canonical adapter bytes selected by one exact digest-locked target descriptor. It requires exact operation-profile, runtime-effect, budget, footprint, cost, and named-failure coverage before - returning an opaque validated adapter. Each effect also carries one typed, - digest-locked target-configuration reference. Edict preserves that reference - but does not interpret its target-owned semantics. + returning an opaque validated adapter. Each callable effect carries one + typed, digest-locked target-configuration reference. A profile with no + semantic effects is request-only and must carry its own exact budget + obligation and target configuration. Edict preserves those references but + does not interpret their target-owned semantics. `prepare_lawpack_compilation` then derives compiler and Target IR facts through the source import's exact alias and manifest digest. [LAWPACKS-REQ-008] - The Hello Echo golden generator compiles the exact source and lawpack closure, lowers the resulting Core module, and pins canonical Core and Target IR bytes under their native domain-framed identities. [LAWPACKS-REQ-009] +- The workspace-snapshot generator binds a requestable capability to the exact + lawpack manifest, compiles one request through a request-only profile, and + pins canonical Core and Target IR with zero callable steps. + [LAWPACKS-REQ-011] - Lowerability may classify an operation as adapted when exactly one digest-locked direct adapter satisfies the required semantic effect, write class, and guard facts. Floating, chained, or ambiguous adapter claims reject @@ -106,8 +115,8 @@ The following are not implemented: - executable target-adapter component loading; v1 currently specifies and implements the direct declarative adapter class only; -- target-owned configuration resource loading and interpretation; -- Echo executable-operation package emission from lowered Target IR; +- target-owned configuration interpretation; +- Echo admission or execution of compiler-emitted external-action requests; - lawpack conformance fixtures and two-lowerer differential trials. The verification matrix is tracked in [test-plan.md](./test-plan.md). diff --git a/docs/topics/lawpacks/test-plan.md b/docs/topics/lawpacks/test-plan.md index 5232b41..9569791 100644 --- a/docs/topics/lawpacks/test-plan.md +++ b/docs/topics/lawpacks/test-plan.md @@ -47,9 +47,10 @@ Out of scope: | LAWPACKS-REQ-005 | implemented | Edict loads canonical `edict.lawpack/v1` manifests and export surfaces into typed values, rejects every value outside the closed CDDL shape with stable failure kinds, corroborates the export digest, and validates a complete supplied dependency set as digest-locked and acyclic before exposing any exports to compilation. | issue #169, crates/edict-syntax/src/lawpack.rs, docs/abi/edict-lawpack.cddl, docs/abi/edict-common.cddl, docs/abi/edict-core.cddl | | LAWPACKS-REQ-006 | implemented | Authority-facts loading accepts digest-locked `lawpack` source identity for first compiler budget and effect write-class facts without claiming full manifest validation. | docs/topics/authority-facts/test-plan.md | | LAWPACKS-REQ-007 | implemented | Provider manifests model lawpacks as generated provider artifacts with digest-locked semantic source and generator provenance; Edict validates the reference/provenance envelope without owning runtime lawpack semantics. | issue #139, docs/topics/providers/test-plan.md | -| LAWPACKS-REQ-008 | implemented | Edict validates one exact direct declarative `edict.lawpack-adapter/v1` resource selected by a loaded lawpack manifest, requires complete profile/effect/budget coverage plus one typed target-configuration resource reference per runtime effect, and corroborates every exported footprint, cost, and named-failure obligation before deriving compiler or target facts. Edict preserves but does not interpret target-owned configuration semantics. | issue #169, docs/abi/edict-lawpack-adapter.cddl | +| LAWPACKS-REQ-008 | implemented | Edict validates one exact direct declarative `edict.lawpack-adapter/v1` resource selected by a loaded lawpack manifest. Callable profiles require complete effect/budget coverage and one typed target-configuration reference per runtime effect. Request-only profiles carry no semantic effects and must bind their own exact budget obligation and target configuration. Edict preserves but does not interpret target-owned configuration semantics. | issues #169 and #176, docs/abi/edict-lawpack-adapter.cddl | | LAWPACKS-REQ-009 | implemented | The standalone Hello Echo fixture pins exact canonical Core and Target IR bytes produced from the digest-locked source/lawpack/adapter closure and computes each identity with the artifact's native domain. | issue #169, fixtures/lawpack/hello-echo/README.md, xtask/src/lawpack_goldens.rs | | LAWPACKS-REQ-010 | implemented | The portable `causal.cell@1.createIfAbsent` capability closure is generated through the executable lawpack, adapter, compiler, and Target IR path, with exact canonical manifest, export, adapter, and target-configuration bytes and digests for external application builds. | fixtures/lawpack/causal-cell/README.md, xtask/src/lawpack_goldens.rs | +| LAWPACKS-REQ-011 | implemented | A request-only lawpack profile supplies an exact compiler budget and opaque target configuration without declaring a callable semantic effect or target intrinsic; the workspace-snapshot closure reproduces one request and zero Target IR steps. | issue #176, fixtures/lawpack/workspace-snapshot/README.md | ## Fixtures @@ -59,6 +60,7 @@ Out of scope: | fixtures/lang/effects/read-greeting.edict | Multi-import source fixture. | Parser preserves shape, lawpack, and target imports for effect-call syntax. | | fixtures/lawpack/hello-echo/README.md | Standalone capability fixture for the first real Edict-to-Echo crossing. | Canonical manifest, exports, and adapter load with exact digests; exact source compiles to pinned canonical Core and Target IR; `createGreeting` exposes a bounded create effect and typed `AlreadyExists` failure without GraphQL or a handwritten Echo package. | | fixtures/lawpack/causal-cell/README.md | Portable capability closure for external application builds. | `cargo xtask lawpack-goldens --check` reproduces the exact canonical closure after validating the bundle and adapter and compiling a source witness through Target IR. | +| fixtures/lawpack/workspace-snapshot/README.md | Request-only capability closure for bounded workspace observation. | `cargo xtask lawpack-goldens --check` reproduces the exact closure and requires one external request with zero callable target steps. | ## Cases @@ -74,6 +76,7 @@ Out of scope: | LAWPACKS-TP-008 | implemented | Direct adapter | LAWPACKS-REQ-008 | The exact Hello Echo adapter selected by the manifest derives all compiler and Echo Target IR facts and exposes the exact target-configuration resource identity, while missing, substituted, non-canonical, incomplete, target-mismatched, import-mismatched, malformed-configuration, undeclared-write-class, or obligation-mismatched adapters fail closed before trusted compiler facts exist. | hello_echo_source_compiles_to_echo_target_ir_from_exact_lawpack_adapter, lawpack_adapter_bytes_must_be_canonical_and_digest_bound, lawpack_adapter_requires_a_typed_target_configuration_reference, lawpack_adapter_rejects_an_undeclared_write_class_at_the_effect_path, lawpack_adapter_selection_requires_one_exact_target_profile, lawpack_adapter_requires_complete_exported_effect_coverage, lawpack_adapter_corroborates_footprint_cost_and_failure_obligations, lawpack_compilation_requires_the_exact_digest_locked_source_import | fixtures/lawpack/hello-echo/README.md, crates/edict-syntax/tests/lawpack.rs | The positive test constructs no `CompilerContext` or `TargetIrLoweringFacts`; Echo-specific configuration interpretation remains outside Edict. | | LAWPACKS-TP-009 | implemented | Compiler artifacts | LAWPACKS-REQ-009 | Compiling and lowering the exact Hello Echo closure reproduces the reviewed Core and Target IR bytes and their native domain-framed identities. | hello_echo_source_compiles_to_echo_target_ir_from_exact_lawpack_adapter | fixtures/lawpack/hello-echo/create-greeting.core.cbor, fixtures/lawpack/hello-echo/create-greeting.target-ir.cbor, crates/edict-syntax/tests/lawpack.rs, xtask/src/lawpack_goldens.rs | The fixtures are outputs of the real compiler pipeline, not handwritten substitutes; `cargo xtask lawpack-goldens --check` reproduces them. | | LAWPACKS-TP-010 | implemented | Portable capability | LAWPACKS-REQ-010 | Generating the causal-cell closure validates its canonical lawpack and direct adapter, then compiles and lowers an Edict source witness that imports the exact generated manifest digest. | lawpack_goldens_match_executable_codec | fixtures/lawpack/causal-cell/README.md, xtask/src/lawpack_goldens.rs, xtask/src/tests.rs | The generator fails if the portable capability no longer reaches a compiler-produced Target IR artifact. | +| LAWPACKS-TP-011 | implemented | Request-only profile | LAWPACKS-REQ-008, LAWPACKS-REQ-011 | A profile with no semantic effects is accepted only when it binds an exact budget obligation and target configuration; it compiles one request without conferring target-call authority. | request_only_profile_supplies_budget_without_callable_effect_authority, request_only_profile_requires_an_exact_budget_obligation, request_only_profile_requires_an_exact_target_configuration | crates/edict-syntax/tests/lawpack.rs, fixtures/lawpack/workspace-snapshot/ | Empty semantic effects are not an unbounded profile escape hatch. | ## Determinism Obligations diff --git a/fixtures/lawpack/workspace-snapshot/README.md b/fixtures/lawpack/workspace-snapshot/README.md new file mode 100644 index 0000000..d6d4df5 --- /dev/null +++ b/fixtures/lawpack/workspace-snapshot/README.md @@ -0,0 +1,35 @@ +# Workspace Snapshot Lawpack Fixture + +This generator-owned closure gives a typed external-action application the +profile, budget, and opaque target-configuration facts needed to construct a +bounded workspace-observation request. + +It grants no callable semantic effect: + +- `operationProfiles` declares `workspace.snapshot@1.observeRequest`; +- `semanticEffects` is empty; +- `budgetObligation` binds the compiler budget; +- `targetConfiguration` binds one exact request-profile resource; +- `effectImplementations` is empty. + +`observe-workspace.edict` imports both the exact lawpack manifest and the +requestable `workspace.snapshot.observe@1` capability using the same manifest +digest. The generated Core and Target IR therefore preserve the complete +capability closure while Target IR contains one external-action request and +zero callable steps. + +Artifacts are generated only through: + +```sh +cargo xtask lawpack-goldens --write +``` + +Checked bytes are verified through: + +```sh +cargo xtask lawpack-goldens --check +``` + +The fixture constructs request data only. It does not observe a workspace, +perform I/O, invoke a target intrinsic, admit a settlement, or resume an Edict +program. diff --git a/fixtures/provider-contracts/v1/edict-provider-contracts.cddl b/fixtures/provider-contracts/v1/edict-provider-contracts.cddl index f309b20..4e3d03b 100644 --- a/fixtures/provider-contracts/v1/edict-provider-contracts.cddl +++ b/fixtures/provider-contracts/v1/edict-provider-contracts.cddl @@ -584,10 +584,19 @@ lawpack-adapter = { } ; Keys are canonical lawpack operation-profile coordinates. -lawpack-adapter-operation-profile = { - core: tstr, - semanticEffects: [+ tstr], -} +lawpack-adapter-operation-profile = + { + core: tstr, + semanticEffects: [+ tstr], + ? budgetObligation: tstr, + ? targetConfiguration: resource-ref, + } / + { + core: tstr, + semanticEffects: [], + budgetObligation: tstr, + targetConfiguration: resource-ref, + } ; Keys are canonical lawpack semantic-effect coordinates. Footprint, cost, and ; failure fields must exactly discharge the matching exported effect. diff --git a/fixtures/provider-contracts/v1/manifest.json b/fixtures/provider-contracts/v1/manifest.json index ee9c0bc..021eba6 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": "3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d70726f76696465722d636f6e7472616374732e6364646c0a3b2047656e6572617465642066726f6d2045646963742d6f776e65642041424920667261676d656e74732e20444f204e4f5420454449542e0a0a3b202d2d2d2065646963742d636f6d6d6f6e2e6364646c202d2d2d0a3b2065646963742d636f6d6d6f6e2e6364646c0a3b20536861726564204344444c20747970657320666f722074686520456469637420414249732c20646566696e6564204f4e4345206865726520736f20746865792063616e6e6f742064726966740a3b202845444943542d4142492d4e4f4455502d303031292e2054776f2067726f7570733a0a3b2020202d207265736f757263652d7265662c207368613235362d6469676573742c206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c0a3b2020202020636f72652d747970652d7265663a20617373656d626c656420776974682065646963742d7461726765742d70726f66696c652e6364646c20616e640a3b202020202065646963742d6c61777061636b2e6364646c20627920746865206275696c643b2074686f736520736368656d617320646f206e6f74207265646566696e65207468656d2e0a3b2020202d206f7065726174696f6e2d70726f66696c652c206f707469632d74656d706c6174652c2061706572747572652d726571756972656d656e7420616e6420746865697220726566733a0a3b2020202020636f6e73756d65642062792074686520436f72652f6f70746963206c61796572202865646963742d636f72652e6364646c2920616e64207265666572656e636564206279207468650a3b20202020206c616e67756167652f7461726765742d70726f66696c652073706563732e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a0a3b2041206e6f726d617469766520737562636f6d706f6e656e74207265666572656e636564206279206964656e7469747920706c7573206469676573742e204d616e696665737473206e657665720a3b20656d626564207468656972206f776e2073656c662d64696765737420696e20746865697220707265696d616765202845444943542d434f52452d53454c46484153482d303031292e0a7265736f757263652d726566203d207b2069643a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20446967657374732061726520617574686f726974617469766520617320747970656420627974652076616c7565732c206e657665722068657820737472696e67732e20526576696577204a534f4e0a3b2072656e64657273207468697320617320227368613235363a3c3634206c6f77657263617365206865783e22202845444943542d4449474553542d574952452d303031292e0a7368613235362d646967657374203d205b20616c676f726974686d3a2022736861323536222c2062797465733a2062737472202e73697a65203332205d0a0a3b2041206e616d6564206c6f772d6c6576656c206661696c75726520616e206566666563742063616e2072616973652e2054686520736f75726365206f62737472756374696f6e206d61700a3b2062696e64732069742028627920636f6f7264696e6174652920616e6420636f6e73747275637473206120747970656420646f6d61696e206f62737472756374696f6e2066726f6d206974730a3b207061796c6f6164202845444943542d4142492d4641494c5552452d4e414d45442d303031292e0a3b20416e20656666656374277320606566666563744661696c7572657360206c697374204d555354206861766520756e697175652060636f6f7264696e61746560733a2073696e6365207468650a3b206f62737472756374696f6e206d6170206973206b6579656420627920636f6f7264696e6174652c2074776f206661696c757265732073686172696e67206120636f6f7264696e61746520286576656e0a3b207769746820646966666572656e7420617574686f72697479436c6173732f7061796c6f61645479706529206d616b652065786861757374697665206d617070696e6720616e642062696e6465720a3b20747970696e6720616d626967756f757320616e64206172652072656a6563746564202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a3b0a3b2045666665637473206361727279207468656972206661696c757265732061732061206d617020607b206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d600a3b202873656520746865207461726765742f6c61777061636b2065666665637420736368656d6173292e20546865206661696c75726520636f6f7264696e61746520697320746865206d61700a3b204b45592c20736f206974206973206e6f7420726570656174656420696e2074686520626f647920616e642063616e6e6f74206469736167726565207769746820746865206b65792e0a6566666563742d6661696c7572652d626f6479203d207b0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164547970653a20636f72652d747970652d7265662c202020202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b2041206661696c75726520636f6f7264696e617465206d7573742062652061206261726520456469637420606964656e746020286c65747465722f756e64657273636f7265207468656e0a3b206c6574746572732f6469676974732f756e64657273636f7265732920414e44206d757374206e6f742062652061207265736572766564206b6579776f72642028652e672e2060656c7365602c0a3b20606261736973602c20607768657265602c206072657175697265602c2060666f72602c2060696660292e2054686520736f75726365206f62737472756374696f6e2d6d6170204c4853206f6e6c790a3b20616363657074732061206e6f6e2d6b6579776f726420606964656e74602c20736f20612068797068656e2f646f742f6b6579776f726420636f6f7264696e61746520776f756c642062650a3b204142492d76616c69642079657420696d706f737369626c6520746f206d617020657868617573746976656c7920696e20736f757263652e20546865207265676578206361707475726573207468650a3b206c65786963616c2073686170653b206b6579776f7264206578636c7573696f6e20697320616e206164646974696f6e616c2076616c69646174696f6e2072756c650a3b202845444943542d4142492d4641494c5552452d4944454e542d303031292e0a6661696c7572652d6964656e74203d2074737472202e72656765787020225b412d5a612d7a5f5d5b412d5a612d7a302d395f5d2a220a0a6566666563742d6b696e64203d20227265616422202f202263726561746522202f2022656e7375726522202f20227265706c61636522202f202264656c65746522202f0a202020202020202020202020202022617070656e6422202f202272656475636522202f202273656d616e7469632e656d697422202f2022637573746f6d220a0a617574686f726974792d636c617373203d2022646f6d61696e4d61707061626c6522202f20227061727469636970616e744f776e656422202f2022696e746567726974794661756c7422202f0a202020202020202020202020202020202020227265736f757263654661756c7422202f2022696e7465726e616c4661756c74220a0a636f72652d747970652d726566203d20747374722020203b2063616e6f6e6963616c20436f7265207479706520636f6f7264696e6174650a0a3b20416e206f7065726174696f6e2070726f66696c6520737570706c69657320746865206f707469632074656d706c617465206120436f726520696e74656e74207265736f6c766573206974730a3b206f707469634b696e642f626f756e646172794b696e642f737570706f7274506f6c6963792f6c6f7373446973706f736974696f6e2066726f6d2e205461726765742070726f66696c657320616e640a3b206c61777061636b73207075626c6973682074686573652061732061206d617020607b20636f6f7264696e617465203d3e206f7065726174696f6e2d70726f66696c65207d602c20736f207468650a3b20636f6f7264696e61746520697320746865204b45592c206e6f7420612076616c7565206669656c64202845444943542d4f505449432d54454d504c4154452d4f574e45522d3030312c0a3b2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65203d207b0a20206f7074696354656d706c6174653a206f707469632d74656d706c6174652c0a20206566666563745072656469636174653a20747374722c20202020202020202020203b20636f6f7264696e617465206f6620746865206f7065726174696f6e2d6d6f6465207072656469636174650a7d0a0a6f707469632d74656d706c617465203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a2020737570706f7274506f6c6963793a20747374722c202020202020202020202020203b2063616e6f6e6963616c20737570706f72742d706f6c69637920636f6f7264696e6174650a20206c6f7373446973706f736974696f6e3a20747374722c20202020202020202020203b2063616e6f6e6963616c206c6f73732d646973706f736974696f6e20636f6f7264696e6174650a20203f20626173697354656d706c6174653a20747374722c20202020202020202020203b206f7074696f6e616c206469676573742d6c6f636b65642062617369732074656d706c61746520636f6f72640a20203b2074686520617065727475726520726571756972656d656e7420746869732074656d706c61746520737570706c6965732e205265717569726564207768656e207468652074656d706c6174650a20203b2069732074686520736f75726365206f66206120436f7265206f707469632773206170657274757265526571756972656d656e742028692e652e2074686520696e74656e7420686173206e6f0a20203b20736f757263652060666f6f747072696e74203c3d202e2e2e60292c2073696e6365206170657274757265526571756972656d656e74206973206d616e6461746f727920696e20436f72650a20203b202845444943542d4f505449432d41504552545552452d5245462d303031292e0a20203f206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a7d0a0a3b206170657274757265526571756972656d656e742069732061207479706564207265666572656e63652c206e65766572206120667265652d666f726d20737472696e672e2041207265766965770a3b2072656e646572696e67206d61792073686f772069747320636f6f7264696e617465202845444943542d4f505449432d41504552545552452d5245462d303031292e0a61706572747572652d726571756972656d656e74203d20666f6f747072696e742d6365696c696e672d726566202f2061627374726163742d666f6f747072696e742d6f626c69676174696f6e2d7265660a666f6f747072696e742d6365696c696e672d726566203d207b206b696e643a2022666f6f747072696e744365696c696e67222c207265663a2074737472207d0a61627374726163742d666f6f747072696e742d6f626c69676174696f6e2d726566203d207b206b696e643a20226162737472616374466f6f747072696e744f626c69676174696f6e222c207265663a2074737472207d0a0a3b202d2d2d2065646963742d636f72652e6364646c202d2d2d0a3b2065646963742d636f72652e6364646c0a3b204e6f726d617469766520736368656d6120666f722074686520456469637420436f72652076312073656d616e746963206d6f64656c2e0a3b0a3b2053636f706520626f756e646172793a20746869732066696c6520646566696e657320436f7265206d65616e696e6720616e6420736368656d61207368617065206f6e6c792e20497420646f65730a3b206e6f7420646566696e6520612063616e6f6e6963616c20656e636f6465722c20436f7265206d6f64756c652068617368206669656c64732c20686173682066697874757265732c207461726765740a3b206c6f776572696e672c2061646d697373696f6e2062756e646c65732c206f72207461726765742d6f776e65642049522e0a0a636f72652d6d6f64756c65203d207b0a202061706956657273696f6e3a202265646963742e636f72652f7631222c0a2020636f6f7264696e6174653a20747374722c0a2020696d706f7274733a205b2a20636f72652d696d706f72745d2c0a202074797065733a207b202a2074737472203d3e20636f72652d74797065207d2c0a2020696e74656e74733a207b202b2074737472203d3e20636f72652d696e74656e74207d2c0a20207265717569726564436f72654361706162696c69746965733a205b2a20747374725d2c0a7d0a0a636f72652d696d706f7274203d207b0a20206b696e643a20226c61777061636b22202f202274617267657422202f2022636f726522202f20226361706162696c697479222c0a20207265663a207265736f757263652d7265662c0a20203f20616c6961733a20747374722c0a7d0a0a3b202d2d2d207479706573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d74797065203d20636f72652d7363616c61722d74797065202f20636f72652d7265636f72642d74797065202f20636f72652d76617269616e742d74797065202f0a202020202020202020202020636f72652d6f7074696f6e2d74797065202f20636f72652d6c6973742d74797065202f20636f72652d6d61702d74797065202f0a202020202020202020202020636f72652d6361706162696c6974792d7265662d74797065202f20636f72652d65787465726e616c2d616374696f6e2d726571756573742d747970650a0a636f72652d7363616c61722d74797065203d20636f72652d626f6f6c2d74797065202f20636f72652d696e742d74797065202f20636f72652d737472696e672d74797065202f0a20202020202020202020202020202020202020636f72652d62797465732d74797065202f20636f72652d756e69742d747970650a0a636f72652d626f6f6c2d74797065203d207b206b696e643a2022426f6f6c22207d0a636f72652d756e69742d74797065203d207b206b696e643a2022556e697422207d0a636f72652d696e742d74797065203d207b0a20206b696e643a202249363422202f202255363422202f202249333222202f202255333222202f202249313622202f202255313622202f2022493822202f20225538222c0a7d0a636f72652d737472696e672d74797065203d207b0a20206b696e643a2022537472696e67222c0a20206d61783a2075696e742c0a202063616e6f6e6963616c3a2022756e69636f64652d7363616c61722d6e666322202f20227261772d75746638222c0a7d0a636f72652d62797465732d74797065203d207b0a20206b696e643a20224279746573222c0a20206d61783a2075696e742c0a7d0a636f72652d7265636f72642d74797065203d207b0a20206b696e643a20225265636f7264222c0a20206669656c64733a207b202a2074737472203d3e20636f72652d747970652d726566207d2c0a7d0a636f72652d76617269616e742d74797065203d207b0a20206b696e643a202256617269616e74222c0a202063617365733a207b202b2074737472203d3e2076617269616e742d636173652d626f6479207d2c0a7d0a76617269616e742d636173652d626f6479203d207b0a20203f207061796c6f61643a20636f72652d747970652d7265662c0a7d0a636f72652d6f7074696f6e2d74797065203d207b0a20206b696e643a20224f7074696f6e222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a636f72652d6c6973742d74797065203d207b0a20206b696e643a20224c697374222c0a20206974656d3a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6d61702d74797065203d207b0a20206b696e643a20224d6170222c0a20206b65793a20636f72652d747970652d7265662c0a202076616c75653a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6361706162696c6974792d7265662d74797065203d207b0a20206b696e643a20224361706162696c697479526566222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a636f72652d65787465726e616c2d616374696f6e2d726571756573742d74797065203d207b0a20206b696e643a202245787465726e616c416374696f6e52657175657374222c0a2020736574746c656d656e743a20636f72652d747970652d7265662c0a7d0a0a3b20636f72652d747970652d72656620697320646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d612e0a0a3b202d2d2d207265666572656e63657320616e642076616c756573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a3b204c6f63616c206964656e7469747920697320616c7068612d737461626c652e20606964602069732074686520636f6d70696c65722d6f776e6564206c6f63616c20636f6f7264696e6174653b0a3b2060616c7068614e616d656020697320746865206e6f726d616c697a65642068756d616e2f6465627567206e616d652e20536f757263652062696e646572207370656c6c696e67206973206e6f740a3b206964656e746974792e0a6c6f63616c2d726566203d207b0a202069643a20747374722c0a2020616c7068614e616d653a20747374722c0a2020747970653a20636f72652d747970652d7265662c0a7d0a0a636f72652d76616c7565203d20636f72652d6e756c6c2d76616c7565202f20636f72652d626f6f6c2d76616c7565202f20636f72652d696e742d76616c7565202f0a20202020202020202020202020636f72652d737472696e672d76616c7565202f20636f72652d62797465732d76616c7565202f20636f72652d7265636f72642d76616c7565202f0a20202020202020202020202020636f72652d76617269616e742d76616c7565202f20636f72652d6c6973742d76616c7565202f20636f72652d6d61702d76616c7565202f0a20202020202020202020202020636f72652d6361706162696c6974792d76616c75650a0a636f72652d6e756c6c2d76616c7565203d207b206b696e643a20226e756c6c22207d0a636f72652d626f6f6c2d76616c7565203d207b206b696e643a2022626f6f6c222c2076616c75653a20626f6f6c207d0a636f72652d696e742d76616c7565203d207b206b696e643a2022696e74222c2077696474683a20747374722c2076616c75653a20696e74207d0a636f72652d737472696e672d76616c7565203d207b206b696e643a2022737472696e67222c2076616c75653a2074737472207d0a636f72652d62797465732d76616c7565203d207b206b696e643a20226279746573222c2076616c75653a2062737472207d0a636f72652d7265636f72642d76616c7565203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d76616c7565207d207d0a636f72652d76617269616e742d76616c7565203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d76616c75652c0a7d0a636f72652d6c6973742d76616c7565203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d76616c75655d207d0a636f72652d6d61702d76616c7565203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d76616c75652c2076616c75653a20636f72652d76616c75655d5d207d0a636f72652d6361706162696c6974792d76616c7565203d207b0a20206b696e643a20226361706162696c697479222c0a2020726563656970743a207368613235362d6469676573742c0a7d0a0a3b2045646963742d617574686f72656420707572652068656c70657273207573652061207075726520436f72652066756e6374696f6e20626f64792e2054686520626f64792063616e2062696e640a3b20707572652065787072657373696f6e7320616e642072657475726e20616e2065787072657373696f6e2c206275742069742063616e6e6f7420636f6e7461696e20436f7265206566666563742c0a3b2067756172642c206272616e63682c206c6f6f702c206d617463682d6e6f64652c206f722070726f6f662d6f626c69676174696f6e206e6f6465732e0a636f72652d666e2d626f6479203d207b0a2020706172616d733a205b2a206c6f63616c2d7265665d2c0a2020626f64793a20636f72652d707572652d626c6f636b2c0a7d0a0a636f72652d707572652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a202062696e64696e67733a205b2a20707572652d6c65742d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a707572652d6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a0a3b202d2d2d2065787072657373696f6e7320616e642070726564696361746573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d65787072203d206c6f63616c2d65787072202f20636f6e73742d65787072202f207265636f72642d65787072202f206669656c642d65787072202f0a20202020202020202020202076617269616e742d65787072202f206d617463682d65787072202f2063616c6c2d65787072202f206c6973742d65787072202f206d61702d65787072202f0a20202020202020202020202069662d657870720a0a6c6f63616c2d65787072203d207b206b696e643a20226c6f63616c222c207265663a206c6f63616c2d726566207d0a636f6e73742d65787072203d207b206b696e643a2022636f6e7374222c2076616c75653a20636f72652d76616c7565207d0a7265636f72642d65787072203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d65787072207d207d0a6669656c642d65787072203d207b206b696e643a20226669656c64222c20626173653a20636f72652d657870722c206669656c643a2074737472207d0a76617269616e742d65787072203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d657870722c0a7d0a6d617463682d65787072203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d61726d5d2c0a7d0a6d617463682d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d657870722c0a7d0a63616c6c2d65787072203d207b0a20206b696e643a202263616c6c222c0a202063616c6c65653a20747374722c0a202074797065417267733a205b2a20636f72652d747970652d7265665d2c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6c6973742d65787072203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d657870725d207d0a6d61702d65787072203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d657870722c2076616c75653a20636f72652d657870725d5d207d0a69662d65787072203d207b0a20206b696e643a20226966222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d657870722c0a2020656c73653a20636f72652d657870722c0a7d0a0a636f72652d707265646963617465203d20747275652d707265646963617465202f2066616c73652d707265646963617465202f206e6f742d707265646963617465202f0a2020202020202020202020202020202020616c6c2d707265646963617465202f20616e792d707265646963617465202f20636f6d706172652d707265646963617465202f0a202020202020202020202020202020202063616c6c2d707265646963617465202f206f62737472756374696f6e2d7072656469636174650a0a747275652d707265646963617465203d207b206b696e643a20227472756522207d0a66616c73652d707265646963617465203d207b206b696e643a202266616c736522207d0a6e6f742d707265646963617465203d207b206b696e643a20226e6f74222c2076616c75653a20636f72652d707265646963617465207d0a616c6c2d707265646963617465203d207b206b696e643a2022616c6c222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a616e792d707265646963617465203d207b206b696e643a2022616e79222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a636f6d706172652d707265646963617465203d207b0a20206b696e643a2022636f6d70617265222c0a20206f703a20223d3d22202f2022213d22202f20223c22202f20223c3d22202f20223e22202f20223e3d222c0a20206c6566743a20636f72652d657870722c0a202072696768743a20636f72652d657870722c0a7d0a63616c6c2d707265646963617465203d207b0a20206b696e643a202263616c6c222c0a20207072656469636174653a20747374722c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6f62737472756374696f6e2d707265646963617465203d207b0a20206b696e643a20226f62737472756374696f6e222c0a2020636f6f7264696e6174653a206661696c7572652d6964656e742c0a20207061796c6f61643a20636f72652d657870722c0a7d0a0a696e7075742d636f6e73747261696e74203d207b0a2020636f6f7264696e6174653a20747374722c0a2020736f757263653a2022776865726522202f2022636f6d70696c6572222c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a3b202d2d2d20696e74656e74732c20626c6f636b732c20616e64206e6f646573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d696e74656e74203d207b0a2020696e7075743a20636f72652d747970652d7265662c0a20206f75747075743a20636f72652d747970652d7265662c0a202072657175697265644f7065726174696f6e50726f66696c653a20747374722c0a20203f2062617369733a20636f72652d657870722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020626f64793a20636f72652d626c6f636b2c0a20203f206f707469633a20636f72652d6f707469632c0a7d0a0a636f72652d627564676574203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a636f72652d6f70746963203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a20206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a2020737570706f7274506f6c6963793a20747374722c0a20206c6f7373446973706f736974696f6e3a20747374722c0a7d0a0a636f72652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a20206e6f6465733a205b2a20636f72652d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a636f72652d6e6f6465203d206c65742d6e6f6465202f20726571756972652d6e6f6465202f206566666563742d6e6f6465202f0a20202020202020202020202065787465726e616c2d616374696f6e2d726571756573742d6e6f6465202f2067756172642d6e6f6465202f206272616e63682d6e6f6465202f0a202020202020202020202020666f722d6e6f6465202f206d617463682d6e6f6465202f2070726f6f662d6f626c69676174696f6e2d6e6f64650a0a6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a726571756972652d6e6f6465203d207b0a20206b696e643a202272657175697265222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a726571756972652d6661696c7572652d61726d203d207465726d696e616c2d726571756972652d6661696c757265202f0a20202020202020202020202020202020202020202020636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c7572650a7465726d696e616c2d726571756972652d6661696c757265203d207b0a20206b696e643a20227465726d696e616c222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c757265203d207b0a20206b696e643a2022636f6e74696e75654f627374727563746564222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a6f62737472756374696f6e2d726561736f6e203d207b0a2020726561736f6e4b696e643a20747374722c0a20207061796c6f61643a207b202a2074737472203d3e20636f72652d65787072207d2c0a7d0a6566666563742d6e6f6465203d207b0a20206b696e643a2022656666656374222c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4d61703a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a65787465726e616c2d616374696f6e2d726571756573742d6e6f6465203d207b0a20206b696e643a202265787465726e616c416374696f6e52657175657374222c0a202062696e64696e673a206c6f63616c2d7265662c0a20206f7065726174696f6e3a207265736f757263652d7265662c0a2020696e707574547970653a20636f72652d747970652d7265662c0a2020736574746c656d656e74547970653a20636f72652d747970652d7265662c0a2020696e707574536368656d613a207265736f757263652d7265662c0a2020736574746c656d656e74536368656d613a207265736f757263652d7265662c0a2020696e7075743a20636f72652d657870722c0a2020617574686f7269747953636f70653a20636f72652d657870722c0a202062617369733a20636f72652d657870722c0a20206275646765743a2065787465726e616c2d616374696f6e2d6275646765742c0a20207265636f6e63696c696174696f6e4c61773a207265736f757263652d7265662c0a202073746174653a20226177616974696e67536574746c656d656e74222c0a2020736574746c656d656e7441646d697373696f6e3a2022736368656d615265717569726564222c0a7d0a65787465726e616c2d616374696f6e2d627564676574203d207b0a20206d6178536574746c656d656e7442797465733a20636f72652d657870722c0a20206d6178417474656d7074733a20636f72652d657870722c0a7d0a6f62737472756374696f6e2d61726d203d207b0a202062696e6465723a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a67756172642d6e6f6465203d207b0a20206b696e643a20226775617264222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f62737472756374696f6e3a20636f72652d657870722c0a7d0a6272616e63682d6e6f6465203d207b0a20206b696e643a20226272616e6368222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d626c6f636b2c0a2020656c73653a20636f72652d626c6f636b2c0a7d0a666f722d6e6f6465203d207b0a20206b696e643a2022666f72222c0a202062696e6465723a206c6f63616c2d7265662c0a2020697465723a20636f72652d657870722c0a2020626f756e643a20636f72652d626f756e642c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a6d617463682d6e6f6465203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d626c6f636b2d61726d5d2c0a7d0a6d617463682d626c6f636b2d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a70726f6f662d6f626c69676174696f6e2d6e6f6465203d207b0a20206b696e643a202270726f6f66222c0a2020636f6f7264696e6174653a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a636f72652d626f756e64203d206c69746572616c2d626f756e64202f20636f6f7264696e6174652d626f756e640a6c69746572616c2d626f756e64203d207b206b696e643a20226c69746572616c222c2076616c75653a2075696e74207d0a636f6f7264696e6174652d626f756e64203d207b206b696e643a2022636f6f7264696e617465222c207265663a2074737472207d0a0a3b20536861726564207265736f757263652d7265662c207368613235362d6469676573742c206661696c7572652d6964656e742c2061706572747572652d726571756972656d656e742c20616e640a3b20636f72652d747970652d7265662061726520646566696e6564206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c2e0a0a3b202d2d2d2065646963742d6c61777061636b2e6364646c202d2d2d0a3b2065646963742d6c61777061636b2e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374206c61777061636b206d616e696665737420616e64206578706f727420737572666163652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e204a534f4e20696e207468652070726f73652073706563730a3b2069732061207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a6c61777061636b2d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e6c61777061636b2f7631222c0a202069643a20747374722c0a202076657273696f6e3a20747374722c0a20206163636570746564436f72654162693a205b2b20747374725d2c0a2020646570656e64656e636965733a205b2a206c61777061636b2d6465705d2c202020202020202020203b20616379636c69632c206469676573742d6c6f636b6564202845444943542d4c41575041434b2d4441472d303031290a20206578706f7274733a207265736f757263652d7265662c0a20203f2074617267657441646170746572733a205b2b207461726765742d616461707465725d2c2020203b207265717569726564206f6e6c7920696620616e792072756e74696d6520656666656374206578697374730a20203f2068656c706572436f6d706f6e656e743a2065786563757461626c652d636f6d706f6e656e742c203b2065786563757461626c652068656c70657273206361727279207468656972206f776e2073616e64626f782b6675656c0a202076657269666965723a2076657269666965722c202020202020202020202020202020202020202020203b20636c61737369666965643a206465636c61726174697665206f722065786563757461626c650a2020636f6d7061746962696c6974793a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b2041207665726966696572206973206569746865722061206465636c617261746976652072756c6573657420286e6f2072756e74696d6529206f7220616e2065786563757461626c650a3b20636f6d706f6e656e742e20416e2065786563757461626c65207665726966696572204d55535420636172727920697473206f776e2073616e64626f7820616e64206675656c206d6f64656c2c0a3b20736f2074686520736368656d6120656e666f726365732074686174206e6f2065786563757461626c6520636f6d706f6e656e74206973206c65667420756e626f756e6465640a3b202845444943542d4142492d56455249464945522d424f554e442d303031292e0a7665726966696572203d206465636c617261746976652d7665726966696572202f2065786563757461626c652d76657269666965720a6465636c617261746976652d7665726966696572203d207b20636c6173733a20226465636c61726174697665222c2072756c657365743a207265736f757263652d726566207d0a65786563757461626c652d7665726966696572203d207b0a2020636c6173733a202265786563757461626c65222c0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a3b20416e792065786563757461626c6520636f6d706f6e656e7420697320626f756e64656420627920697473206f776e2073616e64626f78202b206675656c206d6f64656c2e0a65786563757461626c652d636f6d706f6e656e74203d207b0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a6c61777061636b2d646570203d207b2069643a20747374722c2076657273696f6e3a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20416461707465722073656c656374696f6e206b65797320534f4c454c59206f666620746865206469676573742d6c6f636b65642060616363657074656454617267657450726f66696c65600a3b20286974732060696460206973207468652070726f66696c652069643b206974732060646967657374602070696e73207468652065786163742070726f66696c652f76657273696f6e292e2054686572650a3b20617265206e6f20696e646570656e64656e7420646973706c617920737472696e6773207468617420636f756c64206469736167726565207769746820746865206c6f636b2c20736f20610a3b207265736f6c7665722063616e6e6f742062696e6420616e206164617074657220746f206f6e6520746172676574207768696c6520746865206c6f636b2070726f76657320616e6f746865720a3b202845444943542d4c41575041434b2d414441505445522d54415247455449522d303031292e0a7461726765742d61646170746572203d207b0a2020616363657074656454617267657450726f66696c653a207265736f757263652d7265662c202020203b206469676573742d6c6f636b65642c20617574686f72697461746976652073656c6563746f720a2020616363657074656454617267657449723a207265736f757263652d7265662c2020202020202020203b206469676573742d6c6f636b65640a2020616461707465723a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d206578706f72742073757266616365202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a6c61777061636b2d6578706f727473203d207b0a202074797065733a205b2a206578706f727465642d747970655d2c0a2020636f6e7374616e74733a205b2a206578706f727465642d636f6e7374616e745d2c0a20207075726546756e6374696f6e733a205b2a20707572652d66756e6374696f6e5d2c0a2020656666656374733a205b2a2073656d616e7469632d6566666563745d2c0a20206f62737472756374696f6e733a205b2a206f62737472756374696f6e2d6465665d2c0a20203b206b65796564206279206f7065726174696f6e2d70726f66696c6520636f6f7264696e61746520e2869220756e697175656e65737320656e666f726365640a20203b202845444943542d4142492d4f5050524f46494c452d554e495155452d303031290a20203b206f7065726174696f6e2d70726f66696c65207265636f7264732074686973206c61777061636b206578706f72747320286f707469632074656d706c6174657320746861740a20203b2060696d706c656d656e7473602f6070726f66696c656020636c6175736573207265736f6c766520616761696e7374292e206f7065726174696f6e2d70726f66696c652069730a20203b20646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c20203b206b6579656420627920636f6f7264696e6174650a7d0a0a6578706f727465642d7479706520202020203d207b20636f6f7264696e6174653a20747374722c20646566696e6974696f6e3a20636f72652d747970652d726566207d0a6578706f727465642d636f6e7374616e74203d207b20636f6f7264696e6174653a20747374722c20747970653a20636f72652d747970652d7265662c2076616c75653a20616e79207d0a0a3b204120707572652068656c7065722069732061206469736372696d696e6174656420756e696f6e2062792060736f75726365602c20736f2074686520736368656d6120697473656c660a3b2067756172616e7465657320616e20696d706c656d656e746174696f6e20657869737473202845444943542d4c41575041434b2d505552452d494d504c2d303031293a0a3b2020202d20226564696374223a20617574686f72656420696e2045646963742f436f72653b2074686520436f726520626f6479206973206361727269656420696e6c696e6520286861736865640a3b20202020207769746820746865206578706f72742073757266616365292e2054686520736368656d61207265717569726573207468652060626f647960206669656c642e0a3b2020202d2022636f6d706f6e656e74223a20696d706c656d656e746564206f7574736964652045646963743b2063617272696573206e6f20696e6c696e6520626f647920616e6420696e73746561640a3b20202020206361727269657320697473206f776e206469676573742d6c6f636b65642060696d706c656d656e746174696f6e60202873616e64626f78202b206675656c292e20497420646f65730a3b20202020206e6f7420646570656e64206f6e20746865206f7074696f6e616c206d616e69666573742d6c6576656c2068656c706572436f6d706f6e656e742e0a707572652d66756e6374696f6e203d2065646963742d707572652d66756e6374696f6e202f20636f6d706f6e656e742d707572652d66756e6374696f6e0a0a707572652d66756e6374696f6e2d636f6d6d6f6e203d20280a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020706172616d6574657254797065733a205b2a20636f72652d747970652d7265665d2c2020202020203b20616c6c20626f756e6465640a202072657475726e547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020636f737454656d706c6174653a20747374722c0a202064657465726d696e69736d436c6173733a2022746f74616c22202f2022746f74616c2d776974682d74797065642d646961676e6f73746963222c0a290a0a65646963742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a20226564696374222c0a2020626f64793a20636f72652d666e2d626f64792c2020202020202020202020202020202020202020203b20696e6c696e652c20686173682d7369676e69666963616e740a7d0a0a636f6d706f6e656e742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a2022636f6d706f6e656e74222c0a20203b20746865206469676573742d6c6f636b656420636f6d706f6e656e7420696d706c656d656e74696e6720746869732068656c7065722e2052657175697265642061742074686520736368656d610a20203b206c6576656c20736f206120636f6d706f6e656e742068656c7065722063616e206e657665722076616c696461746520776974686f7574206120686173682d626f756e642c0a20203b2073616e64626f782b6675656c2d64657363726962656420696d706c656d656e746174696f6e202845444943542d4c41575041434b2d505552452d494d504c2d303031292e0a2020696d706c656d656e746174696f6e3a2065786563757461626c652d636f6d706f6e656e742c0a7d0a0a3b20636f72652d666e2d626f647920697320646566696e65642062792065646963742d636f72652e6364646c20616e6420617373656d626c656420776974682074686973206c61777061636b0a3b20736368656d612e2049742069732061207075726520436f72652066756e6374696f6e20626f64792c206e6f7420616e206566666563742d63617061626c6520636f72652d626c6f636b2e0a0a73656d616e7469632d656666656374203d207b0a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020696e707574547970653a20636f72652d747970652d7265662c2020202020202020202020202020203b20626f756e6465640a20206f7574707574547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020657865637574696f6e436c6173733a202270726f6f664f6e6c7922202f202272756e74696d65222c2020203b206f7274686f676f6e616c20746f207772697465436c6173730a20206566666563744b696e6448696e743a206566666563742d6b696e642c0a2020666f6f747072696e744f626c69676174696f6e3a20747374722c0a2020636f73744f626c69676174696f6e3a20747374722c0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c20203b206b6579656420627920636f6f7264696e6174653b20756e697175650a20206775617264537570706f72743a20626f6f6c2c0a7d0a0a6f62737472756374696f6e2d646566203d207b0a2020636f6f7264696e6174653a20747374722c0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164536368656d613a20636f72652d747970652d7265662c20202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d6c61777061636b2d616461707465722e6364646c202d2d2d0a3b2065646963742d6c61777061636b2d616461707465722e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f72206f6e6520646972656374206465636c61726174697665206c61777061636b2074617267657420616461707465722e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b2054686520656e636c6f73696e67206c61777061636b206d616e69666573742073656c6563747320746865206578616374207461726765742070726f66696c652c207461726765742049522c0a3b20616e642061646170746572207265736f75726365206469676573742e2054686f7365206964656e74697469657320617265206e6f7420726570656174656420686572652e0a0a6c61777061636b2d61646170746572203d207b0a202061706956657273696f6e3a202265646963742e6c61777061636b2d616461707465722f7631222c0a2020636c6173733a20226465636c61726174697665222c0a20206f7065726174696f6e50726f66696c65733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6f7065726174696f6e2d70726f66696c650a20207d2c0a2020656666656374496d706c656d656e746174696f6e733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6566666563740a20207d2c0a2020627564676574733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6275646765740a20207d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206c61777061636b206f7065726174696f6e2d70726f66696c6520636f6f7264696e617465732e0a6c61777061636b2d616461707465722d6f7065726174696f6e2d70726f66696c65203d207b0a2020636f72653a20747374722c0a202073656d616e746963456666656374733a205b2b20747374725d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206c61777061636b2073656d616e7469632d65666665637420636f6f7264696e617465732e20466f6f747072696e742c20636f73742c20616e640a3b206661696c757265206669656c6473206d7573742065786163746c792064697363686172676520746865206d61746368696e67206578706f72746564206566666563742e0a6c61777061636b2d616461707465722d656666656374203d207b0a2020746172676574496e7472696e7369633a20747374722c0a2020746172676574436f6e66696775726174696f6e3a207265736f757263652d7265662c0a20207772697465436c6173733a206c61777061636b2d616461707465722d77726974652d636c6173732c0a2020666f6f747072696e744f626c69676174696f6e3a20747374722c0a2020636f73744f626c69676174696f6e3a20747374722c0a20206661696c7572654d617070696e67733a207b202a206661696c7572652d6964656e74203d3e2074737472207d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206578706f7274656420636f73742d6f626c69676174696f6e20636f6f7264696e617465732e0a6c61777061636b2d616461707465722d627564676574203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a6c61777061636b2d616461707465722d77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f0a20202020202020202020202020202020202020202020202020202020202022617070656e6422202f20227265706c61636522202f202264656c65746522202f2022637573746f6d220a0a3b206661696c7572652d6964656e7420697320646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c2e0a0a3b202d2d2d2065646963742d7461726765742d70726f66696c652e6364646c202d2d2d0a3b2065646963742d7461726765742d70726f66696c652e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374207461726765742070726f66696c65206d616e69666573742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76310a3b202873656520535045435f636f6e74696e75756d2d636f6e74726163742d62756e646c652d76312e6d64292e204a534f4e20696e207468652070726f736520737065637320697320610a3b207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d613b2074686973204344444c206973207468652073696e676c6520736f757263650a3b206f66207472757468202845444943542d4142492d4e4f4455502d303031292e0a0a7461726765742d70726f66696c652d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652f7631222c0a202069643a20747374722c202020202020202020202020202020202020202020202020203b20652e672e20226563686f2e64706f220a202076657273696f6e3a20747374722c20202020202020202020202020202020202020203b20652e672e202231220a20206163636570746564436f72654162693a205b2b20747374725d2c20202020202020203b20652e672e205b2265646963742e636f72652f7631225d0a0a2020696e7472696e736963733a207265736f757263652d7265662c0a2020696e7472696e7369634e616d6573706163653a20747374722c0a20203b207075626c697368657320746869732070726f66696c652773206f7065726174696f6e2d70726f66696c65207265636f72647320286f707469632074656d706c6174657320746861740a20203b206070726f66696c65602f60696d706c656d656e74736020636c6175736573207265736f6c766520616761696e7374292e205265666572656e63657320616e0a20203b206f7065726174696f6e2d70726f66696c65732d646f63756d656e74202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207265736f757263652d7265662c0a2020666f6f747072696e74416c67656272613a207265736f757263652d7265662c0a2020636f7374416c67656272613a207265736f757263652d7265662c0a202074617267657449723a207265736f757263652d7265662c0a20206f62737472756374696f6e5461786f6e6f6d793a207265736f757263652d7265662c0a202076657269666965723a207265736f757263652d7265662c0a20206c6f77657265723a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a0a20203b206669656c647320746865206c616e67756167652073706563207265717569726573206f662065766572792070726f66696c650a202062756e646c6550726f66696c653a207265736f757263652d7265662c0a202067656e657261746564417274696661637450726f66696c65733a205b2a207265736f757263652d7265665d2c0a202063616e6f6e6963616c456e636f64696e6752756c65733a207265736f757263652d7265662c0a20203b20412070726f66696c65207468617420616363657074732074686520646972656374206465636c61726174697665206c61777061636b2d6164617074657220414249206e616d65732069740a20203b2065786163746c79206f6e63652e2050726f66696c6573207468617420646f206e6f7420636f6e73756d65206c61777061636b206164617074657273206c6561766520746869730a20203b206f7074696f6e616c20736c6f7420616273656e74206f7220656d7074792e0a20203f2061636365707465644c61777061636b416461707465724162693a205b5d202f205b2265646963742e6c61777061636b2d616461707465722f7631225d2c0a2020646961676e6f737469634162693a207265736f757263652d7265662c0a0a20203b206170706c69636174696f6e20646f637472696e650a20206170706c69636174696f6e4d6f64656c3a202261746f6d6963222c0a202072656164436f6e73697374656e63793a20226170706c69636174696f6e2d736e617073686f7422202f20747374722c0a202067756172644576616c756174696f6e3a2022707265636f6d6d69742d61746f6d696322202f20747374722c0a20206f62737472756374696f6e526f6c6c6261636b3a20226e6f2d76697369626c652d6566666563747322202f20747374722c0a20206d756c74695461726765743a20626f6f6c2c0a20203b207768657468657220746865207461726765742063616e206576616c7561746520707265636f6d6d697420706f7374636f6e646974696f6e20286067756172616e746565602920636865636b730a20203b20696e73696465207468652061746f6d6963206170706c69636174696f6e20756e6974202845444943542d5441524745542d504f5354434f4e442d303031290a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a0a202064657465726d696e6973746963457865637574696f6e3a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d6120627920746865206275696c640a3b202845444943542d4142492d4e4f4455502d303031292e205468657920617265206e6f74207265646566696e656420686572652e0a0a3b202d2d2d20696e7472696e736963207369676e6174757265202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e696665737427732060696e7472696e7369637360207265736f757263652d726566206973207468650a3b20696e7472696e7369632d7369676e617475726520636f7270757320646f63756d656e742062656c6f772e20497473206c61796f757420697320666978656420736f2074776f0a3b20696e646570656e64656e742070726f66696c65732076616c69646174652f686173682074686520636f72707573206964656e746963616c6c790a3b202845444943542d4142492d494e5452494e534943532d444f432d303031292e0a0a3b20696e7472696e736963732069732061204d4150206b6579656420627920636f6f7264696e6174652c20736f2074686520736368656d6120697473656c6620656e666f726365730a3b20636f6f7264696e61746520756e697175656e6573732e20412070726f766964657220726563656976657320746865207265736f6c76656420636f7270757320617320610a3b206469676573742d626f756e642073656d616e74696320696e70757420616e64207265736f6c76657320636f6f7264696e617465732077697468696e20746861742061727469666163742e0a3b2045616368206d6170206b6579204d55535420657175616c20697473207265636f726427732060636f6f7264696e61746560206669656c640a3b202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e696e7472696e736963732f7631222c0a2020696e7472696e736963733a207b202a2074737472203d3e20696e7472696e736963207d2c0a7d0a0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e6966657374277320606f7065726174696f6e50726f66696c657360207265736f757263652d7265662e0a3b206f7065726174696f6e2d70726f66696c65202f206f707469632d74656d706c6174652061726520646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c2e204b657965642062790a3b20636f6f7264696e61746520736f207265736f6c7574696f6e2063616e2774207069636b206265747765656e2074776f2073616d652d636f6f7264696e6174652070726f66696c65730a3b202845444943542d4142492d4f5050524f46494c452d534c4f542d3030312c2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e6f7065726174696f6e2d70726f66696c65732f7631222c0a202070726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c0a7d0a0a3b2041207479706564207072652d6c6f776572696e67207175657374696f6e20746861742063616e2062652070726f706f73656420627920576174736f6e206f7220616e206167656e7420616e640a3b20636865636b65642062792074686520636f6d70696c65722e2049742069732063616e6f6e6963616c2d43424f5220656e636f64656420756e6465720a3b206065646963742e6c6f776572696e672d726571756972656d656e74732f7631603b2074686520636f6d70696c657220636865636b7320746869732061727469666163742c206e6f74207468650a3b2070726f736520746861742070726f64756365642069742e0a6c6f776572696e672d726571756972656d656e7473203d207b0a202061706956657273696f6e3a202265646963742e6c6f776572696e672d726571756972656d656e74732f7631222c0a20206f7065726174696f6e50726f66696c653a20747374722c0a202073656d616e746963456666656374733a205b2a2073656d616e7469632d6566666563742d726571756972656d656e745d2c0a202072657175697265645772697465436c61737365733a205b2a2077726974652d636c6173735d2c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a202061746f6d69636974793a2061746f6d69636974792d726571756972656d656e742c0a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a20206f70746963436f6e74726163743a20747374722c0a7d0a0a73656d616e7469632d6566666563742d726571756972656d656e74203d207b0a2020636f6f7264696e6174653a20747374722c0a20207772697465436c6173733a2077726974652d636c6173732c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a7d0a0a77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a2020202020202020202020202020227265706c61636522202f202264656c65746522202f20747374720a67756172642d6b696e64203d2022707265636f6d6d69742d61746f6d696322202f20747374720a61746f6d69636974792d726571756972656d656e74203d202261746f6d696322202f20747374720a0a3b20412067656e75696e6520756e696f6e3a207075726520636f6e7374727563746f7273206361727279206e6f20656666656374206b696e64206f72206661696c757265733b206566666563740a3b20696e7472696e73696373206d757374202845444943542d5441524745542d494e5452494e5349432d434c4153532d303031292e2054686520736368656d6120656e666f7263657320746869732c0a3b206e6f74206120636f6d6d656e742e0a0a3b2054686520696e7472696e736963277320636f6f7264696e6174652069732074686520696e7472696e73696373206d6170204b45592c206e6f7420612076616c7565206669656c642c20736f207468650a3b206b657920616e6420636f6f7264696e6174652063616e206e65766572206469736167726565202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963203d20707572652d696e7472696e736963202f206566666563742d696e7472696e7369630a0a707572652d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a202270757265222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206775617264537570706f72743a2066616c73652c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20226e6f6e65222c0a7d0a0a6566666563742d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a2022656666656374222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206566666563744b696e643a206566666563742d6b696e642c0a20203b206d6170206b65796564206279206661696c75726520636f6f7264696e61746520286661696c7572652d6964656e74293b20746865206661696c75726520636f6f7264696e6174652069730a20203b20746865206b65792c206e6f7420612076616c7565206669656c642c20736f20756e697175656e657373206973207374727563747572616c0a20203b202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c0a20206775617264537570706f72743a20626f6f6c2c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f20227265706c61636522202f0a20202020202020202020202020202264656c65746522202f2022637573746f6d222c0a202063616e5061727469636970617465496e41746f6d696347756172643a20626f6f6c2c0a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d617574686f726974792d66616374732e6364646c202d2d2d0a3b2065646963742d617574686f726974792d66616374732e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f722074686520666972737420636f6d70696c65722d636f6e7465787420617574686f726974792d666163747320646f63756d656e742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20736f20736f757263652e6469676573742075736573207468650a3b20736861726564207368613235362d6469676573742074797065642076616c75652e204a534f4e2069732061207265766965772f696e7075742072656e646572696e673a206974730a3b20607368613235363a3c3634206865783e6020736f75726365206469676573742069732070726f6a656374656420746f205b60736861323536602c203332207261772062797465735d206f6e0a3b2074686520776972652c20616e64206974732066616374206172726179732070726f6a65637420746f2074686520636f6f7264696e6174652d6b65796564206d6170732062656c6f772e0a0a617574686f726974792d6661637473203d207b0a202061706956657273696f6e3a202265646963742e617574686f726974792d66616374732f7631222c0a2020736f757263653a20617574686f726974792d666163742d736f757263652c0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e20617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374207d2c0a20206566666563745772697465436c61737365733a207b202a2074737472203d3e20617574686f726974792d77726974652d636c617373207d2c0a2020627564676574733a207b202a2074737472203d3e20617574686f726974792d6275646765742d66616374207d2c0a7d0a0a617574686f726974792d666163742d736f75726365203d207b0a20206b696e643a20226c61777061636b22202f202274617267657450726f66696c65222c0a2020636f6f7264696e6174653a20747374722c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a3b20546865206d6170206b65792069732074686520736f75726365206f7065726174696f6e2d70726f66696c6520636f6f7264696e6174652e204974206973206e6f7420726570656174656420696e0a3b207468652076616c75652c20736f2061206b657920616e6420656d62656464656420636f6f7264696e6174652063616e6e6f742064697361677265652e20416c6c6f7765642077726974650a3b20636c61737365732061726520612063616e6f6e6963616c206d61702d7365743a2074686520636c6173732069732074686520756e69717565206b657920616e64206e756c6c206973207468650a3b20756e6974206d61726b65722e2043616e6f6e6963616c2043424f52206669786573206b6579206f7264657220776974686f75742061207365636f6e64206f72646572696e672072756c652e0a617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374203d207b0a2020636f72653a20747374722c0a2020616c6c6f7765645772697465436c61737365733a207b202a20617574686f726974792d77726974652d636c617373203d3e206e756c6c207d2c0a7d0a0a3b20546865206566666563745772697465436c6173736573206d6170206b6579206973207468652073656d616e7469632065666665637420636f6f7264696e6174652e2054686520627564676574730a3b206d6170206b65792069732074686520736f757263652062756467657420636f6f7264696e6174652e2043616e6f6e6963616c2043424f52206d61702d6b657920756e697175656e6573730a3b206d616b6573206475706c6963617465206661637420636f6f7264696e61746573207374727563747572616c6c7920756e726570726573656e7461626c652e0a617574686f726974792d6275646765742d66616374203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a3b20417574686f726974794661637473446f63756d656e7420763120696e74656e74696f6e616c6c792061636365707473206f6e6c792074686520777269746520636c6173736573207468650a3b2063757272656e7420636f6d70696c6572206d6f64656c2063616e20636f6e73756d652e2060637573746f6d602069732074686520736f6c6520763120637573746f6d207370656c6c696e673b0a3b20617262697472617279207461726765742d70726f66696c6520657874656e73696f6e20737472696e677320646f206e6f7420656e746572207468697320636f6d70696c657220706174682e0a617574686f726974792d77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a202020202020202020202020202020202020202020202020227265706c61636522202f202264656c65746522202f2022637573746f6d220a0a3b202d2d2d2065646963742d726573756c742d70726f6a656374696f6e2e6364646c202d2d2d0a3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d726573756c742d70726f6a656374696f6e2e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220636f6d70696c65722d6f776e6564206170706c69636174696f6e2d726573756c742070726f6a656374696f6e732e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a0a726573756c742d70726f6a656374696f6e203d207b0a2020736368656d613a202265646963742e726573756c742d70726f6a656374696f6e2f7631222c0a20206f7065726174696f6e436f6f7264696e6174653a20626f756e6465642d70726f6a656374696f6e2d746578742c0a20206f7574707574547970653a20626f756e6465642d70726f6a656374696f6e2d746578742c0a20206d61784f757470757442797465733a2075696e74202e677420302c0a202065787072657373696f6e3a20726573756c742d70726f6a656374696f6e2d657870722c0a7d0a0a726573756c742d70726f6a656374696f6e2d65787072203d20726573756c742d70726f6a656374696f6e2d7265636f7264202f20726573756c742d70726f6a656374696f6e2d736f757263650a0a726573756c742d70726f6a656374696f6e2d7265636f7264203d207b0a20206b696e643a20227265636f7264222c0a20203b2054686520726f6f74207265636f726420636f756e7473206173206f6e65206f66207468652052757374206465636f6465722773203235362065787072657373696f6e206e6f6465732e0a20203b204e657374656420616767726567617465206e6f646520636f756e742072656d61696e7320616e20617574686f7269746174697665206465636f64657220636865636b2e0a20206669656c64733a207b20302a32353520626f756e6465642d70726f6a656374696f6e2d74657874203d3e20726573756c742d70726f6a656374696f6e2d65787072207d2c0a7d0a0a726573756c742d70726f6a656374696f6e2d736f75726365203d207b0a20206b696e643a2022736f75726365222c0a2020736f757263653a20726573756c742d70726f6a656374696f6e2d736f757263652d6b696e642c0a20203b204d617463686573204d41585f524553554c545f50524f4a454354494f4e5f504154485f5345474d454e545320696e2065646963742d73796e7461782e0a2020706174683a205b302a333220626f756e6465642d70726f6a656374696f6e2d746578745d2c0a7d0a0a726573756c742d70726f6a656374696f6e2d736f757263652d6b696e64203d0a20207b206b696e643a20226170706c69636174696f6e496e70757422207d202f0a20207b0a202020206b696e643a20226361706162696c697479526573756c74222c0a202020207374657049643a20626f756e6465642d70726f6a656374696f6e2d746578742c0a20207d0a0a626f756e6465642d70726f6a656374696f6e2d74657874203d2074737472202e73697a652028312e2e31303234290a0a3b202d2d2d2065646963742d7461726765742d69722e6364646c202d2d2d0a3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d7461726765742d69722e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f72207468652045646963742d6f776e65642054617267657420495220617274696661637420656e76656c6f70652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20616e642065646963742d636f72652e6364646c2e2049740a3b2064656c696265726174656c792072657573657320436f72652065787072657373696f6e732c20707265646963617465732c20627564676574732c206c6f63616c207265666572656e6365732c0a3b206f62737472756374696f6e20726561736f6e732c20616e64206f62737472756374696f6e2061726d7320736f2074686520736368656d61206d617463686573207468652076616c75650a3b20656d6974746564206279207468652063616e6f6e6963616c2054617267657420495220656e636f64657220726174686572207468616e20726573746174696e672074686f73652074797065732e0a3b2049742064657363726962657320746865207374727563747572616c207368617065206f662076616c6964206c6f776572696e672d70726f6475636564206172746966616374732e205468650a3b206c6f776572696e6720616e6420656e636f64657220636f6e7472616374732073657061726174656c7920656e666f7263652073656d616e746963206964656e7469666965722072756c65730a3b20616e642063616e6f6e6963616c206f72646572696e672f64656475706c69636174696f6e20666f72207365742d6c696b652076616c7565732e0a0a3b2054617267657420495220656e636f64696e672072656a6563747320616e20656d707479207461726765742d70726f66696c6520636f6f7264696e617465206265666f72652062797465730a3b2065786973742c20736f207468697320726f6f74207469676874656e732074686520736861726564207374727563747572616c207265736f757263652d726566206163636f7264696e676c792e0a7461726765742d69722d7265736f757263652d726566203d207b0a202069643a2074737472202e7265676578702022283f73292e2b222c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a7461726765742d69722d6172746966616374203d207461726765742d69722d636c6f7365642d6172746966616374202f207461726765742d69722d6c65676163792d61727469666163740a0a7461726765742d69722d61727469666163742d636f6d6d6f6e203d20280a20206b696e643a202274617267657449724172746966616374222c0a2020646f6d61696e3a20747374722c0a202074617267657450726f66696c653a207461726765742d69722d7265736f757263652d7265662c0a2020736f75726365436f7265436f6f7264696e6174653a2074737472202e7265676578702022283f73292e2b222c0a290a0a7461726765742d69722d636c6f7365642d6172746966616374203d207b0a20207461726765742d69722d61727469666163742d636f6d6d6f6e2c0a202073656d616e746963436c6f737572653a207461726765742d69722d73656d616e7469632d636c6f737572652c0a2020696e74656e74733a207b202a2074737472203d3e207461726765742d69722d696e74656e74207d2c0a7d0a0a7461726765742d69722d6c65676163792d6172746966616374203d207b0a20207461726765742d69722d61727469666163742d636f6d6d6f6e2c0a2020696e74656e74733a207b202a2074737472203d3e207461726765742d69722d6c65676163792d696e74656e74207d2c0a7d0a0a7461726765742d69722d73656d616e7469632d636c6f73757265203d207b0a2020736f75726365436f72653a207461726765742d69722d7265736f757263652d7265662c0a20206c61777061636b733a205b2a207461726765742d69722d7265736f757263652d7265665d2c0a20203f206361706162696c69746965733a205b2a207461726765742d69722d7265736f757263652d7265665d2c0a7d0a0a7461726765742d69722d696e74656e74203d207b0a20207461726765742d69722d696e74656e742d636f6d6d6f6e2c0a20203f2062617369733a20636f72652d657870722c0a20203f2065787465726e616c416374696f6e52657175657374733a205b2a207461726765742d69722d65787465726e616c2d616374696f6e2d726571756573745d2c0a7d0a0a7461726765742d69722d6c65676163792d696e74656e74203d207b0a20207461726765742d69722d696e74656e742d636f6d6d6f6e2c0a7d0a0a7461726765742d69722d696e74656e742d636f6d6d6f6e203d20280a20206f7065726174696f6e50726f66696c653a20747374722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020726571756972656d656e74733a205b2a207461726765742d69722d726571756972656d656e745d2c0a202073746570733a205b2a207461726765742d69722d737465705d2c0a2020726573756c743a20636f72652d657870722c0a290a0a7461726765742d69722d726571756972656d656e74203d207b0a202069643a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a0a7461726765742d69722d73746570203d207b0a202069643a20747374722c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020746172676574496e7472696e7369633a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4661696c757265733a205b2a206661696c7572652d6964656e745d2c0a20206f62737472756374696f6e41726d733a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a0a7461726765742d69722d65787465726e616c2d616374696f6e2d72657175657374203d207b0a202069643a20747374722c0a202062696e64696e673a206c6f63616c2d7265662c0a20206f7065726174696f6e3a207461726765742d69722d7265736f757263652d7265662c0a2020696e707574547970653a20636f72652d747970652d7265662c0a2020736574746c656d656e74547970653a20636f72652d747970652d7265662c0a2020696e707574536368656d613a207461726765742d69722d7265736f757263652d7265662c0a2020736574746c656d656e74536368656d613a207461726765742d69722d7265736f757263652d7265662c0a2020696e7075743a20636f72652d657870722c0a2020617574686f7269747953636f70653a20636f72652d657870722c0a202062617369733a20636f72652d657870722c0a20206275646765743a2065787465726e616c2d616374696f6e2d6275646765742c0a20207265636f6e63696c696174696f6e4c61773a207461726765742d69722d7265736f757263652d7265662c0a202073746174653a20226177616974696e67536574746c656d656e74222c0a2020736574746c656d656e7441646d697373696f6e3a2022736368656d615265717569726564222c0a7d0a", - "rawSha256": "4936a5c13f61647f710c99cbdfb347ef180d9ccf4b9db75bfcee68e394fdc480" + "bytesHex": "3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d70726f76696465722d636f6e7472616374732e6364646c0a3b2047656e6572617465642066726f6d2045646963742d6f776e65642041424920667261676d656e74732e20444f204e4f5420454449542e0a0a3b202d2d2d2065646963742d636f6d6d6f6e2e6364646c202d2d2d0a3b2065646963742d636f6d6d6f6e2e6364646c0a3b20536861726564204344444c20747970657320666f722074686520456469637420414249732c20646566696e6564204f4e4345206865726520736f20746865792063616e6e6f742064726966740a3b202845444943542d4142492d4e4f4455502d303031292e2054776f2067726f7570733a0a3b2020202d207265736f757263652d7265662c207368613235362d6469676573742c206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c0a3b2020202020636f72652d747970652d7265663a20617373656d626c656420776974682065646963742d7461726765742d70726f66696c652e6364646c20616e640a3b202020202065646963742d6c61777061636b2e6364646c20627920746865206275696c643b2074686f736520736368656d617320646f206e6f74207265646566696e65207468656d2e0a3b2020202d206f7065726174696f6e2d70726f66696c652c206f707469632d74656d706c6174652c2061706572747572652d726571756972656d656e7420616e6420746865697220726566733a0a3b2020202020636f6e73756d65642062792074686520436f72652f6f70746963206c61796572202865646963742d636f72652e6364646c2920616e64207265666572656e636564206279207468650a3b20202020206c616e67756167652f7461726765742d70726f66696c652073706563732e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a0a3b2041206e6f726d617469766520737562636f6d706f6e656e74207265666572656e636564206279206964656e7469747920706c7573206469676573742e204d616e696665737473206e657665720a3b20656d626564207468656972206f776e2073656c662d64696765737420696e20746865697220707265696d616765202845444943542d434f52452d53454c46484153482d303031292e0a7265736f757263652d726566203d207b2069643a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20446967657374732061726520617574686f726974617469766520617320747970656420627974652076616c7565732c206e657665722068657820737472696e67732e20526576696577204a534f4e0a3b2072656e64657273207468697320617320227368613235363a3c3634206c6f77657263617365206865783e22202845444943542d4449474553542d574952452d303031292e0a7368613235362d646967657374203d205b20616c676f726974686d3a2022736861323536222c2062797465733a2062737472202e73697a65203332205d0a0a3b2041206e616d6564206c6f772d6c6576656c206661696c75726520616e206566666563742063616e2072616973652e2054686520736f75726365206f62737472756374696f6e206d61700a3b2062696e64732069742028627920636f6f7264696e6174652920616e6420636f6e73747275637473206120747970656420646f6d61696e206f62737472756374696f6e2066726f6d206974730a3b207061796c6f6164202845444943542d4142492d4641494c5552452d4e414d45442d303031292e0a3b20416e20656666656374277320606566666563744661696c7572657360206c697374204d555354206861766520756e697175652060636f6f7264696e61746560733a2073696e6365207468650a3b206f62737472756374696f6e206d6170206973206b6579656420627920636f6f7264696e6174652c2074776f206661696c757265732073686172696e67206120636f6f7264696e61746520286576656e0a3b207769746820646966666572656e7420617574686f72697479436c6173732f7061796c6f61645479706529206d616b652065786861757374697665206d617070696e6720616e642062696e6465720a3b20747970696e6720616d626967756f757320616e64206172652072656a6563746564202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a3b0a3b2045666665637473206361727279207468656972206661696c757265732061732061206d617020607b206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d600a3b202873656520746865207461726765742f6c61777061636b2065666665637420736368656d6173292e20546865206661696c75726520636f6f7264696e61746520697320746865206d61700a3b204b45592c20736f206974206973206e6f7420726570656174656420696e2074686520626f647920616e642063616e6e6f74206469736167726565207769746820746865206b65792e0a6566666563742d6661696c7572652d626f6479203d207b0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164547970653a20636f72652d747970652d7265662c202020202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b2041206661696c75726520636f6f7264696e617465206d7573742062652061206261726520456469637420606964656e746020286c65747465722f756e64657273636f7265207468656e0a3b206c6574746572732f6469676974732f756e64657273636f7265732920414e44206d757374206e6f742062652061207265736572766564206b6579776f72642028652e672e2060656c7365602c0a3b20606261736973602c20607768657265602c206072657175697265602c2060666f72602c2060696660292e2054686520736f75726365206f62737472756374696f6e2d6d6170204c4853206f6e6c790a3b20616363657074732061206e6f6e2d6b6579776f726420606964656e74602c20736f20612068797068656e2f646f742f6b6579776f726420636f6f7264696e61746520776f756c642062650a3b204142492d76616c69642079657420696d706f737369626c6520746f206d617020657868617573746976656c7920696e20736f757263652e20546865207265676578206361707475726573207468650a3b206c65786963616c2073686170653b206b6579776f7264206578636c7573696f6e20697320616e206164646974696f6e616c2076616c69646174696f6e2072756c650a3b202845444943542d4142492d4641494c5552452d4944454e542d303031292e0a6661696c7572652d6964656e74203d2074737472202e72656765787020225b412d5a612d7a5f5d5b412d5a612d7a302d395f5d2a220a0a6566666563742d6b696e64203d20227265616422202f202263726561746522202f2022656e7375726522202f20227265706c61636522202f202264656c65746522202f0a202020202020202020202020202022617070656e6422202f202272656475636522202f202273656d616e7469632e656d697422202f2022637573746f6d220a0a617574686f726974792d636c617373203d2022646f6d61696e4d61707061626c6522202f20227061727469636970616e744f776e656422202f2022696e746567726974794661756c7422202f0a202020202020202020202020202020202020227265736f757263654661756c7422202f2022696e7465726e616c4661756c74220a0a636f72652d747970652d726566203d20747374722020203b2063616e6f6e6963616c20436f7265207479706520636f6f7264696e6174650a0a3b20416e206f7065726174696f6e2070726f66696c6520737570706c69657320746865206f707469632074656d706c617465206120436f726520696e74656e74207265736f6c766573206974730a3b206f707469634b696e642f626f756e646172794b696e642f737570706f7274506f6c6963792f6c6f7373446973706f736974696f6e2066726f6d2e205461726765742070726f66696c657320616e640a3b206c61777061636b73207075626c6973682074686573652061732061206d617020607b20636f6f7264696e617465203d3e206f7065726174696f6e2d70726f66696c65207d602c20736f207468650a3b20636f6f7264696e61746520697320746865204b45592c206e6f7420612076616c7565206669656c64202845444943542d4f505449432d54454d504c4154452d4f574e45522d3030312c0a3b2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65203d207b0a20206f7074696354656d706c6174653a206f707469632d74656d706c6174652c0a20206566666563745072656469636174653a20747374722c20202020202020202020203b20636f6f7264696e617465206f6620746865206f7065726174696f6e2d6d6f6465207072656469636174650a7d0a0a6f707469632d74656d706c617465203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a2020737570706f7274506f6c6963793a20747374722c202020202020202020202020203b2063616e6f6e6963616c20737570706f72742d706f6c69637920636f6f7264696e6174650a20206c6f7373446973706f736974696f6e3a20747374722c20202020202020202020203b2063616e6f6e6963616c206c6f73732d646973706f736974696f6e20636f6f7264696e6174650a20203f20626173697354656d706c6174653a20747374722c20202020202020202020203b206f7074696f6e616c206469676573742d6c6f636b65642062617369732074656d706c61746520636f6f72640a20203b2074686520617065727475726520726571756972656d656e7420746869732074656d706c61746520737570706c6965732e205265717569726564207768656e207468652074656d706c6174650a20203b2069732074686520736f75726365206f66206120436f7265206f707469632773206170657274757265526571756972656d656e742028692e652e2074686520696e74656e7420686173206e6f0a20203b20736f757263652060666f6f747072696e74203c3d202e2e2e60292c2073696e6365206170657274757265526571756972656d656e74206973206d616e6461746f727920696e20436f72650a20203b202845444943542d4f505449432d41504552545552452d5245462d303031292e0a20203f206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a7d0a0a3b206170657274757265526571756972656d656e742069732061207479706564207265666572656e63652c206e65766572206120667265652d666f726d20737472696e672e2041207265766965770a3b2072656e646572696e67206d61792073686f772069747320636f6f7264696e617465202845444943542d4f505449432d41504552545552452d5245462d303031292e0a61706572747572652d726571756972656d656e74203d20666f6f747072696e742d6365696c696e672d726566202f2061627374726163742d666f6f747072696e742d6f626c69676174696f6e2d7265660a666f6f747072696e742d6365696c696e672d726566203d207b206b696e643a2022666f6f747072696e744365696c696e67222c207265663a2074737472207d0a61627374726163742d666f6f747072696e742d6f626c69676174696f6e2d726566203d207b206b696e643a20226162737472616374466f6f747072696e744f626c69676174696f6e222c207265663a2074737472207d0a0a3b202d2d2d2065646963742d636f72652e6364646c202d2d2d0a3b2065646963742d636f72652e6364646c0a3b204e6f726d617469766520736368656d6120666f722074686520456469637420436f72652076312073656d616e746963206d6f64656c2e0a3b0a3b2053636f706520626f756e646172793a20746869732066696c6520646566696e657320436f7265206d65616e696e6720616e6420736368656d61207368617065206f6e6c792e20497420646f65730a3b206e6f7420646566696e6520612063616e6f6e6963616c20656e636f6465722c20436f7265206d6f64756c652068617368206669656c64732c20686173682066697874757265732c207461726765740a3b206c6f776572696e672c2061646d697373696f6e2062756e646c65732c206f72207461726765742d6f776e65642049522e0a0a636f72652d6d6f64756c65203d207b0a202061706956657273696f6e3a202265646963742e636f72652f7631222c0a2020636f6f7264696e6174653a20747374722c0a2020696d706f7274733a205b2a20636f72652d696d706f72745d2c0a202074797065733a207b202a2074737472203d3e20636f72652d74797065207d2c0a2020696e74656e74733a207b202b2074737472203d3e20636f72652d696e74656e74207d2c0a20207265717569726564436f72654361706162696c69746965733a205b2a20747374725d2c0a7d0a0a636f72652d696d706f7274203d207b0a20206b696e643a20226c61777061636b22202f202274617267657422202f2022636f726522202f20226361706162696c697479222c0a20207265663a207265736f757263652d7265662c0a20203f20616c6961733a20747374722c0a7d0a0a3b202d2d2d207479706573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d74797065203d20636f72652d7363616c61722d74797065202f20636f72652d7265636f72642d74797065202f20636f72652d76617269616e742d74797065202f0a202020202020202020202020636f72652d6f7074696f6e2d74797065202f20636f72652d6c6973742d74797065202f20636f72652d6d61702d74797065202f0a202020202020202020202020636f72652d6361706162696c6974792d7265662d74797065202f20636f72652d65787465726e616c2d616374696f6e2d726571756573742d747970650a0a636f72652d7363616c61722d74797065203d20636f72652d626f6f6c2d74797065202f20636f72652d696e742d74797065202f20636f72652d737472696e672d74797065202f0a20202020202020202020202020202020202020636f72652d62797465732d74797065202f20636f72652d756e69742d747970650a0a636f72652d626f6f6c2d74797065203d207b206b696e643a2022426f6f6c22207d0a636f72652d756e69742d74797065203d207b206b696e643a2022556e697422207d0a636f72652d696e742d74797065203d207b0a20206b696e643a202249363422202f202255363422202f202249333222202f202255333222202f202249313622202f202255313622202f2022493822202f20225538222c0a7d0a636f72652d737472696e672d74797065203d207b0a20206b696e643a2022537472696e67222c0a20206d61783a2075696e742c0a202063616e6f6e6963616c3a2022756e69636f64652d7363616c61722d6e666322202f20227261772d75746638222c0a7d0a636f72652d62797465732d74797065203d207b0a20206b696e643a20224279746573222c0a20206d61783a2075696e742c0a7d0a636f72652d7265636f72642d74797065203d207b0a20206b696e643a20225265636f7264222c0a20206669656c64733a207b202a2074737472203d3e20636f72652d747970652d726566207d2c0a7d0a636f72652d76617269616e742d74797065203d207b0a20206b696e643a202256617269616e74222c0a202063617365733a207b202b2074737472203d3e2076617269616e742d636173652d626f6479207d2c0a7d0a76617269616e742d636173652d626f6479203d207b0a20203f207061796c6f61643a20636f72652d747970652d7265662c0a7d0a636f72652d6f7074696f6e2d74797065203d207b0a20206b696e643a20224f7074696f6e222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a636f72652d6c6973742d74797065203d207b0a20206b696e643a20224c697374222c0a20206974656d3a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6d61702d74797065203d207b0a20206b696e643a20224d6170222c0a20206b65793a20636f72652d747970652d7265662c0a202076616c75653a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6361706162696c6974792d7265662d74797065203d207b0a20206b696e643a20224361706162696c697479526566222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a636f72652d65787465726e616c2d616374696f6e2d726571756573742d74797065203d207b0a20206b696e643a202245787465726e616c416374696f6e52657175657374222c0a2020736574746c656d656e743a20636f72652d747970652d7265662c0a7d0a0a3b20636f72652d747970652d72656620697320646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d612e0a0a3b202d2d2d207265666572656e63657320616e642076616c756573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a3b204c6f63616c206964656e7469747920697320616c7068612d737461626c652e20606964602069732074686520636f6d70696c65722d6f776e6564206c6f63616c20636f6f7264696e6174653b0a3b2060616c7068614e616d656020697320746865206e6f726d616c697a65642068756d616e2f6465627567206e616d652e20536f757263652062696e646572207370656c6c696e67206973206e6f740a3b206964656e746974792e0a6c6f63616c2d726566203d207b0a202069643a20747374722c0a2020616c7068614e616d653a20747374722c0a2020747970653a20636f72652d747970652d7265662c0a7d0a0a636f72652d76616c7565203d20636f72652d6e756c6c2d76616c7565202f20636f72652d626f6f6c2d76616c7565202f20636f72652d696e742d76616c7565202f0a20202020202020202020202020636f72652d737472696e672d76616c7565202f20636f72652d62797465732d76616c7565202f20636f72652d7265636f72642d76616c7565202f0a20202020202020202020202020636f72652d76617269616e742d76616c7565202f20636f72652d6c6973742d76616c7565202f20636f72652d6d61702d76616c7565202f0a20202020202020202020202020636f72652d6361706162696c6974792d76616c75650a0a636f72652d6e756c6c2d76616c7565203d207b206b696e643a20226e756c6c22207d0a636f72652d626f6f6c2d76616c7565203d207b206b696e643a2022626f6f6c222c2076616c75653a20626f6f6c207d0a636f72652d696e742d76616c7565203d207b206b696e643a2022696e74222c2077696474683a20747374722c2076616c75653a20696e74207d0a636f72652d737472696e672d76616c7565203d207b206b696e643a2022737472696e67222c2076616c75653a2074737472207d0a636f72652d62797465732d76616c7565203d207b206b696e643a20226279746573222c2076616c75653a2062737472207d0a636f72652d7265636f72642d76616c7565203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d76616c7565207d207d0a636f72652d76617269616e742d76616c7565203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d76616c75652c0a7d0a636f72652d6c6973742d76616c7565203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d76616c75655d207d0a636f72652d6d61702d76616c7565203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d76616c75652c2076616c75653a20636f72652d76616c75655d5d207d0a636f72652d6361706162696c6974792d76616c7565203d207b0a20206b696e643a20226361706162696c697479222c0a2020726563656970743a207368613235362d6469676573742c0a7d0a0a3b2045646963742d617574686f72656420707572652068656c70657273207573652061207075726520436f72652066756e6374696f6e20626f64792e2054686520626f64792063616e2062696e640a3b20707572652065787072657373696f6e7320616e642072657475726e20616e2065787072657373696f6e2c206275742069742063616e6e6f7420636f6e7461696e20436f7265206566666563742c0a3b2067756172642c206272616e63682c206c6f6f702c206d617463682d6e6f64652c206f722070726f6f662d6f626c69676174696f6e206e6f6465732e0a636f72652d666e2d626f6479203d207b0a2020706172616d733a205b2a206c6f63616c2d7265665d2c0a2020626f64793a20636f72652d707572652d626c6f636b2c0a7d0a0a636f72652d707572652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a202062696e64696e67733a205b2a20707572652d6c65742d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a707572652d6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a0a3b202d2d2d2065787072657373696f6e7320616e642070726564696361746573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d65787072203d206c6f63616c2d65787072202f20636f6e73742d65787072202f207265636f72642d65787072202f206669656c642d65787072202f0a20202020202020202020202076617269616e742d65787072202f206d617463682d65787072202f2063616c6c2d65787072202f206c6973742d65787072202f206d61702d65787072202f0a20202020202020202020202069662d657870720a0a6c6f63616c2d65787072203d207b206b696e643a20226c6f63616c222c207265663a206c6f63616c2d726566207d0a636f6e73742d65787072203d207b206b696e643a2022636f6e7374222c2076616c75653a20636f72652d76616c7565207d0a7265636f72642d65787072203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d65787072207d207d0a6669656c642d65787072203d207b206b696e643a20226669656c64222c20626173653a20636f72652d657870722c206669656c643a2074737472207d0a76617269616e742d65787072203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d657870722c0a7d0a6d617463682d65787072203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d61726d5d2c0a7d0a6d617463682d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d657870722c0a7d0a63616c6c2d65787072203d207b0a20206b696e643a202263616c6c222c0a202063616c6c65653a20747374722c0a202074797065417267733a205b2a20636f72652d747970652d7265665d2c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6c6973742d65787072203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d657870725d207d0a6d61702d65787072203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d657870722c2076616c75653a20636f72652d657870725d5d207d0a69662d65787072203d207b0a20206b696e643a20226966222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d657870722c0a2020656c73653a20636f72652d657870722c0a7d0a0a636f72652d707265646963617465203d20747275652d707265646963617465202f2066616c73652d707265646963617465202f206e6f742d707265646963617465202f0a2020202020202020202020202020202020616c6c2d707265646963617465202f20616e792d707265646963617465202f20636f6d706172652d707265646963617465202f0a202020202020202020202020202020202063616c6c2d707265646963617465202f206f62737472756374696f6e2d7072656469636174650a0a747275652d707265646963617465203d207b206b696e643a20227472756522207d0a66616c73652d707265646963617465203d207b206b696e643a202266616c736522207d0a6e6f742d707265646963617465203d207b206b696e643a20226e6f74222c2076616c75653a20636f72652d707265646963617465207d0a616c6c2d707265646963617465203d207b206b696e643a2022616c6c222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a616e792d707265646963617465203d207b206b696e643a2022616e79222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a636f6d706172652d707265646963617465203d207b0a20206b696e643a2022636f6d70617265222c0a20206f703a20223d3d22202f2022213d22202f20223c22202f20223c3d22202f20223e22202f20223e3d222c0a20206c6566743a20636f72652d657870722c0a202072696768743a20636f72652d657870722c0a7d0a63616c6c2d707265646963617465203d207b0a20206b696e643a202263616c6c222c0a20207072656469636174653a20747374722c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6f62737472756374696f6e2d707265646963617465203d207b0a20206b696e643a20226f62737472756374696f6e222c0a2020636f6f7264696e6174653a206661696c7572652d6964656e742c0a20207061796c6f61643a20636f72652d657870722c0a7d0a0a696e7075742d636f6e73747261696e74203d207b0a2020636f6f7264696e6174653a20747374722c0a2020736f757263653a2022776865726522202f2022636f6d70696c6572222c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a3b202d2d2d20696e74656e74732c20626c6f636b732c20616e64206e6f646573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d696e74656e74203d207b0a2020696e7075743a20636f72652d747970652d7265662c0a20206f75747075743a20636f72652d747970652d7265662c0a202072657175697265644f7065726174696f6e50726f66696c653a20747374722c0a20203f2062617369733a20636f72652d657870722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020626f64793a20636f72652d626c6f636b2c0a20203f206f707469633a20636f72652d6f707469632c0a7d0a0a636f72652d627564676574203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a636f72652d6f70746963203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a20206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a2020737570706f7274506f6c6963793a20747374722c0a20206c6f7373446973706f736974696f6e3a20747374722c0a7d0a0a636f72652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a20206e6f6465733a205b2a20636f72652d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a636f72652d6e6f6465203d206c65742d6e6f6465202f20726571756972652d6e6f6465202f206566666563742d6e6f6465202f0a20202020202020202020202065787465726e616c2d616374696f6e2d726571756573742d6e6f6465202f2067756172642d6e6f6465202f206272616e63682d6e6f6465202f0a202020202020202020202020666f722d6e6f6465202f206d617463682d6e6f6465202f2070726f6f662d6f626c69676174696f6e2d6e6f64650a0a6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a726571756972652d6e6f6465203d207b0a20206b696e643a202272657175697265222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a726571756972652d6661696c7572652d61726d203d207465726d696e616c2d726571756972652d6661696c757265202f0a20202020202020202020202020202020202020202020636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c7572650a7465726d696e616c2d726571756972652d6661696c757265203d207b0a20206b696e643a20227465726d696e616c222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c757265203d207b0a20206b696e643a2022636f6e74696e75654f627374727563746564222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a6f62737472756374696f6e2d726561736f6e203d207b0a2020726561736f6e4b696e643a20747374722c0a20207061796c6f61643a207b202a2074737472203d3e20636f72652d65787072207d2c0a7d0a6566666563742d6e6f6465203d207b0a20206b696e643a2022656666656374222c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4d61703a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a65787465726e616c2d616374696f6e2d726571756573742d6e6f6465203d207b0a20206b696e643a202265787465726e616c416374696f6e52657175657374222c0a202062696e64696e673a206c6f63616c2d7265662c0a20206f7065726174696f6e3a207265736f757263652d7265662c0a2020696e707574547970653a20636f72652d747970652d7265662c0a2020736574746c656d656e74547970653a20636f72652d747970652d7265662c0a2020696e707574536368656d613a207265736f757263652d7265662c0a2020736574746c656d656e74536368656d613a207265736f757263652d7265662c0a2020696e7075743a20636f72652d657870722c0a2020617574686f7269747953636f70653a20636f72652d657870722c0a202062617369733a20636f72652d657870722c0a20206275646765743a2065787465726e616c2d616374696f6e2d6275646765742c0a20207265636f6e63696c696174696f6e4c61773a207265736f757263652d7265662c0a202073746174653a20226177616974696e67536574746c656d656e74222c0a2020736574746c656d656e7441646d697373696f6e3a2022736368656d615265717569726564222c0a7d0a65787465726e616c2d616374696f6e2d627564676574203d207b0a20206d6178536574746c656d656e7442797465733a20636f72652d657870722c0a20206d6178417474656d7074733a20636f72652d657870722c0a7d0a6f62737472756374696f6e2d61726d203d207b0a202062696e6465723a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a67756172642d6e6f6465203d207b0a20206b696e643a20226775617264222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f62737472756374696f6e3a20636f72652d657870722c0a7d0a6272616e63682d6e6f6465203d207b0a20206b696e643a20226272616e6368222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d626c6f636b2c0a2020656c73653a20636f72652d626c6f636b2c0a7d0a666f722d6e6f6465203d207b0a20206b696e643a2022666f72222c0a202062696e6465723a206c6f63616c2d7265662c0a2020697465723a20636f72652d657870722c0a2020626f756e643a20636f72652d626f756e642c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a6d617463682d6e6f6465203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d626c6f636b2d61726d5d2c0a7d0a6d617463682d626c6f636b2d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a70726f6f662d6f626c69676174696f6e2d6e6f6465203d207b0a20206b696e643a202270726f6f66222c0a2020636f6f7264696e6174653a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a636f72652d626f756e64203d206c69746572616c2d626f756e64202f20636f6f7264696e6174652d626f756e640a6c69746572616c2d626f756e64203d207b206b696e643a20226c69746572616c222c2076616c75653a2075696e74207d0a636f6f7264696e6174652d626f756e64203d207b206b696e643a2022636f6f7264696e617465222c207265663a2074737472207d0a0a3b20536861726564207265736f757263652d7265662c207368613235362d6469676573742c206661696c7572652d6964656e742c2061706572747572652d726571756972656d656e742c20616e640a3b20636f72652d747970652d7265662061726520646566696e6564206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c2e0a0a3b202d2d2d2065646963742d6c61777061636b2e6364646c202d2d2d0a3b2065646963742d6c61777061636b2e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374206c61777061636b206d616e696665737420616e64206578706f727420737572666163652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e204a534f4e20696e207468652070726f73652073706563730a3b2069732061207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a6c61777061636b2d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e6c61777061636b2f7631222c0a202069643a20747374722c0a202076657273696f6e3a20747374722c0a20206163636570746564436f72654162693a205b2b20747374725d2c0a2020646570656e64656e636965733a205b2a206c61777061636b2d6465705d2c202020202020202020203b20616379636c69632c206469676573742d6c6f636b6564202845444943542d4c41575041434b2d4441472d303031290a20206578706f7274733a207265736f757263652d7265662c0a20203f2074617267657441646170746572733a205b2b207461726765742d616461707465725d2c2020203b207265717569726564206f6e6c7920696620616e792072756e74696d6520656666656374206578697374730a20203f2068656c706572436f6d706f6e656e743a2065786563757461626c652d636f6d706f6e656e742c203b2065786563757461626c652068656c70657273206361727279207468656972206f776e2073616e64626f782b6675656c0a202076657269666965723a2076657269666965722c202020202020202020202020202020202020202020203b20636c61737369666965643a206465636c61726174697665206f722065786563757461626c650a2020636f6d7061746962696c6974793a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b2041207665726966696572206973206569746865722061206465636c617261746976652072756c6573657420286e6f2072756e74696d6529206f7220616e2065786563757461626c650a3b20636f6d706f6e656e742e20416e2065786563757461626c65207665726966696572204d55535420636172727920697473206f776e2073616e64626f7820616e64206675656c206d6f64656c2c0a3b20736f2074686520736368656d6120656e666f726365732074686174206e6f2065786563757461626c6520636f6d706f6e656e74206973206c65667420756e626f756e6465640a3b202845444943542d4142492d56455249464945522d424f554e442d303031292e0a7665726966696572203d206465636c617261746976652d7665726966696572202f2065786563757461626c652d76657269666965720a6465636c617261746976652d7665726966696572203d207b20636c6173733a20226465636c61726174697665222c2072756c657365743a207265736f757263652d726566207d0a65786563757461626c652d7665726966696572203d207b0a2020636c6173733a202265786563757461626c65222c0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a3b20416e792065786563757461626c6520636f6d706f6e656e7420697320626f756e64656420627920697473206f776e2073616e64626f78202b206675656c206d6f64656c2e0a65786563757461626c652d636f6d706f6e656e74203d207b0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a6c61777061636b2d646570203d207b2069643a20747374722c2076657273696f6e3a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20416461707465722073656c656374696f6e206b65797320534f4c454c59206f666620746865206469676573742d6c6f636b65642060616363657074656454617267657450726f66696c65600a3b20286974732060696460206973207468652070726f66696c652069643b206974732060646967657374602070696e73207468652065786163742070726f66696c652f76657273696f6e292e2054686572650a3b20617265206e6f20696e646570656e64656e7420646973706c617920737472696e6773207468617420636f756c64206469736167726565207769746820746865206c6f636b2c20736f20610a3b207265736f6c7665722063616e6e6f742062696e6420616e206164617074657220746f206f6e6520746172676574207768696c6520746865206c6f636b2070726f76657320616e6f746865720a3b202845444943542d4c41575041434b2d414441505445522d54415247455449522d303031292e0a7461726765742d61646170746572203d207b0a2020616363657074656454617267657450726f66696c653a207265736f757263652d7265662c202020203b206469676573742d6c6f636b65642c20617574686f72697461746976652073656c6563746f720a2020616363657074656454617267657449723a207265736f757263652d7265662c2020202020202020203b206469676573742d6c6f636b65640a2020616461707465723a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d206578706f72742073757266616365202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a6c61777061636b2d6578706f727473203d207b0a202074797065733a205b2a206578706f727465642d747970655d2c0a2020636f6e7374616e74733a205b2a206578706f727465642d636f6e7374616e745d2c0a20207075726546756e6374696f6e733a205b2a20707572652d66756e6374696f6e5d2c0a2020656666656374733a205b2a2073656d616e7469632d6566666563745d2c0a20206f62737472756374696f6e733a205b2a206f62737472756374696f6e2d6465665d2c0a20203b206b65796564206279206f7065726174696f6e2d70726f66696c6520636f6f7264696e61746520e2869220756e697175656e65737320656e666f726365640a20203b202845444943542d4142492d4f5050524f46494c452d554e495155452d303031290a20203b206f7065726174696f6e2d70726f66696c65207265636f7264732074686973206c61777061636b206578706f72747320286f707469632074656d706c6174657320746861740a20203b2060696d706c656d656e7473602f6070726f66696c656020636c6175736573207265736f6c766520616761696e7374292e206f7065726174696f6e2d70726f66696c652069730a20203b20646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c20203b206b6579656420627920636f6f7264696e6174650a7d0a0a6578706f727465642d7479706520202020203d207b20636f6f7264696e6174653a20747374722c20646566696e6974696f6e3a20636f72652d747970652d726566207d0a6578706f727465642d636f6e7374616e74203d207b20636f6f7264696e6174653a20747374722c20747970653a20636f72652d747970652d7265662c2076616c75653a20616e79207d0a0a3b204120707572652068656c7065722069732061206469736372696d696e6174656420756e696f6e2062792060736f75726365602c20736f2074686520736368656d6120697473656c660a3b2067756172616e7465657320616e20696d706c656d656e746174696f6e20657869737473202845444943542d4c41575041434b2d505552452d494d504c2d303031293a0a3b2020202d20226564696374223a20617574686f72656420696e2045646963742f436f72653b2074686520436f726520626f6479206973206361727269656420696e6c696e6520286861736865640a3b20202020207769746820746865206578706f72742073757266616365292e2054686520736368656d61207265717569726573207468652060626f647960206669656c642e0a3b2020202d2022636f6d706f6e656e74223a20696d706c656d656e746564206f7574736964652045646963743b2063617272696573206e6f20696e6c696e6520626f647920616e6420696e73746561640a3b20202020206361727269657320697473206f776e206469676573742d6c6f636b65642060696d706c656d656e746174696f6e60202873616e64626f78202b206675656c292e20497420646f65730a3b20202020206e6f7420646570656e64206f6e20746865206f7074696f6e616c206d616e69666573742d6c6576656c2068656c706572436f6d706f6e656e742e0a707572652d66756e6374696f6e203d2065646963742d707572652d66756e6374696f6e202f20636f6d706f6e656e742d707572652d66756e6374696f6e0a0a707572652d66756e6374696f6e2d636f6d6d6f6e203d20280a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020706172616d6574657254797065733a205b2a20636f72652d747970652d7265665d2c2020202020203b20616c6c20626f756e6465640a202072657475726e547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020636f737454656d706c6174653a20747374722c0a202064657465726d696e69736d436c6173733a2022746f74616c22202f2022746f74616c2d776974682d74797065642d646961676e6f73746963222c0a290a0a65646963742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a20226564696374222c0a2020626f64793a20636f72652d666e2d626f64792c2020202020202020202020202020202020202020203b20696e6c696e652c20686173682d7369676e69666963616e740a7d0a0a636f6d706f6e656e742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a2022636f6d706f6e656e74222c0a20203b20746865206469676573742d6c6f636b656420636f6d706f6e656e7420696d706c656d656e74696e6720746869732068656c7065722e2052657175697265642061742074686520736368656d610a20203b206c6576656c20736f206120636f6d706f6e656e742068656c7065722063616e206e657665722076616c696461746520776974686f7574206120686173682d626f756e642c0a20203b2073616e64626f782b6675656c2d64657363726962656420696d706c656d656e746174696f6e202845444943542d4c41575041434b2d505552452d494d504c2d303031292e0a2020696d706c656d656e746174696f6e3a2065786563757461626c652d636f6d706f6e656e742c0a7d0a0a3b20636f72652d666e2d626f647920697320646566696e65642062792065646963742d636f72652e6364646c20616e6420617373656d626c656420776974682074686973206c61777061636b0a3b20736368656d612e2049742069732061207075726520436f72652066756e6374696f6e20626f64792c206e6f7420616e206566666563742d63617061626c6520636f72652d626c6f636b2e0a0a73656d616e7469632d656666656374203d207b0a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020696e707574547970653a20636f72652d747970652d7265662c2020202020202020202020202020203b20626f756e6465640a20206f7574707574547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020657865637574696f6e436c6173733a202270726f6f664f6e6c7922202f202272756e74696d65222c2020203b206f7274686f676f6e616c20746f207772697465436c6173730a20206566666563744b696e6448696e743a206566666563742d6b696e642c0a2020666f6f747072696e744f626c69676174696f6e3a20747374722c0a2020636f73744f626c69676174696f6e3a20747374722c0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c20203b206b6579656420627920636f6f7264696e6174653b20756e697175650a20206775617264537570706f72743a20626f6f6c2c0a7d0a0a6f62737472756374696f6e2d646566203d207b0a2020636f6f7264696e6174653a20747374722c0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164536368656d613a20636f72652d747970652d7265662c20202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d6c61777061636b2d616461707465722e6364646c202d2d2d0a3b2065646963742d6c61777061636b2d616461707465722e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f72206f6e6520646972656374206465636c61726174697665206c61777061636b2074617267657420616461707465722e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b2054686520656e636c6f73696e67206c61777061636b206d616e69666573742073656c6563747320746865206578616374207461726765742070726f66696c652c207461726765742049522c0a3b20616e642061646170746572207265736f75726365206469676573742e2054686f7365206964656e74697469657320617265206e6f7420726570656174656420686572652e0a0a6c61777061636b2d61646170746572203d207b0a202061706956657273696f6e3a202265646963742e6c61777061636b2d616461707465722f7631222c0a2020636c6173733a20226465636c61726174697665222c0a20206f7065726174696f6e50726f66696c65733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6f7065726174696f6e2d70726f66696c650a20207d2c0a2020656666656374496d706c656d656e746174696f6e733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6566666563740a20207d2c0a2020627564676574733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6275646765740a20207d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206c61777061636b206f7065726174696f6e2d70726f66696c6520636f6f7264696e617465732e0a6c61777061636b2d616461707465722d6f7065726174696f6e2d70726f66696c65203d0a20207b0a20202020636f72653a20747374722c0a2020202073656d616e746963456666656374733a205b2b20747374725d2c0a202020203f206275646765744f626c69676174696f6e3a20747374722c0a202020203f20746172676574436f6e66696775726174696f6e3a207265736f757263652d7265662c0a20207d202f0a20207b0a20202020636f72653a20747374722c0a2020202073656d616e746963456666656374733a205b5d2c0a202020206275646765744f626c69676174696f6e3a20747374722c0a20202020746172676574436f6e66696775726174696f6e3a207265736f757263652d7265662c0a20207d0a0a3b204b657973206172652063616e6f6e6963616c206c61777061636b2073656d616e7469632d65666665637420636f6f7264696e617465732e20466f6f747072696e742c20636f73742c20616e640a3b206661696c757265206669656c6473206d7573742065786163746c792064697363686172676520746865206d61746368696e67206578706f72746564206566666563742e0a6c61777061636b2d616461707465722d656666656374203d207b0a2020746172676574496e7472696e7369633a20747374722c0a2020746172676574436f6e66696775726174696f6e3a207265736f757263652d7265662c0a20207772697465436c6173733a206c61777061636b2d616461707465722d77726974652d636c6173732c0a2020666f6f747072696e744f626c69676174696f6e3a20747374722c0a2020636f73744f626c69676174696f6e3a20747374722c0a20206661696c7572654d617070696e67733a207b202a206661696c7572652d6964656e74203d3e2074737472207d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206578706f7274656420636f73742d6f626c69676174696f6e20636f6f7264696e617465732e0a6c61777061636b2d616461707465722d627564676574203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a6c61777061636b2d616461707465722d77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f0a20202020202020202020202020202020202020202020202020202020202022617070656e6422202f20227265706c61636522202f202264656c65746522202f2022637573746f6d220a0a3b206661696c7572652d6964656e7420697320646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c2e0a0a3b202d2d2d2065646963742d7461726765742d70726f66696c652e6364646c202d2d2d0a3b2065646963742d7461726765742d70726f66696c652e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374207461726765742070726f66696c65206d616e69666573742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76310a3b202873656520535045435f636f6e74696e75756d2d636f6e74726163742d62756e646c652d76312e6d64292e204a534f4e20696e207468652070726f736520737065637320697320610a3b207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d613b2074686973204344444c206973207468652073696e676c6520736f757263650a3b206f66207472757468202845444943542d4142492d4e4f4455502d303031292e0a0a7461726765742d70726f66696c652d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652f7631222c0a202069643a20747374722c202020202020202020202020202020202020202020202020203b20652e672e20226563686f2e64706f220a202076657273696f6e3a20747374722c20202020202020202020202020202020202020203b20652e672e202231220a20206163636570746564436f72654162693a205b2b20747374725d2c20202020202020203b20652e672e205b2265646963742e636f72652f7631225d0a0a2020696e7472696e736963733a207265736f757263652d7265662c0a2020696e7472696e7369634e616d6573706163653a20747374722c0a20203b207075626c697368657320746869732070726f66696c652773206f7065726174696f6e2d70726f66696c65207265636f72647320286f707469632074656d706c6174657320746861740a20203b206070726f66696c65602f60696d706c656d656e74736020636c6175736573207265736f6c766520616761696e7374292e205265666572656e63657320616e0a20203b206f7065726174696f6e2d70726f66696c65732d646f63756d656e74202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207265736f757263652d7265662c0a2020666f6f747072696e74416c67656272613a207265736f757263652d7265662c0a2020636f7374416c67656272613a207265736f757263652d7265662c0a202074617267657449723a207265736f757263652d7265662c0a20206f62737472756374696f6e5461786f6e6f6d793a207265736f757263652d7265662c0a202076657269666965723a207265736f757263652d7265662c0a20206c6f77657265723a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a0a20203b206669656c647320746865206c616e67756167652073706563207265717569726573206f662065766572792070726f66696c650a202062756e646c6550726f66696c653a207265736f757263652d7265662c0a202067656e657261746564417274696661637450726f66696c65733a205b2a207265736f757263652d7265665d2c0a202063616e6f6e6963616c456e636f64696e6752756c65733a207265736f757263652d7265662c0a20203b20412070726f66696c65207468617420616363657074732074686520646972656374206465636c61726174697665206c61777061636b2d6164617074657220414249206e616d65732069740a20203b2065786163746c79206f6e63652e2050726f66696c6573207468617420646f206e6f7420636f6e73756d65206c61777061636b206164617074657273206c6561766520746869730a20203b206f7074696f6e616c20736c6f7420616273656e74206f7220656d7074792e0a20203f2061636365707465644c61777061636b416461707465724162693a205b5d202f205b2265646963742e6c61777061636b2d616461707465722f7631225d2c0a2020646961676e6f737469634162693a207265736f757263652d7265662c0a0a20203b206170706c69636174696f6e20646f637472696e650a20206170706c69636174696f6e4d6f64656c3a202261746f6d6963222c0a202072656164436f6e73697374656e63793a20226170706c69636174696f6e2d736e617073686f7422202f20747374722c0a202067756172644576616c756174696f6e3a2022707265636f6d6d69742d61746f6d696322202f20747374722c0a20206f62737472756374696f6e526f6c6c6261636b3a20226e6f2d76697369626c652d6566666563747322202f20747374722c0a20206d756c74695461726765743a20626f6f6c2c0a20203b207768657468657220746865207461726765742063616e206576616c7561746520707265636f6d6d697420706f7374636f6e646974696f6e20286067756172616e746565602920636865636b730a20203b20696e73696465207468652061746f6d6963206170706c69636174696f6e20756e6974202845444943542d5441524745542d504f5354434f4e442d303031290a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a0a202064657465726d696e6973746963457865637574696f6e3a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d6120627920746865206275696c640a3b202845444943542d4142492d4e4f4455502d303031292e205468657920617265206e6f74207265646566696e656420686572652e0a0a3b202d2d2d20696e7472696e736963207369676e6174757265202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e696665737427732060696e7472696e7369637360207265736f757263652d726566206973207468650a3b20696e7472696e7369632d7369676e617475726520636f7270757320646f63756d656e742062656c6f772e20497473206c61796f757420697320666978656420736f2074776f0a3b20696e646570656e64656e742070726f66696c65732076616c69646174652f686173682074686520636f72707573206964656e746963616c6c790a3b202845444943542d4142492d494e5452494e534943532d444f432d303031292e0a0a3b20696e7472696e736963732069732061204d4150206b6579656420627920636f6f7264696e6174652c20736f2074686520736368656d6120697473656c6620656e666f726365730a3b20636f6f7264696e61746520756e697175656e6573732e20412070726f766964657220726563656976657320746865207265736f6c76656420636f7270757320617320610a3b206469676573742d626f756e642073656d616e74696320696e70757420616e64207265736f6c76657320636f6f7264696e617465732077697468696e20746861742061727469666163742e0a3b2045616368206d6170206b6579204d55535420657175616c20697473207265636f726427732060636f6f7264696e61746560206669656c640a3b202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e696e7472696e736963732f7631222c0a2020696e7472696e736963733a207b202a2074737472203d3e20696e7472696e736963207d2c0a7d0a0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e6966657374277320606f7065726174696f6e50726f66696c657360207265736f757263652d7265662e0a3b206f7065726174696f6e2d70726f66696c65202f206f707469632d74656d706c6174652061726520646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c2e204b657965642062790a3b20636f6f7264696e61746520736f207265736f6c7574696f6e2063616e2774207069636b206265747765656e2074776f2073616d652d636f6f7264696e6174652070726f66696c65730a3b202845444943542d4142492d4f5050524f46494c452d534c4f542d3030312c2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e6f7065726174696f6e2d70726f66696c65732f7631222c0a202070726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c0a7d0a0a3b2041207479706564207072652d6c6f776572696e67207175657374696f6e20746861742063616e2062652070726f706f73656420627920576174736f6e206f7220616e206167656e7420616e640a3b20636865636b65642062792074686520636f6d70696c65722e2049742069732063616e6f6e6963616c2d43424f5220656e636f64656420756e6465720a3b206065646963742e6c6f776572696e672d726571756972656d656e74732f7631603b2074686520636f6d70696c657220636865636b7320746869732061727469666163742c206e6f74207468650a3b2070726f736520746861742070726f64756365642069742e0a6c6f776572696e672d726571756972656d656e7473203d207b0a202061706956657273696f6e3a202265646963742e6c6f776572696e672d726571756972656d656e74732f7631222c0a20206f7065726174696f6e50726f66696c653a20747374722c0a202073656d616e746963456666656374733a205b2a2073656d616e7469632d6566666563742d726571756972656d656e745d2c0a202072657175697265645772697465436c61737365733a205b2a2077726974652d636c6173735d2c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a202061746f6d69636974793a2061746f6d69636974792d726571756972656d656e742c0a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a20206f70746963436f6e74726163743a20747374722c0a7d0a0a73656d616e7469632d6566666563742d726571756972656d656e74203d207b0a2020636f6f7264696e6174653a20747374722c0a20207772697465436c6173733a2077726974652d636c6173732c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a7d0a0a77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a2020202020202020202020202020227265706c61636522202f202264656c65746522202f20747374720a67756172642d6b696e64203d2022707265636f6d6d69742d61746f6d696322202f20747374720a61746f6d69636974792d726571756972656d656e74203d202261746f6d696322202f20747374720a0a3b20412067656e75696e6520756e696f6e3a207075726520636f6e7374727563746f7273206361727279206e6f20656666656374206b696e64206f72206661696c757265733b206566666563740a3b20696e7472696e73696373206d757374202845444943542d5441524745542d494e5452494e5349432d434c4153532d303031292e2054686520736368656d6120656e666f7263657320746869732c0a3b206e6f74206120636f6d6d656e742e0a0a3b2054686520696e7472696e736963277320636f6f7264696e6174652069732074686520696e7472696e73696373206d6170204b45592c206e6f7420612076616c7565206669656c642c20736f207468650a3b206b657920616e6420636f6f7264696e6174652063616e206e65766572206469736167726565202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963203d20707572652d696e7472696e736963202f206566666563742d696e7472696e7369630a0a707572652d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a202270757265222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206775617264537570706f72743a2066616c73652c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20226e6f6e65222c0a7d0a0a6566666563742d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a2022656666656374222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206566666563744b696e643a206566666563742d6b696e642c0a20203b206d6170206b65796564206279206661696c75726520636f6f7264696e61746520286661696c7572652d6964656e74293b20746865206661696c75726520636f6f7264696e6174652069730a20203b20746865206b65792c206e6f7420612076616c7565206669656c642c20736f20756e697175656e657373206973207374727563747572616c0a20203b202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c0a20206775617264537570706f72743a20626f6f6c2c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f20227265706c61636522202f0a20202020202020202020202020202264656c65746522202f2022637573746f6d222c0a202063616e5061727469636970617465496e41746f6d696347756172643a20626f6f6c2c0a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d617574686f726974792d66616374732e6364646c202d2d2d0a3b2065646963742d617574686f726974792d66616374732e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f722074686520666972737420636f6d70696c65722d636f6e7465787420617574686f726974792d666163747320646f63756d656e742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20736f20736f757263652e6469676573742075736573207468650a3b20736861726564207368613235362d6469676573742074797065642076616c75652e204a534f4e2069732061207265766965772f696e7075742072656e646572696e673a206974730a3b20607368613235363a3c3634206865783e6020736f75726365206469676573742069732070726f6a656374656420746f205b60736861323536602c203332207261772062797465735d206f6e0a3b2074686520776972652c20616e64206974732066616374206172726179732070726f6a65637420746f2074686520636f6f7264696e6174652d6b65796564206d6170732062656c6f772e0a0a617574686f726974792d6661637473203d207b0a202061706956657273696f6e3a202265646963742e617574686f726974792d66616374732f7631222c0a2020736f757263653a20617574686f726974792d666163742d736f757263652c0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e20617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374207d2c0a20206566666563745772697465436c61737365733a207b202a2074737472203d3e20617574686f726974792d77726974652d636c617373207d2c0a2020627564676574733a207b202a2074737472203d3e20617574686f726974792d6275646765742d66616374207d2c0a7d0a0a617574686f726974792d666163742d736f75726365203d207b0a20206b696e643a20226c61777061636b22202f202274617267657450726f66696c65222c0a2020636f6f7264696e6174653a20747374722c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a3b20546865206d6170206b65792069732074686520736f75726365206f7065726174696f6e2d70726f66696c6520636f6f7264696e6174652e204974206973206e6f7420726570656174656420696e0a3b207468652076616c75652c20736f2061206b657920616e6420656d62656464656420636f6f7264696e6174652063616e6e6f742064697361677265652e20416c6c6f7765642077726974650a3b20636c61737365732061726520612063616e6f6e6963616c206d61702d7365743a2074686520636c6173732069732074686520756e69717565206b657920616e64206e756c6c206973207468650a3b20756e6974206d61726b65722e2043616e6f6e6963616c2043424f52206669786573206b6579206f7264657220776974686f75742061207365636f6e64206f72646572696e672072756c652e0a617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374203d207b0a2020636f72653a20747374722c0a2020616c6c6f7765645772697465436c61737365733a207b202a20617574686f726974792d77726974652d636c617373203d3e206e756c6c207d2c0a7d0a0a3b20546865206566666563745772697465436c6173736573206d6170206b6579206973207468652073656d616e7469632065666665637420636f6f7264696e6174652e2054686520627564676574730a3b206d6170206b65792069732074686520736f757263652062756467657420636f6f7264696e6174652e2043616e6f6e6963616c2043424f52206d61702d6b657920756e697175656e6573730a3b206d616b6573206475706c6963617465206661637420636f6f7264696e61746573207374727563747572616c6c7920756e726570726573656e7461626c652e0a617574686f726974792d6275646765742d66616374203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a3b20417574686f726974794661637473446f63756d656e7420763120696e74656e74696f6e616c6c792061636365707473206f6e6c792074686520777269746520636c6173736573207468650a3b2063757272656e7420636f6d70696c6572206d6f64656c2063616e20636f6e73756d652e2060637573746f6d602069732074686520736f6c6520763120637573746f6d207370656c6c696e673b0a3b20617262697472617279207461726765742d70726f66696c6520657874656e73696f6e20737472696e677320646f206e6f7420656e746572207468697320636f6d70696c657220706174682e0a617574686f726974792d77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a202020202020202020202020202020202020202020202020227265706c61636522202f202264656c65746522202f2022637573746f6d220a0a3b202d2d2d2065646963742d726573756c742d70726f6a656374696f6e2e6364646c202d2d2d0a3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d726573756c742d70726f6a656374696f6e2e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220636f6d70696c65722d6f776e6564206170706c69636174696f6e2d726573756c742070726f6a656374696f6e732e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a0a726573756c742d70726f6a656374696f6e203d207b0a2020736368656d613a202265646963742e726573756c742d70726f6a656374696f6e2f7631222c0a20206f7065726174696f6e436f6f7264696e6174653a20626f756e6465642d70726f6a656374696f6e2d746578742c0a20206f7574707574547970653a20626f756e6465642d70726f6a656374696f6e2d746578742c0a20206d61784f757470757442797465733a2075696e74202e677420302c0a202065787072657373696f6e3a20726573756c742d70726f6a656374696f6e2d657870722c0a7d0a0a726573756c742d70726f6a656374696f6e2d65787072203d20726573756c742d70726f6a656374696f6e2d7265636f7264202f20726573756c742d70726f6a656374696f6e2d736f757263650a0a726573756c742d70726f6a656374696f6e2d7265636f7264203d207b0a20206b696e643a20227265636f7264222c0a20203b2054686520726f6f74207265636f726420636f756e7473206173206f6e65206f66207468652052757374206465636f6465722773203235362065787072657373696f6e206e6f6465732e0a20203b204e657374656420616767726567617465206e6f646520636f756e742072656d61696e7320616e20617574686f7269746174697665206465636f64657220636865636b2e0a20206669656c64733a207b20302a32353520626f756e6465642d70726f6a656374696f6e2d74657874203d3e20726573756c742d70726f6a656374696f6e2d65787072207d2c0a7d0a0a726573756c742d70726f6a656374696f6e2d736f75726365203d207b0a20206b696e643a2022736f75726365222c0a2020736f757263653a20726573756c742d70726f6a656374696f6e2d736f757263652d6b696e642c0a20203b204d617463686573204d41585f524553554c545f50524f4a454354494f4e5f504154485f5345474d454e545320696e2065646963742d73796e7461782e0a2020706174683a205b302a333220626f756e6465642d70726f6a656374696f6e2d746578745d2c0a7d0a0a726573756c742d70726f6a656374696f6e2d736f757263652d6b696e64203d0a20207b206b696e643a20226170706c69636174696f6e496e70757422207d202f0a20207b0a202020206b696e643a20226361706162696c697479526573756c74222c0a202020207374657049643a20626f756e6465642d70726f6a656374696f6e2d746578742c0a20207d0a0a626f756e6465642d70726f6a656374696f6e2d74657874203d2074737472202e73697a652028312e2e31303234290a0a3b202d2d2d2065646963742d7461726765742d69722e6364646c202d2d2d0a3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d7461726765742d69722e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f72207468652045646963742d6f776e65642054617267657420495220617274696661637420656e76656c6f70652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20616e642065646963742d636f72652e6364646c2e2049740a3b2064656c696265726174656c792072657573657320436f72652065787072657373696f6e732c20707265646963617465732c20627564676574732c206c6f63616c207265666572656e6365732c0a3b206f62737472756374696f6e20726561736f6e732c20616e64206f62737472756374696f6e2061726d7320736f2074686520736368656d61206d617463686573207468652076616c75650a3b20656d6974746564206279207468652063616e6f6e6963616c2054617267657420495220656e636f64657220726174686572207468616e20726573746174696e672074686f73652074797065732e0a3b2049742064657363726962657320746865207374727563747572616c207368617065206f662076616c6964206c6f776572696e672d70726f6475636564206172746966616374732e205468650a3b206c6f776572696e6720616e6420656e636f64657220636f6e7472616374732073657061726174656c7920656e666f7263652073656d616e746963206964656e7469666965722072756c65730a3b20616e642063616e6f6e6963616c206f72646572696e672f64656475706c69636174696f6e20666f72207365742d6c696b652076616c7565732e0a0a3b2054617267657420495220656e636f64696e672072656a6563747320616e20656d707479207461726765742d70726f66696c6520636f6f7264696e617465206265666f72652062797465730a3b2065786973742c20736f207468697320726f6f74207469676874656e732074686520736861726564207374727563747572616c207265736f757263652d726566206163636f7264696e676c792e0a7461726765742d69722d7265736f757263652d726566203d207b0a202069643a2074737472202e7265676578702022283f73292e2b222c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a7461726765742d69722d6172746966616374203d207461726765742d69722d636c6f7365642d6172746966616374202f207461726765742d69722d6c65676163792d61727469666163740a0a7461726765742d69722d61727469666163742d636f6d6d6f6e203d20280a20206b696e643a202274617267657449724172746966616374222c0a2020646f6d61696e3a20747374722c0a202074617267657450726f66696c653a207461726765742d69722d7265736f757263652d7265662c0a2020736f75726365436f7265436f6f7264696e6174653a2074737472202e7265676578702022283f73292e2b222c0a290a0a7461726765742d69722d636c6f7365642d6172746966616374203d207b0a20207461726765742d69722d61727469666163742d636f6d6d6f6e2c0a202073656d616e746963436c6f737572653a207461726765742d69722d73656d616e7469632d636c6f737572652c0a2020696e74656e74733a207b202a2074737472203d3e207461726765742d69722d696e74656e74207d2c0a7d0a0a7461726765742d69722d6c65676163792d6172746966616374203d207b0a20207461726765742d69722d61727469666163742d636f6d6d6f6e2c0a2020696e74656e74733a207b202a2074737472203d3e207461726765742d69722d6c65676163792d696e74656e74207d2c0a7d0a0a7461726765742d69722d73656d616e7469632d636c6f73757265203d207b0a2020736f75726365436f72653a207461726765742d69722d7265736f757263652d7265662c0a20206c61777061636b733a205b2a207461726765742d69722d7265736f757263652d7265665d2c0a20203f206361706162696c69746965733a205b2a207461726765742d69722d7265736f757263652d7265665d2c0a7d0a0a7461726765742d69722d696e74656e74203d207b0a20207461726765742d69722d696e74656e742d636f6d6d6f6e2c0a20203f2062617369733a20636f72652d657870722c0a20203f2065787465726e616c416374696f6e52657175657374733a205b2a207461726765742d69722d65787465726e616c2d616374696f6e2d726571756573745d2c0a7d0a0a7461726765742d69722d6c65676163792d696e74656e74203d207b0a20207461726765742d69722d696e74656e742d636f6d6d6f6e2c0a7d0a0a7461726765742d69722d696e74656e742d636f6d6d6f6e203d20280a20206f7065726174696f6e50726f66696c653a20747374722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020726571756972656d656e74733a205b2a207461726765742d69722d726571756972656d656e745d2c0a202073746570733a205b2a207461726765742d69722d737465705d2c0a2020726573756c743a20636f72652d657870722c0a290a0a7461726765742d69722d726571756972656d656e74203d207b0a202069643a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a0a7461726765742d69722d73746570203d207b0a202069643a20747374722c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020746172676574496e7472696e7369633a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4661696c757265733a205b2a206661696c7572652d6964656e745d2c0a20206f62737472756374696f6e41726d733a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a0a7461726765742d69722d65787465726e616c2d616374696f6e2d72657175657374203d207b0a202069643a20747374722c0a202062696e64696e673a206c6f63616c2d7265662c0a20206f7065726174696f6e3a207461726765742d69722d7265736f757263652d7265662c0a2020696e707574547970653a20636f72652d747970652d7265662c0a2020736574746c656d656e74547970653a20636f72652d747970652d7265662c0a2020696e707574536368656d613a207461726765742d69722d7265736f757263652d7265662c0a2020736574746c656d656e74536368656d613a207461726765742d69722d7265736f757263652d7265662c0a2020696e7075743a20636f72652d657870722c0a2020617574686f7269747953636f70653a20636f72652d657870722c0a202062617369733a20636f72652d657870722c0a20206275646765743a2065787465726e616c2d616374696f6e2d6275646765742c0a20207265636f6e63696c696174696f6e4c61773a207461726765742d69722d7265736f757263652d7265662c0a202073746174653a20226177616974696e67536574746c656d656e74222c0a2020736574746c656d656e7441646d697373696f6e3a2022736368656d615265717569726564222c0a7d0a", + "rawSha256": "5f3444227430f499292a3bf2983e1b77c3ab773ee23f51f2c8b6a7f2ca5432f6" }, "contracts": [ {"contract": "authority-facts", "rootRule": "authority-facts"}, diff --git a/fixtures/providers/echo-target-profile/README.md b/fixtures/providers/echo-target-profile/README.md new file mode 100644 index 0000000..3e844c1 --- /dev/null +++ b/fixtures/providers/echo-target-profile/README.md @@ -0,0 +1,21 @@ +# Echo Target Profile Fixture + +`generated/primary/target-profile.echo-dpo.cbor` is the exact Echo-owned target +profile consumed by the Edict public application-build integration test. + +Provenance: + +- repository: `flyingrobots/echo`; +- source commit: `5413f55316e5baf2d3af93fd64bb71dc7f84e27d`; +- source path: + `crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/target-profile.echo-dpo.cbor`; +- Echo generator identity: + `echo-wesley-gen.provider-artifact-generator@1`; +- Edict domain-framed identity: + `sha256:2e2494121aecf5e6a2d920f5fb85408825d394765fad41484c416397c920fb04`; +- raw file SHA-256: + `1b105d1b1f6cdf5fecdef98b7adeb238525047d43581fe9fd8c44fd213e1788e`. + +The fixture is metadata authority only. Edict does not interpret Echo runtime +semantics and does not invoke a provider component on the external-action build +route. From d316113342740ae55e4917710c8b5c57c5ca0965 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 29 Jul 2026 17:29:43 -0700 Subject: [PATCH 06/16] docs: record external request artifact publication --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index df8c129..f583323 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,16 @@ versions still track specification maturity rather than a released product. ### Added +- Added explicit `externalAction` application builds that validate one exact + request-only source, lawpack/adapter/configuration closure, and provider-owned + target profile before publishing canonical `core.cbor` and `target-ir.cbor`. + The route requires a typed request, rejects callable Target IR steps and + substituted capability manifests, invokes no provider component, replaces + the output pair transactionally, and clears stale executable-operation + outputs. Request-only lawpack profiles now bind their own exact budget and + opaque target configuration while carrying no semantic effect or target + intrinsic. A generator-owned workspace-snapshot closure and mirrored + Echo-owned target profile make the full public build reproducible in Edict. - Added typed external-action request values without adding external execution authority to Edict. Digest-locked capability imports and `request` statements preserve exact operation, schema, scope, basis, budget, input, reconciliation, From f4d5d31a8d54f1b4c0318903c847b5e77c6a0e98 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 29 Jul 2026 17:33:15 -0700 Subject: [PATCH 07/16] docs: repair external request evidence links --- docs/topics/cli/test-plan.md | 4 ++-- docs/topics/external-action-requests/test-plan.md | 2 +- docs/topics/lawpacks/test-plan.md | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/topics/cli/test-plan.md b/docs/topics/cli/test-plan.md index d294ec2..3080d85 100644 --- a/docs/topics/cli/test-plan.md +++ b/docs/topics/cli/test-plan.md @@ -42,7 +42,7 @@ Out of scope: | CLI-REQ-012 | implemented | The checked-in CLI golden corpus can be regenerated by `cargo xtask cli-goldens --write` and checked by `cargo xtask cli-goldens --check`; `cargo xtask verify` runs the check mode. | xtask/src/goldens.rs, xtask/src/main.rs | | CLI-REQ-013 | implemented | The `project` operation accepts dirty editor source from `source` input records and emits structured syntax, diagnostics, Core, Target IR, digest, and status records without requiring a source file on disk. | crates/edict-cli/tests/jsonl_cli.rs | | CLI-REQ-014 | implemented | Compiler-level projection failures are emitted as structured projection records and diagnostics, not process-level CLI failures. | crates/edict-cli/tests/jsonl_cli.rs | -| CLI-REQ-015 | implemented | The executable-operation `build` route loads one `edict.application/v1` request, validates the exact Edict source and complete lawpack/adapter closure, invokes the selected target provider lowerer and independent verifier through the bounded provider host, and writes only the accepted canonical package and verification-report bytes. | crates/edict-cli/src/application_build.rs, external Hello Echo build witness | +| CLI-REQ-015 | implemented | The executable-operation `build` route loads one `edict.application/v1` request, validates the exact Edict source and complete lawpack/adapter closure, invokes the selected target provider lowerer and independent verifier through the bounded provider host, and writes only the accepted canonical package and verification-report bytes. | crates/edict-cli/src/application_build.rs | | CLI-REQ-016 | implemented | The explicit external-action `build` route validates a request-only source and complete capability/adapter/target-profile closure, invokes no provider component, and atomically publishes the owning encoders' exact Core and Target IR bytes while clearing stale executable outputs. | issue #176, crates/edict-cli/src/application_build.rs | ## Fixtures @@ -103,7 +103,7 @@ Out of scope: | CLI-TP-027 | implemented | Build request dispatch | CLI-REQ-015 | A `build` settings record is accepted without compiler input records and reaches application-config loading; a missing application config fails as a structured `ApplicationConfigReadFailed` build diagnostic rather than as invalid settings or missing compiler input. | build_accepts_application_request_without_compiler_input_records | crates/edict-cli/tests/jsonl_cli.rs | Covers the public request shape and dispatch boundary only. | | CLI-TP-028 | planned | Verified application build | CLI-REQ-015 | A repository-owned provider-component fixture builds one exact source and complete lawpack closure, independently accepts the package, and publishes the canonical package/report pair. | - | - | The standalone external Hello Echo build is a green integration witness; this planned case makes the full crossing reproducible inside Edict's own automated suite. | | CLI-TP-029 | implemented | External request projection | CLI-REQ-013 | A `project` request carrying exact external-action source emits Core and Target IR review data with the capability resource, complete request payload, explicit awaiting-settlement posture, capability semantic closure, and zero callable target steps. | project_exposes_external_requests_as_non_callable_review_data | crates/edict-cli/tests/jsonl_cli.rs | Review JSON exposes compiler data only; the CLI performs no external action. | -| CLI-TP-030 | implemented | External-action build | CLI-REQ-016 | An explicit request-only application manifest loads exact source, lawpack, adapter, configuration, and Echo target-profile artifacts; emits exact checked Core and Target IR bytes; removes stale executable outputs; and reruns deterministically. | public_external_action_build_emits_exact_compiler_artifacts | crates/edict-cli/src/application_build.rs, fixtures/lawpack/workspace-snapshot/, fixtures/providers/echo-target-profile/ | The provider component host is not invoked. | +| CLI-TP-030 | implemented | External-action build | CLI-REQ-016 | An explicit request-only application manifest loads exact source, lawpack, adapter, configuration, and Echo target-profile artifacts; emits exact checked Core and Target IR bytes; removes stale executable outputs; and reruns deterministically. | public_external_action_build_emits_exact_compiler_artifacts | crates/edict-cli/src/application_build.rs, fixtures/lawpack/workspace-snapshot/README.md, fixtures/providers/echo-target-profile/README.md | The provider component host is not invoked. | | CLI-TP-031 | implemented | External-action refusal | CLI-REQ-016 | Missing requests, callable-step mixtures, substituted capability manifests, and failed pair replacement refuse before a passing publication witness. | external_action_build_requires_a_typed_request, external_action_build_rejects_mixed_callable_execution, public_external_action_build_rejects_capability_substitution, failed_external_action_pair_publication_preserves_previous_core | crates/edict-cli/src/application_build.rs | Stable failure kinds distinguish closure, execution-class, and output failures. | ## Determinism Obligations diff --git a/docs/topics/external-action-requests/test-plan.md b/docs/topics/external-action-requests/test-plan.md index 90cdcab..500dc34 100644 --- a/docs/topics/external-action-requests/test-plan.md +++ b/docs/topics/external-action-requests/test-plan.md @@ -65,7 +65,7 @@ Out of scope: | EXTREQ-TP-012 | implemented | Canonical identity guard | EXTREQ-REQ-004 | Canonical Core encoding rejects empty request schema or reconciliation coordinates, and canonical Target IR encoding rejects duplicate request ids within one intent. | request_resource_coordinates_must_be_nonempty, duplicate_target_request_ids_reject_before_identity | crates/edict-syntax/tests/external_action_requests.rs | Waiting and settlement identity cannot be ambiguous or anonymous. | | EXTREQ-TP-013 | implemented | Tooling guard | EXTREQ-REQ-001 | A non-call request operation has its own stable parser kind, and `request` is highlighted as a keyword. | non_call_request_operation_has_a_request_specific_parse_kind, request_statement_introducer_is_highlighted_as_a_keyword | crates/edict-syntax/tests/external_action_requests.rs, crates/edict-syntax/tests/highlighting.rs | Request syntax remains distinct from semantic effect syntax. | | EXTREQ-TP-014 | implemented | Golden artifact | EXTREQ-REQ-002, EXTREQ-REQ-003, EXTREQ-REQ-004, EXTREQ-REQ-005 | The checked workspace-snapshot source reproduces exact compiler-owned Core and Target IR canonical bytes and domain-framed digests. | core_goldens_match_executable_encoder, target_ir_goldens_match_executable_encoder | fixtures/lang/external-actions/workspace-snapshot.edict, fixtures/core/canonical/workspace-snapshot.core.cbor, fixtures/core/canonical/workspace-snapshot.core.sha256, fixtures/target-ir/canonical/workspace-snapshot.target-ir.cbor, fixtures/target-ir/canonical/workspace-snapshot.target-ir.sha256 | Generated only through the owning xtask commands. | -| EXTREQ-TP-015 | implemented | Public build | EXTREQ-REQ-009 | A real `edict.application/v1` request loads the generated workspace closure and exact Echo target profile, publishes checked canonical Core and Target IR bytes, removes stale executable outputs, and reruns byte-identically. | public_external_action_build_emits_exact_compiler_artifacts | crates/edict-cli/src/application_build.rs, fixtures/lawpack/workspace-snapshot/, fixtures/providers/echo-target-profile/ | Provider components are outside the request-only route. | +| EXTREQ-TP-015 | implemented | Public build | EXTREQ-REQ-009 | A real `edict.application/v1` request loads the generated workspace closure and exact Echo target profile, publishes checked canonical Core and Target IR bytes, removes stale executable outputs, and reruns byte-identically. | public_external_action_build_emits_exact_compiler_artifacts | crates/edict-cli/src/application_build.rs, fixtures/lawpack/workspace-snapshot/README.md, fixtures/providers/echo-target-profile/README.md | Provider components are outside the request-only route. | | EXTREQ-TP-016 | implemented | Closure refusal | EXTREQ-REQ-004, EXTREQ-REQ-009 | A request operation whose digest no longer equals its owning supplied capability manifest is rejected before output publication. | external_action_build_rejects_a_substituted_capability_manifest, public_external_action_build_rejects_capability_substitution | crates/edict-cli/src/application_build.rs | Internal Core closure remains necessary but is not sufficient for public application authority. | | EXTREQ-TP-017 | implemented | Execution-class refusal | EXTREQ-REQ-003, EXTREQ-REQ-009 | The request-only build rejects zero requests and any artifact mixing external requests with callable Target IR steps. | external_action_build_requires_a_typed_request, external_action_build_rejects_mixed_callable_execution | crates/edict-cli/src/application_build.rs | The first host route has one execution class. | | EXTREQ-TP-018 | implemented | Publication transaction | EXTREQ-REQ-009 | Paired request artifacts are deterministic under fixed-seed and stress corpora; stale executable outputs are removed; publication failure preserves the prior request pair. | external_action_pair_publication_is_deterministic_for_a_fixed_seed_corpus, external_action_pair_publication_remains_bounded_under_stress, external_action_publication_removes_stale_executable_outputs, failed_external_action_pair_publication_preserves_previous_core | crates/edict-cli/src/application_build.rs | Output ownership is symmetric across build kinds. | diff --git a/docs/topics/lawpacks/test-plan.md b/docs/topics/lawpacks/test-plan.md index 9569791..2a9b769 100644 --- a/docs/topics/lawpacks/test-plan.md +++ b/docs/topics/lawpacks/test-plan.md @@ -47,7 +47,7 @@ Out of scope: | LAWPACKS-REQ-005 | implemented | Edict loads canonical `edict.lawpack/v1` manifests and export surfaces into typed values, rejects every value outside the closed CDDL shape with stable failure kinds, corroborates the export digest, and validates a complete supplied dependency set as digest-locked and acyclic before exposing any exports to compilation. | issue #169, crates/edict-syntax/src/lawpack.rs, docs/abi/edict-lawpack.cddl, docs/abi/edict-common.cddl, docs/abi/edict-core.cddl | | LAWPACKS-REQ-006 | implemented | Authority-facts loading accepts digest-locked `lawpack` source identity for first compiler budget and effect write-class facts without claiming full manifest validation. | docs/topics/authority-facts/test-plan.md | | LAWPACKS-REQ-007 | implemented | Provider manifests model lawpacks as generated provider artifacts with digest-locked semantic source and generator provenance; Edict validates the reference/provenance envelope without owning runtime lawpack semantics. | issue #139, docs/topics/providers/test-plan.md | -| LAWPACKS-REQ-008 | implemented | Edict validates one exact direct declarative `edict.lawpack-adapter/v1` resource selected by a loaded lawpack manifest. Callable profiles require complete effect/budget coverage and one typed target-configuration reference per runtime effect. Request-only profiles carry no semantic effects and must bind their own exact budget obligation and target configuration. Edict preserves but does not interpret target-owned configuration semantics. | issues #169 and #176, docs/abi/edict-lawpack-adapter.cddl | +| LAWPACKS-REQ-008 | implemented | Edict validates one exact direct declarative `edict.lawpack-adapter/v1` resource selected by a loaded lawpack manifest. Callable profiles require complete effect/budget coverage and one typed target-configuration reference per runtime effect. Request-only profiles carry no semantic effects and must bind their own exact budget obligation and target configuration. Edict preserves but does not interpret target-owned configuration semantics. | issue #169, issue #176, docs/abi/edict-lawpack-adapter.cddl | | LAWPACKS-REQ-009 | implemented | The standalone Hello Echo fixture pins exact canonical Core and Target IR bytes produced from the digest-locked source/lawpack/adapter closure and computes each identity with the artifact's native domain. | issue #169, fixtures/lawpack/hello-echo/README.md, xtask/src/lawpack_goldens.rs | | LAWPACKS-REQ-010 | implemented | The portable `causal.cell@1.createIfAbsent` capability closure is generated through the executable lawpack, adapter, compiler, and Target IR path, with exact canonical manifest, export, adapter, and target-configuration bytes and digests for external application builds. | fixtures/lawpack/causal-cell/README.md, xtask/src/lawpack_goldens.rs | | LAWPACKS-REQ-011 | implemented | A request-only lawpack profile supplies an exact compiler budget and opaque target configuration without declaring a callable semantic effect or target intrinsic; the workspace-snapshot closure reproduces one request and zero Target IR steps. | issue #176, fixtures/lawpack/workspace-snapshot/README.md | @@ -76,7 +76,7 @@ Out of scope: | LAWPACKS-TP-008 | implemented | Direct adapter | LAWPACKS-REQ-008 | The exact Hello Echo adapter selected by the manifest derives all compiler and Echo Target IR facts and exposes the exact target-configuration resource identity, while missing, substituted, non-canonical, incomplete, target-mismatched, import-mismatched, malformed-configuration, undeclared-write-class, or obligation-mismatched adapters fail closed before trusted compiler facts exist. | hello_echo_source_compiles_to_echo_target_ir_from_exact_lawpack_adapter, lawpack_adapter_bytes_must_be_canonical_and_digest_bound, lawpack_adapter_requires_a_typed_target_configuration_reference, lawpack_adapter_rejects_an_undeclared_write_class_at_the_effect_path, lawpack_adapter_selection_requires_one_exact_target_profile, lawpack_adapter_requires_complete_exported_effect_coverage, lawpack_adapter_corroborates_footprint_cost_and_failure_obligations, lawpack_compilation_requires_the_exact_digest_locked_source_import | fixtures/lawpack/hello-echo/README.md, crates/edict-syntax/tests/lawpack.rs | The positive test constructs no `CompilerContext` or `TargetIrLoweringFacts`; Echo-specific configuration interpretation remains outside Edict. | | LAWPACKS-TP-009 | implemented | Compiler artifacts | LAWPACKS-REQ-009 | Compiling and lowering the exact Hello Echo closure reproduces the reviewed Core and Target IR bytes and their native domain-framed identities. | hello_echo_source_compiles_to_echo_target_ir_from_exact_lawpack_adapter | fixtures/lawpack/hello-echo/create-greeting.core.cbor, fixtures/lawpack/hello-echo/create-greeting.target-ir.cbor, crates/edict-syntax/tests/lawpack.rs, xtask/src/lawpack_goldens.rs | The fixtures are outputs of the real compiler pipeline, not handwritten substitutes; `cargo xtask lawpack-goldens --check` reproduces them. | | LAWPACKS-TP-010 | implemented | Portable capability | LAWPACKS-REQ-010 | Generating the causal-cell closure validates its canonical lawpack and direct adapter, then compiles and lowers an Edict source witness that imports the exact generated manifest digest. | lawpack_goldens_match_executable_codec | fixtures/lawpack/causal-cell/README.md, xtask/src/lawpack_goldens.rs, xtask/src/tests.rs | The generator fails if the portable capability no longer reaches a compiler-produced Target IR artifact. | -| LAWPACKS-TP-011 | implemented | Request-only profile | LAWPACKS-REQ-008, LAWPACKS-REQ-011 | A profile with no semantic effects is accepted only when it binds an exact budget obligation and target configuration; it compiles one request without conferring target-call authority. | request_only_profile_supplies_budget_without_callable_effect_authority, request_only_profile_requires_an_exact_budget_obligation, request_only_profile_requires_an_exact_target_configuration | crates/edict-syntax/tests/lawpack.rs, fixtures/lawpack/workspace-snapshot/ | Empty semantic effects are not an unbounded profile escape hatch. | +| LAWPACKS-TP-011 | implemented | Request-only profile | LAWPACKS-REQ-008, LAWPACKS-REQ-011 | A profile with no semantic effects is accepted only when it binds an exact budget obligation and target configuration; it compiles one request without conferring target-call authority. | request_only_profile_supplies_budget_without_callable_effect_authority, request_only_profile_requires_an_exact_budget_obligation, request_only_profile_requires_an_exact_target_configuration | crates/edict-syntax/tests/lawpack.rs, fixtures/lawpack/workspace-snapshot/README.md | Empty semantic effects are not an unbounded profile escape hatch. | ## Determinism Obligations From 4bb1cbc5d0071c473cae5ccd56930488c6306994 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 29 Jul 2026 17:38:09 -0700 Subject: [PATCH 08/16] fix: clarify external request build evidence --- crates/edict-cli/src/application_build.rs | 8 ++++---- fixtures/lawpack/workspace-snapshot/README.md | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/crates/edict-cli/src/application_build.rs b/crates/edict-cli/src/application_build.rs index 9bb94ef..3893b27 100644 --- a/crates/edict-cli/src/application_build.rs +++ b/crates/edict-cli/src/application_build.rs @@ -129,7 +129,7 @@ pub(crate) fn build_application(config_path: &Path) -> Result<(), ApplicationBui let source = config.sources.first().ok_or_else(|| { failure( "InvalidApplicationConfig", - "the executable-operation build requires exactly one Edict source", + "the application build requires exactly one Edict source", ) })?; let source_path = confined_existing_path( @@ -176,7 +176,7 @@ pub(crate) fn build_application(config_path: &Path) -> Result<(), ApplicationBui let loaded = loaded_lawpacks.first().ok_or_else(|| { failure( "InvalidApplicationConfig", - "the executable-operation build requires a root lawpack", + "the application build requires a root lawpack", ) })?; @@ -509,13 +509,13 @@ fn validate_application_manifest( if config.sources.len() != 1 { return Err(failure( "InvalidApplicationConfig", - "the executable-operation build currently requires exactly one Edict source", + "the application build currently requires exactly one Edict source", )); } if config.lawpacks.is_empty() { return Err(failure( "InvalidApplicationConfig", - "the executable-operation build requires one root lawpack followed by its complete dependency closure", + "the application build requires one root lawpack followed by its complete dependency closure", )); } let paths = config diff --git a/fixtures/lawpack/workspace-snapshot/README.md b/fixtures/lawpack/workspace-snapshot/README.md index d6d4df5..6381195 100644 --- a/fixtures/lawpack/workspace-snapshot/README.md +++ b/fixtures/lawpack/workspace-snapshot/README.md @@ -18,6 +18,13 @@ digest. The generated Core and Target IR therefore preserve the complete capability closure while Target IR contains one external-action request and zero callable steps. +This real-lawpack corpus is distinct from +`fixtures/lang/external-actions/workspace-snapshot.edict`. That earlier +compiler fixture uses synthetic context facts and a placeholder capability +digest to isolate request encoding. The files here use the generated manifest, +profile, and budget closure, so their canonical Core and Target IR identities +are intentionally different. + Artifacts are generated only through: ```sh From 6eef31b46ffc7fdd79e610e95f5bd3b9d5824c84 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 29 Jul 2026 17:58:38 -0700 Subject: [PATCH 09/16] test: reject mixed request authority closure --- crates/edict-cli/src/application_build.rs | 76 +++++++++++++++ crates/edict-syntax/tests/lawpack.rs | 112 +++++++++++++++++++++- 2 files changed, 185 insertions(+), 3 deletions(-) diff --git a/crates/edict-cli/src/application_build.rs b/crates/edict-cli/src/application_build.rs index 3893b27..cb2d76d 100644 --- a/crates/edict-cli/src/application_build.rs +++ b/crates/edict-cli/src/application_build.rs @@ -2102,6 +2102,82 @@ mod tests { test_ok(fs::remove_dir_all(root), "remove substituted build tree"); } + #[test] + fn public_external_action_build_rejects_a_disconnected_lawpack() { + let root = temp_tree("public-external-action-disconnected-lawpack"); + let config_path = write_external_action_application(&root); + let lawpack_directory = root.join("vendor/hello-echo"); + test_ok( + fs::create_dir_all(&lawpack_directory), + "create disconnected lawpack directory", + ); + for (name, bytes) in [ + ( + "manifest.cbor", + include_bytes!("../../../fixtures/lawpack/hello-echo/manifest.cbor").as_slice(), + ), + ( + "exports.cbor", + include_bytes!("../../../fixtures/lawpack/hello-echo/exports.cbor").as_slice(), + ), + ( + "adapter.cbor", + include_bytes!("../../../fixtures/lawpack/hello-echo/adapter.cbor").as_slice(), + ), + ( + "target-configuration.cbor", + include_bytes!( + "../../../fixtures/lawpack/hello-echo/echo-operation-configuration.cbor" + ) + .as_slice(), + ), + ] { + test_ok( + fs::write(lawpack_directory.join(name), bytes), + "write disconnected lawpack fixture", + ); + } + let mut application = test_ok( + serde_json::from_slice::(&test_ok( + fs::read(&config_path), + "read application manifest", + )), + "decode application manifest", + ); + let Some(lawpacks) = application + .get_mut("lawpacks") + .and_then(serde_json::Value::as_array_mut) + else { + panic!("application manifest lawpacks"); + }; + lawpacks.push(serde_json::json!({ + "manifest": "vendor/hello-echo/manifest.cbor", + "exports": "vendor/hello-echo/exports.cbor", + "adapter": "vendor/hello-echo/adapter.cbor", + "targetConfiguration": "vendor/hello-echo/target-configuration.cbor" + })); + test_ok( + fs::write( + &config_path, + test_ok( + serde_json::to_vec_pretty(&application), + "encode application manifest", + ), + ), + "write application manifest", + ); + + let failure = test_err( + build_application(&config_path), + "a disconnected supplied lawpack must reject", + ); + + assert_eq!(failure.kind, "InvalidLawpackClosure"); + assert!(!root.join(".build/application/core.cbor").exists()); + assert!(!root.join(".build/application/target-ir.cbor").exists()); + test_ok(fs::remove_dir_all(root), "remove disconnected build tree"); + } + #[test] fn relative_application_config_uses_the_current_directory_as_root() { let actual = test_ok( diff --git a/crates/edict-syntax/tests/lawpack.rs b/crates/edict-syntax/tests/lawpack.rs index ae5048f..7026b78 100644 --- a/crates/edict-syntax/tests/lawpack.rs +++ b/crates/edict-syntax/tests/lawpack.rs @@ -7,9 +7,10 @@ use edict_syntax::{ compile_to_core, decode_canonical_cbor, decode_lawpack_adapter, decode_lawpack_bundle, digest_core_module, digest_target_ir_artifact, encode_canonical_cbor, encode_core_module, encode_target_ir_artifact, lower_to_target_ir, parse_module, prepare_lawpack_compilation, - validate_lawpack_dependency_graph, CanonicalValue, LawpackAdapterFailureKind, - LawpackExecutionClass, LawpackPureFunctionImplementation, LawpackValidationFailureKind, - LawpackVerifierClass, TargetLoweringStatus, ValidatedLawpackBundle, + validate_lawpack_dependency_graph, CanonicalValue, CompilerErrorKind, CompilerStage, + LawpackAdapterFailureKind, LawpackExecutionClass, LawpackPureFunctionImplementation, + LawpackValidationFailureKind, LawpackVerifierClass, TargetLoweringStatus, + ValidatedLawpackBundle, }; use sha2::{Digest, Sha256}; @@ -352,6 +353,111 @@ intent observe(input: ObserveInput) ); } +#[test] +fn request_only_profile_rejects_another_profiles_budget() { + let mut exports = hello_echo_exports(); + array_mut(field_mut(&mut exports, "effects")).clear(); + let exported_profiles = map_mut(field_mut(&mut exports, "operationProfiles")); + let second_exported_profile = exported_profiles + .first() + .map(|(_coordinate, profile)| profile.clone()) + .expect("exported operation profile"); + exported_profiles.push(( + text("hello.echo@1.observeGreeting"), + second_exported_profile, + )); + + let mut adapter = decode_canonical_cbor(ADAPTER_BYTES).expect("decode canonical adapter"); + let target_configuration = field_mut( + first_map_value_mut(field_mut(&mut adapter, "effectImplementations")), + "targetConfiguration", + ) + .clone(); + map_mut(field_mut(&mut adapter, "effectImplementations")).clear(); + let adapter_profiles = map_mut(field_mut(&mut adapter, "operationProfiles")); + let first_profile = adapter_profiles + .first_mut() + .map(|(_coordinate, profile)| profile) + .expect("adapter operation profile"); + array_mut(field_mut(first_profile, "semanticEffects")).clear(); + insert_field( + first_profile, + "budgetObligation", + text("hello.echo@1.smallCreateBudget"), + ); + insert_field(first_profile, "targetConfiguration", target_configuration); + let mut second_profile = first_profile.clone(); + replace_field( + &mut second_profile, + "budgetObligation", + text("hello.echo@1.largeObservationBudget"), + ); + adapter_profiles.push((text("hello.echo@1.observeGreeting"), second_profile)); + let budgets = map_mut(field_mut(&mut adapter, "budgets")); + let large_budget = budgets + .first() + .map(|(_coordinate, budget)| budget.clone()) + .expect("adapter budget"); + budgets.push((text("hello.echo@1.largeObservationBudget"), large_budget)); + + let (bundle, adapter) = bundle_and_adapter(&exports, &adapter); + let source = format!( + r#"package examples.workspace_observer@1; + +use lawpack hello.echo@1 digest "{}" as hello; +use capability workspace.snapshot.observe@1 + digest "sha256:{}" + as snapshot; + +type ObserveInput = {{ + payload: Bytes, + scope: Bytes, + basis: Bytes, + maxSettlementBytes: U64, + maxAttempts: U32, +}}; + +intent observe(input: ObserveInput) + returns ExternalActionRequest> + profile hello.createGreeting + basis input.basis + budget <= hello.largeObservationBudget +{{ + request pending: ExternalActionRequest> = + snapshot(input.payload) + input schema workspace.snapshot.input@1 digest "sha256:{}" + settlement schema workspace.snapshot.settlement@1 digest "sha256:{}" + authority input.scope + basis input.basis + budget maxSettlementBytes input.maxSettlementBytes maxAttempts input.maxAttempts + reconcile workspace.snapshot.reconcile@1 digest "sha256:{}"; + return pending; +}} +"#, + bundle.manifest_digest_review_string(), + "a".repeat(64), + "b".repeat(64), + "c".repeat(64), + "d".repeat(64), + ); + let module = parse_module(&source).expect("parse mismatched-budget application"); + let preparation = prepare_lawpack_compilation(&module, &bundle, &adapter) + .expect("prepare mismatched-budget application"); + let failures = compile_to_core(&module, preparation.compiler_context()) + .expect_err("a profile must reject another profile's budget"); + + assert_eq!(failures.len(), 1); + assert_eq!(failures[0].stage, CompilerStage::Resolve); + assert_eq!(failures[0].kind, CompilerErrorKind::MissingContextFact); + assert!( + failures[0] + .message + .contains("profile `hello.createGreeting` requires budget `hello.smallCreateBudget`"), + "unexpected mismatch diagnostic: {}", + failures[0].message + ); +} + #[test] fn request_only_profile_requires_an_exact_budget_obligation() { let mut exports = hello_echo_exports(); From 3ef0d162905fbad125f8e7498cdc05ea8ad3adb7 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 29 Jul 2026 18:00:22 -0700 Subject: [PATCH 10/16] fix: bind external request authority closure --- crates/edict-cli/src/application_build.rs | 86 +++++++++++++++++++--- crates/edict-syntax/src/compiler.rs | 34 ++++++++- crates/edict-syntax/src/lawpack_adapter.rs | 7 +- 3 files changed, 115 insertions(+), 12 deletions(-) diff --git a/crates/edict-cli/src/application_build.rs b/crates/edict-cli/src/application_build.rs index cb2d76d..70e0af0 100644 --- a/crates/edict-cli/src/application_build.rs +++ b/crates/edict-cli/src/application_build.rs @@ -163,16 +163,7 @@ pub(crate) fn build_application(config_path: &Path) -> Result<(), ApplicationBui for lawpack in &config.lawpacks { loaded_lawpacks.push(load_lawpack(&root, lawpack)?); } - let bundles = loaded_lawpacks - .iter() - .map(|loaded| loaded.bundle.clone()) - .collect::>(); - validate_lawpack_dependency_graph(&bundles).map_err(|failures| { - failure( - "InvalidLawpackClosure", - format!("application lawpack dependency closure is invalid: {failures:?}"), - ) - })?; + validate_application_lawpack_closure(&loaded_lawpacks)?; let loaded = loaded_lawpacks.first().ok_or_else(|| { failure( "InvalidApplicationConfig", @@ -484,6 +475,81 @@ fn validate_external_action_artifacts( Ok(()) } +fn validate_application_lawpack_closure( + loaded_lawpacks: &[LoadedLawpack], +) -> Result<(), ApplicationBuildFailure> { + let bundles = loaded_lawpacks + .iter() + .map(|loaded| loaded.bundle.clone()) + .collect::>(); + validate_lawpack_dependency_graph(&bundles).map_err(|failures| { + failure( + "InvalidLawpackClosure", + format!("application lawpack dependency closure is invalid: {failures:?}"), + ) + })?; + let root = loaded_lawpacks.first().ok_or_else(|| { + failure( + "InvalidLawpackClosure", + "application lawpack dependency closure has no root", + ) + })?; + let by_identity = loaded_lawpacks + .iter() + .map(|loaded| { + ( + ( + loaded.bundle.manifest().id.clone(), + loaded.bundle.manifest().version.clone(), + ), + &loaded.bundle, + ) + }) + .collect::>(); + let mut pending = vec![( + root.bundle.manifest().id.clone(), + root.bundle.manifest().version.clone(), + )]; + let mut reachable = BTreeSet::new(); + while let Some(identity) = pending.pop() { + if !reachable.insert(identity.clone()) { + continue; + } + let Some(bundle) = by_identity.get(&identity) else { + return Err(failure( + "InvalidLawpackClosure", + format!( + "application root dependency closure omitted lawpack `{}@{}`", + identity.0, identity.1 + ), + )); + }; + pending.extend( + bundle + .manifest() + .dependencies + .iter() + .map(|dependency| (dependency.id.clone(), dependency.version.clone())), + ); + } + if let Some(unreachable) = by_identity + .keys() + .find(|identity| !reachable.contains(*identity)) + { + return Err(failure( + "InvalidLawpackClosure", + format!( + "supplied lawpack `{}@{}` is unreachable from root `{}@{}`", + unreachable.0, + unreachable.1, + root.bundle.manifest().id, + root.bundle.manifest().version + ), + )); + } + Ok(()) +} + fn validate_application_manifest( config: &ApplicationManifest, ) -> Result<(), ApplicationBuildFailure> { diff --git a/crates/edict-syntax/src/compiler.rs b/crates/edict-syntax/src/compiler.rs index ce9ed8c..aa79459 100644 --- a/crates/edict-syntax/src/compiler.rs +++ b/crates/edict-syntax/src/compiler.rs @@ -64,6 +64,7 @@ pub struct CompilerError { pub struct CompilerContext { operation_profiles: BTreeMap, operation_profile_write_classes: BTreeMap>, + operation_profile_budgets: BTreeMap, effect_write_classes: BTreeMap, budgets: BTreeMap, } @@ -101,6 +102,17 @@ impl CompilerContext { self } + #[must_use] + pub fn with_operation_profile_budget( + mut self, + source_profile: impl Into, + source_budget: impl Into, + ) -> Self { + self.operation_profile_budgets + .insert(source_profile.into(), source_budget.into()); + self + } + #[must_use] pub fn with_effect_write_class( mut self, @@ -321,8 +333,10 @@ fn resolve_intent( errors: &mut Vec, ) -> Option { let mut profile = None; + let mut profile_source = None; let mut allowed_write_classes = None; let mut budget = None; + let mut budget_source = None; for clause in &intent.clauses { match clause { IntentClause::Profile(path) => { @@ -330,6 +344,7 @@ fn resolve_intent( match context.operation_profiles.get(&key) { Some(value) => { profile = Some(value.clone()); + profile_source = Some(key.clone()); allowed_write_classes = context.operation_profile_write_classes.get(&key).cloned(); } @@ -342,7 +357,10 @@ fn resolve_intent( IntentClause::Budget(path) => { let key = path_key(path); match context.budgets.get(&key) { - Some(value) => budget = Some(value.clone()), + Some(value) => { + budget = Some(value.clone()); + budget_source = Some(key); + } None => errors.push(missing_context_fact( format!("budget `{key}` has no compiler context fact"), intent.span, @@ -359,6 +377,20 @@ fn resolve_intent( } } + if let (Some(profile_source), Some(budget_source)) = (&profile_source, &budget_source) { + if let Some(required_budget) = context.operation_profile_budgets.get(profile_source) { + if required_budget != budget_source { + errors.push(missing_context_fact( + format!( + "profile `{profile_source}` requires budget `{required_budget}`, got `{budget_source}`" + ), + intent.span, + )); + return None; + } + } + } + Some(ResolvedIntent { name: intent.name.clone(), profile: profile?, diff --git a/crates/edict-syntax/src/lawpack_adapter.rs b/crates/edict-syntax/src/lawpack_adapter.rs index f82e8d8..f45a20c 100644 --- a/crates/edict-syntax/src/lawpack_adapter.rs +++ b/crates/edict-syntax/src/lawpack_adapter.rs @@ -225,7 +225,12 @@ pub fn prepare_lawpack_compilation( } compiler_context = compiler_context .with_operation_profile(local_profile.clone(), profile.core.clone()) - .with_operation_profile_write_classes(local_profile, write_classes); + .with_operation_profile_write_classes(local_profile.clone(), write_classes); + if let Some(budget) = &profile.budget_obligation { + let local_budget = local_coordinate(&alias, &prefix, budget)?; + compiler_context = + compiler_context.with_operation_profile_budget(local_profile, local_budget); + } operation_profiles.insert(profile.core.clone()); } From 3e68eb09750df3a41e8568e1110ba55802b6740d Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 29 Jul 2026 18:01:24 -0700 Subject: [PATCH 11/16] docs: close external request authority gaps --- docs/topics/cli/README.md | 13 ++++++++----- docs/topics/cli/test-plan.md | 2 +- docs/topics/external-action-requests/README.md | 8 ++++++-- docs/topics/external-action-requests/test-plan.md | 4 ++-- docs/topics/lawpacks/README.md | 10 +++++++--- docs/topics/lawpacks/test-plan.md | 4 ++-- 6 files changed, 26 insertions(+), 15 deletions(-) diff --git a/docs/topics/cli/README.md b/docs/topics/cli/README.md index b7c2d69..cb0d7c8 100644 --- a/docs/topics/cli/README.md +++ b/docs/topics/cli/README.md @@ -29,9 +29,10 @@ manifest names one exact Edict source, its complete lawpack closure, the selected target profile and provider package, and the output directory. Both application routes accept exactly one source and a non-empty ordered lawpack closure whose first entry is the root. They validate the complete supplied -dependency graph, compile and lower the source through the root lawpack's -declarative target adapter, and resolve the selected target profile only from -its checked provider-package manifest. +dependency graph, reject any supplied lawpack unreachable from that root, +compile and lower the source through the root lawpack's declarative target +adapter, and resolve the selected target profile only from its checked +provider-package manifest. ```json {"schema":"edict.compiler.settings/v1","type":"compilerSettings","operation":"build","application":"edict.application.json"} @@ -86,8 +87,10 @@ selects the request-only route explicitly: The request-only route requires at least one compiler-emitted external-action request, rejects any callable Target IR step, and requires every request -operation to be bound to one exact supplied lawpack manifest. It invokes no -provider component. The owning canonical encoders publish: +operation to be bound to one exact root-reachable lawpack manifest. The source +budget must equal the exact obligation declared by its selected request-only +profile. It invokes no provider component. The owning canonical encoders +publish: - `core.cbor`; - `target-ir.cbor`. diff --git a/docs/topics/cli/test-plan.md b/docs/topics/cli/test-plan.md index 3080d85..3f18fa2 100644 --- a/docs/topics/cli/test-plan.md +++ b/docs/topics/cli/test-plan.md @@ -43,7 +43,7 @@ Out of scope: | CLI-REQ-013 | implemented | The `project` operation accepts dirty editor source from `source` input records and emits structured syntax, diagnostics, Core, Target IR, digest, and status records without requiring a source file on disk. | crates/edict-cli/tests/jsonl_cli.rs | | CLI-REQ-014 | implemented | Compiler-level projection failures are emitted as structured projection records and diagnostics, not process-level CLI failures. | crates/edict-cli/tests/jsonl_cli.rs | | CLI-REQ-015 | implemented | The executable-operation `build` route loads one `edict.application/v1` request, validates the exact Edict source and complete lawpack/adapter closure, invokes the selected target provider lowerer and independent verifier through the bounded provider host, and writes only the accepted canonical package and verification-report bytes. | crates/edict-cli/src/application_build.rs | -| CLI-REQ-016 | implemented | The explicit external-action `build` route validates a request-only source and complete capability/adapter/target-profile closure, invokes no provider component, and atomically publishes the owning encoders' exact Core and Target IR bytes while clearing stale executable outputs. | issue #176, crates/edict-cli/src/application_build.rs | +| CLI-REQ-016 | implemented | The explicit external-action `build` route validates a request-only source and complete root-reachable capability/adapter/target-profile closure, rejects profile-budget mismatches and disconnected lawpacks, invokes no provider component, and atomically publishes the owning encoders' exact Core and Target IR bytes while clearing stale executable outputs. | issue #176, crates/edict-cli/src/application_build.rs | ## Fixtures diff --git a/docs/topics/external-action-requests/README.md b/docs/topics/external-action-requests/README.md index 5cb771b..959bc63 100644 --- a/docs/topics/external-action-requests/README.md +++ b/docs/topics/external-action-requests/README.md @@ -73,8 +73,12 @@ provider-owned target profile. It then: 1. compiles and lowers through the real lawpack closure; 2. requires at least one typed request and zero callable Target IR steps; -3. binds each request operation to an exact supplied lawpack manifest digest; -4. writes the owning encoders' exact `core.cbor` and `target-ir.cbor` bytes. +3. rejects supplied lawpacks unreachable from the first/root manifest; +4. requires the source budget selected for each request-only profile to equal + that profile's exact declared obligation; +5. binds each request operation to an exact root-reachable lawpack manifest + digest; +6. writes the owning encoders' exact `core.cbor` and `target-ir.cbor` bytes. Publication is a locked pair replacement. A failure restores the previous pair, and a successful request build removes stale executable-operation package and diff --git a/docs/topics/external-action-requests/test-plan.md b/docs/topics/external-action-requests/test-plan.md index 500dc34..efde9a5 100644 --- a/docs/topics/external-action-requests/test-plan.md +++ b/docs/topics/external-action-requests/test-plan.md @@ -36,7 +36,7 @@ Out of scope: | EXTREQ-REQ-006 | implemented | Runtime-valued authority scope, basis, and budget expressions survive compilation for Echo admission; Edict performs no external action while compiling or lowering them. | issue #172 | | EXTREQ-REQ-007 | implemented | The request-family allowlist contains only the domain-specific `workspace` root; raw filesystem, process, network, Git, GitHub, model, shell, case-variant, abbreviation, and unregistered roots are outside the requestable capability vocabulary. | issue #172 | | EXTREQ-REQ-008 | implemented | Request construction remains bounded under a fixed-seed mutation corpus and a 64-request stress module. | issue #172 | -| EXTREQ-REQ-009 | implemented | The public application build explicitly selects request-only publication, validates the exact source/lawpack/adapter/target-profile closure, rejects zero requests, callable-step mixtures, and substituted capability manifests, and atomically publishes exact canonical Core and Target IR bytes without invoking a provider component. | issue #176 | +| EXTREQ-REQ-009 | implemented | The public application build explicitly selects request-only publication, validates the exact source/root-reachable-lawpack/adapter/target-profile closure, rejects zero requests, callable-step mixtures, disconnected lawpacks, profile-budget mismatches, and substituted capability manifests, and atomically publishes exact canonical Core and Target IR bytes without invoking a provider component. | issue #176 | ## Fixtures @@ -66,7 +66,7 @@ Out of scope: | EXTREQ-TP-013 | implemented | Tooling guard | EXTREQ-REQ-001 | A non-call request operation has its own stable parser kind, and `request` is highlighted as a keyword. | non_call_request_operation_has_a_request_specific_parse_kind, request_statement_introducer_is_highlighted_as_a_keyword | crates/edict-syntax/tests/external_action_requests.rs, crates/edict-syntax/tests/highlighting.rs | Request syntax remains distinct from semantic effect syntax. | | EXTREQ-TP-014 | implemented | Golden artifact | EXTREQ-REQ-002, EXTREQ-REQ-003, EXTREQ-REQ-004, EXTREQ-REQ-005 | The checked workspace-snapshot source reproduces exact compiler-owned Core and Target IR canonical bytes and domain-framed digests. | core_goldens_match_executable_encoder, target_ir_goldens_match_executable_encoder | fixtures/lang/external-actions/workspace-snapshot.edict, fixtures/core/canonical/workspace-snapshot.core.cbor, fixtures/core/canonical/workspace-snapshot.core.sha256, fixtures/target-ir/canonical/workspace-snapshot.target-ir.cbor, fixtures/target-ir/canonical/workspace-snapshot.target-ir.sha256 | Generated only through the owning xtask commands. | | EXTREQ-TP-015 | implemented | Public build | EXTREQ-REQ-009 | A real `edict.application/v1` request loads the generated workspace closure and exact Echo target profile, publishes checked canonical Core and Target IR bytes, removes stale executable outputs, and reruns byte-identically. | public_external_action_build_emits_exact_compiler_artifacts | crates/edict-cli/src/application_build.rs, fixtures/lawpack/workspace-snapshot/README.md, fixtures/providers/echo-target-profile/README.md | Provider components are outside the request-only route. | -| EXTREQ-TP-016 | implemented | Closure refusal | EXTREQ-REQ-004, EXTREQ-REQ-009 | A request operation whose digest no longer equals its owning supplied capability manifest is rejected before output publication. | external_action_build_rejects_a_substituted_capability_manifest, public_external_action_build_rejects_capability_substitution | crates/edict-cli/src/application_build.rs | Internal Core closure remains necessary but is not sufficient for public application authority. | +| EXTREQ-TP-016 | implemented | Closure refusal | EXTREQ-REQ-004, EXTREQ-REQ-009 | A request operation whose digest no longer equals its owning supplied capability manifest, or a supplied lawpack unreachable from the ordered root, is rejected before output publication. | external_action_build_rejects_a_substituted_capability_manifest, public_external_action_build_rejects_capability_substitution, public_external_action_build_rejects_a_disconnected_lawpack | crates/edict-cli/src/application_build.rs | Internal Core closure and a graph-valid disconnected manifest are each insufficient for public application authority. | | EXTREQ-TP-017 | implemented | Execution-class refusal | EXTREQ-REQ-003, EXTREQ-REQ-009 | The request-only build rejects zero requests and any artifact mixing external requests with callable Target IR steps. | external_action_build_requires_a_typed_request, external_action_build_rejects_mixed_callable_execution | crates/edict-cli/src/application_build.rs | The first host route has one execution class. | | EXTREQ-TP-018 | implemented | Publication transaction | EXTREQ-REQ-009 | Paired request artifacts are deterministic under fixed-seed and stress corpora; stale executable outputs are removed; publication failure preserves the prior request pair. | external_action_pair_publication_is_deterministic_for_a_fixed_seed_corpus, external_action_pair_publication_remains_bounded_under_stress, external_action_publication_removes_stale_executable_outputs, failed_external_action_pair_publication_preserves_previous_core | crates/edict-cli/src/application_build.rs | Output ownership is symmetric across build kinds. | diff --git a/docs/topics/lawpacks/README.md b/docs/topics/lawpacks/README.md index 5ec0e7b..59e6498 100644 --- a/docs/topics/lawpacks/README.md +++ b/docs/topics/lawpacks/README.md @@ -72,7 +72,9 @@ The current executable Rust surfaces touching lawpacks are: cannot be fabricated or mutated by callers. [LAWPACKS-REQ-005] - Dependency validation resolves the complete supplied set by `(id, version)`, detects cycles independent of input ordering, then corroborates every edge - against the exact resolved manifest digest. [LAWPACKS-REQ-005] + against the exact resolved manifest digest. Public application builds + additionally require every supplied lawpack to be reachable from the + manifest's first/root lawpack. [LAWPACKS-REQ-005] - v1 target profiles accept the exact `edict.lawpack-adapter/v1` identifier. Unknown and duplicate declarations reject. [LAWPACKS-REQ-003] - `decode_lawpack_adapter` accepts only canonical adapter bytes selected by one @@ -81,8 +83,10 @@ The current executable Rust surfaces touching lawpacks are: returning an opaque validated adapter. Each callable effect carries one typed, digest-locked target-configuration reference. A profile with no semantic effects is request-only and must carry its own exact budget - obligation and target configuration. Edict preserves those references but - does not interpret their target-owned semantics. + obligation and target configuration. Compilation preserves the + profile-to-budget association and rejects source that selects another + profile's budget. Edict preserves those references but does not interpret + their target-owned semantics. `prepare_lawpack_compilation` then derives compiler and Target IR facts through the source import's exact alias and manifest digest. [LAWPACKS-REQ-008] diff --git a/docs/topics/lawpacks/test-plan.md b/docs/topics/lawpacks/test-plan.md index 2a9b769..68001f1 100644 --- a/docs/topics/lawpacks/test-plan.md +++ b/docs/topics/lawpacks/test-plan.md @@ -47,7 +47,7 @@ Out of scope: | LAWPACKS-REQ-005 | implemented | Edict loads canonical `edict.lawpack/v1` manifests and export surfaces into typed values, rejects every value outside the closed CDDL shape with stable failure kinds, corroborates the export digest, and validates a complete supplied dependency set as digest-locked and acyclic before exposing any exports to compilation. | issue #169, crates/edict-syntax/src/lawpack.rs, docs/abi/edict-lawpack.cddl, docs/abi/edict-common.cddl, docs/abi/edict-core.cddl | | LAWPACKS-REQ-006 | implemented | Authority-facts loading accepts digest-locked `lawpack` source identity for first compiler budget and effect write-class facts without claiming full manifest validation. | docs/topics/authority-facts/test-plan.md | | LAWPACKS-REQ-007 | implemented | Provider manifests model lawpacks as generated provider artifacts with digest-locked semantic source and generator provenance; Edict validates the reference/provenance envelope without owning runtime lawpack semantics. | issue #139, docs/topics/providers/test-plan.md | -| LAWPACKS-REQ-008 | implemented | Edict validates one exact direct declarative `edict.lawpack-adapter/v1` resource selected by a loaded lawpack manifest. Callable profiles require complete effect/budget coverage and one typed target-configuration reference per runtime effect. Request-only profiles carry no semantic effects and must bind their own exact budget obligation and target configuration. Edict preserves but does not interpret target-owned configuration semantics. | issue #169, issue #176, docs/abi/edict-lawpack-adapter.cddl | +| LAWPACKS-REQ-008 | implemented | Edict validates one exact direct declarative `edict.lawpack-adapter/v1` resource selected by a loaded lawpack manifest. Callable profiles require complete effect/budget coverage and one typed target-configuration reference per runtime effect. Request-only profiles carry no semantic effects, bind their own exact budget obligation and target configuration, and reject source selecting another profile's budget. Edict preserves but does not interpret target-owned configuration semantics. | issue #169, issue #176, docs/abi/edict-lawpack-adapter.cddl | | LAWPACKS-REQ-009 | implemented | The standalone Hello Echo fixture pins exact canonical Core and Target IR bytes produced from the digest-locked source/lawpack/adapter closure and computes each identity with the artifact's native domain. | issue #169, fixtures/lawpack/hello-echo/README.md, xtask/src/lawpack_goldens.rs | | LAWPACKS-REQ-010 | implemented | The portable `causal.cell@1.createIfAbsent` capability closure is generated through the executable lawpack, adapter, compiler, and Target IR path, with exact canonical manifest, export, adapter, and target-configuration bytes and digests for external application builds. | fixtures/lawpack/causal-cell/README.md, xtask/src/lawpack_goldens.rs | | LAWPACKS-REQ-011 | implemented | A request-only lawpack profile supplies an exact compiler budget and opaque target configuration without declaring a callable semantic effect or target intrinsic; the workspace-snapshot closure reproduces one request and zero Target IR steps. | issue #176, fixtures/lawpack/workspace-snapshot/README.md | @@ -76,7 +76,7 @@ Out of scope: | LAWPACKS-TP-008 | implemented | Direct adapter | LAWPACKS-REQ-008 | The exact Hello Echo adapter selected by the manifest derives all compiler and Echo Target IR facts and exposes the exact target-configuration resource identity, while missing, substituted, non-canonical, incomplete, target-mismatched, import-mismatched, malformed-configuration, undeclared-write-class, or obligation-mismatched adapters fail closed before trusted compiler facts exist. | hello_echo_source_compiles_to_echo_target_ir_from_exact_lawpack_adapter, lawpack_adapter_bytes_must_be_canonical_and_digest_bound, lawpack_adapter_requires_a_typed_target_configuration_reference, lawpack_adapter_rejects_an_undeclared_write_class_at_the_effect_path, lawpack_adapter_selection_requires_one_exact_target_profile, lawpack_adapter_requires_complete_exported_effect_coverage, lawpack_adapter_corroborates_footprint_cost_and_failure_obligations, lawpack_compilation_requires_the_exact_digest_locked_source_import | fixtures/lawpack/hello-echo/README.md, crates/edict-syntax/tests/lawpack.rs | The positive test constructs no `CompilerContext` or `TargetIrLoweringFacts`; Echo-specific configuration interpretation remains outside Edict. | | LAWPACKS-TP-009 | implemented | Compiler artifacts | LAWPACKS-REQ-009 | Compiling and lowering the exact Hello Echo closure reproduces the reviewed Core and Target IR bytes and their native domain-framed identities. | hello_echo_source_compiles_to_echo_target_ir_from_exact_lawpack_adapter | fixtures/lawpack/hello-echo/create-greeting.core.cbor, fixtures/lawpack/hello-echo/create-greeting.target-ir.cbor, crates/edict-syntax/tests/lawpack.rs, xtask/src/lawpack_goldens.rs | The fixtures are outputs of the real compiler pipeline, not handwritten substitutes; `cargo xtask lawpack-goldens --check` reproduces them. | | LAWPACKS-TP-010 | implemented | Portable capability | LAWPACKS-REQ-010 | Generating the causal-cell closure validates its canonical lawpack and direct adapter, then compiles and lowers an Edict source witness that imports the exact generated manifest digest. | lawpack_goldens_match_executable_codec | fixtures/lawpack/causal-cell/README.md, xtask/src/lawpack_goldens.rs, xtask/src/tests.rs | The generator fails if the portable capability no longer reaches a compiler-produced Target IR artifact. | -| LAWPACKS-TP-011 | implemented | Request-only profile | LAWPACKS-REQ-008, LAWPACKS-REQ-011 | A profile with no semantic effects is accepted only when it binds an exact budget obligation and target configuration; it compiles one request without conferring target-call authority. | request_only_profile_supplies_budget_without_callable_effect_authority, request_only_profile_requires_an_exact_budget_obligation, request_only_profile_requires_an_exact_target_configuration | crates/edict-syntax/tests/lawpack.rs, fixtures/lawpack/workspace-snapshot/README.md | Empty semantic effects are not an unbounded profile escape hatch. | +| LAWPACKS-TP-011 | implemented | Request-only profile | LAWPACKS-REQ-008, LAWPACKS-REQ-011 | A profile with no semantic effects is accepted only when it binds an exact budget obligation and target configuration; it compiles one request without conferring target-call authority and rejects another profile's budget. | request_only_profile_supplies_budget_without_callable_effect_authority, request_only_profile_requires_an_exact_budget_obligation, request_only_profile_requires_an_exact_target_configuration, request_only_profile_rejects_another_profiles_budget | crates/edict-syntax/tests/lawpack.rs, fixtures/lawpack/workspace-snapshot/README.md | Empty semantic effects and adapter-wide budget availability are not authority escape hatches. | ## Determinism Obligations From 66ee4f39d24ea8b02e6322141024fa2410a43ef2 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 29 Jul 2026 18:01:47 -0700 Subject: [PATCH 12/16] docs: record request authority closure --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f583323..0d622e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,8 +33,10 @@ versions still track specification maturity rather than a released product. the output pair transactionally, and clears stale executable-operation outputs. Request-only lawpack profiles now bind their own exact budget and opaque target configuration while carrying no semantic effect or target - intrinsic. A generator-owned workspace-snapshot closure and mirrored - Echo-owned target profile make the full public build reproducible in Edict. + intrinsic; compilation rejects another profile's budget, and application + builds reject supplied lawpacks outside the ordered root's dependency + closure. A generator-owned workspace-snapshot closure and mirrored Echo-owned + target profile make the full public build reproducible in Edict. - Added typed external-action request values without adding external execution authority to Edict. Digest-locked capability imports and `request` statements preserve exact operation, schema, scope, basis, budget, input, reconciliation, From 7df5756438a08d05858384df3b6c639fb7f5f796 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 29 Jul 2026 18:05:05 -0700 Subject: [PATCH 13/16] test: bind request authority by manifest identity --- crates/edict-cli/src/application_build.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/crates/edict-cli/src/application_build.rs b/crates/edict-cli/src/application_build.rs index 70e0af0..eeef49e 100644 --- a/crates/edict-cli/src/application_build.rs +++ b/crates/edict-cli/src/application_build.rs @@ -2019,6 +2019,22 @@ mod tests { ); } + #[test] + fn external_action_build_binds_operation_authority_by_manifest_digest() { + let closure = [external_action_loaded_lawpack()]; + let mut external = external_action_target_ir(); + let Some(observe) = external.intents.get_mut("observe") else { + panic!("workspace observer intent exists"); + }; + observe.external_action_requests[0].operation.coordinate = + "workspace.snapshot.observe@2".to_owned(); + + test_ok( + validate_external_action_artifacts(&external, &closure), + "operation identity is independent of its authority manifest version", + ); + } + #[test] fn external_action_build_requires_a_typed_request() { let failure = test_err( From e24528b8bb1445fe8a1c72eaae820c0beee8d95c Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 29 Jul 2026 18:06:16 -0700 Subject: [PATCH 14/16] fix: bind request authority by manifest digest --- crates/edict-cli/src/application_build.rs | 30 ++------ crates/edict-syntax/src/lawpack_adapter.rs | 2 + crates/edict-syntax/tests/lawpack.rs | 90 +++++++--------------- 3 files changed, 39 insertions(+), 83 deletions(-) diff --git a/crates/edict-cli/src/application_build.rs b/crates/edict-cli/src/application_build.rs index eeef49e..de374b4 100644 --- a/crates/edict-cli/src/application_build.rs +++ b/crates/edict-cli/src/application_build.rs @@ -433,41 +433,27 @@ fn validate_external_action_artifacts( "external-action application build cannot mix requests with callable target steps", )); } - let capability_manifests = loaded_lawpacks + let capability_manifest_digests = loaded_lawpacks .iter() - .map(|loaded| { - ( - loaded.bundle.manifest().id.as_str(), - loaded.bundle.manifest().version.as_str(), - loaded.bundle.manifest_digest_review_string(), - ) - }) - .collect::>(); + .map(|loaded| loaded.bundle.manifest_digest_review_string()) + .collect::>(); let substituted = target_ir.intents.values().find_map(|intent| { intent .external_action_requests .iter() .map(|request| &request.operation) .find(|operation| { - !capability_manifests.iter().any(|(id, version, digest)| { - let Some((operation_id, operation_version)) = - operation.coordinate.rsplit_once('@') - else { - return false; - }; - operation.digest.as_deref() == Some(digest.as_str()) - && operation_version == *version - && operation_id - .strip_prefix(&format!("{id}.")) - .is_some_and(|suffix| !suffix.is_empty()) - }) + !operation + .digest + .as_ref() + .is_some_and(|digest| capability_manifest_digests.contains(digest)) }) }); if let Some(operation) = substituted { return Err(failure( "ExternalActionCapabilityClosureMismatch", format!( - "request operation `{}` is not bound to one exact application lawpack manifest", + "request operation `{}` is not bound to one exact root-reachable application lawpack manifest digest", operation.coordinate ), )); diff --git a/crates/edict-syntax/src/lawpack_adapter.rs b/crates/edict-syntax/src/lawpack_adapter.rs index f45a20c..0af1e57 100644 --- a/crates/edict-syntax/src/lawpack_adapter.rs +++ b/crates/edict-syntax/src/lawpack_adapter.rs @@ -65,7 +65,9 @@ pub struct LawpackAdapterFailure { pub struct LawpackAdapterOperationProfile { pub core: String, pub semantic_effects: Vec, + /// Exact source budget required when `semantic_effects` is empty. pub budget_obligation: Option, + /// Opaque target configuration required when `semantic_effects` is empty. pub target_configuration: Option, } diff --git a/crates/edict-syntax/tests/lawpack.rs b/crates/edict-syntax/tests/lawpack.rs index 7026b78..f76ea7d 100644 --- a/crates/edict-syntax/tests/lawpack.rs +++ b/crates/edict-syntax/tests/lawpack.rs @@ -275,23 +275,7 @@ fn lawpack_adapter_requires_complete_exported_effect_coverage() { #[test] fn request_only_profile_supplies_budget_without_callable_effect_authority() { - let mut exports = hello_echo_exports(); - array_mut(field_mut(&mut exports, "effects")).clear(); - let mut adapter = decode_canonical_cbor(ADAPTER_BYTES).expect("decode canonical adapter"); - let target_configuration = field_mut( - first_map_value_mut(field_mut(&mut adapter, "effectImplementations")), - "targetConfiguration", - ) - .clone(); - map_mut(field_mut(&mut adapter, "effectImplementations")).clear(); - let profile = first_map_value_mut(field_mut(&mut adapter, "operationProfiles")); - array_mut(field_mut(profile, "semanticEffects")).clear(); - insert_field( - profile, - "budgetObligation", - text("hello.echo@1.smallCreateBudget"), - ); - insert_field(profile, "targetConfiguration", target_configuration); + let (exports, adapter) = request_only_adapter(Some("hello.echo@1.smallCreateBudget"), true); let (bundle, adapter) = bundle_and_adapter(&exports, &adapter); let source = format!( r#"package examples.workspace_observer@1; @@ -355,8 +339,8 @@ intent observe(input: ObserveInput) #[test] fn request_only_profile_rejects_another_profiles_budget() { - let mut exports = hello_echo_exports(); - array_mut(field_mut(&mut exports, "effects")).clear(); + let (mut exports, mut adapter) = + request_only_adapter(Some("hello.echo@1.smallCreateBudget"), true); let exported_profiles = map_mut(field_mut(&mut exports, "operationProfiles")); let second_exported_profile = exported_profiles .first() @@ -367,25 +351,11 @@ fn request_only_profile_rejects_another_profiles_budget() { second_exported_profile, )); - let mut adapter = decode_canonical_cbor(ADAPTER_BYTES).expect("decode canonical adapter"); - let target_configuration = field_mut( - first_map_value_mut(field_mut(&mut adapter, "effectImplementations")), - "targetConfiguration", - ) - .clone(); - map_mut(field_mut(&mut adapter, "effectImplementations")).clear(); let adapter_profiles = map_mut(field_mut(&mut adapter, "operationProfiles")); let first_profile = adapter_profiles .first_mut() .map(|(_coordinate, profile)| profile) .expect("adapter operation profile"); - array_mut(field_mut(first_profile, "semanticEffects")).clear(); - insert_field( - first_profile, - "budgetObligation", - text("hello.echo@1.smallCreateBudget"), - ); - insert_field(first_profile, "targetConfiguration", target_configuration); let mut second_profile = first_profile.clone(); replace_field( &mut second_profile, @@ -460,23 +430,7 @@ intent observe(input: ObserveInput) #[test] fn request_only_profile_requires_an_exact_budget_obligation() { - let mut exports = hello_echo_exports(); - array_mut(field_mut(&mut exports, "effects")).clear(); - let mut adapter = decode_canonical_cbor(ADAPTER_BYTES).expect("decode canonical adapter"); - let target_configuration = field_mut( - first_map_value_mut(field_mut(&mut adapter, "effectImplementations")), - "targetConfiguration", - ) - .clone(); - map_mut(field_mut(&mut adapter, "effectImplementations")).clear(); - let profile = first_map_value_mut(field_mut(&mut adapter, "operationProfiles")); - array_mut(field_mut(profile, "semanticEffects")).clear(); - insert_field( - profile, - "budgetObligation", - text("hello.echo@1.missingBudget"), - ); - insert_field(profile, "targetConfiguration", target_configuration); + let (exports, adapter) = request_only_adapter(Some("hello.echo@1.missingBudget"), true); let bundle = bundle_with_exports_and_adapter(&exports, &adapter); let bytes = encode_canonical_cbor(&adapter).expect("encode request-only adapter"); @@ -491,17 +445,7 @@ fn request_only_profile_requires_an_exact_budget_obligation() { #[test] fn request_only_profile_requires_an_exact_target_configuration() { - let mut exports = hello_echo_exports(); - array_mut(field_mut(&mut exports, "effects")).clear(); - let mut adapter = decode_canonical_cbor(ADAPTER_BYTES).expect("decode canonical adapter"); - map_mut(field_mut(&mut adapter, "effectImplementations")).clear(); - let profile = first_map_value_mut(field_mut(&mut adapter, "operationProfiles")); - array_mut(field_mut(profile, "semanticEffects")).clear(); - insert_field( - profile, - "budgetObligation", - text("hello.echo@1.smallCreateBudget"), - ); + let (exports, adapter) = request_only_adapter(Some("hello.echo@1.smallCreateBudget"), false); let bundle = bundle_with_exports_and_adapter(&exports, &adapter); let bytes = encode_canonical_cbor(&adapter).expect("encode request-only adapter"); @@ -1097,6 +1041,30 @@ fn hello_echo_exports() -> CanonicalValue { decode_canonical_cbor(EXPORTS_BYTES).expect("decode fixture exports") } +fn request_only_adapter( + budget_obligation: Option<&str>, + include_target_configuration: bool, +) -> (CanonicalValue, CanonicalValue) { + let mut exports = hello_echo_exports(); + array_mut(field_mut(&mut exports, "effects")).clear(); + let mut adapter = decode_canonical_cbor(ADAPTER_BYTES).expect("decode canonical adapter"); + let target_configuration = field_mut( + first_map_value_mut(field_mut(&mut adapter, "effectImplementations")), + "targetConfiguration", + ) + .clone(); + map_mut(field_mut(&mut adapter, "effectImplementations")).clear(); + let profile = first_map_value_mut(field_mut(&mut adapter, "operationProfiles")); + array_mut(field_mut(profile, "semanticEffects")).clear(); + if let Some(budget) = budget_obligation { + insert_field(profile, "budgetObligation", text(budget)); + } + if include_target_configuration { + insert_field(profile, "targetConfiguration", target_configuration); + } + (exports, adapter) +} + fn resource_ref(id: &str, digest: [u8; 32]) -> CanonicalValue { map([ ("id", text(id)), From 4f6572015167ac69a440506d37b6894814eca81e Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 29 Jul 2026 18:07:12 -0700 Subject: [PATCH 15/16] docs: reconcile external request review evidence --- docs/REQUIREMENTS.md | 1 + docs/topics/cli/README.md | 11 +++++++---- docs/topics/cli/test-plan.md | 4 ++-- docs/topics/external-action-requests/README.md | 5 +++-- .../external-action-requests/test-plan.md | 9 +++++---- .../providers/echo-target-profile/README.md | 18 ++++++++---------- 6 files changed, 26 insertions(+), 22 deletions(-) diff --git a/docs/REQUIREMENTS.md b/docs/REQUIREMENTS.md index 66261f8..1b26bfb 100644 --- a/docs/REQUIREMENTS.md +++ b/docs/REQUIREMENTS.md @@ -42,6 +42,7 @@ embedded snippet, schema, or fixture no longer matches the locked digests. - `EDICT-ABI-*` — cross-ABI rules (no-duplication, display sidecars). - `EDICT-ADMISSION-*` — Edict-owned admission-boundary artifact and operation semantics. +- `EDICT-CLI-*` — public CLI and application-build contracts. - `EDICT-CONFORMANCE-*` — conformance/differential testing. - `CONTINUUM-*` — contract bundle and admission. diff --git a/docs/topics/cli/README.md b/docs/topics/cli/README.md index cb0d7c8..ae9bc40 100644 --- a/docs/topics/cli/README.md +++ b/docs/topics/cli/README.md @@ -87,10 +87,13 @@ selects the request-only route explicitly: The request-only route requires at least one compiler-emitted external-action request, rejects any callable Target IR step, and requires every request -operation to be bound to one exact root-reachable lawpack manifest. The source -budget must equal the exact obligation declared by its selected request-only -profile. It invokes no provider component. The owning canonical encoders -publish: +operation digest to equal one exact root-reachable lawpack manifest digest; the +operation coordinate remains its own independently versioned resource identity. +The source budget must equal the exact obligation declared by its selected +request-only profile. `providerPackage` remains required because the route +loads and verifies its provider manifest and selected target-profile artifact. +Omitting it is `InvalidApplicationConfig`, but no provider component is invoked. +The owning canonical encoders publish: - `core.cbor`; - `target-ir.cbor`. diff --git a/docs/topics/cli/test-plan.md b/docs/topics/cli/test-plan.md index 3f18fa2..159cb7a 100644 --- a/docs/topics/cli/test-plan.md +++ b/docs/topics/cli/test-plan.md @@ -42,7 +42,7 @@ Out of scope: | CLI-REQ-012 | implemented | The checked-in CLI golden corpus can be regenerated by `cargo xtask cli-goldens --write` and checked by `cargo xtask cli-goldens --check`; `cargo xtask verify` runs the check mode. | xtask/src/goldens.rs, xtask/src/main.rs | | CLI-REQ-013 | implemented | The `project` operation accepts dirty editor source from `source` input records and emits structured syntax, diagnostics, Core, Target IR, digest, and status records without requiring a source file on disk. | crates/edict-cli/tests/jsonl_cli.rs | | CLI-REQ-014 | implemented | Compiler-level projection failures are emitted as structured projection records and diagnostics, not process-level CLI failures. | crates/edict-cli/tests/jsonl_cli.rs | -| CLI-REQ-015 | implemented | The executable-operation `build` route loads one `edict.application/v1` request, validates the exact Edict source and complete lawpack/adapter closure, invokes the selected target provider lowerer and independent verifier through the bounded provider host, and writes only the accepted canonical package and verification-report bytes. | crates/edict-cli/src/application_build.rs | +| CLI-REQ-015 | planned | The executable-operation `build` route loads one `edict.application/v1` request, validates the exact Edict source and complete lawpack/adapter closure, invokes the selected target provider lowerer and independent verifier through the bounded provider host, and writes only the accepted canonical package and verification-report bytes. | crates/edict-cli/src/application_build.rs | | CLI-REQ-016 | implemented | The explicit external-action `build` route validates a request-only source and complete root-reachable capability/adapter/target-profile closure, rejects profile-budget mismatches and disconnected lawpacks, invokes no provider component, and atomically publishes the owning encoders' exact Core and Target IR bytes while clearing stale executable outputs. | issue #176, crates/edict-cli/src/application_build.rs | ## Fixtures @@ -104,7 +104,7 @@ Out of scope: | CLI-TP-028 | planned | Verified application build | CLI-REQ-015 | A repository-owned provider-component fixture builds one exact source and complete lawpack closure, independently accepts the package, and publishes the canonical package/report pair. | - | - | The standalone external Hello Echo build is a green integration witness; this planned case makes the full crossing reproducible inside Edict's own automated suite. | | CLI-TP-029 | implemented | External request projection | CLI-REQ-013 | A `project` request carrying exact external-action source emits Core and Target IR review data with the capability resource, complete request payload, explicit awaiting-settlement posture, capability semantic closure, and zero callable target steps. | project_exposes_external_requests_as_non_callable_review_data | crates/edict-cli/tests/jsonl_cli.rs | Review JSON exposes compiler data only; the CLI performs no external action. | | CLI-TP-030 | implemented | External-action build | CLI-REQ-016 | An explicit request-only application manifest loads exact source, lawpack, adapter, configuration, and Echo target-profile artifacts; emits exact checked Core and Target IR bytes; removes stale executable outputs; and reruns deterministically. | public_external_action_build_emits_exact_compiler_artifacts | crates/edict-cli/src/application_build.rs, fixtures/lawpack/workspace-snapshot/README.md, fixtures/providers/echo-target-profile/README.md | The provider component host is not invoked. | -| CLI-TP-031 | implemented | External-action refusal | CLI-REQ-016 | Missing requests, callable-step mixtures, substituted capability manifests, and failed pair replacement refuse before a passing publication witness. | external_action_build_requires_a_typed_request, external_action_build_rejects_mixed_callable_execution, public_external_action_build_rejects_capability_substitution, failed_external_action_pair_publication_preserves_previous_core | crates/edict-cli/src/application_build.rs | Stable failure kinds distinguish closure, execution-class, and output failures. | +| CLI-TP-031 | implemented | External-action refusal | CLI-REQ-016 | Missing requests, callable-step mixtures, substituted capability manifests, disconnected lawpacks, profile-budget mismatches, and failed pair replacement refuse before a passing publication witness. | external_action_build_requires_a_typed_request, external_action_build_rejects_mixed_callable_execution, public_external_action_build_rejects_capability_substitution, public_external_action_build_rejects_a_disconnected_lawpack, request_only_profile_rejects_another_profiles_budget, failed_external_action_pair_publication_preserves_previous_core | crates/edict-cli/src/application_build.rs, crates/edict-syntax/tests/lawpack.rs | Stable failure kinds distinguish closure, authority, execution-class, and output failures. | ## Determinism Obligations diff --git a/docs/topics/external-action-requests/README.md b/docs/topics/external-action-requests/README.md index 959bc63..3ce0f1e 100644 --- a/docs/topics/external-action-requests/README.md +++ b/docs/topics/external-action-requests/README.md @@ -76,8 +76,9 @@ provider-owned target profile. It then: 3. rejects supplied lawpacks unreachable from the first/root manifest; 4. requires the source budget selected for each request-only profile to equal that profile's exact declared obligation; -5. binds each request operation to an exact root-reachable lawpack manifest - digest; +5. binds each request operation digest to an exact root-reachable lawpack + manifest digest without inventing a namespace or version relationship + between the two independent resource coordinates; 6. writes the owning encoders' exact `core.cbor` and `target-ir.cbor` bytes. Publication is a locked pair replacement. A failure restores the previous pair, diff --git a/docs/topics/external-action-requests/test-plan.md b/docs/topics/external-action-requests/test-plan.md index efde9a5..ba50042 100644 --- a/docs/topics/external-action-requests/test-plan.md +++ b/docs/topics/external-action-requests/test-plan.md @@ -44,6 +44,7 @@ Out of scope: | --- | --- | --- | | In-test `workspace.snapshot.observe@1` source | First bounded read-only external request. | Public parser, compiler, canonical encoders, and Target IR lowerer preserve the exact request contract. | | Fixed seed `0x4558_5452_4551_0001` | Determinism and mutation corpus. | Repeated compilation is byte-identical and distinct capability identities produce distinct Core identities. | +| CLI publication seed `0x5eed_1a77_c105_0a11` | Deterministic paired-publication corpus. | Sixteen fixed-seed Core/Target IR pairs publish byte-exactly, and 64 bounded pairs publish without growth. | | 64-request generated module | Bounded stress case. | All 64 requests survive Core and Target IR without becoming callable steps. | | `fixtures/lawpack/workspace-snapshot/` | Exact public-build capability closure. | The owning generator reproduces manifest, exports, request-only adapter, target configuration, source, Core, and Target IR; the public build reproduces the checked compiler bytes. | @@ -66,16 +67,16 @@ Out of scope: | EXTREQ-TP-013 | implemented | Tooling guard | EXTREQ-REQ-001 | A non-call request operation has its own stable parser kind, and `request` is highlighted as a keyword. | non_call_request_operation_has_a_request_specific_parse_kind, request_statement_introducer_is_highlighted_as_a_keyword | crates/edict-syntax/tests/external_action_requests.rs, crates/edict-syntax/tests/highlighting.rs | Request syntax remains distinct from semantic effect syntax. | | EXTREQ-TP-014 | implemented | Golden artifact | EXTREQ-REQ-002, EXTREQ-REQ-003, EXTREQ-REQ-004, EXTREQ-REQ-005 | The checked workspace-snapshot source reproduces exact compiler-owned Core and Target IR canonical bytes and domain-framed digests. | core_goldens_match_executable_encoder, target_ir_goldens_match_executable_encoder | fixtures/lang/external-actions/workspace-snapshot.edict, fixtures/core/canonical/workspace-snapshot.core.cbor, fixtures/core/canonical/workspace-snapshot.core.sha256, fixtures/target-ir/canonical/workspace-snapshot.target-ir.cbor, fixtures/target-ir/canonical/workspace-snapshot.target-ir.sha256 | Generated only through the owning xtask commands. | | EXTREQ-TP-015 | implemented | Public build | EXTREQ-REQ-009 | A real `edict.application/v1` request loads the generated workspace closure and exact Echo target profile, publishes checked canonical Core and Target IR bytes, removes stale executable outputs, and reruns byte-identically. | public_external_action_build_emits_exact_compiler_artifacts | crates/edict-cli/src/application_build.rs, fixtures/lawpack/workspace-snapshot/README.md, fixtures/providers/echo-target-profile/README.md | Provider components are outside the request-only route. | -| EXTREQ-TP-016 | implemented | Closure refusal | EXTREQ-REQ-004, EXTREQ-REQ-009 | A request operation whose digest no longer equals its owning supplied capability manifest, or a supplied lawpack unreachable from the ordered root, is rejected before output publication. | external_action_build_rejects_a_substituted_capability_manifest, public_external_action_build_rejects_capability_substitution, public_external_action_build_rejects_a_disconnected_lawpack | crates/edict-cli/src/application_build.rs | Internal Core closure and a graph-valid disconnected manifest are each insufficient for public application authority. | +| EXTREQ-TP-016 | implemented | Closure refusal | EXTREQ-REQ-004, EXTREQ-REQ-009 | A request operation whose digest no longer equals its owning supplied capability manifest, or a supplied lawpack unreachable from the ordered root, is rejected before output publication; an independently versioned operation coordinate remains valid when its authority digest is exact. | external_action_build_rejects_a_substituted_capability_manifest, public_external_action_build_rejects_capability_substitution, public_external_action_build_rejects_a_disconnected_lawpack, external_action_build_binds_operation_authority_by_manifest_digest | crates/edict-cli/src/application_build.rs | Internal Core closure and a graph-valid disconnected manifest are each insufficient for public application authority; no undeclared coordinate derivation convention is inferred. | | EXTREQ-TP-017 | implemented | Execution-class refusal | EXTREQ-REQ-003, EXTREQ-REQ-009 | The request-only build rejects zero requests and any artifact mixing external requests with callable Target IR steps. | external_action_build_requires_a_typed_request, external_action_build_rejects_mixed_callable_execution | crates/edict-cli/src/application_build.rs | The first host route has one execution class. | -| EXTREQ-TP-018 | implemented | Publication transaction | EXTREQ-REQ-009 | Paired request artifacts are deterministic under fixed-seed and stress corpora; stale executable outputs are removed; publication failure preserves the prior request pair. | external_action_pair_publication_is_deterministic_for_a_fixed_seed_corpus, external_action_pair_publication_remains_bounded_under_stress, external_action_publication_removes_stale_executable_outputs, failed_external_action_pair_publication_preserves_previous_core | crates/edict-cli/src/application_build.rs | Output ownership is symmetric across build kinds. | +| EXTREQ-TP-018 | implemented | Publication transaction | EXTREQ-REQ-009 | Paired request artifacts are deterministic under the recorded CLI publication seed and bounded stress corpus; stale executable outputs are removed; publication failure preserves the prior request pair. | external_action_pair_publication_is_deterministic_for_a_fixed_seed_corpus, external_action_pair_publication_remains_bounded_under_stress, external_action_publication_removes_stale_executable_outputs, failed_external_action_pair_publication_preserves_previous_core | crates/edict-cli/src/application_build.rs | Output ownership is symmetric across build kinds. | ## Determinism Obligations - Tests use no filesystem discovery, network access, clock, environment, or randomness. -- The property corpus uses the recorded fixed seed and a local deterministic - generator. +- The property and CLI publication corpora use their recorded fixed seeds and + local deterministic generators. - Canonical comparisons assert decoded structured fields and exact bytes, not diagnostics or log text. - Stress cardinality is fixed at 64. diff --git a/fixtures/providers/echo-target-profile/README.md b/fixtures/providers/echo-target-profile/README.md index 3e844c1..350c33d 100644 --- a/fixtures/providers/echo-target-profile/README.md +++ b/fixtures/providers/echo-target-profile/README.md @@ -5,16 +5,14 @@ profile consumed by the Edict public application-build integration test. Provenance: -- repository: `flyingrobots/echo`; -- source commit: `5413f55316e5baf2d3af93fd64bb71dc7f84e27d`; -- source path: - `crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/target-profile.echo-dpo.cbor`; -- Echo generator identity: - `echo-wesley-gen.provider-artifact-generator@1`; -- Edict domain-framed identity: - `sha256:2e2494121aecf5e6a2d920f5fb85408825d394765fad41484c416397c920fb04`; -- raw file SHA-256: - `1b105d1b1f6cdf5fecdef98b7adeb238525047d43581fe9fd8c44fd213e1788e`. +| Field | Value | +| --- | --- | +| Repository | `flyingrobots/echo` | +| Source commit | `5413f55316e5baf2d3af93fd64bb71dc7f84e27d` | +| Source path | `crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/primary/target-profile.echo-dpo.cbor` | +| Echo generator identity | `echo-wesley-gen.provider-artifact-generator@1` | +| Edict domain-framed identity | `sha256:2e2494121aecf5e6a2d920f5fb85408825d394765fad41484c416397c920fb04` | +| Raw file SHA-256 | `1b105d1b1f6cdf5fecdef98b7adeb238525047d43581fe9fd8c44fd213e1788e` | The fixture is metadata authority only. Edict does not interpret Echo runtime semantics and does not invoke a provider component on the external-action build From 7668c2077c791420ef9f3e4fae01e74ee66b5133 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 29 Jul 2026 18:07:28 -0700 Subject: [PATCH 16/16] docs: record manifest-digest request authority --- CHANGELOG.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d622e0..81844cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,14 +29,16 @@ versions still track specification maturity rather than a released product. request-only source, lawpack/adapter/configuration closure, and provider-owned target profile before publishing canonical `core.cbor` and `target-ir.cbor`. The route requires a typed request, rejects callable Target IR steps and - substituted capability manifests, invokes no provider component, replaces - the output pair transactionally, and clears stale executable-operation - outputs. Request-only lawpack profiles now bind their own exact budget and - opaque target configuration while carrying no semantic effect or target - intrinsic; compilation rejects another profile's budget, and application - builds reject supplied lawpacks outside the ordered root's dependency - closure. A generator-owned workspace-snapshot closure and mirrored Echo-owned - target profile make the full public build reproducible in Edict. + substituted capability manifests, binds authority by exact root-reachable + manifest digest without inventing a coordinate-version relationship, invokes + no provider component, replaces the output pair transactionally, and clears + stale executable-operation outputs. Request-only lawpack profiles now bind + their own exact budget and opaque target configuration while carrying no + semantic effect or target intrinsic; compilation rejects another profile's + budget, and application builds reject supplied lawpacks outside the ordered + root's dependency closure. A generator-owned workspace-snapshot closure and + mirrored Echo-owned target profile make the full public build reproducible in + Edict. - Added typed external-action request values without adding external execution authority to Edict. Digest-locked capability imports and `request` statements preserve exact operation, schema, scope, basis, budget, input, reconciliation,