diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 2fe856b..6a6c6d7 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -36,7 +36,8 @@ exports: - source/surface semantic validation; - compiler context facts and authority-fact loading; - source-to-Core compiler spine for the current supported subset; -- Core IR data structures; +- Core IR data structures, including non-callable typed external-action request + values; - depth-bounded canonical-CBOR encoding/decoding plus canonical Core, Target IR, bundle-layer encoders and digest helpers; - target-profile conformance checks; @@ -64,15 +65,15 @@ Module map: | `ast` | Source-level syntax tree types. | | `semantic` | Surface validation that does not require import resolution or target facts. | | `authority_facts` | File-backed compiler context facts for profiles, budgets, write classes, and source identity. | -| `compiler` | Resolve, type-check, and lower the supported source subset to Core IR. | -| `core_ir` | Runtime-neutral Core module, intent, expression, budget, import, and obstruction data. | +| `compiler` | Resolve, type-check, and lower the supported source subset to Core IR, including typed external-action request construction without execution authority. | +| `core_ir` | Runtime-neutral Core module, intent, expression, budget, import, obstruction, and external-action request data. | | `canonical` | Canonical value model, depth-bounded canonical CBOR encoder/decoder, digest frames, and reviewed golden digest helpers. | | `target_profile` | Runtime-neutral target-profile manifest conformance. | | `lowerability` | Checks whether Core requirements can be satisfied natively, by a direct adapter, or not at all. | | `provider` | Runtime-neutral provider manifest and generated/component provenance envelope validation. | | `provider_invocation` | Pure host-contract, explicitly injected owning-schema, WIT-shaped request/result, canonical artifact, limit, and sealed output-manifest validation. | | `provider_lowering` | Explicit in-process compatibility adapters over the current built-in target lowerers. | -| `target_ir` | Current Echo and git-warp Target IR artifact construction from Core plus lowering facts. | +| `target_ir` | Current Echo and git-warp Target IR artifact construction from Core plus lowering facts, preserving external requests outside callable target steps. | | `contract_bundle` | Participant-neutral bundle assembly, bundle digest preimages, validation, and assurance evidence binding. | | `admission` | Edict-owned Gate C request/receipt shape and binding validation without participant policy execution. | | `highlight` | Lexical highlight roles consumed by editor tooling. | @@ -171,7 +172,7 @@ source text -> semantic surface validation -> compiler context facts -> compiler spine - -> Core IR + -> Core IR (including typed external-action request data) -> canonical Core bytes and digest -> lowerability + target facts -> direct lowering or built-in lowerer compatibility adapter @@ -220,6 +221,8 @@ Use these rules when placing new code: This workspace does not yet implement: - target runtime execution; +- external-action request admission, adapter execution, or settlement + resumption; - participant admission execution; - participant policy evaluation; - trusted lawpack or target-profile authorship; diff --git a/CHANGELOG.md b/CHANGELOG.md index 47ce11d..df8c129 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,17 @@ versions still track specification maturity rather than a released product. ### Added +- 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, + and awaiting-settlement data through canonical Core and Target IR. Operation + families remain bound in the semantic capability closure and the current + allowlist admits only the domain-specific `workspace` root. Floating + capabilities, empty request resources, duplicate request ids, capability + aliases outside request position, and raw ambient operation families reject + with stable kinds. Target steps remain non-callable, the provider seam gains + no host import, and checked Core/Target request goldens bind the complete + public request identity. - Added the compiler-owned `edict.result-projection/v1` artifact for preserving typed application results across the Echo target boundary. The bounded, canonical projection names only declared application input and diff --git a/EDICT.md b/EDICT.md index 53c3b82..3537613 100644 --- a/EDICT.md +++ b/EDICT.md @@ -1267,9 +1267,9 @@ intent (input: ) | Family | Codes | | --- | --- | -| Parser (19) | `Lex`, `ExpectedToken`, `ExpectedKeyword`, `ExpectedIdentifier`, `ExpectedExpression`, `InvalidInteger`, `InvalidDigest`, `InvalidVersion`, `ReservedKeyword`, `UnsupportedSyntax`, `InvalidName`, `EmptyEnum`, `EmptyObstructionMap`, `EmptyMatch`, `MissingRequiredField`, `DuplicateField`, `NonCallEffect`, `ReturnInYieldBlock`, `InvalidTypeCall` | +| Parser (20) | `Lex`, `ExpectedToken`, `ExpectedKeyword`, `ExpectedIdentifier`, `ExpectedExpression`, `InvalidInteger`, `InvalidDigest`, `InvalidVersion`, `ReservedKeyword`, `UnsupportedSyntax`, `InvalidName`, `EmptyEnum`, `EmptyObstructionMap`, `EmptyMatch`, `MissingRequiredField`, `DuplicateField`, `NonCallEffect`, `NonCallExternalActionOperation`, `ReturnInYieldBlock`, `InvalidTypeCall` | | Semantic (7) | `UnboundedScalar`, `MissingOperationMode`, `MissingBudget`, `MissingBasis`, `DuplicateIntentClause`, `DuplicateName`, `ShadowedName` | -| Compiler (10) | `SurfaceValidation`, `MissingContextFact`, `UnsupportedSourceShape`, `UnresolvedType`, `UnknownField`, `TypeMismatch`, `ExpectedPredicate`, `ProfileEffectMismatch`, `DuplicateObstructionFailure`, `DuplicateObstructionPayloadField` | +| Compiler (11) | `SurfaceValidation`, `MissingContextFact`, `UnsupportedSourceShape`, `UnresolvedType`, `UnknownField`, `TypeMismatch`, `ExpectedPredicate`, `ProfileEffectMismatch`, `UnrequestableExternalOperation`, `DuplicateObstructionFailure`, `DuplicateObstructionPayloadField` | | CLI exit codes | `0` ok · `1` compiler/validation diagnostics · `2` invalid CLI input | ### D.5 Digest domains diff --git a/README.md b/README.md index 1b63740..87f6d5d 100644 --- a/README.md +++ b/README.md @@ -575,10 +575,14 @@ What exists today: `let ... = effect(arg) else { failure(binder) => Obstruction }` source shape lowers through file-backed authority facts into typed Core with a semantic effect node and deterministic obstruction map +- 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 - 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 - the initial pure local-record Core artifact + the initial pure local-record Core artifact and typed workspace-snapshot + external request - Typed v1 target-profile manifest conformance for runtime-neutral profile validation, including `echo.dpo@1` and `kv.transactional@1` shaped profiles - Typed v1 lowerability checks for `LoweringRequirements` against explicit @@ -605,8 +609,8 @@ What exists today: through the binary - Published `v0.11.0-alpha.1` release notes for contract-bundle assembly and canonical Target IR artifact bytes/digests: semantic/release bundle digest - goldens, Echo/git-warp Target IR byte/digest goldens, and computed - `targetIrDigest` bundle assembly from real `TargetIrArtifact` values + goldens, Echo/git-warp/workspace-request Target IR byte/digest goldens, and + computed `targetIrDigest` bundle assembly from real `TargetIrArtifact` values - Published `v0.8.0-alpha.1` release notes for the minimal effectful compiler-spine alpha - Published `v0.7.0-alpha.1` release notes for the file-backed @@ -638,6 +642,8 @@ What doesn't exist yet: participant acceptance policy - Target-runtime execution, Echo verifier reports, git-warp commit object creation, or git-warp CRDT reducer verification +- External-action admission, adapter execution, settlement witnessing, or + settlement-driven resumption - Canonical bytes for full `ContractBundleManifest` values - Full admission execution tooling - Participant policy evaluation, capability delegation, and revocation logic diff --git a/ROADMAP.md b/ROADMAP.md index aee9202..d0e0135 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -723,6 +723,33 @@ Edict owns deterministic validation of fact provenance, digest binding, review references, and artifact shape. Continuum and participants own trust policy, identity, delegation, revocation, and acceptance decisions. +## Parallel Integration Track: Typed External Requests + +Milestone: `Typed External Requests` (#17) + +Primary issue: #172 + +Roadmap label: `roadmap-a1` + +This cross-repository track follows Echo's durable external-action protocol and +precedes Hello Echo's first bounded workspace observation: + +```text +Echo #694 durable request and settlement protocol + -> Edict #172 typed request values + -> Hello Echo #10 observed workspace settlement proof +``` + +Edict owns deterministic request construction, exact operation/schema/law +identity, capability closure, and non-callable Core/Target IR representation. +Echo owns request admission, durability, adapter coordination, settlement, +recovery, and replay. Adapters alone own external authority. + +The milestone closes when request source, Core, Target IR, canonical identity, +negative authority cases, and provider-seam non-authority are implemented and +validated. It does not add direct I/O, general algebraic effects, native stack +suspension, writable model authority, or the autonomous delivery loop. + ## v2-design - Future Design Track Milestone: `v2-design` @@ -762,6 +789,7 @@ Milestones: - `v0.13.0-alpha.1`: planned, issues TBD - `v0.14.0-alpha.1`: planned, issues TBD - `v0.15.0-alpha.1`: planned, issues TBD +- `Typed External Requests`: #172 - `v2-design`: #4 Alpha-train release labels: diff --git a/crates/edict-cli/src/main.rs b/crates/edict-cli/src/main.rs index 7bffee1..ad4244e 100644 --- a/crates/edict-cli/src/main.rs +++ b/crates/edict-cli/src/main.rs @@ -17,9 +17,9 @@ use edict_syntax::{ CoreBudget, CoreExpr, CoreImport, CoreIntent, CoreNode, CoreObstructionArm, CoreObstructionReason, CorePredicate, CoreRequireFailureArm, CoreType, CoreValue, HighlightRole, InputConstraint, InputConstraintSource, ParseError, ResourceRef, SemanticError, - Span, TargetEffectLowering, TargetIrArtifact, TargetIrIntent, TargetIrLoweringFacts, - TargetIrRequireFailure, TargetIrRequirement, TargetIrSemanticClosure, TargetIrStep, - TargetLoweringFailure, TargetLoweringFailureKind, WriteClass, + Span, TargetEffectLowering, TargetIrArtifact, TargetIrExternalActionRequest, TargetIrIntent, + TargetIrLoweringFacts, TargetIrRequireFailure, TargetIrRequirement, TargetIrSemanticClosure, + TargetIrStep, TargetLoweringFailure, TargetLoweringFailureKind, WriteClass, }; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; @@ -1189,6 +1189,7 @@ fn compiler_error_kind_name(kind: CompilerErrorKind) -> &'static str { CompilerErrorKind::TypeMismatch => "TypeMismatch", CompilerErrorKind::ExpectedPredicate => "ExpectedPredicate", CompilerErrorKind::ProfileEffectMismatch => "ProfileEffectMismatch", + CompilerErrorKind::UnrequestableExternalOperation => "UnrequestableExternalOperation", CompilerErrorKind::DuplicateObstructionFailure => "DuplicateObstructionFailure", CompilerErrorKind::DuplicateObstructionPayloadField => "DuplicateObstructionPayloadField", } @@ -1261,6 +1262,11 @@ fn target_ir_semantic_closure_review(closure: &TargetIrSemanticClosure) -> Value .iter() .map(resource_ref_review) .collect::>(), + "capabilities": closure + .capabilities + .iter() + .map(resource_ref_review) + .collect::>(), }) } @@ -1279,6 +1285,11 @@ fn target_ir_intent_review(intent: &TargetIrIntent) -> Value { .map(target_ir_requirement_review) .collect::>(), "steps": intent.steps.iter().map(target_ir_step_review).collect::>(), + "externalActionRequests": intent + .external_action_requests + .iter() + .map(target_ir_external_action_request_review) + .collect::>(), "result": core_expr_review(&intent.result), }); insert_optional_review_field( @@ -1331,6 +1342,28 @@ fn target_ir_step_review(step: &TargetIrStep) -> Value { }) } +fn target_ir_external_action_request_review(request: &TargetIrExternalActionRequest) -> Value { + json!({ + "id": request.id, + "binding": local_ref_review(&request.binding), + "operation": resource_ref_review(&request.operation), + "inputType": request.input_type, + "settlementType": request.settlement_type, + "inputSchema": resource_ref_review(&request.input_schema), + "settlementSchema": resource_ref_review(&request.settlement_schema), + "input": core_expr_review(&request.input), + "authorityScope": core_expr_review(&request.authority_scope), + "basis": core_expr_review(&request.basis), + "budget": { + "maxSettlementBytes": core_expr_review(&request.budget.max_settlement_bytes), + "maxAttempts": core_expr_review(&request.budget.max_attempts), + }, + "reconciliationLaw": resource_ref_review(&request.reconciliation_law), + "state": "awaitingSettlement", + "settlementAdmission": "schemaRequired", + }) +} + fn core_import_review(import: &CoreImport) -> Value { json!({ "kind": import.kind.as_str(), @@ -1362,6 +1395,9 @@ fn core_type_review(ty: &CoreType) -> Value { json!({ "kind": "map", "key": key, "value": value, "max": max }) } CoreType::CapabilityRef { item } => json!({ "kind": "capabilityRef", "item": item }), + CoreType::ExternalActionRequest { settlement } => { + json!({ "kind": "externalActionRequest", "settlement": settlement }) + } } } @@ -1423,6 +1459,37 @@ fn core_node_review(node: &CoreNode) -> Value { .map(|(name, arm)| (name.clone(), obstruction_arm_review(arm))) .collect::>(), }), + CoreNode::ExternalActionRequest { + binding, + operation, + input_type, + settlement_type, + input_schema, + settlement_schema, + input, + authority_scope, + basis, + budget, + reconciliation_law, + } => json!({ + "kind": "externalActionRequest", + "binding": local_ref_review(binding), + "operation": resource_ref_review(operation), + "inputType": input_type, + "settlementType": settlement_type, + "inputSchema": resource_ref_review(input_schema), + "settlementSchema": resource_ref_review(settlement_schema), + "input": core_expr_review(input), + "authorityScope": core_expr_review(authority_scope), + "basis": core_expr_review(basis), + "budget": { + "maxSettlementBytes": core_expr_review(&budget.max_settlement_bytes), + "maxAttempts": core_expr_review(&budget.max_attempts), + }, + "reconciliationLaw": resource_ref_review(reconciliation_law), + "state": "awaitingSettlement", + "settlementAdmission": "schemaRequired", + }), } } diff --git a/crates/edict-cli/tests/jsonl_cli.rs b/crates/edict-cli/tests/jsonl_cli.rs index e91d6c0..9d9892f 100644 --- a/crates/edict-cli/tests/jsonl_cli.rs +++ b/crates/edict-cli/tests/jsonl_cli.rs @@ -49,6 +49,36 @@ intent replaceThing(input: Input) } "#; +const EXTERNAL_REQUEST_SOURCE: &str = r#"package demo.external_request@1; + +use capability workspace.snapshot.observe@1 digest "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" as snapshot; + +type Input = { + payload: Bytes, + scope: Bytes, + basis: Bytes, + maxSettlementBytes: U64, + maxAttempts: U32, +}; + +intent observe(input: Input) + returns ExternalActionRequest> + profile hello.readOnly + basis input.basis + budget <= hello.tinyBudget +{ + 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; +} +"#; + const ECHO_TARGET_PROFILE_DIGEST: &str = "sha256:1111111111111111111111111111111111111111111111111111111111111111"; @@ -245,6 +275,100 @@ fn project_accepts_dirty_source_and_emits_syntax_core_target_ir_projection() { assert_available_target_ir_projection(&stdout, &expected_target_digest); } +#[test] +fn project_exposes_external_requests_as_non_callable_review_data() { + let output = run_edict(&jsonl([ + projection_settings(["diagnostics", "core", "targetIr"]), + json!({ + "schema": "edict.compiler.input/v1", + "type": "compilerInput", + "kind": "source", + "name": "unsaved/external-request.edict", + "source": EXTERNAL_REQUEST_SOURCE, + }), + ])); + + let stdout = assert_successful_projection_output(&output); + assert_empty_projection_diagnostics(&stdout); + + let core = record_of_type(&stdout, "core"); + assert_eq!(core.get("state").and_then(Value::as_str), Some("available")); + assert_eq!( + core.pointer("/review/intents/observe/body/nodes/0/kind") + .and_then(Value::as_str), + Some("externalActionRequest") + ); + assert_eq!( + core.pointer("/review/intents/observe/body/nodes/0/operation/coordinate") + .and_then(Value::as_str), + Some("workspace.snapshot.observe@1") + ); + assert_external_request_review( + core.pointer("/review/intents/observe/body/nodes/0") + .expect("Core request review exists"), + ); + + let target = record_of_type(&stdout, "targetIr"); + assert_eq!( + target.get("state").and_then(Value::as_str), + Some("available") + ); + assert_eq!( + target + .pointer("/review/semanticClosure/capabilities/0/coordinate") + .and_then(Value::as_str), + Some("workspace.snapshot.observe@1") + ); + assert_eq!( + target + .pointer("/review/intents/observe/externalActionRequests/0/operation/coordinate") + .and_then(Value::as_str), + Some("workspace.snapshot.observe@1") + ); + assert_external_request_review( + target + .pointer("/review/intents/observe/externalActionRequests/0") + .expect("Target IR request review exists"), + ); + assert_eq!( + target + .pointer("/review/intents/observe/steps") + .and_then(Value::as_array) + .map(Vec::len), + Some(0) + ); +} + +fn assert_external_request_review(request: &Value) { + for (pointer, expected) in [ + ("/operation/coordinate", "workspace.snapshot.observe@1"), + ("/inputType", "Bytes"), + ("/settlementType", "Bytes"), + ("/inputSchema/coordinate", "workspace.snapshot.input@1"), + ( + "/settlementSchema/coordinate", + "workspace.snapshot.settlement@1", + ), + ("/input/field", "payload"), + ("/authorityScope/field", "scope"), + ("/basis/field", "basis"), + ("/budget/maxSettlementBytes/field", "maxSettlementBytes"), + ("/budget/maxAttempts/field", "maxAttempts"), + ( + "/reconciliationLaw/coordinate", + "workspace.snapshot.reconcile@1", + ), + ("/state", "awaitingSettlement"), + ("/settlementAdmission", "schemaRequired"), + ] { + assert_eq!( + request.pointer(pointer).and_then(Value::as_str), + Some(expected), + "{pointer}" + ); + } +} + #[test] fn project_invalid_source_emits_diagnostics_without_process_failure() { let output = run_edict(&jsonl([ @@ -1048,7 +1172,10 @@ fn projection_settings(emit: [&str; N]) -> Value { "coordinate": "echo.dpo@1", "profileDigest": ECHO_TARGET_PROFILE_DIGEST, "irDomain": "echo.span-ir/v1", - "operationProfiles": ["continuum.profile.write/v1"], + "operationProfiles": [ + "continuum.profile.read-only/v1", + "continuum.profile.write/v1" + ], "obstructionCoordinates": ["rejected"], "effectLowerings": [ { @@ -1356,7 +1483,10 @@ fn projection_target_facts() -> TargetIrLoweringFacts { digest: Some(ECHO_TARGET_PROFILE_DIGEST.to_owned()), }, target_ir_domain: ECHO_SPAN_IR_DOMAIN.to_owned(), - operation_profiles: vec!["continuum.profile.write/v1".to_owned()], + operation_profiles: vec![ + "continuum.profile.read-only/v1".to_owned(), + "continuum.profile.write/v1".to_owned(), + ], obstruction_coordinates: vec!["rejected".to_owned()], effect_lowerings: vec![TargetEffectLowering { effect: "target.replace".to_owned(), diff --git a/crates/edict-provider-schema/tests/provider_contract_pack.rs b/crates/edict-provider-schema/tests/provider_contract_pack.rs index 45ae88b..7b7032e 100644 --- a/crates/edict-provider-schema/tests/provider_contract_pack.rs +++ b/crates/edict-provider-schema/tests/provider_contract_pack.rs @@ -14,9 +14,10 @@ use edict_syntax::{ canonical_target_profile_contract_resources, compile_to_core, decode_canonical_cbor, digest_target_profile_contract_resource, encode_core_module, encode_result_projection, encode_target_ir_artifact, parse_module, CanonicalValue, CompilerContext, CoreBudget, CoreExpr, - CoreObstructionReason, CorePredicate, CoreValue, ProviderArtifactSchemaValidationErrorKind, - ResourceRef, ResultProjection, ResultProjectionExpr, ResultProjectionSource, TargetIrArtifact, - TargetIrIntent, TargetIrRequireFailure, TargetIrRequirement, TargetIrSemanticClosure, + CoreExternalActionBudget, CoreObstructionReason, CorePredicate, CoreValue, LocalRef, + ProviderArtifactSchemaValidationErrorKind, ResourceRef, ResultProjection, ResultProjectionExpr, + ResultProjectionSource, TargetIrArtifact, TargetIrExternalActionRequest, TargetIrIntent, + TargetIrRequireFailure, TargetIrRequirement, TargetIrSemanticClosure, TargetProfileContractResource, WriteClass, AUTHORITY_FACTS_API_VERSION, CORE_MODULE_DIGEST_DOMAIN, PROVIDER_LAWPACK_ARTIFACT_DOMAIN, RESULT_PROJECTION_DIGEST_DOMAIN, TARGET_IR_ARTIFACT_DIGEST_DOMAIN, TARGET_PROFILE_API_VERSION, @@ -34,6 +35,8 @@ const RESULT_PROJECTION_CDDL: &[u8] = const TARGET_IR_CDDL: &[u8] = include_bytes!("../../../docs/abi/edict-target-ir.cddl"); const CORE_FIXTURE: &[u8] = include_bytes!("../../../fixtures/core/canonical/bounded-hello.core.cbor"); +const EXTERNAL_REQUEST_CORE_FIXTURE: &[u8] = + include_bytes!("../../../fixtures/core/canonical/workspace-snapshot.core.cbor"); const AUTHORITY_FACTS_FIXTURE: &[u8] = include_bytes!( "../../../fixtures/authority-facts/canonical/example-effectful.authority-facts.cbor" ); @@ -41,6 +44,8 @@ const TARGET_IR_FIXTURE: &[u8] = include_bytes!("../../../fixtures/target-ir/canonical/echo-effectful.target-ir.cbor"); const ALTERNATE_TARGET_IR_FIXTURE: &[u8] = include_bytes!("../../../fixtures/target-ir/canonical/gitwarp-append.target-ir.cbor"); +const EXTERNAL_REQUEST_TARGET_IR_FIXTURE: &[u8] = + include_bytes!("../../../fixtures/target-ir/canonical/workspace-snapshot.target-ir.cbor"); const LAWPACK_ADAPTER_FIXTURE: &[u8] = include_bytes!("../../../fixtures/lawpack/hello-echo/adapter.cbor"); const RESULT_PROJECTION_FIXTURE: &[u8] = @@ -185,10 +190,24 @@ fn every_published_root_validates_reference_and_rejects_mutation() { ); } +#[test] +fn core_root_matches_reference_encoder() { + let pack = assemble(canonical_target_profile_contract_resources()); + for fixture in [CORE_FIXTURE, EXTERNAL_REQUEST_CORE_FIXTURE] { + let value = decode_canonical_cbor(fixture).expect("reviewed Core is canonical"); + pack.validate_domain(CORE_MODULE_DIGEST_DOMAIN, &value) + .expect("reviewed Core satisfies the Edict-owned root"); + } +} + #[test] fn target_ir_root_matches_reference_encoder() { let pack = assemble(canonical_target_profile_contract_resources()); - for fixture in [TARGET_IR_FIXTURE, ALTERNATE_TARGET_IR_FIXTURE] { + for fixture in [ + TARGET_IR_FIXTURE, + ALTERNATE_TARGET_IR_FIXTURE, + EXTERNAL_REQUEST_TARGET_IR_FIXTURE, + ] { let value = decode_canonical_cbor(fixture).expect("reviewed Target IR is canonical"); pack.validate_domain(TARGET_IR_ARTIFACT_DIGEST_DOMAIN, &value) .expect("reviewed Target IR satisfies the Edict-owned root"); @@ -197,6 +216,17 @@ fn target_ir_root_matches_reference_encoder() { pack.validate_domain(TARGET_IR_ARTIFACT_DIGEST_DOMAIN, &encoded_requirements) .expect("encoder output with both requirement dispositions satisfies the root"); + let encoded_request = encoded_target_ir_with_external_request(); + pack.validate_domain(TARGET_IR_ARTIFACT_DIGEST_DOMAIN, &encoded_request) + .expect("encoder output with an external request satisfies the closed root"); + let mut request_without_closure = encoded_request; + remove_map_field(&mut request_without_closure, "semanticClosure"); + assert_eq!( + pack.validate_domain(TARGET_IR_ARTIFACT_DIGEST_DOMAIN, &request_without_closure), + Err(ProviderArtifactSchemaValidationErrorKind::SchemaMismatch), + "external requests cannot validate through the legacy closure-free root" + ); + let mut missing_semantic_closure = encoded_target_ir_with_coordinate("example.target@1"); remove_map_field(&mut missing_semantic_closure, "semanticClosure"); assert_eq!( @@ -708,6 +738,66 @@ fn encoded_target_ir_with_requirements() -> CanonicalValue { } fn encoded_target_ir_with_coordinate(coordinate: &str) -> CanonicalValue { + encode_target_ir_value(&representative_target_ir(coordinate)) +} + +fn encoded_target_ir_with_external_request() -> CanonicalValue { + let mut artifact = representative_target_ir("example.target-profile@1"); + let operation = ResourceRef { + coordinate: "workspace.snapshot.observe@1".to_owned(), + digest: Some(format!("sha256:{}", "4".repeat(64))), + }; + artifact + .semantic_closure + .as_mut() + .expect("representative Target IR is closed") + .capabilities + .push(operation.clone()); + artifact + .intents + .get_mut("apply") + .expect("representative intent") + .external_action_requests + .push(TargetIrExternalActionRequest { + id: "apply.request.0".to_owned(), + binding: LocalRef { + id: "local.0".to_owned(), + alpha_name: "$local0".to_owned(), + ty: "edict.external-action.request/v1>".to_owned(), + }, + operation, + input_type: "Bytes".to_owned(), + settlement_type: "Bytes".to_owned(), + input_schema: ResourceRef { + coordinate: "workspace.snapshot.input@1".to_owned(), + digest: Some(format!("sha256:{}", "5".repeat(64))), + }, + settlement_schema: ResourceRef { + coordinate: "workspace.snapshot.settlement@1".to_owned(), + digest: Some(format!("sha256:{}", "6".repeat(64))), + }, + input: CoreExpr::Const(CoreValue::Bytes(vec![1, 2, 3])), + authority_scope: CoreExpr::Const(CoreValue::Bytes(vec![4, 5])), + basis: CoreExpr::Const(CoreValue::Bytes(vec![6, 7])), + budget: CoreExternalActionBudget { + max_settlement_bytes: CoreExpr::Const(CoreValue::Int { + width: "U64".to_owned(), + value: "64".to_owned(), + }), + max_attempts: CoreExpr::Const(CoreValue::Int { + width: "U32".to_owned(), + value: "3".to_owned(), + }), + }, + reconciliation_law: ResourceRef { + coordinate: "workspace.snapshot.reconcile@1".to_owned(), + digest: Some(format!("sha256:{}", "7".repeat(64))), + }, + }); + encode_target_ir_value(&artifact) +} + +fn representative_target_ir(coordinate: &str) -> TargetIrArtifact { let terminal_reason = CoreObstructionReason { kind: "example.Terminal".to_owned(), payload: BTreeMap::from([( @@ -722,7 +812,7 @@ fn encoded_target_ir_with_coordinate(coordinate: &str) -> CanonicalValue { CoreExpr::Const(CoreValue::String("preserved".to_owned())), )]), }; - let artifact = TargetIrArtifact { + TargetIrArtifact { domain: "example.target-ir/v1".to_owned(), target_profile: ResourceRef { coordinate: coordinate.to_owned(), @@ -738,6 +828,7 @@ fn encoded_target_ir_with_coordinate(coordinate: &str) -> CanonicalValue { coordinate: "example.lawpack@1".to_owned(), digest: Some(format!("sha256:{}", "3".repeat(64))), }], + capabilities: Vec::new(), }), intents: BTreeMap::from([( "apply".to_owned(), @@ -769,11 +860,15 @@ fn encoded_target_ir_with_coordinate(coordinate: &str) -> CanonicalValue { }, ], steps: Vec::new(), + external_action_requests: Vec::new(), result: CoreExpr::Const(CoreValue::Null), }, )]), - }; - let bytes = encode_target_ir_artifact(&artifact).expect("representative Target IR encodes"); + } +} + +fn encode_target_ir_value(artifact: &TargetIrArtifact) -> CanonicalValue { + let bytes = encode_target_ir_artifact(artifact).expect("representative Target IR encodes"); decode_canonical_cbor(&bytes).expect("encoded representative Target IR is canonical") } diff --git a/crates/edict-syntax/src/ast.rs b/crates/edict-syntax/src/ast.rs index a2c73e6..7c34203 100644 --- a/crates/edict-syntax/src/ast.rs +++ b/crates/edict-syntax/src/ast.rs @@ -29,7 +29,7 @@ pub enum ImportKind { Lawpack, Target, Core, - /// `use capability ... as ...` — present in product sketches; rejected by v1. + /// Digest-bound external operation family available only to `request`. Capability, } @@ -47,6 +47,14 @@ pub struct Import { pub span: Span, } +/// A digest-locked package resource carried by source syntax. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DigestLockedPackageRef { + pub package: PackageRef, + pub digest: String, + pub span: Span, +} + /// A top-level declaration. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Decl { @@ -205,6 +213,20 @@ pub enum Stmt { els: Option, span: Span, }, + /// Construct one typed external-action request value without performing it. + ExternalActionRequest { + name: String, + request_type: TypeRef, + operation: Expr, + input_schema: DigestLockedPackageRef, + settlement_schema: DigestLockedPackageRef, + authority_scope: Box, + basis: Box, + max_settlement_bytes: Box, + max_attempts: Box, + reconciliation_law: DigestLockedPackageRef, + span: Span, + }, /// `require predicate else ;` (always carries `else`). Require { predicate: Expr, diff --git a/crates/edict-syntax/src/canonical.rs b/crates/edict-syntax/src/canonical.rs index 53891ca..b45e8d4 100644 --- a/crates/edict-syntax/src/canonical.rs +++ b/crates/edict-syntax/src/canonical.rs @@ -13,12 +13,13 @@ use sha2::{Digest, Sha256}; use crate::core_ir::{ is_lowercase_sha256_review_digest, parse_core_integer, CompareOp, CoreBlock, CoreBudget, - CoreExpr, CoreImport, CoreIntent, CoreModule, CoreNode, CoreObstructionArm, CorePredicate, - CoreType, CoreValue, InputConstraint, InputConstraintSource, LocalRef, ResourceRef, + CoreExpr, CoreExternalActionBudget, CoreImport, CoreImportKind, CoreIntent, CoreModule, + CoreNode, CoreObstructionArm, CorePredicate, CoreType, CoreValue, InputConstraint, + InputConstraintSource, LocalRef, ResourceRef, }; use crate::target_ir::{ - TargetIrArtifact, TargetIrIntent, TargetIrRequireFailure, TargetIrRequirement, - TargetIrSemanticClosure, TargetIrStep, + TargetIrArtifact, TargetIrExternalActionRequest, TargetIrIntent, TargetIrRequireFailure, + TargetIrRequirement, TargetIrSemanticClosure, TargetIrStep, }; /// Canonical encoding profile for Core artifacts. @@ -492,17 +493,43 @@ fn target_ir_artifact_value(artifact: &TargetIrArtifact) -> Result Result Result { - let mut lawpacks = BTreeMap::<&str, &ResourceRef>::new(); - for resource in &closure.lawpacks { - if let Some(prior) = lawpacks.get(resource.coordinate.as_str()) { + let lawpacks = target_ir_resource_set(&closure.lawpacks, "lawpack")?; + let capabilities = target_ir_resource_set(&closure.capabilities, "capability")?; + let mut entries = vec![ + ( + "sourceCore", + target_ir_resource_ref_value(&closure.source_core)?, + ), + ( + "lawpacks", + sorted_array_results(lawpacks.into_values().map(target_ir_resource_ref_value))?, + ), + ]; + if !capabilities.is_empty() { + entries.push(( + "capabilities", + sorted_array_results(capabilities.into_values().map(target_ir_resource_ref_value))?, + )); + } + Ok(map(entries)) +} + +fn target_ir_resource_set<'a>( + resources: &'a [ResourceRef], + field_name: &str, +) -> Result, CanonicalError> { + let mut indexed = BTreeMap::new(); + for resource in resources { + if let Some(prior) = indexed.get(resource.coordinate.as_str()) { if *prior != resource { return Err(CanonicalError::new( CanonicalErrorKind::UnsupportedValue, format!( - "Target IR lawpack coordinate `{}` is bound to conflicting resources", + "Target IR {field_name} coordinate `{}` is bound to conflicting resources", resource.coordinate ), )); } } else { - lawpacks.insert(resource.coordinate.as_str(), resource); + indexed.insert(resource.coordinate.as_str(), resource); } } - Ok(map([ - ( - "sourceCore", - target_ir_resource_ref_value(&closure.source_core)?, - ), - ( - "lawpacks", - sorted_array_results(lawpacks.into_values().map(target_ir_resource_ref_value))?, - ), - ])) + Ok(indexed) } fn target_ir_resource_ref_value(resource: &ResourceRef) -> Result { @@ -596,6 +639,18 @@ fn target_ir_resource_ref_value(resource: &ResourceRef) -> Result Result { + let mut request_ids = BTreeSet::new(); + for request in &intent.external_action_requests { + if !request_ids.insert(request.id.as_str()) { + return Err(CanonicalError::new( + CanonicalErrorKind::UnsupportedValue, + format!( + "Target IR external-action request id `{}` is duplicated", + request.id + ), + )); + } + } let mut entries = vec![ ("operationProfile", text(&intent.operation_profile)), ( @@ -619,9 +674,53 @@ fn target_ir_intent_value(intent: &TargetIrIntent) -> Result Result { + Ok(map([ + ("id", text(&request.id)), + ("binding", local_ref_value(&request.binding)), + ( + "operation", + target_ir_resource_ref_value(&request.operation)?, + ), + ("inputType", text(&request.input_type)), + ("settlementType", text(&request.settlement_type)), + ( + "inputSchema", + target_ir_resource_ref_value(&request.input_schema)?, + ), + ( + "settlementSchema", + target_ir_resource_ref_value(&request.settlement_schema)?, + ), + ("input", core_expr_value(&request.input)?), + ("authorityScope", core_expr_value(&request.authority_scope)?), + ("basis", core_expr_value(&request.basis)?), + ("budget", external_action_budget_value(&request.budget)?), + ( + "reconciliationLaw", + target_ir_resource_ref_value(&request.reconciliation_law)?, + ), + ("state", text("awaitingSettlement")), + ("settlementAdmission", text("schemaRequired")), + ])) +} + fn target_ir_requirement_value( requirement: &TargetIrRequirement, ) -> Result { @@ -673,6 +772,31 @@ fn target_ir_step_value(step: &TargetIrStep) -> Result Result { + let capability_imports = module + .imports + .iter() + .filter(|import| import.kind == CoreImportKind::Capability) + .map(|import| &import.resource) + .collect::>(); + for request in module + .intents + .values() + .flat_map(|intent| &intent.body.nodes) + .filter_map(|node| match node { + CoreNode::ExternalActionRequest { operation, .. } => Some(operation), + CoreNode::Let { .. } | CoreNode::Require { .. } | CoreNode::Effect { .. } => None, + }) + { + if !capability_imports.contains(&request) { + return Err(CanonicalError::new( + CanonicalErrorKind::UnsupportedValue, + format!( + "Core request operation `{}` is absent from the capability imports", + request.coordinate + ), + )); + } + } Ok(map([ ("apiVersion", text(&module.api_version)), ("coordinate", text(&module.coordinate)), @@ -713,6 +837,12 @@ fn core_import_value(import: &CoreImport) -> Result Result { + if resource.coordinate.is_empty() { + return Err(CanonicalError::new( + CanonicalErrorKind::UnsupportedValue, + "Core resource coordinate is empty", + )); + } let mut entries = vec![("id", text(&resource.coordinate))]; let Some(digest) = &resource.digest else { return Err(CanonicalError::new( @@ -807,6 +937,10 @@ fn core_type_value(ty: &CoreType) -> CanonicalValue { CoreType::CapabilityRef { item } => { map([("kind", text("CapabilityRef")), ("item", text(item))]) } + CoreType::ExternalActionRequest { settlement } => map([ + ("kind", text("ExternalActionRequest")), + ("settlement", text(settlement)), + ]), } } @@ -900,9 +1034,49 @@ fn core_node_value(node: &CoreNode) -> Result { }))?, ), ])), + CoreNode::ExternalActionRequest { + binding, + operation, + input_type, + settlement_type, + input_schema, + settlement_schema, + input, + authority_scope, + basis, + budget, + reconciliation_law, + } => Ok(map([ + ("kind", text("externalActionRequest")), + ("binding", local_ref_value(binding)), + ("operation", resource_ref_value(operation)?), + ("inputType", text(input_type)), + ("settlementType", text(settlement_type)), + ("inputSchema", resource_ref_value(input_schema)?), + ("settlementSchema", resource_ref_value(settlement_schema)?), + ("input", core_expr_value(input)?), + ("authorityScope", core_expr_value(authority_scope)?), + ("basis", core_expr_value(basis)?), + ("budget", external_action_budget_value(budget)?), + ("reconciliationLaw", resource_ref_value(reconciliation_law)?), + ("state", text("awaitingSettlement")), + ("settlementAdmission", text("schemaRequired")), + ])), } } +fn external_action_budget_value( + budget: &CoreExternalActionBudget, +) -> Result { + Ok(map([ + ( + "maxSettlementBytes", + core_expr_value(&budget.max_settlement_bytes)?, + ), + ("maxAttempts", core_expr_value(&budget.max_attempts)?), + ])) +} + fn core_require_failure_arm_value( arm: &crate::core_ir::CoreRequireFailureArm, ) -> Result { diff --git a/crates/edict-syntax/src/compiler.rs b/crates/edict-syntax/src/compiler.rs index b8d8feb..ce9ed8c 100644 --- a/crates/edict-syntax/src/compiler.rs +++ b/crates/edict-syntax/src/compiler.rs @@ -6,15 +6,17 @@ use std::collections::{BTreeMap, BTreeSet}; use crate::ast::{ - BinOp, Block, BoundRef, Decl, ElseClause, Expr, FieldDecl, Import, ImportKind, IntentClause, - IntentDecl, Module, ObstructionArm, ObstructionHandler, ObstructionTarget, RecordEntry, - RequireElseArm, ScalarRefine, Stmt, TypeDecl, TypeExpr, TypeRef, UnOp, YieldBlock, + BinOp, Block, BoundRef, Decl, DigestLockedPackageRef, ElseClause, Expr, FieldDecl, Import, + ImportKind, IntentClause, IntentDecl, Module, ObstructionArm, ObstructionHandler, + ObstructionTarget, RecordEntry, RequireElseArm, ScalarRefine, Stmt, TypeDecl, TypeExpr, + TypeRef, UnOp, YieldBlock, }; use crate::core_ir::{ - parse_core_integer, CompareOp, CoreBlock, CoreBudget, CoreExpr, CoreImport, CoreImportKind, - CoreIntent, CoreModule, CoreNode, CoreObstructionArm, CoreObstructionReason, CorePredicate, - CoreRequireFailureArm, CoreType, CoreValue, InputConstraint, InputConstraintSource, LocalRef, - ResourceRef, CORE_API_VERSION, CORE_APPLICATION_INPUT_LOCAL_ID, + is_lowercase_sha256_review_digest, parse_core_integer, CompareOp, CoreBlock, CoreBudget, + CoreExpr, CoreExternalActionBudget, CoreImport, CoreImportKind, CoreIntent, CoreModule, + CoreNode, CoreObstructionArm, CoreObstructionReason, CorePredicate, CoreRequireFailureArm, + CoreType, CoreValue, InputConstraint, InputConstraintSource, LocalRef, ResourceRef, + CORE_API_VERSION, CORE_APPLICATION_INPUT_LOCAL_ID, }; use crate::lowerability::WriteClass; use crate::semantic::validate_surface; @@ -40,6 +42,7 @@ pub enum CompilerErrorKind { TypeMismatch, ExpectedPredicate, ProfileEffectMismatch, + UnrequestableExternalOperation, DuplicateObstructionFailure, DuplicateObstructionPayloadField, } @@ -201,7 +204,7 @@ pub fn resolve_module( ) -> Result> { let mut errors = Vec::new(); let coordinate = package_coordinate(&module.package.path, &module.package.version); - let imports = resolve_imports(&module.imports, &mut errors); + let imports = resolve_imports(&module.imports); let mut types = Vec::new(); let mut intents = Vec::new(); @@ -281,18 +284,10 @@ pub fn lower_core(typed: &TypedModule) -> Result> }) } -fn resolve_imports(imports: &[Import], errors: &mut Vec) -> Vec { +fn resolve_imports(imports: &[Import]) -> Vec { let mut out = Vec::new(); for import in imports { let Some(kind) = core_import_kind(import.kind) else { - if import.kind == ImportKind::Capability { - errors.push(error( - CompilerStage::Resolve, - CompilerErrorKind::UnsupportedSourceShape, - "capability imports are not supported by the v1 compiler spine", - import.span, - )); - } continue; }; let Some(package) = &import.package else { @@ -315,7 +310,8 @@ fn core_import_kind(kind: ImportKind) -> Option { ImportKind::Lawpack => Some(CoreImportKind::Lawpack), ImportKind::Target => Some(CoreImportKind::Target), ImportKind::Core => Some(CoreImportKind::Core), - ImportKind::Shape | ImportKind::Capability => None, + ImportKind::Capability => Some(CoreImportKind::Capability), + ImportKind::Shape => None, } } @@ -394,6 +390,7 @@ enum TypeKind { String { max: u64, canonical: String }, Bytes { max: u64 }, Record(BTreeMap), + ExternalActionRequest { settlement: Box }, } impl TypeShape { @@ -414,6 +411,19 @@ impl TypeShape { .map(|(name, shape)| (name.clone(), shape.coord.clone())) .collect(), }, + TypeKind::ExternalActionRequest { settlement } => CoreType::ExternalActionRequest { + settlement: settlement.coord.clone(), + }, + } + } + + fn value_type_coord(&self) -> String { + match &self.kind { + TypeKind::Bool => "Bool".to_owned(), + TypeKind::Int { width } => width.clone(), + TypeKind::String { max, canonical } => string_type_coord(*max, canonical), + TypeKind::Bytes { max } => bytes_type_coord(*max), + TypeKind::Record(_) | TypeKind::ExternalActionRequest { .. } => self.coord.clone(), } } } @@ -564,6 +574,17 @@ impl<'a> TypeChecker<'a> { None } } + TypeRef::Named { path, args } + if path.as_slice() == ["ExternalActionRequest"] && args.len() == 1 => + { + let settlement = self.type_ref_shape(&args[0], span, None)?; + Some(TypeShape { + coord: format!("edict.external-action.request/v1<{}>", settlement.coord), + kind: TypeKind::ExternalActionRequest { + settlement: Box::new(settlement), + }, + }) + } TypeRef::StringTy(Some(refine)) => self.string_shape(refine, span, coord_hint), TypeRef::BytesTy(Some(bound)) => self.bytes_shape(bound, span, coord_hint), TypeRef::BytesTy(None) @@ -801,6 +822,34 @@ impl<'a> TypeChecker<'a> { self.unsupported_stmt(*span, "effect statement"); } } + Stmt::ExternalActionRequest { + name, + request_type, + operation, + input_schema, + settlement_schema, + authority_scope, + basis, + max_settlement_bytes, + max_attempts, + reconciliation_law, + span, + } => self.check_external_action_request( + name, + request_type, + operation, + input_schema, + settlement_schema, + authority_scope, + basis, + max_settlement_bytes, + max_attempts, + reconciliation_law, + *span, + env, + locals, + state, + ), Stmt::Require { predicate, arm, .. } => { self.check_require_stmt(predicate, arm, env, state); } @@ -811,6 +860,192 @@ impl<'a> TypeChecker<'a> { } } + #[allow(clippy::too_many_arguments)] + fn check_external_action_request( + &mut self, + name: &str, + request_type: &TypeRef, + operation: &Expr, + input_schema: &DigestLockedPackageRef, + settlement_schema: &DigestLockedPackageRef, + authority_scope: &Expr, + basis: &Expr, + max_settlement_bytes: &Expr, + max_attempts: &Expr, + reconciliation_law: &DigestLockedPackageRef, + span: Span, + env: &mut BTreeMap, + locals: &mut Vec, + state: &mut BodyState, + ) { + let Some(request_shape) = self.type_ref_shape(request_type, span, None) else { + return; + }; + let TypeKind::ExternalActionRequest { settlement } = &request_shape.kind else { + self.errors.push(error( + CompilerStage::TypeCheck, + CompilerErrorKind::TypeMismatch, + "request binding type must be ExternalActionRequest", + span, + )); + return; + }; + let settlement_type = settlement.coord.clone(); + let Some((operation_resource, input)) = + self.check_external_action_operation(operation, env, span) + else { + return; + }; + if !requestable_operation_family(&operation_resource.coordinate) { + self.errors.push(error( + CompilerStage::TypeCheck, + CompilerErrorKind::UnrequestableExternalOperation, + format!( + "external operation family `{}` is not requestable", + operation_resource.coordinate + ), + span, + )); + return; + } + let Some(authority_scope) = self.check_expr(authority_scope, env) else { + return; + }; + let Some(basis) = self.check_expr(basis, env) else { + return; + }; + let settlement_budget_shape = integer_shape("U64"); + let Some(max_settlement_bytes) = self.check_expr_with_expected( + max_settlement_bytes, + env, + Some(&settlement_budget_shape), + ) else { + return; + }; + let attempt_budget_shape = integer_shape("U32"); + let Some(max_attempts) = + self.check_expr_with_expected(max_attempts, env, Some(&attempt_budget_shape)) + else { + return; + }; + let Some(input_schema) = self.request_resource(input_schema) else { + return; + }; + let Some(settlement_schema) = self.request_resource(settlement_schema) else { + return; + }; + let Some(reconciliation_law) = self.request_resource(reconciliation_law) else { + return; + }; + let local = next_local(&mut state.local_index, request_shape.coord.clone()); + state.nodes.push(CoreNode::ExternalActionRequest { + binding: local.clone(), + operation: operation_resource, + input_type: input.ty.value_type_coord(), + settlement_type, + input_schema, + settlement_schema, + input: input.expr, + authority_scope: Box::new(authority_scope.expr), + basis: Box::new(basis.expr), + budget: Box::new(CoreExternalActionBudget { + max_settlement_bytes: max_settlement_bytes.expr, + max_attempts: max_attempts.expr, + }), + reconciliation_law, + }); + locals.push(local.clone()); + env.insert(name.to_owned(), (local, request_shape)); + } + + fn check_external_action_operation( + &mut self, + operation: &Expr, + env: &BTreeMap, + span: Span, + ) -> Option<(ResourceRef, TypedValue)> { + let Expr::Call { + callee, + type_args, + args, + .. + } = operation + else { + self.unsupported_stmt(span, "external-action request operation"); + return None; + }; + if !type_args.is_empty() { + self.errors.push(error( + CompilerStage::TypeCheck, + CompilerErrorKind::UnsupportedSourceShape, + "external-action request operation does not accept type arguments", + span, + )); + return None; + } + let Expr::Ident { name: alias, .. } = callee.as_ref() else { + self.errors.push(error( + CompilerStage::TypeCheck, + CompilerErrorKind::UnsupportedSourceShape, + "external-action request operation must name one capability import alias", + span, + )); + return None; + }; + let Some(operation_resource) = self + .resolved + .imports + .iter() + .find(|import| { + import.kind == CoreImportKind::Capability + && import.alias.as_deref() == Some(alias.as_str()) + && import + .resource + .digest + .as_deref() + .is_some_and(is_lowercase_sha256_review_digest) + }) + .map(|import| import.resource.clone()) + else { + self.errors.push(error( + CompilerStage::TypeCheck, + CompilerErrorKind::MissingContextFact, + format!( + "external-action operation `{alias}` has no digest-locked capability import" + ), + span, + )); + return None; + }; + let [input] = args.as_slice() else { + self.errors.push(error( + CompilerStage::TypeCheck, + CompilerErrorKind::UnsupportedSourceShape, + "external-action request operation accepts exactly one input value", + span, + )); + return None; + }; + let input = self.check_expr(input, env)?; + Some((operation_resource, input)) + } + + fn request_resource(&mut self, resource: &DigestLockedPackageRef) -> Option { + if !is_lowercase_sha256_review_digest(&resource.digest) { + self.errors.push(error( + CompilerStage::TypeCheck, + CompilerErrorKind::UnsupportedSourceShape, + "external-action resource digest must use lowercase sha256 review rendering", + resource.span, + )); + return None; + } + Some(ResourceRef { + coordinate: package_coordinate(&resource.package.path, &resource.package.version), + digest: Some(resource.digest.clone()), + }) + } + fn check_require_stmt( &mut self, predicate: &Expr, @@ -860,6 +1095,9 @@ impl<'a> TypeChecker<'a> { target: &ObstructionTarget, env: &BTreeMap, ) -> Option { + if self.reject_capability_obstruction_root(&target.coordinate, target.span) { + return None; + } let payload = match &target.payload { Some(Expr::Record { entries, .. }) => { self.check_reason_payload(entries, env, ReasonPayloadMode::PreserveReasonField)? @@ -897,6 +1135,15 @@ impl<'a> TypeChecker<'a> { return None; }; if let Some(root) = plain_path_root(reason) { + if self.capability_import_alias(root) { + self.errors.push(error( + CompilerStage::TypeCheck, + CompilerErrorKind::UnsupportedSourceShape, + "capability imports are legal only as request operations", + span, + )); + return None; + } if env.contains_key(root) { self.errors.push(error( CompilerStage::TypeCheck, @@ -1096,6 +1343,17 @@ impl<'a> TypeChecker<'a> { } fn check_effect_profile(&mut self, intent: &ResolvedIntent, call: &Expr, span: Span) -> bool { + if let Some(alias) = self.capability_import_alias_for_call(call) { + self.errors.push(error( + CompilerStage::TypeCheck, + CompilerErrorKind::UnsupportedSourceShape, + format!( + "external-action capability `{alias}` is request data, not a semantic effect" + ), + span, + )); + return false; + } let Some(effect) = effect_coordinate(call) else { self.unsupported_stmt(span, "effect call"); return false; @@ -1137,6 +1395,20 @@ impl<'a> TypeChecker<'a> { } } + fn capability_import_alias_for_call(&self, call: &Expr) -> Option { + let Expr::Call { callee, .. } = call else { + return None; + }; + let alias = plain_path_root(callee)?; + self.resolved + .imports + .iter() + .any(|import| { + import.kind == CoreImportKind::Capability && import.alias.as_deref() == Some(alias) + }) + .then(|| alias.to_owned()) + } + fn effect_binding_shape(&mut self, ty: Option<&TypeRef>, span: Span) -> Option { let Some(ty) = ty else { self.errors.push(error( @@ -1266,6 +1538,9 @@ impl<'a> TypeChecker<'a> { } fn check_obstruction_target(&mut self, target: &ObstructionTarget) -> Option { + if self.reject_capability_obstruction_root(&target.coordinate, target.span) { + return None; + } if target.payload.is_some() { self.errors.push(error( CompilerStage::TypeCheck, @@ -1282,6 +1557,29 @@ impl<'a> TypeChecker<'a> { }) } + fn reject_capability_obstruction_root(&mut self, coordinate: &[String], span: Span) -> bool { + if coordinate + .first() + .is_some_and(|root| self.capability_import_alias(root)) + { + self.errors.push(error( + CompilerStage::TypeCheck, + CompilerErrorKind::UnsupportedSourceShape, + "capability imports are legal only as request operations", + span, + )); + true + } else { + false + } + } + + fn capability_import_alias(&self, alias: &str) -> bool { + self.resolved.imports.iter().any(|import| { + import.kind == CoreImportKind::Capability && import.alias.as_deref() == Some(alias) + }) + } + fn check_known_effect_profiles(&mut self, intent: &ResolvedIntent, expr: &Expr) -> bool { let mut accepted = true; if let Some(effect) = effect_coordinate(expr) { @@ -1404,6 +1702,21 @@ impl<'a> TypeChecker<'a> { } } Stmt::Effect { call, span, .. } => self.check_effect_profile(intent, call, *span), + Stmt::ExternalActionRequest { + operation, + authority_scope, + basis, + max_settlement_bytes, + max_attempts, + .. + } => { + let mut accepted = self.check_known_effect_profiles(intent, operation); + accepted &= self.check_known_effect_profiles(intent, authority_scope); + accepted &= self.check_known_effect_profiles(intent, basis); + accepted &= self.check_known_effect_profiles(intent, max_settlement_bytes); + accepted &= self.check_known_effect_profiles(intent, max_attempts); + accepted + } Stmt::Require { predicate, .. } | Stmt::Guarantee { predicate, .. } | Stmt::Assert { predicate, .. } => self.check_known_effect_profiles(intent, predicate), @@ -1859,10 +2172,23 @@ fn compatible(expected: &TypeShape, actual: &TypeShape) -> bool { (TypeKind::Bool, TypeKind::Bool) => true, (TypeKind::Int { width: expected }, TypeKind::Int { width: actual }) => expected == actual, (TypeKind::Bytes { max: expected }, TypeKind::Bytes { max: actual }) => actual <= expected, + ( + TypeKind::ExternalActionRequest { + settlement: expected, + }, + TypeKind::ExternalActionRequest { settlement: actual }, + ) => compatible(expected, actual), _ => false, } } +fn requestable_operation_family(coordinate: &str) -> bool { + coordinate + .split(['.', '@']) + .next() + .is_some_and(|root| root.eq_ignore_ascii_case("workspace")) +} + fn comparable(left: &TypeShape, right: &TypeShape) -> bool { compatible(left, right) || compatible(right, left) } diff --git a/crates/edict-syntax/src/contract_bundle.rs b/crates/edict-syntax/src/contract_bundle.rs index f6e071b..9a31645 100644 --- a/crates/edict-syntax/src/contract_bundle.rs +++ b/crates/edict-syntax/src/contract_bundle.rs @@ -603,6 +603,13 @@ fn corroborate_target_ir_semantic_closure( "Target IR source Core identity does not match the supplied Core module", )); } + if !actual.capabilities.is_empty() { + return Err(ContractBundleAssemblyError::new( + ContractBundleAssemblyErrorKind::TargetIrSourceMismatch, + "target_ir_artifact.semantic_closure.capabilities", + "Target IR capability closure does not match the supplied Core module", + )); + } canonical_resource_set(&actual.lawpacks) } (Some(expected), Some(actual)) => { @@ -613,15 +620,18 @@ fn corroborate_target_ir_semantic_closure( "Target IR source Core identity does not match the supplied Core module", )); } - if canonical_resource_set(&actual.lawpacks) - != canonical_resource_set(&expected.lawpacks) - { - return Err(ContractBundleAssemblyError::new( - ContractBundleAssemblyErrorKind::TargetIrSourceMismatch, - "target_ir_artifact.semantic_closure.lawpacks", - "Target IR lawpack closure does not match the supplied Core module", - )); - } + require_matching_resource_set( + &actual.lawpacks, + &expected.lawpacks, + "target_ir_artifact.semantic_closure.lawpacks", + "Target IR lawpack closure does not match the supplied Core module", + )?; + require_matching_resource_set( + &actual.capabilities, + &expected.capabilities, + "target_ir_artifact.semantic_closure.capabilities", + "Target IR capability closure does not match the supplied Core module", + )?; canonical_resource_set(&expected.lawpacks) } }; @@ -659,6 +669,22 @@ fn canonical_resource_set(resources: &[ResourceRef]) -> Vec<(String, Option Result<(), ContractBundleAssemblyError> { + if canonical_resource_set(actual) != canonical_resource_set(expected) { + return Err(ContractBundleAssemblyError::new( + ContractBundleAssemblyErrorKind::TargetIrSourceMismatch, + field, + message, + )); + } + Ok(()) +} + fn target_profile_from_target_ir_artifact( target_profile: &ResourceRef, ) -> Result { diff --git a/crates/edict-syntax/src/core_ir.rs b/crates/edict-syntax/src/core_ir.rs index 3a458e9..0276d2a 100644 --- a/crates/edict-syntax/src/core_ir.rs +++ b/crates/edict-syntax/src/core_ir.rs @@ -39,6 +39,7 @@ pub enum CoreImportKind { Lawpack, Target, Core, + Capability, } impl CoreImportKind { @@ -48,6 +49,7 @@ impl CoreImportKind { Self::Lawpack => "lawpack", Self::Target => "target", Self::Core => "core", + Self::Capability => "capability", } } } @@ -147,6 +149,9 @@ pub enum CoreType { CapabilityRef { item: String, }, + ExternalActionRequest { + settlement: String, + }, } /// Alpha-stable local reference. @@ -276,6 +281,26 @@ pub enum CoreNode { input: CoreExpr, obstruction_map: BTreeMap, }, + ExternalActionRequest { + binding: LocalRef, + operation: ResourceRef, + input_type: String, + settlement_type: String, + input_schema: ResourceRef, + settlement_schema: ResourceRef, + input: CoreExpr, + authority_scope: Box, + basis: Box, + budget: Box, + reconciliation_law: ResourceRef, + }, +} + +/// Runtime-valued bounds carried by a typed external-action request. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CoreExternalActionBudget { + pub max_settlement_bytes: CoreExpr, + pub max_attempts: CoreExpr, } /// Core disposition for a failed `require` predicate. diff --git a/crates/edict-syntax/src/highlight.rs b/crates/edict-syntax/src/highlight.rs index 1ed34b1..28b3f7d 100644 --- a/crates/edict-syntax/src/highlight.rs +++ b/crates/edict-syntax/src/highlight.rs @@ -117,6 +117,7 @@ fn is_highlight_keyword(text: &str) -> bool { | "where" | "let" | "return" + | "request" | "require" | "guarantee" | "assert" diff --git a/crates/edict-syntax/src/lib.rs b/crates/edict-syntax/src/lib.rs index 0d6db55..7267a67 100644 --- a/crates/edict-syntax/src/lib.rs +++ b/crates/edict-syntax/src/lib.rs @@ -131,10 +131,10 @@ pub use contract_bundle::{ SourceArtifactRef, SuppliedDigest, SuppliedTargetIrResource, CONTRACT_BUNDLE_API_VERSION, }; pub use core_ir::{ - CompareOp, CoreBlock, CoreBudget, CoreExpr, CoreImport, CoreImportKind, CoreIntent, CoreModule, - CoreNode, CoreObstructionArm, CoreObstructionReason, CorePredicate, CoreRequireFailureArm, - CoreType, CoreValue, InputConstraint, InputConstraintSource, LocalRef, ResourceRef, - CORE_API_VERSION, + CompareOp, CoreBlock, CoreBudget, CoreExpr, CoreExternalActionBudget, CoreImport, + CoreImportKind, CoreIntent, CoreModule, CoreNode, CoreObstructionArm, CoreObstructionReason, + CorePredicate, CoreRequireFailureArm, CoreType, CoreValue, InputConstraint, + InputConstraintSource, LocalRef, ResourceRef, CORE_API_VERSION, }; pub use highlight::{highlight_source, HighlightRole, HighlightToken}; pub use lawpack::{ @@ -207,10 +207,10 @@ pub use result_projection::{ }; pub use semantic::{validate_module, validate_surface, SemanticError, SemanticErrorKind}; pub use target_ir::{ - lower_to_target_ir, TargetEffectLowering, TargetIrArtifact, TargetIrIntent, - TargetIrLoweringFacts, TargetIrRequireFailure, TargetIrRequirement, TargetIrSemanticClosure, - TargetIrStep, TargetLoweringFailure, TargetLoweringFailureKind, TargetLoweringReport, - TargetLoweringStatus, ECHO_DPO_TARGET_PROFILE, ECHO_SPAN_IR_DOMAIN, + lower_to_target_ir, TargetEffectLowering, TargetIrArtifact, TargetIrExternalActionRequest, + TargetIrIntent, TargetIrLoweringFacts, TargetIrRequireFailure, TargetIrRequirement, + TargetIrSemanticClosure, TargetIrStep, TargetLoweringFailure, TargetLoweringFailureKind, + TargetLoweringReport, TargetLoweringStatus, ECHO_DPO_TARGET_PROFILE, ECHO_SPAN_IR_DOMAIN, GITWARP_COMMIT_REDUCER_IR_DOMAIN, GITWARP_REF_CRDT_TARGET_PROFILE, }; pub use target_profile::{ @@ -332,4 +332,7 @@ mod topic_shelf_doctests { #[doc = include_str!("../../../docs/topics/developer-tooling/README.md")] pub struct DeveloperToolingTopicDocs; + + #[doc = include_str!("../../../docs/topics/external-action-requests/README.md")] + pub struct ExternalActionRequestsTopicDocs; } diff --git a/crates/edict-syntax/src/parser.rs b/crates/edict-syntax/src/parser.rs index a5d1b7e..0cb624c 100644 --- a/crates/edict-syntax/src/parser.rs +++ b/crates/edict-syntax/src/parser.rs @@ -4,10 +4,11 @@ //! text so they remain usable as member names after `.`. use crate::ast::{ - BinOp, Block, BoundRef, ContinueObstructedArm, Decl, ElseClause, EnumDecl, Expr, - FieldConstraint, FieldDecl, Import, ImportKind, IntentClause, IntentDecl, MatchArm, Module, - ObstructionArm, ObstructionHandler, ObstructionTarget, PackageRef, Param, RecordEntry, - RequireElseArm, ScalarRefine, Stmt, TypeDecl, TypeExpr, TypeRef, UnOp, VariantCase, YieldBlock, + BinOp, Block, BoundRef, ContinueObstructedArm, Decl, DigestLockedPackageRef, ElseClause, + EnumDecl, Expr, FieldConstraint, FieldDecl, Import, ImportKind, IntentClause, IntentDecl, + MatchArm, Module, ObstructionArm, ObstructionHandler, ObstructionTarget, PackageRef, Param, + RecordEntry, RequireElseArm, ScalarRefine, Stmt, TypeDecl, TypeExpr, TypeRef, UnOp, + VariantCase, YieldBlock, }; use crate::token::{lex, Span, Token, TokenKind}; @@ -31,6 +32,7 @@ pub enum ParseErrorKind { MissingRequiredField, DuplicateField, NonCallEffect, + NonCallExternalActionOperation, ReturnInYieldBlock, InvalidTypeCall, } @@ -63,6 +65,7 @@ impl ParseErrorKind { ParseErrorKind::MissingRequiredField => "MissingRequiredField", ParseErrorKind::DuplicateField => "DuplicateField", ParseErrorKind::NonCallEffect => "NonCallEffect", + ParseErrorKind::NonCallExternalActionOperation => "NonCallExternalActionOperation", ParseErrorKind::ReturnInYieldBlock => "ReturnInYieldBlock", ParseErrorKind::InvalidTypeCall => "InvalidTypeCall", } @@ -135,6 +138,7 @@ pub(crate) fn is_keyword(s: &str) -> bool { | "budget" | "where" | "let" + | "request" | "return" | "require" | "guarantee" @@ -190,6 +194,7 @@ fn stmt_contains_return(stmt: &Stmt) -> bool { Stmt::For { body, .. } => block_contains_return(body), Stmt::Let { .. } | Stmt::Effect { .. } + | Stmt::ExternalActionRequest { .. } | Stmt::Require { .. } | Stmt::Guarantee { .. } | Stmt::Assert { .. } => false, @@ -487,12 +492,9 @@ impl Parser { } else if self.eat_kw("core") { ImportKind::Core } else if self.eat_kw("capability") { - return self.err_kind( - ParseErrorKind::UnsupportedSyntax, - "`use capability` is not accepted in Edict v1", - ); + ImportKind::Capability } else { - return self.err("expected import kind (shape|lawpack|target|core)"); + return self.err("expected import kind (shape|lawpack|target|core|capability)"); }; let (package, shape_path) = if kind == ImportKind::Shape { @@ -893,6 +895,8 @@ impl Parser { els, span: Span::new(start, self.prev_end()), }) + } else if self.eat_kw("request") { + self.external_action_request_stmt(start) } else if self.eat_kw("return") { let value = self.expr()?; self.expect(&TokenKind::Semi)?; @@ -953,6 +957,63 @@ impl Parser { } } + fn external_action_request_stmt(&mut self, start: usize) -> Result { + let name = self.binder()?; + self.expect(&TokenKind::Colon)?; + let request_type = self.type_ref()?; + self.expect(&TokenKind::Eq)?; + let operation = self.expr()?; + if !is_call_expr(&operation) { + return self.err_kind( + ParseErrorKind::NonCallExternalActionOperation, + "external-action request operation must be a call expression", + ); + } + self.expect_kw("input")?; + self.expect_kw("schema")?; + let input_schema = self.digest_locked_package_ref()?; + self.expect_kw("settlement")?; + self.expect_kw("schema")?; + let settlement_schema = self.digest_locked_package_ref()?; + self.expect_kw("authority")?; + let authority_scope = self.expr()?; + self.expect_kw("basis")?; + let basis = self.expr()?; + self.expect_kw("budget")?; + self.expect_kw("maxSettlementBytes")?; + let max_settlement_bytes = self.expr()?; + self.expect_kw("maxAttempts")?; + let max_attempts = self.expr()?; + self.expect_kw("reconcile")?; + let reconciliation_law = self.digest_locked_package_ref()?; + self.expect(&TokenKind::Semi)?; + Ok(Stmt::ExternalActionRequest { + name, + request_type, + operation, + input_schema, + settlement_schema, + authority_scope: Box::new(authority_scope), + basis: Box::new(basis), + max_settlement_bytes: Box::new(max_settlement_bytes), + max_attempts: Box::new(max_attempts), + reconciliation_law, + span: Span::new(start, self.prev_end()), + }) + } + + fn digest_locked_package_ref(&mut self) -> Result { + let start = self.peek_span().start; + let package = self.package_ref()?; + self.expect_kw("digest")?; + let digest = self.digest_lit()?; + Ok(DigestLockedPackageRef { + package, + digest, + span: Span::new(start, self.prev_end()), + }) + } + /// The right-hand side of a `let`: either an ordinary expression, or the /// effectful branch-yield form (legal *only* here). Both start with `if`, /// so we disambiguate on what follows the predicate: `then` is the pure @@ -1621,6 +1682,10 @@ mod parse_error_kind_codes { (ParseErrorKind::MissingRequiredField, "MissingRequiredField"), (ParseErrorKind::DuplicateField, "DuplicateField"), (ParseErrorKind::NonCallEffect, "NonCallEffect"), + ( + ParseErrorKind::NonCallExternalActionOperation, + "NonCallExternalActionOperation", + ), (ParseErrorKind::ReturnInYieldBlock, "ReturnInYieldBlock"), (ParseErrorKind::InvalidTypeCall, "InvalidTypeCall"), ]; diff --git a/crates/edict-syntax/src/result_projection.rs b/crates/edict-syntax/src/result_projection.rs index c29b042..ffd3243 100644 --- a/crates/edict-syntax/src/result_projection.rs +++ b/crates/edict-syntax/src/result_projection.rs @@ -519,7 +519,9 @@ fn resolve_capability_sources( input, .. } => Some((binding, effect, input)), - CoreNode::Let { .. } | CoreNode::Require { .. } => None, + CoreNode::Let { .. } + | CoreNode::Require { .. } + | CoreNode::ExternalActionRequest { .. } => None, }) .collect::>(); if core_effects.len() != target_intent.steps.len() { @@ -818,6 +820,10 @@ fn types_are_compatible(core: &CoreModule, left: &str, right: &str, depth: usize | (CoreType::CapabilityRef { item: left }, CoreType::CapabilityRef { item: right }) => { types_are_compatible(core, left, right, depth + 1) } + ( + CoreType::ExternalActionRequest { settlement: left }, + CoreType::ExternalActionRequest { settlement: right }, + ) => types_are_compatible(core, left, right, depth + 1), ( CoreType::List { item: left, diff --git a/crates/edict-syntax/src/semantic.rs b/crates/edict-syntax/src/semantic.rs index db1fe53..586f43c 100644 --- a/crates/edict-syntax/src/semantic.rs +++ b/crates/edict-syntax/src/semantic.rs @@ -366,6 +366,25 @@ fn validate_stmt(stmt: &Stmt, names: &mut NameEnv, errors: &mut Vec { + names.bind(name, *span, errors); + validate_type_ref(request_type, *span, errors); + validate_expr(operation, names, errors); + validate_expr(authority_scope, names, errors); + validate_expr(basis, names, errors); + validate_expr(max_settlement_bytes, names, errors); + validate_expr(max_attempts, names, errors); + } Stmt::Require { predicate, arm, .. } => { validate_expr(predicate, names, errors); validate_require_else_arm(arm, names, errors); diff --git a/crates/edict-syntax/src/target_ir.rs b/crates/edict-syntax/src/target_ir.rs index 65e59fd..0862dab 100644 --- a/crates/edict-syntax/src/target_ir.rs +++ b/crates/edict-syntax/src/target_ir.rs @@ -7,9 +7,9 @@ use std::collections::{BTreeMap, BTreeSet}; use crate::core_ir::{ - is_lowercase_sha256_review_digest, CoreBudget, CoreExpr, CoreImportKind, CoreIntent, - CoreModule, CoreNode, CoreObstructionArm, CoreObstructionReason, CorePredicate, - CoreRequireFailureArm, InputConstraint, LocalRef, ResourceRef, CORE_API_VERSION, + is_lowercase_sha256_review_digest, CoreBudget, CoreExpr, CoreExternalActionBudget, + CoreImportKind, CoreIntent, CoreModule, CoreNode, CoreObstructionArm, CoreObstructionReason, + CorePredicate, CoreRequireFailureArm, InputConstraint, LocalRef, ResourceRef, CORE_API_VERSION, }; use crate::digest_core_module; use crate::lowerability::{LowerabilityEffectStatus, LowerabilityReport, LowerabilityStatus}; @@ -203,6 +203,7 @@ pub struct TargetIrArtifact { pub struct TargetIrSemanticClosure { pub source_core: ResourceRef, pub lawpacks: Vec, + pub capabilities: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -213,6 +214,7 @@ pub struct TargetIrIntent { pub core_evaluation_budget: CoreBudget, pub requirements: Vec, pub steps: Vec, + pub external_action_requests: Vec, pub result: CoreExpr, } @@ -240,6 +242,23 @@ pub struct TargetIrStep { pub obstruction_arms: BTreeMap, } +/// A deterministic external-action request preserved as data for Echo admission. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TargetIrExternalActionRequest { + pub id: String, + pub binding: LocalRef, + pub operation: ResourceRef, + pub input_type: String, + pub settlement_type: String, + pub input_schema: ResourceRef, + pub settlement_schema: ResourceRef, + pub input: CoreExpr, + pub authority_scope: CoreExpr, + pub basis: CoreExpr, + pub budget: CoreExternalActionBudget, + pub reconciliation_law: ResourceRef, +} + #[must_use] pub fn lower_to_target_ir( core: &CoreModule, @@ -345,11 +364,39 @@ fn lower_result_projections( pub(crate) fn semantic_closure_for_core( core: &CoreModule, ) -> Result, TargetLoweringFailure> { - let mut lawpacks = BTreeMap::::new(); + let lawpacks = digest_locked_import_set(core, CoreImportKind::Lawpack, "lawpack")?; + let capabilities = digest_locked_import_set(core, CoreImportKind::Capability, "capability")?; + let has_explicit_basis = core.intents.values().any(|intent| intent.basis.is_some()); + if !has_explicit_basis && lawpacks.is_empty() && capabilities.is_empty() { + return Ok(None); + } + + let digest = digest_core_module(core).map_err(|error| TargetLoweringFailure { + kind: TargetLoweringFailureKind::InvalidCoreIdentity, + intent: None, + node_index: None, + detail: error.to_string(), + })?; + Ok(Some(TargetIrSemanticClosure { + source_core: ResourceRef { + coordinate: core.coordinate.clone(), + digest: Some(digest.to_review_string()), + }, + lawpacks: lawpacks.into_values().collect(), + capabilities: capabilities.into_values().collect(), + })) +} + +fn digest_locked_import_set( + core: &CoreModule, + kind: CoreImportKind, + field_name: &str, +) -> Result, TargetLoweringFailure> { + let mut resources = BTreeMap::new(); for resource in core .imports .iter() - .filter(|import| import.kind == CoreImportKind::Lawpack) + .filter(|import| import.kind == kind) .map(|import| &import.resource) { if !resource @@ -364,40 +411,23 @@ pub(crate) fn semantic_closure_for_core( detail: resource.coordinate.clone(), }); } - if let Some(prior) = lawpacks.get(&resource.coordinate) { + if let Some(prior) = resources.get(&resource.coordinate) { if prior != resource { return Err(TargetLoweringFailure { kind: TargetLoweringFailureKind::InvalidCoreIdentity, intent: None, node_index: None, detail: format!( - "lawpack coordinate `{}` is bound to conflicting resources", + "{field_name} coordinate `{}` is bound to conflicting resources", resource.coordinate ), }); } } else { - lawpacks.insert(resource.coordinate.clone(), resource.clone()); + resources.insert(resource.coordinate.clone(), resource.clone()); } } - let has_explicit_basis = core.intents.values().any(|intent| intent.basis.is_some()); - if !has_explicit_basis && lawpacks.is_empty() { - return Ok(None); - } - - let digest = digest_core_module(core).map_err(|error| TargetLoweringFailure { - kind: TargetLoweringFailureKind::InvalidCoreIdentity, - intent: None, - node_index: None, - detail: error.to_string(), - })?; - Ok(Some(TargetIrSemanticClosure { - source_core: ResourceRef { - coordinate: core.coordinate.clone(), - digest: Some(digest.to_review_string()), - }, - lawpacks: lawpacks.into_values().collect(), - })) + Ok(resources) } fn validate_target_selection( @@ -505,7 +535,11 @@ fn lower_intent( for (node_index, node) in intent.body.nodes.iter().enumerate() { lower_node(intent_name, node_index, node, context, &mut state, failures); } - if state.requirements.is_empty() && state.steps.is_empty() && intent.body.nodes.is_empty() { + if state.requirements.is_empty() + && state.steps.is_empty() + && state.external_action_requests.is_empty() + && intent.body.nodes.is_empty() + { failures.push(TargetLoweringFailure { kind: TargetLoweringFailureKind::NoTargetSteps, intent: Some(intent_name.to_owned()), @@ -521,6 +555,7 @@ fn lower_intent( core_evaluation_budget: intent.core_evaluation_budget.clone(), requirements: state.requirements, steps: state.steps, + external_action_requests: state.external_action_requests, result: intent.body.result.clone(), } } @@ -535,6 +570,7 @@ struct TargetLoweringContext<'a> { struct IntentLoweringState { requirements: Vec, steps: Vec, + external_action_requests: Vec, step_outputs: BTreeSet, } @@ -571,6 +607,38 @@ fn lower_node( state.step_outputs.insert(binding.id.clone()); } } + CoreNode::ExternalActionRequest { + binding, + operation, + input_type, + settlement_type, + input_schema, + settlement_schema, + input, + authority_scope, + basis, + budget, + reconciliation_law, + } => state + .external_action_requests + .push(TargetIrExternalActionRequest { + id: format!( + "{}.request.{}", + intent_name, + state.external_action_requests.len() + ), + binding: binding.clone(), + operation: operation.clone(), + input_type: input_type.clone(), + settlement_type: settlement_type.clone(), + input_schema: input_schema.clone(), + settlement_schema: settlement_schema.clone(), + input: input.clone(), + authority_scope: authority_scope.as_ref().clone(), + basis: basis.as_ref().clone(), + budget: budget.as_ref().clone(), + reconciliation_law: reconciliation_law.clone(), + }), CoreNode::Let { .. } => failures.push(TargetLoweringFailure { kind: TargetLoweringFailureKind::UnsupportedCoreNode, intent: Some(intent_name.to_owned()), diff --git a/crates/edict-syntax/tests/contract_bundle.rs b/crates/edict-syntax/tests/contract_bundle.rs index 13f724e..7e134e7 100644 --- a/crates/edict-syntax/tests/contract_bundle.rs +++ b/crates/edict-syntax/tests/contract_bundle.rs @@ -498,6 +498,7 @@ mod contract_bundle_assembly { .iter() .map(DigestLockedResource::to_resource_ref) .collect(), + capabilities: Vec::new(), }); input } @@ -850,6 +851,30 @@ mod contract_bundle_assembly { assert_eq!(err.field(), "target_ir_artifact.semantic_closure.lawpacks"); } + #[test] + fn assembly_from_target_ir_rejects_artifact_capability_substitution() { + let mut input = assembly_from_target_ir_input(); + input + .target_ir_artifact + .semantic_closure + .as_mut() + .expect("lawpack-bearing fixture has a closure") + .capabilities + .push(digest_locked("workspace.snapshot.observe@1", 'e')); + + let err = assemble_contract_bundle_from_target_ir(input) + .expect_err("artifact capabilities must equal the supplied Core closure"); + + assert_eq!( + err.kind(), + ContractBundleAssemblyErrorKind::TargetIrSourceMismatch + ); + assert_eq!( + err.field(), + "target_ir_artifact.semantic_closure.capabilities" + ); + } + #[test] fn assembly_from_target_ir_preserves_canonical_core_failure() { let mut input = assembly_from_target_ir_input(); diff --git a/crates/edict-syntax/tests/external_action_requests.rs b/crates/edict-syntax/tests/external_action_requests.rs new file mode 100644 index 0000000..9ba8793 --- /dev/null +++ b/crates/edict-syntax/tests/external_action_requests.rs @@ -0,0 +1,615 @@ +//! RED contract for typed external-action request values. +//! +//! The tests use only public compiler surfaces so the first failure is the +//! absence of request syntax/semantics, not a test compile error. + +use std::{collections::BTreeSet, fmt::Write as _}; + +use edict_syntax::{ + compile_to_core, decode_canonical_cbor, digest_core_module, encode_core_module, + encode_target_ir_artifact, lower_to_target_ir, parse_module, CanonicalValue, CompilerContext, + CompilerErrorKind, CoreBudget, CoreNode, ResourceRef, TargetIrLoweringFacts, + TargetLoweringStatus, WriteClass, ECHO_DPO_TARGET_PROFILE, ECHO_SPAN_IR_DOMAIN, +}; + +const OPERATION_DIGEST: char = 'a'; +const INPUT_SCHEMA_DIGEST: char = 'b'; +const SETTLEMENT_SCHEMA_DIGEST: char = 'c'; +const RECONCILIATION_DIGEST: char = 'd'; +const TARGET_PROFILE_DIGEST: char = 'e'; +const PROPERTY_SEED: u64 = 0x4558_5452_4551_0001; + +fn digest(hex: char) -> String { + format!("sha256:{}", hex.to_string().repeat(64)) +} + +fn context() -> CompilerContext { + CompilerContext::new() + .with_operation_profile("workspace.read", "continuum.profile.read-only/v1") + .with_budget( + "workspace.tiny", + CoreBudget { + max_steps: 512, + max_allocated_bytes: 256 * 1024, + max_output_bytes: 128 * 1024, + }, + ) +} + +fn target_facts() -> TargetIrLoweringFacts { + TargetIrLoweringFacts { + target_profile: ResourceRef { + coordinate: ECHO_DPO_TARGET_PROFILE.to_owned(), + digest: Some(digest(TARGET_PROFILE_DIGEST)), + }, + target_ir_domain: ECHO_SPAN_IR_DOMAIN.to_owned(), + operation_profiles: vec!["continuum.profile.read-only/v1".to_owned()], + obstruction_coordinates: Vec::new(), + effect_lowerings: Vec::new(), + } +} + +struct RequestSource<'a> { + capability_coordinate: &'a str, + operation_alias: &'a str, + operation_digest: &'a str, + input_schema_digest: &'a str, + settlement_schema_digest: &'a str, + input_expr: &'a str, + authority_expr: &'a str, + basis_expr: &'a str, + max_settlement_bytes: &'a str, + max_attempts: &'a str, + reconciliation_digest: &'a str, +} + +fn request_source(request: &RequestSource<'_>) -> String { + let RequestSource { + capability_coordinate, + operation_alias, + operation_digest, + input_schema_digest, + settlement_schema_digest, + input_expr, + authority_expr, + basis_expr, + max_settlement_bytes, + max_attempts, + reconciliation_digest, + } = request; + format!( + r#"package examples.workspace_observer@1; + +use capability {capability_coordinate} digest "{operation_digest}" as snapshot; + +type ObserveInput = {{ + payload: Bytes, + alternatePayload: Bytes, + scope: Bytes, + alternateScope: Bytes, + basis: Bytes, + alternateBasis: Bytes, + maxSettlementBytes: U64, + alternateMaxSettlementBytes: U64, + maxAttempts: U32, + alternateMaxAttempts: U32, +}}; + +intent observe(input: ObserveInput) returns ExternalActionRequest> + profile workspace.read + basis input.basis + budget <= workspace.tiny +{{ + request pending: ExternalActionRequest> = + {operation_alias}({input_expr}) + input schema workspace.snapshot.input@1 digest "{input_schema_digest}" + settlement schema workspace.snapshot.settlement@1 digest "{settlement_schema_digest}" + authority {authority_expr} + basis {basis_expr} + budget maxSettlementBytes {max_settlement_bytes} maxAttempts {max_attempts} + reconcile workspace.snapshot.reconcile@1 digest "{reconciliation_digest}"; + return pending; +}} +"# + ) +} + +fn baseline_source() -> String { + request_source(&RequestSource { + capability_coordinate: "workspace.snapshot.observe@1", + operation_alias: "snapshot", + operation_digest: &digest(OPERATION_DIGEST), + input_schema_digest: &digest(INPUT_SCHEMA_DIGEST), + settlement_schema_digest: &digest(SETTLEMENT_SCHEMA_DIGEST), + input_expr: "input.payload", + authority_expr: "input.scope", + basis_expr: "input.basis", + max_settlement_bytes: "input.maxSettlementBytes", + max_attempts: "input.maxAttempts", + reconciliation_digest: &digest(RECONCILIATION_DIGEST), + }) +} + +fn compile_source(source: &str) -> edict_syntax::CoreModule { + let module = parse_module(source).expect("external-action source parses"); + compile_to_core(&module, &context()).expect("external-action source compiles") +} + +fn lower_source(source: &str) -> (edict_syntax::CoreModule, edict_syntax::TargetIrArtifact) { + let core = compile_source(source); + let report = lower_to_target_ir(&core, &target_facts()); + assert_eq!(report.status, TargetLoweringStatus::Lowered); + assert_eq!(report.failures, Vec::new()); + (core, report.artifact.expect("external-action Target IR")) +} + +fn map_field<'a>(value: &'a CanonicalValue, field: &str) -> &'a CanonicalValue { + let CanonicalValue::Map(entries) = value else { + panic!("expected map while looking up {field:?}, got {value:?}"); + }; + entries + .iter() + .find_map(|(key, value)| match key { + CanonicalValue::Text(key) if key == field => Some(value), + _ => None, + }) + .unwrap_or_else(|| panic!("missing canonical field {field:?}")) +} + +fn text_field<'a>(value: &'a CanonicalValue, field: &str) -> &'a str { + let CanonicalValue::Text(value) = map_field(value, field) else { + panic!("canonical field {field:?} is not text"); + }; + value +} + +fn array_field<'a>(value: &'a CanonicalValue, field: &str) -> &'a [CanonicalValue] { + let CanonicalValue::Array(values) = map_field(value, field) else { + panic!("canonical field {field:?} is not an array"); + }; + values +} + +fn core_request_node(core: &edict_syntax::CoreModule) -> CanonicalValue { + let encoded = encode_core_module(core).expect("Core request encodes"); + let value = decode_canonical_cbor(&encoded).expect("Core request decodes"); + let intent = map_field(map_field(&value, "intents"), "observe"); + array_field(map_field(intent, "body"), "nodes")[0].clone() +} + +fn target_request_values(target: &edict_syntax::TargetIrArtifact) -> Vec { + let encoded = encode_target_ir_artifact(target).expect("Target IR request encodes"); + let value = decode_canonical_cbor(&encoded).expect("Target IR request decodes"); + let intent = map_field(map_field(&value, "intents"), "observe"); + array_field(intent, "externalActionRequests").to_vec() +} + +#[test] +fn workspace_observation_request_compiles_as_non_callable_data() { + let (core, target) = lower_source(&baseline_source()); + let core_value = decode_canonical_cbor(&encode_core_module(&core).expect("Core encodes")) + .expect("Core CBOR"); + let imports = array_field(&core_value, "imports"); + assert_eq!(imports.len(), 1); + assert_eq!(text_field(&imports[0], "kind"), "capability"); + assert_eq!( + text_field(map_field(&imports[0], "ref"), "id"), + "workspace.snapshot.observe@1" + ); + + let request = core_request_node(&core); + assert_eq!(text_field(&request, "kind"), "externalActionRequest"); + assert_eq!(text_field(&request, "inputType"), "Bytes"); + assert_eq!(text_field(&request, "settlementType"), "Bytes"); + assert_eq!(text_field(&request, "state"), "awaitingSettlement"); + assert_eq!( + text_field(&request, "settlementAdmission"), + "schemaRequired" + ); + + let target_bytes = encode_target_ir_artifact(&target).expect("Target IR encodes"); + let target_value = decode_canonical_cbor(&target_bytes).expect("Target IR CBOR"); + let closure = map_field(&target_value, "semanticClosure"); + let capabilities = array_field(closure, "capabilities"); + assert_eq!(capabilities.len(), 1); + assert_eq!( + text_field(&capabilities[0], "id"), + "workspace.snapshot.observe@1" + ); + let intent = map_field(map_field(&target_value, "intents"), "observe"); + assert_eq!(array_field(intent, "steps"), []); + let requests = array_field(intent, "externalActionRequests"); + assert_eq!(requests.len(), 1); + assert_eq!( + text_field(map_field(&requests[0], "operation"), "id"), + "workspace.snapshot.observe@1" + ); + assert_eq!( + text_field(&requests[0], "settlementAdmission"), + "schemaRequired" + ); + assert!(!target_bytes + .windows("targetIntrinsic".len()) + .any(|window| window == b"targetIntrinsic")); +} + +#[test] +fn undeclared_or_floating_operation_families_fail_closed() { + let undeclared = baseline_source().replace("snapshot(input.payload)", "missing(input.payload)"); + let module = parse_module(&undeclared).expect("undeclared operation source parses"); + let errors = compile_to_core(&module, &context()).expect_err("undeclared operation rejects"); + assert_eq!(errors[0].kind, CompilerErrorKind::MissingContextFact); + + let floating = + baseline_source().replace(&format!(" digest \"{}\"", digest(OPERATION_DIGEST)), ""); + let module = parse_module(&floating).expect("floating capability source parses"); + assert_eq!( + compile_to_core(&module, &context()) + .expect_err("floating operation rejects during compilation")[0] + .kind, + CompilerErrorKind::MissingContextFact + ); +} + +#[test] +fn request_operation_must_remain_in_core_and_target_capability_closure() { + let (mut core, mut target) = lower_source(&baseline_source()); + core.imports.clear(); + assert_eq!( + encode_core_module(&core) + .expect_err("Core request without capability import rejects") + .kind(), + edict_syntax::CanonicalErrorKind::UnsupportedValue + ); + + target + .semantic_closure + .as_mut() + .expect("request-bearing Target IR has a closure") + .capabilities + .clear(); + assert_eq!( + encode_target_ir_artifact(&target) + .expect_err("Target IR request without capability closure rejects") + .kind(), + edict_syntax::CanonicalErrorKind::UnsupportedValue + ); +} + +#[test] +fn request_resource_coordinates_must_be_nonempty() { + let core = compile_source(&baseline_source()); + + for field in ["inputSchema", "settlementSchema", "reconciliationLaw"] { + let mut changed = core.clone(); + let request = changed + .intents + .get_mut("observe") + .expect("observe intent exists") + .body + .nodes + .first_mut() + .expect("request node exists"); + let CoreNode::ExternalActionRequest { + input_schema, + settlement_schema, + reconciliation_law, + .. + } = request + else { + panic!("first node is an external-action request"); + }; + match field { + "inputSchema" => input_schema.coordinate.clear(), + "settlementSchema" => settlement_schema.coordinate.clear(), + "reconciliationLaw" => reconciliation_law.coordinate.clear(), + _ => unreachable!("bounded field corpus"), + } + + assert_eq!( + encode_core_module(&changed) + .expect_err("empty request resource coordinate rejects") + .kind(), + edict_syntax::CanonicalErrorKind::UnsupportedValue, + "{field}" + ); + } +} + +#[test] +fn duplicate_target_request_ids_reject_before_identity() { + let (_, mut target) = lower_source(&baseline_source()); + let intent = target + .intents + .get_mut("observe") + .expect("observe intent exists"); + intent + .external_action_requests + .push(intent.external_action_requests[0].clone()); + + assert_eq!( + encode_target_ir_artifact(&target) + .expect_err("duplicate target request ids reject") + .kind(), + edict_syntax::CanonicalErrorKind::UnsupportedValue + ); +} + +#[test] +fn capability_import_cannot_be_called_as_an_effect() { + let source = format!( + r#"package examples.direct_call@1; +use capability workspace.snapshot.observe@1 digest "{}" as snapshot; +type Input = {{ payload: Bytes, }}; +intent observe(input: Input) returns Bytes + profile workspace.read + basis none + budget <= workspace.tiny {{ + let output: Bytes = snapshot(input.payload) + else {{ rejected(reason) => workspace.Rejected }}; + return output; +}} +"#, + digest(OPERATION_DIGEST) + ); + let module = parse_module(&source).expect("direct call source parses"); + let permissive_effect_facts = context() + .with_operation_profile_write_classes("workspace.read", [WriteClass::Read]) + .with_effect_write_class("snapshot", WriteClass::Read); + let errors = compile_to_core(&module, &permissive_effect_facts) + .expect_err("capability alias rejects despite matching effect facts"); + assert_eq!(errors[0].kind, CompilerErrorKind::UnsupportedSourceShape); +} + +#[test] +fn capability_import_cannot_be_used_as_an_obstruction_coordinate() { + let require_source = baseline_source().replace( + "{\n request pending:", + "{\n require false else snapshot;\n request pending:", + ); + let module = parse_module(&require_source).expect("capability-require source parses"); + let errors = compile_to_core(&module, &context()) + .expect_err("capability alias in obstruction position rejects"); + assert_eq!(errors[0].kind, CompilerErrorKind::UnsupportedSourceShape); + + let effect_source = format!( + r#"package examples.obstruction_alias@1; +use capability workspace.snapshot.observe@1 digest "{}" as snapshot; +type Input = {{ payload: Bytes, }}; +type Output = {{ payload: Bytes, }}; +intent observe(input: Input) returns Output + profile workspace.read + basis none + budget <= workspace.tiny {{ + let output: Output = target.read(input.payload) + else {{ rejected(reason) => snapshot }}; + return output; +}} +"#, + digest(OPERATION_DIGEST) + ); + let module = parse_module(&effect_source).expect("capability-effect-obstruction source parses"); + let effect_context = context() + .with_operation_profile_write_classes("workspace.read", [WriteClass::Read]) + .with_effect_write_class("target.read", WriteClass::Read); + let errors = compile_to_core(&module, &effect_context) + .expect_err("capability alias in effect obstruction position rejects"); + assert_eq!(errors[0].kind, CompilerErrorKind::UnsupportedSourceShape); +} + +#[test] +fn ambient_operation_families_are_not_requestable() { + for coordinate in [ + "filesystem.read@1", + "Filesystem.read@1", + "process.spawn@1", + "network.fetch@1", + "NETWORK.fetch@1", + "git.push@1", + "github.open_pull_request@1", + "model.invoke@1", + "shell.command@1", + "fs.read@1", + "net.fetch@1", + "http.get@1", + "exec.command@1", + "gh.open_pull_request@1", + "calendar.observe@1", + ] { + let source = request_source(&RequestSource { + capability_coordinate: coordinate, + operation_alias: "snapshot", + operation_digest: &digest(OPERATION_DIGEST), + input_schema_digest: &digest(INPUT_SCHEMA_DIGEST), + settlement_schema_digest: &digest(SETTLEMENT_SCHEMA_DIGEST), + input_expr: "input.payload", + authority_expr: "input.scope", + basis_expr: "input.basis", + max_settlement_bytes: "input.maxSettlementBytes", + max_attempts: "input.maxAttempts", + reconciliation_digest: &digest(RECONCILIATION_DIGEST), + }); + let module = parse_module(&source).expect("ambient-family source parses structurally"); + let errors = + compile_to_core(&module, &context()).expect_err("ambient operation family rejects"); + assert_eq!( + errors[0].kind, + CompilerErrorKind::UnrequestableExternalOperation, + "{coordinate}" + ); + } +} + +#[test] +fn non_call_request_operation_has_a_request_specific_parse_kind() { + let source = baseline_source().replace("snapshot(input.payload)", "input.payload"); + let error = parse_module(&source).expect_err("non-call request operation rejects"); + assert_eq!(error.kind.code(), "NonCallExternalActionOperation"); +} + +#[test] +fn dynamic_admission_values_survive_without_compile_time_execution() { + let core = compile_source(&baseline_source()); + let request = core_request_node(&core); + assert_eq!( + text_field(map_field(&request, "authorityScope"), "field"), + "scope" + ); + assert_eq!(text_field(map_field(&request, "basis"), "field"), "basis"); + let budget = map_field(&request, "budget"); + assert_eq!( + text_field(map_field(budget, "maxSettlementBytes"), "field"), + "maxSettlementBytes" + ); + assert_eq!( + text_field(map_field(budget, "maxAttempts"), "field"), + "maxAttempts" + ); +} + +#[test] +fn request_artifacts_are_reproducible() { + let source = baseline_source(); + let (left_core, left_target) = lower_source(&source); + let (right_core, right_target) = lower_source(&source); + assert_eq!( + encode_core_module(&left_core).expect("left Core"), + encode_core_module(&right_core).expect("right Core") + ); + assert_eq!( + encode_target_ir_artifact(&left_target).expect("left Target IR"), + encode_target_ir_artifact(&right_target).expect("right Target IR") + ); +} + +#[test] +fn every_request_authority_field_moves_core_and_target_identity() { + let baseline = baseline_source(); + let (baseline_core, baseline_target) = lower_source(&baseline); + let baseline_core_digest = digest_core_module(&baseline_core).expect("baseline Core digest"); + let baseline_target_bytes = + encode_target_ir_artifact(&baseline_target).expect("baseline Target IR"); + let mutations = [ + baseline.replace(&digest(OPERATION_DIGEST), &digest('1')), + baseline.replace(&digest(INPUT_SCHEMA_DIGEST), &digest('2')), + baseline.replace(&digest(SETTLEMENT_SCHEMA_DIGEST), &digest('3')), + baseline.replace("authority input.scope", "authority input.alternateScope"), + baseline.replace("basis input.basis", "basis input.alternateBasis"), + baseline.replace( + "maxSettlementBytes input.maxSettlementBytes", + "maxSettlementBytes input.alternateMaxSettlementBytes", + ), + baseline.replace( + "maxAttempts input.maxAttempts", + "maxAttempts input.alternateMaxAttempts", + ), + baseline.replace( + "snapshot(input.payload)", + "snapshot(input.alternatePayload)", + ), + baseline.replace(&digest(RECONCILIATION_DIGEST), &digest('4')), + ]; + for mutation in mutations { + let (mutated_core, mutated_target) = lower_source(&mutation); + assert_ne!( + digest_core_module(&mutated_core).expect("mutated Core digest"), + baseline_core_digest + ); + assert_ne!( + encode_target_ir_artifact(&mutated_target).expect("mutated Target IR"), + baseline_target_bytes + ); + } +} + +#[test] +fn fixed_seed_request_identity_corpus_is_deterministic() { + let mut state = PROPERTY_SEED; + let mut observed = BTreeSet::new(); + for _ in 0..32 { + state = state + .wrapping_mul(6_364_136_223_846_793_005) + .wrapping_add(1_442_695_040_888_963_407); + let mut hex = format!("{state:016x}"); + hex = hex.repeat(4); + let operation_digest = format!("sha256:{hex}"); + let source = request_source(&RequestSource { + capability_coordinate: "workspace.snapshot.observe@1", + operation_alias: "snapshot", + operation_digest: &operation_digest, + input_schema_digest: &digest(INPUT_SCHEMA_DIGEST), + settlement_schema_digest: &digest(SETTLEMENT_SCHEMA_DIGEST), + input_expr: "input.payload", + authority_expr: "input.scope", + basis_expr: "input.basis", + max_settlement_bytes: "input.maxSettlementBytes", + max_attempts: "input.maxAttempts", + reconciliation_digest: &digest(RECONCILIATION_DIGEST), + }); + let first = digest_core_module(&compile_source(&source)).expect("first property digest"); + let second = digest_core_module(&compile_source(&source)).expect("second property digest"); + assert_eq!(first, second); + assert!(observed.insert(first)); + } + assert_eq!(observed.len(), 32); +} + +#[test] +fn sixty_four_requests_remain_bounded_non_callable_data() { + let mut requests = String::new(); + for index in 0..64 { + write!( + requests, + r#" request pending{index}: ExternalActionRequest> = + snapshot(input.payload) + input schema workspace.snapshot.input@1 digest "{}" + settlement schema workspace.snapshot.settlement@1 digest "{}" + authority input.scope + basis input.basis + budget maxSettlementBytes input.maxSettlementBytes maxAttempts input.maxAttempts + reconcile workspace.snapshot.reconcile@1 digest "{}"; +"#, + digest(INPUT_SCHEMA_DIGEST), + digest(SETTLEMENT_SCHEMA_DIGEST), + digest(RECONCILIATION_DIGEST), + ) + .expect("writing to a String cannot fail"); + } + let source = format!( + r#"package examples.workspace_stress@1; +use capability workspace.snapshot.observe@1 digest "{}" as snapshot; +type ObserveInput = {{ + payload: Bytes, + scope: Bytes, + basis: Bytes, + maxSettlementBytes: U64, + maxAttempts: U32, +}}; +intent observe(input: ObserveInput) returns ExternalActionRequest> + profile workspace.read + basis input.basis + budget <= workspace.tiny +{{ +{requests} return pending63; +}} +"#, + digest(OPERATION_DIGEST), + ); + let (core, target) = lower_source(&source); + let core_value = + decode_canonical_cbor(&encode_core_module(&core).expect("stress Core encodes")) + .expect("stress Core decodes"); + let core_intent = map_field(map_field(&core_value, "intents"), "observe"); + assert_eq!( + array_field(map_field(core_intent, "body"), "nodes").len(), + 64 + ); + assert_eq!(target_request_values(&target).len(), 64); + + let target_value = decode_canonical_cbor( + &encode_target_ir_artifact(&target).expect("stress Target IR encodes"), + ) + .expect("stress Target IR decodes"); + let target_intent = map_field(map_field(&target_value, "intents"), "observe"); + assert_eq!(array_field(target_intent, "steps"), []); +} diff --git a/crates/edict-syntax/tests/highlighting.rs b/crates/edict-syntax/tests/highlighting.rs index f6d2146..17e6b9b 100644 --- a/crates/edict-syntax/tests/highlighting.rs +++ b/crates/edict-syntax/tests/highlighting.rs @@ -50,3 +50,11 @@ fn highlight_source_emits_editor_roles_for_fixture() { assert_eq!(find_role(src, &tokens, "<="), HighlightRole::Operator); assert_eq!(find_role(src, &tokens, ";"), HighlightRole::Punctuation); } + +#[test] +fn request_statement_introducer_is_highlighted_as_a_keyword() { + let src = "request pending: ExternalActionRequest> = snapshot(input);"; + let tokens = highlight_source(src).expect("request source highlights"); + + assert_eq!(find_role(src, &tokens, "request"), HighlightRole::Keyword); +} diff --git a/crates/edict-syntax/tests/parse_review_regressions.rs b/crates/edict-syntax/tests/parse_review_regressions.rs index 7a89754..3beceeb 100644 --- a/crates/edict-syntax/tests/parse_review_regressions.rs +++ b/crates/edict-syntax/tests/parse_review_regressions.rs @@ -5,7 +5,9 @@ mod common; use common::{body, intent_of}; -use edict_syntax::ast::{BinOp, BoundRef, Decl, Expr, RequireElseArm, Stmt, TypeExpr, TypeRef}; +use edict_syntax::ast::{ + BinOp, BoundRef, Decl, Expr, ImportKind, RequireElseArm, Stmt, TypeExpr, TypeRef, +}; use edict_syntax::token::IntSuffix; use edict_syntax::{parse_module, ParseErrorKind}; @@ -61,11 +63,11 @@ fn reserved_keywords_are_rejected_as_import_aliases() { } #[test] -fn capability_imports_are_rejected_in_v1() { - reject_kind( - "package a.b@1;\nuse capability c.d@1 as c;", - ParseErrorKind::UnsupportedSyntax, - ); +fn capability_imports_parse_as_external_operation_references() { + let source = format!("package a.b@1;\nuse capability c.d@1 digest \"{ZERO_DIGEST}\" as c;"); + let module = parse_module(&source).expect("capability import parses"); + assert_eq!(module.imports[0].kind, ImportKind::Capability); + assert_eq!(module.imports[0].digest.as_deref(), Some(ZERO_DIGEST)); } #[test] diff --git a/docs/REQUIREMENTS.md b/docs/REQUIREMENTS.md index a0310e6..7b321b6 100644 --- a/docs/REQUIREMENTS.md +++ b/docs/REQUIREMENTS.md @@ -67,6 +67,7 @@ but owned by a follow-up issue; no fixtures until its dependency lands). | EDICT-LANG-BUDGET-SPLIT-001 | Core/target/admitted budget split; target dims not in Core | Language | `lang/budget/split` | `lang/budget/target-dim-in-core` | spec | | 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-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/SPEC_edict-language-v1.md b/docs/SPEC_edict-language-v1.md index d5cebda..74cbcec 100644 --- a/docs/SPEC_edict-language-v1.md +++ b/docs/SPEC_edict-language-v1.md @@ -1339,7 +1339,8 @@ package-decl = "package" , package-ref , ";" ; import-decl = shape-import | lawpack-import | target-import - | core-import ; + | core-import + | capability-import ; shape-import = "use" , "shape" , string-lit , digest-clause? , "as" , ident , ";" ; @@ -1349,6 +1350,8 @@ target-import = "use" , "target" , package-ref , digest-clause? , "as" , ident , ";" ; core-import = "use" , "core" , package-ref , digest-clause? , "as" , ident , ";" ; +capability-import = "use" , "capability" , package-ref , digest-clause , + "as" , ident , ";" ; digest-clause = "digest" , digest-lit ; declaration = type-decl @@ -1383,6 +1386,7 @@ type-ref = qual-ident , type-args? | "Bytes" , bytes-refine? | "Option" , "<" , type-ref , ">" | "CapabilityRef" , "<" , type-ref , ">" + | "ExternalActionRequest" , "<" , type-ref , ">" | "List" , "<" , type-ref , "," , "max" , "=" , bound-ref , ">" | "Map" , "<" , type-ref , "," , type-ref , "," , "max" , "=" , bound-ref , ">" ; @@ -1420,6 +1424,7 @@ param = ident , ":" , type-ref ; block = "{" , statement* , "}" ; statement = let-stmt + | external-action-request-stmt | assert-stmt | require-stmt | guarantee-stmt @@ -1433,6 +1438,19 @@ let-stmt = "let" , ident , type-annotation? , "=" , let-rhs , effect-else-clause? , ";" ; let-rhs = expr | effect-branch-expr ; type-annotation = ":" , type-ref ; +external-action-request-stmt = + "request" , ident , ":" , + "ExternalActionRequest" , "<" , type-ref , ">" , "=" , + call-expr , + "input" , "schema" , digest-locked-package-ref , + "settlement" , "schema" , digest-locked-package-ref , + "authority" , expr , + "basis" , expr , + "budget" , + "maxSettlementBytes" , expr , + "maxAttempts" , expr , + "reconcile" , digest-locked-package-ref , ";" ; +digest-locked-package-ref = package-ref , digest-clause ; assert-stmt = "assert" , predicate , ";" ; require-stmt = "require" , predicate , obstruction-clause , ";" ; guarantee-stmt = "guarantee" , predicate , obstruction-clause? , ";" ; @@ -1928,6 +1946,41 @@ diagnostic that the compiler can force authors to handle. `hash` is a source-level helper, not the artifact hash primitive. The label must be a string literal so digest domains are stable and reviewable. +## External-Action Requests + +Edict expresses an external boundary crossing as deterministic data, not as an +effect performed by the compiler, lowerer, provider, or model +(`EDICT-LANG-EXTERNAL-REQUEST-001`). + +A `use capability` import binds one digest-locked, domain-specific external +operation family. Its alias is legal only as the single-argument operation call +inside an `external-action-request-stmt`. It is not a semantic-effect import and +cannot be invoked from `let` or effect-statement position. Raw filesystem, +process, network, Git, GitHub, model, and shell operation families are not +requestable language capabilities. + +Every request carries: + +- an input value and its exact schema; +- an exact settlement schema; +- runtime-valued authority scope and basis expressions; +- runtime-valued maximum settlement bytes and attempt bounds; +- an exact reconciliation law; +- a typed `ExternalActionRequest` result. + +Core and Target IR preserve the request as a separate node/collection with +fixed `awaitingSettlement` and `schemaRequired` posture. The exact operation +resource remains in the digest-bound capability closure. Target lowering must +not translate the request into a callable target intrinsic or provider import. + +Execution is outside Edict. Echo admits a durable request before an authorized +adapter touches the world, admits a schema-valid settlement afterward, and +resumes deterministic interpretation from history. Waiting is explicit program +state keyed by request identity; no native stack, continuation, callback, or +`async` host frame is serialized. Runtime path, ref, basis, budget, adapter, and +settlement checks fail closed at Echo admission rather than pretending dynamic +facts are compile-time properties. + ## Effects And Footprints An effect is a typed, imported operation emitted by a target intrinsic or diff --git a/docs/abi/edict-core.cddl b/docs/abi/edict-core.cddl index 6ef395e..21cf2a0 100644 --- a/docs/abi/edict-core.cddl +++ b/docs/abi/edict-core.cddl @@ -15,7 +15,7 @@ core-module = { } core-import = { - kind: "lawpack" / "target" / "core", + kind: "lawpack" / "target" / "core" / "capability", ref: resource-ref, ? alias: tstr, } @@ -24,7 +24,7 @@ core-import = { core-type = core-scalar-type / core-record-type / core-variant-type / core-option-type / core-list-type / core-map-type / - core-capability-ref-type + core-capability-ref-type / core-external-action-request-type core-scalar-type = core-bool-type / core-int-type / core-string-type / core-bytes-type / core-unit-type @@ -73,6 +73,10 @@ core-capability-ref-type = { kind: "CapabilityRef", item: core-type-ref, } +core-external-action-request-type = { + kind: "ExternalActionRequest", + settlement: core-type-ref, +} ; core-type-ref is defined in edict-common.cddl and assembled with this schema. @@ -237,7 +241,8 @@ core-block = { result: core-expr, } -core-node = let-node / require-node / effect-node / guard-node / branch-node / +core-node = let-node / require-node / effect-node / + external-action-request-node / guard-node / branch-node / for-node / match-node / proof-obligation-node let-node = { @@ -271,6 +276,26 @@ effect-node = { input: core-expr, obstructionMap: { * failure-ident => obstruction-arm }, } +external-action-request-node = { + kind: "externalActionRequest", + binding: local-ref, + operation: resource-ref, + inputType: core-type-ref, + settlementType: core-type-ref, + inputSchema: resource-ref, + settlementSchema: resource-ref, + input: core-expr, + authorityScope: core-expr, + basis: core-expr, + budget: external-action-budget, + reconciliationLaw: resource-ref, + state: "awaitingSettlement", + settlementAdmission: "schemaRequired", +} +external-action-budget = { + maxSettlementBytes: core-expr, + maxAttempts: core-expr, +} obstruction-arm = { binder: local-ref, value: core-expr, diff --git a/docs/abi/edict-target-ir.cddl b/docs/abi/edict-target-ir.cddl index e78c4e5..62f8a6d 100644 --- a/docs/abi/edict-target-ir.cddl +++ b/docs/abi/edict-target-ir.cddl @@ -41,11 +41,13 @@ target-ir-legacy-artifact = { target-ir-semantic-closure = { sourceCore: target-ir-resource-ref, lawpacks: [* target-ir-resource-ref], + ? capabilities: [* target-ir-resource-ref], } target-ir-intent = { target-ir-intent-common, ? basis: core-expr, + ? externalActionRequests: [* target-ir-external-action-request], } target-ir-legacy-intent = { @@ -76,3 +78,20 @@ target-ir-step = { obstructionFailures: [* failure-ident], obstructionArms: { * failure-ident => obstruction-arm }, } + +target-ir-external-action-request = { + id: tstr, + binding: local-ref, + operation: target-ir-resource-ref, + inputType: core-type-ref, + settlementType: core-type-ref, + inputSchema: target-ir-resource-ref, + settlementSchema: target-ir-resource-ref, + input: core-expr, + authorityScope: core-expr, + basis: core-expr, + budget: external-action-budget, + reconciliationLaw: target-ir-resource-ref, + state: "awaitingSettlement", + settlementAdmission: "schemaRequired", +} diff --git a/docs/topics/README.md b/docs/topics/README.md index ebb99e1..1a8ce88 100644 --- a/docs/topics/README.md +++ b/docs/topics/README.md @@ -52,6 +52,9 @@ cargo xtask verify - [Developer Tooling](./developer-tooling/README.md): editor-facing source highlighting roles, Tree-sitter grammar source, TextMate grammar scopes, and fixture-backed tooling behavior. +- [External-Action Requests](./external-action-requests/README.md): + deterministic typed requests for Echo-coordinated external effects, with + digest-locked capability closure and no ambient execution authority. - [Fixtures](./fixtures/README.md): shared executable fixture corpus and reviewed Core golden artifact contract. - [Lawpacks](./lawpacks/README.md): lawpack import, direct-adapter, bundle diff --git a/docs/topics/cli/test-plan.md b/docs/topics/cli/test-plan.md index 20563d7..422bb38 100644 --- a/docs/topics/cli/test-plan.md +++ b/docs/topics/cli/test-plan.md @@ -99,6 +99,7 @@ Out of scope: | CLI-TP-026 | implemented | Schema guard | CLI-REQ-003 | The compiler settings schema rejects empty `project` emit lists. | compiler_settings_schema_declares_jsonl_contract | docs/schemas/edict.compiler-settings.v1.schema.json | The schema and runtime both require at least one requested projection slot. | | 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. | ## Determinism Obligations diff --git a/docs/topics/contract-bundles/test-plan.md b/docs/topics/contract-bundles/test-plan.md index 5c1f093..3ae4c73 100644 --- a/docs/topics/contract-bundles/test-plan.md +++ b/docs/topics/contract-bundles/test-plan.md @@ -43,7 +43,7 @@ Out of scope: | BUNDLE-REQ-005 | implemented | HOLMES, Watson, and Moriarty evidence entries are optional in the typed bundle; when present, each entry must bind to the manifest's selected bundle subject digest, target profile digest, and target IR digest. | issue #1, docs/GUIDE_edict-assurance-transparency.md | | BUNDLE-REQ-006 | implemented | Admission artifacts remain out of the participant-neutral contract bundle manifest; non-empty admission references are rejected. | docs/SPEC_continuum-contract-bundle-v1.md | | BUNDLE-REQ-007 | implemented | The typed bundle pins `canonicalization_profile.coordinate` to `edict.canonical-cbor/v1`. | docs/SPEC_continuum-contract-bundle-v1.md | -| BUNDLE-REQ-008 | implemented | The crate can assemble a `ContractBundleManifest`, computing `semanticBundleDigest` and `releaseBundleDigest` per the exact spec preimages from digest-locked references, with `coreIrDigest` computed from a real compiled Core module and `targetIrDigest` either supplied as a typed reference or computed from a real `TargetIrArtifact`; the real-artifact path corroborates the Target IR semantic closure against the exact supplied Core and explicit bundle lawpacks, canonicalizes the lawpack set, and preserves malformed-Core canonical failure taxonomy; the assembled manifest validates. | docs/SPEC_continuum-contract-bundle-v1.md, docs/design/contract-bundle-assembly-v0.11.md, docs/design/canonical-target-ir-v0.11.md | +| BUNDLE-REQ-008 | implemented | The crate can assemble a `ContractBundleManifest`, computing `semanticBundleDigest` and `releaseBundleDigest` per the exact spec preimages from digest-locked references, with `coreIrDigest` computed from a real compiled Core module and `targetIrDigest` either supplied as a typed reference or computed from a real `TargetIrArtifact`; the real-artifact path corroborates the Target IR semantic closure against the exact supplied Core, capability imports, and explicit bundle lawpacks, canonicalizes resource sets, and preserves malformed-Core canonical failure taxonomy; the assembled manifest validates. | docs/SPEC_continuum-contract-bundle-v1.md, docs/design/contract-bundle-assembly-v0.11.md, docs/design/canonical-target-ir-v0.11.md | | BUNDLE-REQ-009 | implemented | Direct and built-in-lowerer Target IR artifacts produce identical semantic and release bundle identities under identical explicit assembly inputs; lowerer identity remains release-only. | issue #140, docs/design/provider-artifact-pipeline-alpha.md | | BUNDLE-REQ-010 | implemented | When Core does not declare a semantic closure, the computed-Target-IR assembly path may accept a provider-supplied closure only when it binds the exact supplied Core and its canonical lawpack set equals the explicitly supplied bundle lawpack set; missing or substituted bindings remain rejected. | docs/topics/contract-bundles/README.md | @@ -68,7 +68,7 @@ Out of scope: | BUNDLE-TP-007 | implemented | Boundary guard | BUNDLE-REQ-001 | Removing the release-only build-provenance digest returns `InvalidArtifactReference` on `build_provenance`. | release_bundle_inputs_must_be_digest_locked | crates/edict-syntax/tests/contract_bundle.rs | Proves release digest preimage inputs are represented by the typed manifest. | | BUNDLE-TP-008 | implemented | Boundary guard | BUNDLE-REQ-007 | Changing `canonicalization_profile.coordinate` returns `UnsupportedCanonicalizationProfile`. | canonicalization_profile_must_be_the_v1_cbor_profile | crates/edict-syntax/tests/contract_bundle.rs | Pins the v1 bundle to the canonical CBOR profile. | | BUNDLE-TP-009 | implemented | Golden path | BUNDLE-REQ-001 | Empty lawpack, generated-artifact, and conformance-corpus lists remain valid because optional lists bind what is present without creating non-empty obligations. | optional_artifact_lists_may_be_empty | crates/edict-syntax/tests/contract_bundle.rs | Source artifacts remain the required artifact set. | -| BUNDLE-TP-010 | implemented | Golden path and substitution boundary | BUNDLE-REQ-008 | Assembling a bundle from a real compiled Core module plus supplied digest-locked references produces a manifest that `validate_contract_bundle_manifest` returns `Valid` for; the real-Target-IR path computes `targetIrDigest`, rejects source-coordinate, source-Core-digest, missing, mismatched-provider, artifact-lawpack, and bundle-lawpack substitutions, preserves malformed-Core canonical failures, canonicalizes equivalent bundle lawpack order and duplicates, reports invalid embedded target-profile digests with stable fields, and the checked-in bundle digest golden matches regenerated assembler output. | assembled_bundle_from_real_core_validates, assembled_bundle_from_real_target_ir_computes_target_ir_digest, assembly_from_target_ir_rejects_mismatched_core_source, assembly_from_target_ir_rejects_core_digest_substitution, assembly_from_target_ir_rejects_stripped_lawpack_only_closure, assembly_from_target_ir_rejects_mismatched_provider_semantic_closure, assembly_from_target_ir_rejects_artifact_lawpack_substitution, assembly_from_target_ir_rejects_lawpack_set_substitution, assembly_from_target_ir_preserves_canonical_core_failure, assembly_from_target_ir_canonicalizes_bundle_lawpacks, assembly_from_target_ir_rejects_invalid_target_profile_digest_with_stable_field, assembly_rejects_inputs_that_would_not_validate, bundle_digest_goldens_match_assembly | crates/edict-syntax/tests/contract_bundle.rs, fixtures/bundle/assembly/bounded-hello.bundle-digests.txt | The computed-artifact path recomputes semantic closure from Core when Core declares it and otherwise corroborates an exact provider-supplied closure against explicit bundle lawpacks; standalone canonical Target IR validation remains self-validation and does not reconstruct erased source dependencies. `cargo xtask bundle-goldens --check` guards golden drift. | +| BUNDLE-TP-010 | implemented | Golden path and substitution boundary | BUNDLE-REQ-008 | Assembling a bundle from a real compiled Core module plus supplied digest-locked references produces a manifest that `validate_contract_bundle_manifest` returns `Valid` for; the real-Target-IR path computes `targetIrDigest`, rejects source-coordinate, source-Core-digest, missing, mismatched-provider, artifact-lawpack, artifact-capability, and bundle-lawpack substitutions, preserves malformed-Core canonical failures, canonicalizes equivalent bundle lawpack order and duplicates, reports invalid embedded target-profile digests with stable fields, and the checked-in bundle digest golden matches regenerated assembler output. | assembled_bundle_from_real_core_validates, assembled_bundle_from_real_target_ir_computes_target_ir_digest, assembly_from_target_ir_rejects_mismatched_core_source, assembly_from_target_ir_rejects_core_digest_substitution, assembly_from_target_ir_rejects_stripped_lawpack_only_closure, assembly_from_target_ir_rejects_mismatched_provider_semantic_closure, assembly_from_target_ir_rejects_artifact_lawpack_substitution, assembly_from_target_ir_rejects_artifact_capability_substitution, assembly_from_target_ir_rejects_lawpack_set_substitution, assembly_from_target_ir_preserves_canonical_core_failure, assembly_from_target_ir_canonicalizes_bundle_lawpacks, assembly_from_target_ir_rejects_invalid_target_profile_digest_with_stable_field, assembly_rejects_inputs_that_would_not_validate, bundle_digest_goldens_match_assembly | crates/edict-syntax/tests/contract_bundle.rs, fixtures/bundle/assembly/bounded-hello.bundle-digests.txt | The computed-artifact path recomputes semantic closure from Core when Core declares it and otherwise corroborates an exact provider-supplied closure against explicit bundle lawpacks; standalone canonical Target IR validation remains self-validation and does not reconstruct erased source dependencies. `cargo xtask bundle-goldens --check` guards golden drift. | | BUNDLE-TP-011 | implemented | Boundary guard | BUNDLE-REQ-008 | Changing any semantic-layer preimage component (Core digest, supplied `targetIrDigest` reference, target profile, lawpack, generated artifact, conformance corpus, verifier report, `sourceProfileSemanticFactsDigest`, `canonicalizationProfileDigest`, or semantic compile options) changes both `semanticBundleDigest` and `releaseBundleDigest`. | semantic_preimage_mutations_change_semantic_and_release_digests | crates/edict-syntax/tests/contract_bundle.rs | Semantic-layer mutation propagates to both digests. | | BUNDLE-TP-012 | implemented | Boundary guard | BUNDLE-REQ-008 | Changing any release-only preimage component (provenance-only source digest or logical path, compiler/lowerer/verifier identity with produced artifacts unchanged, nonsemantic compile options, build provenance, or compile explanation) changes `releaseBundleDigest` only, leaving `semanticBundleDigest` unchanged. | release_only_preimage_mutations_leave_semantic_digest_unchanged | crates/edict-syntax/tests/contract_bundle.rs | Honors the semantic/release split; diagnostic and provenance-only changes do not move semantic identity. | | BUNDLE-TP-013 | implemented | Boundary guard | BUNDLE-REQ-008 | The assembly input API distinguishes the computed Core digest from supplied references by type, rejects non-lowercase supplied digest references, and uses the supplied `targetIrDigest` as the single source of truth for both the manifest and semantic preimage. | assembly_rejects_uppercase_supplied_target_ir_digest, assembly_rejects_uppercase_supplied_artifact_digest, target_ir_digest_is_single_source_of_truth | crates/edict-syntax/tests/contract_bundle.rs | The separate computed Target IR path is covered by BUNDLE-TP-010 and TIR-TP-022. | diff --git a/docs/topics/core-ir/README.md b/docs/topics/core-ir/README.md index 75f7a71..5307aca 100644 --- a/docs/topics/core-ir/README.md +++ b/docs/topics/core-ir/README.md @@ -38,11 +38,12 @@ outside the schema under `fixtures/core/canonical/`. [COREIR-REQ-007] capabilities. Imports are digest-locked `resource-ref` values, but the Core module does not contain its own self-hash field. [COREIR-REQ-001] [COREIR-REQ-007] -- Core types cover bounded scalars, records, variants, options, lists, maps, and - capability references. Runtime-sized collections remain explicitly bounded at - the Core schema boundary. Integer type and value identity retains exact width - and signedness, and byte payloads carry an explicit maximum. [COREIR-REQ-002] - [COREIR-REQ-019] +- Core types cover bounded scalars, records, variants, options, lists, maps, + capability references, and typed external-action requests. Runtime-sized + collections remain explicitly bounded at the Core schema boundary. Integer + type and value identity retains exact width and signedness, and byte payloads + carry an explicit maximum. [COREIR-REQ-002] [COREIR-REQ-019] + [COREIR-REQ-020] - Core expressions and predicates are separate schema families. Expressions compute values; predicates express boolean obligations and input constraints. [COREIR-REQ-003] @@ -54,6 +55,11 @@ outside the schema under `fixtures/core/canonical/`. [COREIR-REQ-007] deterministic obstruction map. They also represent `require` nodes with distinct terminal and preserved-obstruction failure arms. [COREIR-REQ-015] [COREIR-REQ-016] +- External-action requests are distinct Core nodes, not semantic effects. They + bind exact operation, schema, scope, basis, budget, and reconciliation data + plus fixed awaiting-settlement posture. Canonical encoding requires the + operation resource to remain in the module's capability imports. + [COREIR-REQ-020] - Local references are alpha-stable: each `local-ref` carries a compiler-owned `id`, normalized `alphaName`, and type reference. Source binder spelling is not identity. [COREIR-REQ-005] @@ -95,6 +101,9 @@ outside the schema under `fixtures/core/canonical/`. [COREIR-REQ-007] sensitivity, and source alpha-renaming invariance. [COREIR-REQ-005] [COREIR-REQ-012] [COREIR-REQ-013] [COREIR-REQ-014] [COREIR-REQ-015] [COREIR-REQ-016] [COREIR-REQ-017] +- External request coverage proves byte reproducibility, mutation sensitivity, + exact capability closure, a fixed-seed 32-case identity corpus, and a bounded + 64-request module. [COREIR-REQ-020] ## Deferred diff --git a/docs/topics/core-ir/canonical-encoding.md b/docs/topics/core-ir/canonical-encoding.md index 4489efb..34927c6 100644 --- a/docs/topics/core-ir/canonical-encoding.md +++ b/docs/topics/core-ir/canonical-encoding.md @@ -174,7 +174,9 @@ public digest function and reviewed digest contract remain unchanged. The reviewed Core fixture pair is: - [`fixtures/core/canonical/bounded-hello.core.cbor`](../../../fixtures/core/canonical/bounded-hello.core.cbor); -- [`fixtures/core/canonical/bounded-hello.core.sha256`](../../../fixtures/core/canonical/bounded-hello.core.sha256). +- [`fixtures/core/canonical/bounded-hello.core.sha256`](../../../fixtures/core/canonical/bounded-hello.core.sha256); +- [`fixtures/core/canonical/workspace-snapshot.core.cbor`](../../../fixtures/core/canonical/workspace-snapshot.core.cbor); +- [`fixtures/core/canonical/workspace-snapshot.core.sha256`](../../../fixtures/core/canonical/workspace-snapshot.core.sha256). Both are generated from [`fixtures/lang/bounds/bounded-hello.edict`](../../../fixtures/lang/bounds/bounded-hello.edict) diff --git a/docs/topics/core-ir/test-plan.md b/docs/topics/core-ir/test-plan.md index 55611a1..0bbb918 100644 --- a/docs/topics/core-ir/test-plan.md +++ b/docs/topics/core-ir/test-plan.md @@ -52,6 +52,7 @@ Out of scope: | COREIR-REQ-017 | implemented | Canonical encoding and decoding accept values through the public `MAX_CANONICAL_NESTING_DEPTH` of 128 nested containers and reject the next level with stable `CanonicalErrorKind::NestingLimitExceeded`, without changing canonical Core bytes or digests below the limit. | issue #146, crates/edict-syntax/src/canonical.rs | | COREIR-REQ-018 | implemented | A Core intent may bind one optional typed basis expression. The expression participates in canonical bytes and digest when present, while the absent representation preserves existing `basis none` Core bytes. | docs/SPEC_edict-language-v1.md, docs/abi/edict-core.cddl | | COREIR-REQ-019 | implemented | Core integer type and value identity preserves exact supported width and signedness, and canonical encoding rejects values outside the declared integer domain. | docs/SPEC_edict-language-v1.md, EDICT-LANG-INTLIT-001 | +| COREIR-REQ-020 | implemented | Core represents typed external-action requests as deterministic non-callable data and requires each request operation to remain in the exact digest-locked capability-import closure before canonical identity exists. | issue #172, docs/abi/edict-core.cddl | ## Fixtures @@ -59,6 +60,7 @@ Out of scope: | --- | --- | --- | | docs/abi/edict-core.cddl | Normative Core semantic schema. | Required semantic declarations exist and forbidden byte/hash freeze fields are absent. | | fixtures/lang/bounds/bounded-hello.edict | Initial pure local-record source-to-Core fixture. | Compiled Core module canonicalizes deterministically and produces the reviewed Core golden artifacts. | +| fixtures/lang/external-actions/workspace-snapshot.edict | Typed external-request source-to-Core fixture. | Compiled Core preserves the complete request and exact capability closure in generated canonical bytes. | | fixtures/core/schema/accepted/core-module-minimal.fields | Accepted Core module field-shape fixture. | Required `core-module` fields are present and no unknown fields appear. | | fixtures/core/schema/accepted/core-intent-minimal.fields | Accepted Core intent field-shape fixture. | Required `core-intent` fields are present and no unknown fields appear. | | fixtures/core/schema/accepted/core-fn-body-minimal.fields | Accepted pure Core function body field-shape fixture. | Required `core-fn-body` fields are present and no unknown fields appear. | @@ -68,6 +70,8 @@ Out of scope: | fixtures/core/schema/rejected/core-fn-body-effect-node-field.fields | Rejected pure Core function body field-shape fixture. | Effect-capable `nodes` field rejects as non-Core helper body shape. | | fixtures/core/canonical/bounded-hello.core.cbor | Reviewed canonical Core byte fixture for the initial pure local-record source fixture. | Executable Core encoding exactly matches the checked-in byte fixture. | | fixtures/core/canonical/bounded-hello.core.sha256 | Exact reviewed Core module digest for the initial pure local-record source fixture. | Domain-separated executable Core digest exactly matches the checked-in review rendering. | +| fixtures/core/canonical/workspace-snapshot.core.cbor | Reviewed canonical Core byte fixture for the typed workspace-snapshot request. | `cargo xtask core-goldens --check` compares the checked-in bytes to executable regeneration. | +| fixtures/core/canonical/workspace-snapshot.core.sha256 | Exact reviewed Core module digest for the typed workspace-snapshot request. | `cargo xtask core-goldens --check` compares the checked-in review digest to executable regeneration. | ## Test Cases @@ -100,6 +104,7 @@ Out of scope: | COREIR-TP-025 | implemented | Canonical validation | COREIR-REQ-017 | Encoding and decoding accept exactly 128 nested empty arrays or maps and reject the 129th container even when it has no child value that would otherwise cross the depth guard. | canonical_nesting_limit_counts_empty_terminal_containers | crates/edict-syntax/src/canonical.rs | Code Lawyer regression for PR #150; prevents empty terminal containers from bypassing the public container-count bound. | | COREIR-TP-026 | implemented | Canonical encoding | COREIR-REQ-012, COREIR-REQ-014, COREIR-REQ-018 | Adding or changing an explicit Core basis expression changes canonical Core bytes and digest; the reference-encoded operation Core satisfies the published schema, while omitting basis retains the reviewed `basis none` golden bytes and digest. | explicit_basis_and_semantic_input_mutations_move_target_identity, core_root_accepts_reference_encoded_operation_basis, reviewed_core_golden_bytes_match_executable_encoder, reviewed_core_digest_matches_exact_fixture | fixtures/lang/operations/explicit-basis-u64.edict, fixtures/core/canonical/bounded-hello.core.cbor | Proves basis is semantic while retaining provider-v1 compatibility. | | COREIR-TP-027 | implemented | Canonical validation | COREIR-REQ-012, COREIR-REQ-019 | `U64::MAX` and the signed fixed-width minima canonicalize under their exact declared domains; an out-of-range value or a value incompatible with its declared width rejects before canonical identity exists, and the reference-encoded exact-width operation satisfies the published schema. | operation_prerequisite_fixture_preserves_fixed_width_basis_and_lawpack_closure, signed_fixed_width_minima_preserve_exact_domains, out_of_range_u64_and_cross_width_values_reject_before_core, canonical_core_rejects_values_outside_their_declared_integer_domain, core_root_accepts_reference_encoded_operation_basis | fixtures/lang/operations/explicit-basis-u64.edict | Prevents host-width and GraphQL-width narrowing from entering Core. | +| COREIR-TP-028 | implemented | External request | COREIR-REQ-012, COREIR-REQ-014, COREIR-REQ-020 | Request fields survive canonical Core encoding, repeated compilation is byte-identical, authority mutations move Core and Target identity, removing the exact capability import makes the artifact unencodable, and the owner-generated request golden reproduces exact bytes and digest. | workspace_observation_request_compiles_as_non_callable_data, request_artifacts_are_reproducible, every_request_authority_field_moves_core_and_target_identity, request_operation_must_remain_in_core_and_target_capability_closure, core_goldens_match_executable_encoder | crates/edict-syntax/tests/external_action_requests.rs, fixtures/core/canonical/workspace-snapshot.core.cbor, fixtures/core/canonical/workspace-snapshot.core.sha256 | Request construction performs no external effect. | ## Determinism Obligations diff --git a/docs/topics/developer-tooling/test-plan.md b/docs/topics/developer-tooling/test-plan.md index a587f68..9240f71 100644 --- a/docs/topics/developer-tooling/test-plan.md +++ b/docs/topics/developer-tooling/test-plan.md @@ -48,7 +48,7 @@ Out of scope: | ID | Status | Kind | Requirement | Scenario | Evidence | Fixtures | Oracle | | --- | --- | --- | --- | --- | --- | --- | --- | -| DEVTOOLS-TP-001 | implemented | Golden path | DEVTOOLS-REQ-001 | Highlighting a representative source fixture emits stable roles and spans for editor adapters. | highlight_source_emits_editor_roles_for_fixture | fixtures/lang/tooling/highlight-smoke.edict | Comments, keywords, identifiers, type identifiers, strings, numbers, operators, and punctuation are classified distinctly; whitespace-only spans are not emitted. | +| DEVTOOLS-TP-001 | implemented | Golden path | DEVTOOLS-REQ-001 | Highlighting a representative source fixture emits stable roles and spans for editor adapters, and the `request` statement introducer is a keyword rather than an identifier. | highlight_source_emits_editor_roles_for_fixture, request_statement_introducer_is_highlighted_as_a_keyword | fixtures/lang/tooling/highlight-smoke.edict, crates/edict-syntax/tests/highlighting.rs | Comments, keywords, identifiers, type identifiers, strings, numbers, operators, and punctuation are classified distinctly; whitespace-only spans are not emitted. | | DEVTOOLS-TP-002 | implemented | Contract artifact | DEVTOOLS-REQ-002 | Tree-sitter grammar artifacts expose the current editor syntax surface and highlight roles. | tree_sitter_grammar_declares_current_editor_contract | grammars/tree-sitter-edict/grammar.js, grammars/tree-sitter-edict/queries/highlights.scm | Grammar rules cover package, imports, declarations, intent clauses, blocks, statements, match/call/type-call/record expressions, comments, strings, and numbers; highlight captures cover the public editor roles. | | DEVTOOLS-TP-003 | implemented | Corpus alignment | DEVTOOLS-REQ-003 | Tree-sitter corpus examples remain accepted Edict source under the reference parser. | tree_sitter_corpus_examples_match_reference_parser | grammars/tree-sitter-edict/test/corpus/current-subset.txt | Corpus cases are nonempty, unique, include expected trees, cover the accepted fixture families, and parse through `parse_module`. | | DEVTOOLS-TP-004 | implemented | Contract artifact | DEVTOOLS-REQ-002 | Tree-sitter keyword captures stay aligned with the public lexical highlighter keyword role. | tree_sitter_query_covers_public_keyword_roles | grammars/tree-sitter-edict/queries/highlights.scm | Every keyword lexeme emitted by `highlight_source` for current and reserved public keyword tokens is covered by the Tree-sitter keyword capture contract. | @@ -56,7 +56,7 @@ Out of scope: | DEVTOOLS-TP-006 | implemented | Corpus alignment | DEVTOOLS-REQ-003 | Tree-sitter accepts uppercase bare identifiers in positions where the reference parser accepts them. | tree_sitter_corpus_examples_match_reference_parser | grammars/tree-sitter-edict/test/corpus/current-subset.txt | Uppercase import aliases, intent names, parameters, local binders, and record shorthands parse without error and remain accepted by `parse_module`; `tree-sitter test` verifies the checked-in syntax tree. | | DEVTOOLS-TP-007 | implemented | Contract artifact | DEVTOOLS-REQ-002 | Tree-sitter operator and punctuation captures stay aligned with the public lexical highlighter roles. | tree_sitter_query_operator_and_punctuation_roles_match_public_highlighter | grammars/tree-sitter-edict/queries/highlights.scm | Query captures do not assign a lexeme to both operator and punctuation, and captured operator or punctuation lexemes match the role emitted by `highlight_source`. | | DEVTOOLS-TP-008 | implemented | Contract artifact | DEVTOOLS-REQ-004 | TextMate grammar declares the current editor contract for `.edict` lexical scopes. | textmate_grammar_declares_current_editor_contract | grammars/textmate/edict.tmLanguage.json | The grammar is valid JSON, names `source.edict`, registers `.edict`, includes comments, strings, numbers, keywords, types, operators, punctuation, and identifiers, and scopes line/block comments distinctly. | -| DEVTOOLS-TP-009 | implemented | Contract artifact | DEVTOOLS-REQ-004 | TextMate grammar keyword, operator, punctuation, type, and identifier patterns stay aligned with public highlighter roles. | textmate_grammar_covers_public_highlight_roles | grammars/textmate/edict.tmLanguage.json | Lexemes emitted by `highlight_source` as keywords, operators including `->`, punctuation, type identifiers, and identifiers are covered by the corresponding TextMate scope patterns. | +| DEVTOOLS-TP-009 | implemented | Contract artifact | DEVTOOLS-REQ-004 | TextMate grammar keyword, operator, punctuation, type, and identifier patterns stay aligned with public highlighter roles, including the `request` statement introducer. | textmate_grammar_covers_public_highlight_roles | grammars/textmate/edict.tmLanguage.json | Lexemes emitted by `highlight_source` as keywords, operators including `->`, punctuation, type identifiers, and identifiers are covered by the corresponding TextMate scope patterns. | | DEVTOOLS-TP-010 | implemented | Contract artifact | DEVTOOLS-REQ-004 | TextMate grammar number patterns stay aligned with public highlighter spans for package version labels. | textmate_grammar_scopes_public_number_spans_in_version_labels | grammars/textmate/edict.tmLanguage.json | In a valid version label such as `@1_beta`, a number scope exactly covers the public `Number` span emitted by `highlight_source`. | | DEVTOOLS-TP-011 | implemented | Contract artifact | DEVTOOLS-REQ-005 | VS Code/Cursor package registration stays aligned with the canonical TextMate grammar. | vscode_extension_declares_textmate_language_contract | editors/vscode/package.json, editors/vscode/syntaxes/edict.tmLanguage.json | The extension contributes language id `edict`, extension `.edict`, scope `source.edict`, and a vendored grammar identical to the canonical TextMate grammar artifact. | | DEVTOOLS-TP-012 | implemented | Contract artifact | DEVTOOLS-REQ-005 | VS Code/Cursor language configuration exposes Edict lexer comment and delimiter boundaries. | vscode_language_configuration_matches_lexer_boundaries | editors/vscode/package.json, editors/vscode/language-configuration.json | The language configuration declares `//`, `/* */`, `{}`, `[]`, and `()` boundaries consistent with the lexer and grammar artifacts. | diff --git a/docs/topics/external-action-requests/README.md b/docs/topics/external-action-requests/README.md new file mode 100644 index 0000000..03ae27a --- /dev/null +++ b/docs/topics/external-action-requests/README.md @@ -0,0 +1,104 @@ +# External-Action Requests + +Status: current HEAD contract. + +Edict can construct a typed request for one external operation without +performing that operation. The request is deterministic compiler data. Echo +later admits and records it, an authorized adapter performs the boundary +crossing, and a schema-validated settlement may resume the program. This topic +owns request construction only. + +## Source Surface + +A requestable operation family enters source through a digest-locked +`capability` import: + +```edict +use capability workspace.snapshot.observe@1 + digest "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + as snapshot; +``` + +The alias is callable only in a `request` statement: + +```edict +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"; +``` + +The operation takes one typed input. Scope, basis, and both budgets are ordinary +typed expressions, so their runtime values survive compilation for Echo +admission. Schemas and the reconciliation law are exact digest-locked +resources. [EXTREQ-REQ-001] [EXTREQ-REQ-006] + +## Core And Target IR + +Core emits `ExternalActionRequest` as a distinct node containing: + +- compiler-owned binding identity; +- exact operation resource; +- input and settlement type coordinates; +- exact input and settlement schemas; +- input, authority-scope, and basis expressions; +- maximum settlement bytes and attempt expressions; +- exact reconciliation law; +- fixed `awaitingSettlement` state and `schemaRequired` settlement posture. + +The operation resource must equal one `capability` import. Canonical Core +encoding fails closed when that closure is missing. [EXTREQ-REQ-002] +[EXTREQ-REQ-004] + +Target lowering copies the node into `externalActionRequests`. It never turns +the operation into a target step or `targetIntrinsic`. The Target IR semantic +closure binds the exact capability resource alongside the source Core and any +lawpacks; canonical Target IR encoding refuses a request whose operation is +absent from that closure. [EXTREQ-REQ-003] [EXTREQ-REQ-004] + +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] + +## Authority Boundary + +Request authority is not performance authority: + +- a capability alias cannot be invoked as an ordinary semantic effect; +- the current request-family allowlist contains only the domain-specific + `workspace` root used by `workspace.snapshot.observe@1`; +- raw `filesystem`, `process`, `network`, Git, GitHub, `model`, and `shell` + operation families, case variants, abbreviations, and unregistered roots are + rejected with `UnrequestableExternalOperation`; +- compiling and lowering perform no I/O; +- the compiler/provider component interface gains no callable import; +- dynamic path, ref, basis, budget, adapter, and settlement constraints remain + Echo admission obligations. + +The first admitted family is domain-specific +`workspace.snapshot.observe@1`, not ambient filesystem access. +[EXTREQ-REQ-003] [EXTREQ-REQ-006] [EXTREQ-REQ-007] + +## Waiting And Settlement + +The request binding and Target IR request id identify explicit +`awaitingSettlement` program state. No native stack, continuation, callback, or +`async` host frame is serialized. This Edict slice does not define settlement +execution or resumption; Echo owns durable request, claim, settlement, recovery, +and replay semantics. + +## Deferred + +- Echo admission and durable settlement of the compiler-emitted request; +- bounded workspace-observation adapter execution; +- settlement-driven deterministic resumption; +- basis-bound validated patch application; +- Git, GitHub, process, network, timer, and model adapters; +- the autonomous delivery loop. + +The verification matrix, fixed seed, and stress bound are in +[test-plan.md](./test-plan.md). diff --git a/docs/topics/external-action-requests/test-plan.md b/docs/topics/external-action-requests/test-plan.md new file mode 100644 index 0000000..96863f6 --- /dev/null +++ b/docs/topics/external-action-requests/test-plan.md @@ -0,0 +1,81 @@ +# External-Action Requests Test Plan + +Status: current verification design for issue #172. + +## Scope + +In scope: + +- digest-locked `capability` imports that name requestable external operation + families; +- source-authored external-action request declarations; +- deterministic Core and Target IR representation of request data; +- package-closure binding for operation, schema, and reconciliation resources; +- explicit awaiting-settlement and schema-admission posture; +- compile-time denial of undeclared or directly callable operation families; +- omission of filesystem, process, network, Git, GitHub, model, and shell + authority from compiler and provider interfaces. + +Out of scope: + +- performing external actions; +- adapter selection or authorization; +- live path, ref, basis, budget, or settlement admission; +- stack suspension, continuations, or `async` host frames; +- general-purpose effects or ambient I/O. + +## Requirements + +| ID | Status | Requirement | Source | +| --- | --- | --- | --- | +| EXTREQ-REQ-001 | implemented | Source syntax binds each requestable operation through one digest-locked `capability` import and carries exact input-schema, settlement-schema, authority-scope, basis, budget, input, and reconciliation-law values. | issue #172 | +| EXTREQ-REQ-002 | implemented | Core represents external-action requests as data with compiler-owned binding identity, typed input and settlement coordinates, explicit `awaitingSettlement` state, and required schema admission. | issue #172 | +| EXTREQ-REQ-003 | implemented | Target IR preserves external-action requests separately from callable target steps and target intrinsics. | issue #172 | +| EXTREQ-REQ-004 | implemented | Core and Target IR semantic closure bind the exact capability resource; undeclared operations, floating capability imports, empty request resource coordinates, and duplicate Target request identities fail closed. | issue #172 | +| EXTREQ-REQ-005 | implemented | Equivalent admitted source and compiler facts produce byte-identical Core and Target IR; every request authority or meaning mutation moves the corresponding digest. | issue #172 | +| 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 | + +## Fixtures + +| Fixture | Purpose | Oracle | +| --- | --- | --- | +| 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. | + +## Cases + +| ID | Status | Category | Requirement | Oracle | Evidence | Fixtures | Notes | +| --- | --- | --- | --- | --- | --- | --- | --- | +| EXTREQ-TP-001 | implemented | Golden path | EXTREQ-REQ-001, EXTREQ-REQ-002, EXTREQ-REQ-003 | One workspace observation request parses, compiles, and lowers with exact structured fields, awaiting posture, and zero callable target steps. | workspace_observation_request_compiles_as_non_callable_data | crates/edict-syntax/tests/external_action_requests.rs | The target owns later admission and execution. | +| EXTREQ-TP-002 | implemented | Closure guard | EXTREQ-REQ-004 | Missing operation imports and floating capability digests reject during compilation; removing the exact capability from an already constructed Core or Target artifact rejects during canonical encoding. | undeclared_or_floating_operation_families_fail_closed, request_operation_must_remain_in_core_and_target_capability_closure | crates/edict-syntax/tests/external_action_requests.rs | A source alias is not authority by itself. | +| EXTREQ-TP-003 | implemented | Authority guard | EXTREQ-REQ-003, EXTREQ-REQ-007 | Direct invocation or obstruction-position use of a capability import fails; case variants, abbreviations, unregistered roots, and raw ambient operation families reject with stable compiler kinds. | capability_import_cannot_be_called_as_an_effect, capability_import_cannot_be_used_as_an_obstruction_coordinate, ambient_operation_families_are_not_requestable | crates/edict-syntax/tests/external_action_requests.rs | Request authority and performance authority remain distinct. | +| EXTREQ-TP-004 | implemented | Dynamic boundary | EXTREQ-REQ-006 | Scope, basis, maximum settlement bytes, and attempt count remain Core expressions sourced from admitted input. | dynamic_admission_values_survive_without_compile_time_execution | crates/edict-syntax/tests/external_action_requests.rs | Echo owns value-instance admission. | +| EXTREQ-TP-005 | implemented | Determinism | EXTREQ-REQ-005 | Repeated compilation and lowering produce identical canonical bytes and digests. | request_artifacts_are_reproducible | crates/edict-syntax/tests/external_action_requests.rs | No clock, randomness, filesystem, or network input enters the compiler. | +| EXTREQ-TP-006 | implemented | Mutation sensitivity | EXTREQ-REQ-005 | Operation, input schema, settlement schema, authority scope, basis, both budget fields, input, and reconciliation mutations move both Core and Target IR identity. | every_request_authority_field_moves_core_and_target_identity | crates/edict-syntax/tests/external_action_requests.rs | Each field is request-authoritative. | +| EXTREQ-TP-007 | implemented | Property | EXTREQ-REQ-005, EXTREQ-REQ-008 | A fixed-seed 32-case capability-digest corpus is reproducible and collision-free within the corpus. | fixed_seed_request_identity_corpus_is_deterministic | crates/edict-syntax/tests/external_action_requests.rs | Seed is recorded in the fixture table. | +| EXTREQ-TP-008 | implemented | Stress | EXTREQ-REQ-008 | A generated module with 64 request declarations emits 64 Core requests and 64 Target IR requests with zero callable steps. | sixty_four_requests_remain_bounded_non_callable_data | crates/edict-syntax/tests/external_action_requests.rs | Bound is fixed for CI. | +| EXTREQ-TP-009 | implemented | Closure guard | EXTREQ-REQ-004 | Canonical Core and Target IR encoding reject request operations removed from their exact capability closure. | request_operation_must_remain_in_core_and_target_capability_closure | crates/edict-syntax/tests/external_action_requests.rs | Manual artifact construction cannot bypass compiler-owned closure. | +| EXTREQ-TP-010 | implemented | Bundle boundary | EXTREQ-REQ-004 | Contract-bundle assembly rejects a digest-locked Target IR capability absent from the supplied Core closure. | assembly_from_target_ir_rejects_artifact_capability_substitution | crates/edict-syntax/tests/contract_bundle.rs | A self-consistent target artifact cannot substitute source-owned request authority. | +| EXTREQ-TP-011 | implemented | Public projection | EXTREQ-REQ-002, EXTREQ-REQ-003, EXTREQ-REQ-004 | The public CLI review projection carries the complete Core and Target IR request, exact capability closure, awaiting-settlement posture, and no callable target step. | project_exposes_external_requests_as_non_callable_review_data | crates/edict-cli/tests/jsonl_cli.rs | Projection is review data, not execution authority. | +| 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. | + +## 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. +- Canonical comparisons assert decoded structured fields and exact bytes, not + diagnostics or log text. +- Stress cardinality is fixed at 64. + +## Open Gaps + +- Echo admission of compiler-emitted request values. +- A concrete workspace-observation adapter and settlement witness. +- Basis-bound validated patch application. diff --git a/docs/topics/providers/README.md b/docs/topics/providers/README.md index 9f62eab..a6fd724 100644 --- a/docs/topics/providers/README.md +++ b/docs/topics/providers/README.md @@ -128,6 +128,11 @@ callable or unknown import. The generated component's exact type-only conveys types rather than a host capability. No WASI linker or callable host function is installed. +Source `use capability` declarations establish digest-locked module-import +closure resources. Core external-action request nodes carry the canonical +request fields. Both pass through Core and Target IR without becoming component +imports, host functions, or provider-owned execution authority. + The host owns one immutable Wasmtime engine and creates a fresh store for every invocation. The prepared component, opaque validated request proof, and concrete schema registry must share one manifest and validator authority, and a prepared diff --git a/docs/topics/providers/test-plan.md b/docs/topics/providers/test-plan.md index 4e54f2c..a9a3cef 100644 --- a/docs/topics/providers/test-plan.md +++ b/docs/topics/providers/test-plan.md @@ -63,7 +63,7 @@ Out of scope: | PROVIDERS-REQ-019 | implemented | Every artifact domain admitted by one provider manifest is bound exactly once to a digest-locked generated schema artifact, schema format, and root rule. Missing, duplicate, ambiguous, unsupported, or provenance-invalid bindings reject before component invocation. | issue #145, EDICT-ABI-PROVIDER-AUTHORITY-001 | | PROVIDERS-REQ-020 | implemented | The concrete provider artifact-schema registry is constructed completely from a validated manifest plus the exact manifest-bound closure of explicit in-memory schema bytes. Construction verifies schema digests, compiles and totality-checks every selected root before invocation, proves required-domain closure, and performs no discovery, filesystem, network, environment, clock, randomness, or mutable-global access. | issue #145, EDICT-ABI-PROVIDER-AUTHORITY-001 | | PROVIDERS-REQ-021 | implemented | Every registered domain performs real schema-instance validation over canonical CBOR. Unsupported domains and schema mismatches remain stable structured failures, and construction and validation results are independent of input insertion order. | issue #145, EDICT-ABI-PROVIDER-AUTHORITY-001 | -| PROVIDERS-REQ-022 | implemented | The Wasmtime host uses one explicitly configured component-model engine and a fresh store for every invocation. It permits only the frozen protocol's type-only instance import and registers no callable WASI, filesystem, network, environment, clock, randomness, registry, or other capability-bearing import. | issue #145, EDICT-ABI-PROVIDER-HOST-001 | +| PROVIDERS-REQ-022 | implemented | The Wasmtime host uses one explicitly configured component-model engine and a fresh store for every invocation. It permits only the frozen protocol's type-only instance import and registers no callable WASI, filesystem, network, environment, clock, randomness, registry, or other capability-bearing import. Source external-action capability declarations remain digest-locked module-import closure resources, while Core request nodes carry canonical request data; neither becomes a provider import. | issue #145, issue #172, EDICT-ABI-PROVIDER-HOST-001 | | PROVIDERS-REQ-023 | implemented | The host accepts only opaque validated request proofs, preflights an exact WIT-logical request byte bound and request-authorized output ceiling, enforces explicit fuel, lifting, and store resource limits, invokes the typed frozen lowerer or verifier export, and exposes an artifact only after the pure response validator admits the complete result. | issue #145, issue #146, EDICT-ABI-PROVIDER-HOST-001 | | PROVIDERS-REQ-024 | implemented | Digest, contract, decode, instantiation, fuel, resource, input, output, diagnostic, response-lifting, trap, malformed-response, response-validation, and host-invariant failures use stable host-owned kinds. Wasmtime implementation details remain bounded diagnostics and never enter Edict's public failure identity. | issue #145, EDICT-ABI-PROVIDER-HOST-001 | | PROVIDERS-REQ-025 | implemented | Provider replay executes the same prepared component, validated request, concrete schema authority, and limits twice through independent fresh stores. Exact sealed outcomes must be equal; host failures compare by stable kind, phase, and structured validation report while excluding opaque engine diagnostics. A disposition, completed-outcome, or stable-failure-identity difference returns a dedicated structured replay mismatch. | issue #147 | @@ -135,7 +135,7 @@ Out of scope: | PROVIDERS-TP-039 | implemented | Failure matrix | PROVIDERS-REQ-028 | Unsupported protocol and duplicate request roles reject before proof construction; noncanonical output, wrong output domain, duplicate returned roles, undeclared output, path traversal, malformed WIT, trap, and fuel exhaustion retain their stable layer-owned failure kinds and the test process continues to a conforming invocation. | requests_reject_unsupported_protocol_version, semantic_input_closure_and_role_order_are_exact, provider_output_failure_matrix_preserves_stable_validation_kinds, malformed_canonical_abi_result_is_not_a_guest_trap_or_envelope_failure, fuel_resource_and_guest_traps_remain_distinct | crates/edict-syntax/tests/provider_invocation.rs, crates/edict-provider-host-wasmtime/tests/invocation.rs | Pure-envelope cases remain authoritative for invalid requests that cannot cross the opaque validated-request boundary; host cases prove guest and transport failures remain contained. | | PROVIDERS-TP-040 | implemented | Generated provenance routing | PROVIDERS-REQ-029 | A manifest fixture containing a digest-locked generated `generationProvenance` artifact round-trips and validates; changing that entry to component provenance rejects with `GeneratedRoleRequiresGeneratedSource`. | generation_provenance_is_generated_provider_metadata | fixtures/providers/echo-generated/provider-manifest.json, crates/edict-syntax/tests/provider.rs | Edict validates only the generic routing and provenance envelope; the provider owns the provenance schema and evidence semantics. | | PROVIDERS-TP-041 | implemented | Contract publication | PROVIDERS-REQ-030 | Repeated in-memory assembly yields byte-identical self-contained CDDL and manifest bytes with one sorted binding per published provider contract, and unresolved external rules reject explicitly. | contract_pack_is_self_contained_and_repeatable, assembly_rejects_missing_invalid_uncompilable_and_incomplete_schema_fragments | fixtures/provider-contracts/v1/edict-provider-contracts.cddl, fixtures/provider-contracts/v1/manifest.json, crates/edict-provider-schema/tests/provider_contract_pack.rs | The generated pack is transport-neutral contract authority, not a provider package or registry lookup mechanism. | -| PROVIDERS-TP-042 | implemented | Schema fidelity | PROVIDERS-REQ-030 | Every published root accepts a reference canonical value and rejects a representative structural mutation; `target-ir-artifact` accepts bytes emitted by the executable Target IR encoder. | target_ir_root_matches_reference_encoder, every_published_root_validates_reference_and_rejects_mutation | fixtures/target-ir/canonical/echo-effectful.target-ir.cbor, crates/edict-provider-schema/tests/provider_contract_pack.rs | Instance validation uses the generated self-contained document rather than recognizing domain names. | +| PROVIDERS-TP-042 | implemented | Schema fidelity | PROVIDERS-REQ-030 | Every published root accepts a reference canonical value and rejects a representative structural mutation; `target-ir-artifact` accepts executable encoder output carrying an exact capability-closed external request and rejects that request when injected into the closure-free legacy artifact. | target_ir_root_matches_reference_encoder, every_published_root_validates_reference_and_rejects_mutation | fixtures/target-ir/canonical/echo-effectful.target-ir.cbor, crates/edict-provider-schema/tests/provider_contract_pack.rs | Instance validation uses the generated self-contained document rather than recognizing domain names. The provider schema cannot revive an authority shape refused by the canonical Target IR encoder. | | PROVIDERS-TP-043 | implemented | Authority boundary | PROVIDERS-REQ-030 | Missing, duplicate, unknown, reordered-conflicting, byte-tampered, digest-tampered, or provenance-tampered pack members reject with stable structured kinds and yield no partial authority. | contract_pack_rejects_missing_duplicate_and_tampered_members, manifest_validation_recomputes_schema_and_resource_identity | crates/edict-provider-schema/tests/provider_contract_pack.rs | Recomputing a digest over replacement bytes does not grant authority to replace an Edict-owned contract. | | PROVIDERS-TP-044 | implemented | Drift gate | PROVIDERS-REQ-030 | Check mode reproduces exact CDDL and manifest fixtures and fails on either mismatch without rewriting files. | provider_contract_pack_goldens_match_executable_contract, provider_contract_pack_check_rejects_drift_without_rewriting | fixtures/provider-contracts/v1/edict-provider-contracts.cddl, fixtures/provider-contracts/v1/manifest.json, xtask/src/provider_contract_pack.rs, xtask/src/tests.rs | Write mode is an explicit review action; `cargo xtask verify` uses check mode only. | | PROVIDERS-TP-045 | implemented | Drift recovery | PROVIDERS-REQ-030 | A contract-pack drift failure identifies the exact supported regeneration command. | provider_contract_pack_check_rejects_drift_without_rewriting | xtask/src/provider_contract_pack.rs, xtask/src/tests.rs | The diagnostic does not direct users to the unrelated `*-goldens` command family. | diff --git a/docs/topics/syntax/README.md b/docs/topics/syntax/README.md index 82363dc..b2a372b 100644 --- a/docs/topics/syntax/README.md +++ b/docs/topics/syntax/README.md @@ -36,8 +36,9 @@ programs, prove bounds, or itself lower to Core IR. [SYNTAX-REQ-001] - A module starts with one `package` declaration, then zero or more imports, then declarations. Package and import versions preserve source-significant version spelling, including `_beta` style labels. [SYNTAX-REQ-002] -- Supported imports are `shape`, `lawpack`, `target`, and `core`; `capability` - import syntax is rejected in minimal-v1. Digest clauses accept only +- Supported imports are `shape`, `lawpack`, `target`, `core`, and `capability`. + A capability import names a digest-bound external operation family for + `request`; it is not a callable effect import. Digest clauses accept only `sha256:` plus 64 hex characters. [SYNTAX-REQ-003] - Type declarations parse record types, refined `String`, max-only `Bytes`, `Option`, `CapabilityRef`, bounded `List`, bounded `Map`, enum declarations, @@ -47,9 +48,11 @@ programs, prove bounds, or itself lower to Core IR. [SYNTAX-REQ-001] - Intent declarations parse parameters, return type, clause surface, statement blocks, and expression bodies. Clause requiredness is semantic validation, not parser validation. [SYNTAX-REQ-006] -- Statements parse `let`, `return`, `require`, `guarantee`, `assert`, effect - call statements, `if` / `else if` / `else`, and bounded `for`. Effect - positions must be calls. [SYNTAX-REQ-007] +- Statements parse `let`, `request`, `return`, `require`, `guarantee`, `assert`, + effect call statements, `if` / `else if` / `else`, and bounded `for`. + External-action requests carry exact schemas, scope, basis, budgets, and + reconciliation law. Effect and request operation positions must be calls. + [SYNTAX-REQ-007] - Expressions parse the full Phase 1 precedence chain, calls, type-calls, field access, record literals, booleans, digest literals, pure ternary `if ... then ... else`, branch-yield conditional effects in `let` right-hand diff --git a/docs/topics/syntax/test-plan.md b/docs/topics/syntax/test-plan.md index a654311..a5ee9c2 100644 --- a/docs/topics/syntax/test-plan.md +++ b/docs/topics/syntax/test-plan.md @@ -29,11 +29,11 @@ Out of scope: | --- | --- | --- | --- | | SYNTAX-REQ-001 | implemented | `parse_module` returns source AST only; later phases own semantic validation and lowering. | crates/edict-syntax/src/lib.rs | | SYNTAX-REQ-002 | implemented | Module/package/import coordinates parse and preserve source-significant version spelling. | docs/SPEC_edict-language-v1.md | -| SYNTAX-REQ-003 | implemented | Imports parse supported kinds, validate digest literals, and reject minimal-v1 unsupported `capability`. | docs/SPEC_edict-language-v1.md | +| SYNTAX-REQ-003 | implemented | Imports parse supported kinds, including digest-bound external-operation `capability` references, and validate digest literals. | docs/SPEC_edict-language-v1.md | | SYNTAX-REQ-004 | implemented | Type declarations parse Phase 1 type surface and reject empty enum declarations. | docs/SPEC_edict-language-v1.md | | SYNTAX-REQ-005 | implemented | Integer suffixes remain source-significant in expression literals and static bounds. | docs/SPEC_edict-language-v1.md | | SYNTAX-REQ-006 | implemented | Intent declaration syntax parses parameters, return type, clauses, and blocks; semantic requiredness is deferred. | docs/SPEC_edict-language-v1.md | -| SYNTAX-REQ-007 | implemented | Statement syntax includes effect call positions, guards, control flow, and bounded loops. | docs/SPEC_edict-language-v1.md | +| SYNTAX-REQ-007 | implemented | Statement syntax includes external-action requests, effect call positions, guards, control flow, and bounded loops. | docs/SPEC_edict-language-v1.md | | SYNTAX-REQ-008 | implemented | Expression syntax includes precedence, records, literals, conditionals, variants, and match. | docs/SPEC_edict-language-v1.md | | SYNTAX-REQ-009 | implemented | Reserved keywords reject in bare-name positions while remaining legal after `.`. | docs/SPEC_edict-language-v1.md | | SYNTAX-REQ-010 | implemented | Negative tests assert stable error kinds, not diagnostic prose or incidental output. | crates/edict-syntax/src/parser.rs | @@ -56,7 +56,7 @@ Out of scope: | --- | --- | --- | --- | --- | --- | --- | --- | | SYNTAX-TP-001 | implemented | Golden path | SYNTAX-REQ-001 | Public API returns a `Module` with expected package/declaration shape. | bounded_hello_parses | fixtures/lang/bounds/bounded-hello.edict | Source AST, not Core IR. | | SYNTAX-TP-002 | implemented | Golden path | SYNTAX-REQ-002 | Exact version strings match source spelling. | multi_part_package_version, package_versions_preserve_underscores, import_versions_preserve_underscore_labels | - | Covers `_beta` labels. | -| SYNTAX-TP-003 | implemented | Error handling | SYNTAX-REQ-003 | Invalid digest and unsupported import syntax produce stable error kinds. | import_digest_literals_are_validated, capability_imports_are_rejected_in_v1 | - | Digest oracle is fixed `sha256:` length/hex rule. | +| SYNTAX-TP-003 | implemented | Import boundary | SYNTAX-REQ-003 | Invalid digests reject with a stable error kind, while a digest-bound capability import preserves its distinct AST kind and digest. | import_digest_literals_are_validated, capability_imports_parse_as_external_operation_references | - | Capability import is request authority, not performance authority. | | SYNTAX-TP-004 | implemented | Golden path | SYNTAX-REQ-004 | Type AST nodes match expected records, bytes, variants, enums, and bounds. | bounded_hello_parses, bytes_accept_coordinate_bounds, enum_decl_parses, variant_type_with_and_without_payloads_parses | fixtures/lang/bounds/bounded-hello.edict | Structural AST equality. | | SYNTAX-TP-005 | implemented | Error handling | SYNTAX-REQ-004 | Empty enums reject with `ParseErrorKind::EmptyEnum`. | empty_enum_and_empty_obstruction_maps_reject | - | Parser-level syntactic emptiness. | | SYNTAX-TP-006 | implemented | Edge case | SYNTAX-REQ-005 | Integer suffix is preserved in `Expr::Int` and `BoundRef::Int`. | typed_integer_suffix, bound_integer_suffixes_are_preserved | - | Source-significant suffix oracle. | @@ -77,6 +77,7 @@ Out of scope: | SYNTAX-TP-021 | implemented | Semantic validation | SYNTAX-REQ-011 | The source/surface validator returns stable diagnostic kinds for context-free source errors and accepts unresolved downstream facts. | validate_module_remains_surface_stage_compatibility_alias, surface_validation_defers_import_and_name_resolution, surface_validation_defers_contextual_typing_and_loop_bound_proof, surface_validation_defers_obstruction_exhaustiveness | - | Owned by the semantic-validation shelf. | | SYNTAX-TP-022 | planned | Golden artifact | SYNTAX-REQ-012 | Full source lowering emits byte-stable canonical artifacts. | - | - | Initial Core golden artifacts exist in the Core IR shelf; full source-language coverage remains planned. | | SYNTAX-TP-023 | implemented | Syntax guard | SYNTAX-REQ-013 | `else continue obstructed { reason: ... }` parses as a distinct require-else arm, while `else continueInObstructedStrand(...)` remains a terminal obstruction target. | continue_obstructed_source_arm_parses, helper_shaped_continue_in_obstructed_strand_is_terminal, stale_basis_obstruction_strand_fixture_parses | crates/edict-syntax/tests/parse_review_regressions.rs, fixtures/obstruction-strands/v0/stale-basis/source.edict | Prevents hidden control-flow semantics in ordinary obstruction constructors. | +| SYNTAX-TP-024 | implemented | Golden path | SYNTAX-REQ-003, SYNTAX-REQ-007 | A request statement parses its typed binding, single operation call, schemas, authority scope, basis, two budgets, and reconciliation resource before source-to-Core compilation. A non-call request operation rejects with `NonCallExternalActionOperation`, independently of semantic-effect call diagnostics. | workspace_observation_request_compiles_as_non_callable_data, non_call_request_operation_has_a_request_specific_parse_kind | crates/edict-syntax/tests/external_action_requests.rs | Execution and settlement remain outside the parser. | ## Determinism Obligations diff --git a/docs/topics/target-ir/README.md b/docs/topics/target-ir/README.md index e01e5d6..7d80d78 100644 --- a/docs/topics/target-ir/README.md +++ b/docs/topics/target-ir/README.md @@ -14,8 +14,8 @@ The current target IR implementation is deliberately narrow: - selected target profile: `echo.dpo@1` or `gitwarp.ref_crdt@1`; - selected Target IR artifact domain: `echo.span-ir/v1` or `gitwarp.commit-reducer-ir/v1`; -- selected source/Core shape: the first supported effectful Core effect node - and Echo `require` guard requirements; +- selected source/Core shape: the first supported effectful Core effect node, + Echo `require` guard requirements, and typed external-action request data; - selected outcome: a deterministic target-owned review artifact with canonical `edict.canonical-cbor/v1` bytes and a reviewed `edict.target-ir.artifact/v1` digest; @@ -24,7 +24,7 @@ The current target IR implementation is deliberately narrow: The `edict_syntax` crate exposes `lower_to_target_ir`, `TargetIrLoweringFacts`, `TargetLoweringReport`, `TargetIrArtifact`, -`TargetIrSemanticClosure`, +`TargetIrSemanticClosure`, `TargetIrExternalActionRequest`, `encode_target_ir_artifact`, `digest_target_ir_artifact`, and stable `TargetLoweringFailureKind` values. The lowerer consumes an already-built `CoreModule` and explicit target-lowering facts supplied by the caller. It does @@ -80,6 +80,12 @@ becomes a deterministic Target IR step that records: - the structured Core input expression; - sorted obstruction failure keys and their structured obstruction arm values. +Each supported Core external-action request instead becomes one +`externalActionRequests` entry. It preserves the operation, input and settlement +types, schemas, input, scope, basis, budgets, reconciliation law, compiler-owned +binding, deterministic request id, and fixed awaiting-settlement posture. It +does not emit a target step or target intrinsic. + For the supported Echo slice, each supported Core `require` node before any target step becomes a deterministic Target IR requirement that records: @@ -107,14 +113,15 @@ expression for the supported slice. This records authored basis, preconditions, evaluation limits, guard dispositions, and success-output semantics without resolving a runtime basis, executing Echo, or admitting a bundle. -When any intent has an explicit basis or the Core module imports a lawpack, the -artifact carries a `TargetIrSemanticClosure`. The closure binds the exact -canonical Core coordinate/digest and a coordinate-keyed, lowercase -digest-locked lawpack set. Equivalent lawpack order and duplicate identical -references canonicalize to the same Target IR identity; conflicting resources, -an empty source Core coordinate, an unidentifiable Core, or a basis-bearing -artifact without its closure rejects before Target IR identity exists. -[TIR-REQ-015] +When any intent has an explicit basis, the Core module imports a lawpack, or the +Core module imports a requestable capability, the artifact carries a +`TargetIrSemanticClosure`. The closure binds the exact canonical Core +coordinate/digest plus coordinate-keyed, lowercase digest-locked lawpack and +capability sets. Equivalent resource order and duplicate identical references +canonicalize to the same Target IR identity. Conflicting resources, an empty +source Core coordinate, an unidentifiable Core, a basis-bearing artifact without +its closure, or a request operation absent from the capability closure rejects +before Target IR identity exists. [TIR-REQ-015] [TIR-REQ-017] Canonical Target IR uses an intentional artifact-envelope value model rather than Rust struct serialization. The reviewed digest is SHA-256 over canonical @@ -128,10 +135,11 @@ The canonical value includes the artifact's own domain, digest-locked target profile resource, non-empty source Core coordinate, optional semantic closure, sorted intent map, optional explicit basis expressions, input constraints, Core evaluation budget, source-ordered requirements, requirement predicates and -failure dispositions, source-ordered target steps, sorted obstruction failure -keys and arms, and structured Core result expression. Target profile and -semantic-closure digests are strict artifact references: missing digests and -non-lowercase `sha256:<64 hex>` review strings reject before hashing. +failure dispositions, source-ordered target steps, source-ordered external +requests, sorted obstruction failure keys and arms, and structured Core result +expression. Target profile and semantic-closure digests are strict artifact +references: missing digests and non-lowercase `sha256:<64 hex>` review strings +reject before hashing. Reviewed Echo and git-warp Target IR byte/digest goldens live under `fixtures/target-ir/canonical/`. `cargo xtask target-ir-goldens --check` @@ -173,14 +181,15 @@ with an unsupported ABI rejects with floating imports rejects with `TargetLoweringFailureKind::UndigestedCoreImport`. Supplying unsupported Core capability flags rejects with `TargetLoweringFailureKind::UnsupportedCoreCapability`. Supplying Core nodes -outside the supported effect and Echo requirement shapes rejects with +outside the supported effect, external-request, and Echo requirement shapes +rejects with `TargetLoweringFailureKind::UnsupportedCoreNode`. Supplying a target-specific Core feature that the selected target does not support rejects with `TargetLoweringFailureKind::UnsupportedTargetFeature`. Missing or ambiguous effect lowering facts, non-Echo target intrinsics, missing operation-profile support, and obstruction keys absent from the selected target facts also reject -before any artifact is emitted. A Core intent with no target-owned requirements -or steps, or a Core module with no intents, rejects with +before any artifact is emitted. A Core intent with no target-owned requirements, +steps, or external requests, or a Core module with no intents, rejects with `TargetLoweringFailureKind::NoTargetSteps`. Duplicate target-lowering facts are ambiguous only when they match an effect used by the Core module being lowered; unrelated duplicate facts do not block the supported artifact. @@ -196,6 +205,7 @@ The following are not implemented by this slice: - git-warp runtime execution, commit object creation, and CRDT reducer verification; - Echo runtime receipts for first-class resumable obstruction strands; +- Echo request admission, adapter execution, and settlement resumption; - additional target profiles beyond Echo and git-warp; - v2 chained or composite adapter resolution. diff --git a/docs/topics/target-ir/test-plan.md b/docs/topics/target-ir/test-plan.md index 7b6e8f0..2d992c3 100644 --- a/docs/topics/target-ir/test-plan.md +++ b/docs/topics/target-ir/test-plan.md @@ -53,6 +53,7 @@ Out of scope: | TIR-REQ-014 | implemented | `edict-target-ir.cddl` defines `target-ir-artifact` from the executable canonical value shape and is published in the self-contained provider contract pack. | issue #161, EDICT-ABI-PROVIDER-CONTRACT-PACK-001 | | TIR-REQ-015 | implemented | Target IR preserves an explicit Core basis expression and, when an operation carries an explicit basis or lawpack import, binds a semantic closure containing the exact source Core digest and the sorted digest-locked lawpack resource set. Existing no-basis/no-lawpack Target IR bytes remain unchanged. | docs/SPEC_edict-language-v1.md, docs/design/canonical-target-ir-v0.11.md | | TIR-REQ-016 | implemented | A validated direct lawpack adapter derives the exact target profile, Target IR domain, operation-profile support, effect intrinsic, and named-failure support used for Target IR lowering; callers do not hand-assemble `TargetIrLoweringFacts`. | issue #169, docs/abi/edict-lawpack-adapter.cddl | +| TIR-REQ-017 | implemented | Target IR preserves typed external-action requests in a separate non-callable collection and binds each exact request operation in the semantic capability closure. | issue #172, docs/abi/edict-target-ir.cddl | ## Fixtures @@ -65,6 +66,8 @@ Out of scope: | fixtures/target-ir/canonical/echo-effectful.target-ir.sha256 | Reviewed Echo Target IR digest golden. | `cargo xtask target-ir-goldens --check` compares the checked-in review digest to executable regeneration. | | fixtures/target-ir/canonical/gitwarp-append.target-ir.cbor | Reviewed git-warp Target IR canonical byte golden. | `cargo xtask target-ir-goldens --check` compares the checked-in bytes to executable regeneration. | | fixtures/target-ir/canonical/gitwarp-append.target-ir.sha256 | Reviewed git-warp Target IR digest golden. | `cargo xtask target-ir-goldens --check` compares the checked-in review digest to executable regeneration. | +| fixtures/target-ir/canonical/workspace-snapshot.target-ir.cbor | Reviewed typed external-request Target IR canonical byte golden. | `cargo xtask target-ir-goldens --check` compares the checked-in bytes to executable regeneration. | +| fixtures/target-ir/canonical/workspace-snapshot.target-ir.sha256 | Reviewed typed external-request Target IR digest golden. | `cargo xtask target-ir-goldens --check` compares the checked-in review digest to executable regeneration. | | docs/abi/edict-target-ir.cddl | Edict-owned canonical Target IR artifact schema. | The self-contained provider contract pack compiles the root and validates both reviewed Target IR artifacts. | ## Test Cases @@ -91,7 +94,7 @@ Out of scope: | TIR-TP-018 | implemented | Determinism guard | TIR-REQ-008 | Equivalent Target IR construction order for maps, obstruction failures, and input constraints does not change canonical bytes or digest, while semantic list order for steps remains preserved. | target_ir_artifact_canonicalization_ignores_equivalent_construction_order, target_ir_step_order_changes_digest | crates/edict-syntax/tests/target_ir.rs | Prevents Rust construction order from becoming a cryptographic contract. | | TIR-TP-019 | implemented | Mutation sensitivity | TIR-REQ-008 | Target profile digest, source Core coordinate, intent name, effect coordinate, selected intrinsic, input expression, obstruction failure/arm, input constraint, budget, and result mutations each move the Target IR digest. | target_ir_digest_moves_for_artifact_semantic_mutations, target_ir_obstruction_arm_value_mutation_moves_digest | crates/edict-syntax/tests/target_ir.rs | Freezes the reviewed value shape without re-litigating lowering semantics. | | TIR-TP-020 | implemented | Boundary guard | TIR-REQ-008 | Canonical Target IR encoding rejects missing target-profile digests and non-lowercase digest review strings before hashing. | target_ir_encoder_rejects_unlocked_or_uppercase_target_profile_digest | crates/edict-syntax/tests/target_ir.rs | Target IR artifact references use the strict bundle-artifact digest policy. | -| TIR-TP-021 | implemented | Golden path | TIR-REQ-009 | `xtask target-ir-goldens --check` fails on drift and `--write` regenerates Echo and git-warp byte/digest golden fixtures from executable assembly. | target_ir_goldens_match_executable_encoder | xtask/src/goldens.rs, xtask/src/main.rs, xtask/src/tests.rs, fixtures/target-ir/canonical/echo-effectful.target-ir.cbor, fixtures/target-ir/canonical/echo-effectful.target-ir.sha256, fixtures/target-ir/canonical/gitwarp-append.target-ir.cbor, fixtures/target-ir/canonical/gitwarp-append.target-ir.sha256 | Golden scope is Target IR artifact bytes and digest review strings only. | +| TIR-TP-021 | implemented | Golden path | TIR-REQ-009, TIR-REQ-017 | `xtask target-ir-goldens --check` fails on drift and `--write` regenerates Echo, git-warp, and typed workspace-request byte/digest golden fixtures from executable assembly. | target_ir_goldens_match_executable_encoder | xtask/src/goldens.rs, xtask/src/main.rs, xtask/src/tests.rs, fixtures/target-ir/canonical/echo-effectful.target-ir.cbor, fixtures/target-ir/canonical/echo-effectful.target-ir.sha256, fixtures/target-ir/canonical/gitwarp-append.target-ir.cbor, fixtures/target-ir/canonical/gitwarp-append.target-ir.sha256, fixtures/target-ir/canonical/workspace-snapshot.target-ir.cbor, fixtures/target-ir/canonical/workspace-snapshot.target-ir.sha256 | Golden scope is Target IR artifact bytes and digest review strings only. | | TIR-TP-022 | implemented | Integration | TIR-REQ-010 | Bundle assembly from a real `TargetIrArtifact` computes `targetIrDigest`, writes that same digest into the manifest, and changes bundle digests when the Target IR artifact changes. | assembled_bundle_from_real_target_ir_computes_target_ir_digest | crates/edict-syntax/tests/contract_bundle.rs | Keeps generated Target IR and manifest `target_ir.digest` as one source of truth. | | TIR-TP-023 | implemented | Golden path | TIR-REQ-011 | Echo lowering emits explicit Target IR requirements for terminal and preserved-obstruction Core `require` arms while leaving effect steps unchanged. | echo_target_ir_contains_obstruction_requirement_payload, terminal_and_preserved_requirements_are_target_ir_distinct | crates/edict-syntax/tests/target_ir.rs | Target IR emission remains distinct from Echo acceptance or runtime execution. | | TIR-TP-024 | implemented | Mutation sensitivity | TIR-REQ-012 | Requirement reason kind, reason payload value, predicate, and terminal-vs-preserved disposition mutations move the Target IR digest. | target_ir_requirement_mutations_move_digest | crates/edict-syntax/tests/target_ir.rs | Prevents obstruction semantics from collapsing in canonical Target IR bytes. | @@ -99,12 +102,13 @@ Out of scope: | TIR-TP-026 | implemented | Boundary guard | TIR-REQ-011, TIR-REQ-012 | A Target IR requirement after an emitted target step rejects with a stable target-feature failure kind and no artifact, with a more specific detail when it reads an earlier step output. | requirement_after_target_step_rejects_with_stable_feature_kind, requirement_that_reads_step_output_rejects_with_stable_feature_kind | crates/edict-syntax/tests/target_ir.rs | Intent-level requirements are pre-step guards until the artifact model owns ordered or step-attached guards. | | TIR-TP-027 | implemented | Integration | TIR-REQ-013 | Built-in Echo and git-warp lowerer adapters return the same artifacts, canonical bytes, and digests as direct lowering for identical Core and facts. | builtin_echo_lowerer_matches_direct_target_ir, builtin_gitwarp_lowerer_matches_direct_target_ir | crates/edict-syntax/tests/provider_lowering.rs | No Target IR golden moves when the invocation path changes. | | TIR-TP-028 | implemented | Boundary guard | TIR-REQ-013 | Matched-profile target and target-profile-digest failures pass through unchanged, while cross-profile lowerer selection rejects with a stable compatibility failure before invocation. | builtin_lowerers_preserve_structured_lowering_failures, builtin_lowerers_preserve_target_profile_digest_failures, builtin_lowerers_reject_mismatched_target_profiles | crates/edict-syntax/tests/provider_lowering.rs | Lowerer selection compatibility remains distinct from target semantic refusal; coordinate matching does not bypass target artifact validation. | -| TIR-TP-029 | implemented | Schema fidelity | TIR-REQ-014 | Canonical Echo and git-warp Target IR bytes plus encoder output containing both requirement dispositions satisfy `target-ir-artifact`; null, missing envelope fields, and malformed nested Target IR values reject through the same compiled root. | target_ir_root_matches_reference_encoder, every_published_root_validates_reference_and_rejects_mutation, target_ir_goldens_match_executable_encoder | docs/abi/edict-target-ir.cddl, fixtures/target-ir/canonical/echo-effectful.target-ir.cbor, fixtures/target-ir/canonical/gitwarp-append.target-ir.cbor, crates/edict-provider-schema/tests/provider_contract_pack.rs, xtask/src/tests.rs | The schema is derived from the canonical encoder contract, not target-specific runtime semantics. | +| TIR-TP-029 | implemented | Schema fidelity | TIR-REQ-014, TIR-REQ-017 | Canonical Echo, git-warp, and typed workspace-request Target IR bytes plus encoder output containing both requirement dispositions satisfy `target-ir-artifact`; null, missing envelope fields, malformed nested Target IR values, and an external request injected into the closure-free legacy artifact reject through the same compiled root. | target_ir_root_matches_reference_encoder, every_published_root_validates_reference_and_rejects_mutation, target_ir_goldens_match_executable_encoder | docs/abi/edict-target-ir.cddl, fixtures/target-ir/canonical/echo-effectful.target-ir.cbor, fixtures/target-ir/canonical/gitwarp-append.target-ir.cbor, fixtures/target-ir/canonical/workspace-snapshot.target-ir.cbor, crates/edict-provider-schema/tests/provider_contract_pack.rs, xtask/src/tests.rs | The schema is derived from the canonical encoder contract, including the rule that external requests require exact semantic authority rather than the legacy compatibility shape. | | TIR-TP-030 | implemented | Schema fidelity | TIR-REQ-014 | A target-profile coordinate containing only LF is accepted by both the canonical Target IR encoder and the published `target-ir-artifact` root. | target_ir_root_accepts_encoder_valid_line_feed_coordinate | crates/edict-provider-schema/tests/provider_contract_pack.rs | The CDDL nonempty constraint cannot use a regex whose wildcard excludes line terminators accepted by the encoder. | | TIR-TP-031 | implemented | Integration | TIR-REQ-001, TIR-REQ-015 | Compiling and lowering the operation prerequisite fixture preserves the exact Core basis expression and binds the computed Core digest plus exact lawpack coordinate/digest in Target IR; an empty Core coordinate, unidentifiable or noncanonical resource, coordinate conflict, internally contradictory closure, or missing closure on a basis-bearing artifact refuses without an artifact identity. The canonical encoder and published CDDL root reject an empty source Core coordinate, and the CDDL root independently rejects the externally constructed missing-closure shape. | operation_prerequisite_fixture_preserves_fixed_width_basis_and_lawpack_closure, target_lowering_refuses_an_unidentifiable_semantic_closure, legacy_target_ir_encoder_rejects_an_empty_source_core_coordinate, invalid_or_conflicting_lawpack_resources_refuse_before_target_artifact, semantic_closure_cannot_substitute_a_different_source_core_coordinate, explicit_basis_target_ir_cannot_drop_its_semantic_closure, target_ir_root_matches_reference_encoder | fixtures/lang/operations/explicit-basis-u64.edict | The target artifact binds semantic inputs but remains distinct from package admission or runtime execution. | | TIR-TP-032 | implemented | Mutation sensitivity | TIR-REQ-008, TIR-REQ-015 | Changing the basis expression, source Core meaning, lawpack coordinate, or lawpack digest moves the Target IR digest, while equivalent lawpack construction order does not. | explicit_basis_and_semantic_input_mutations_move_target_identity, semantic_closure_lawpack_set_is_order_invariant | fixtures/lang/operations/explicit-basis-u64.edict | Prevents Target IR identity from floating over operation semantic inputs. | | TIR-TP-033 | implemented | Compatibility | TIR-REQ-009, TIR-REQ-015 | Existing reviewed Echo and git-warp fixtures with `basis none` and no lawpack import retain byte-identical Target IR and digest goldens. | target_ir_goldens_match_executable_encoder | fixtures/target-ir/canonical/echo-effectful.target-ir.cbor, fixtures/target-ir/canonical/gitwarp-append.target-ir.cbor | Provider-v1 compatibility artifacts remain stable alongside the new operation prerequisites. | | TIR-TP-034 | implemented | Integration | TIR-REQ-001, TIR-REQ-002, TIR-REQ-015, TIR-REQ-016 | Hello Echo lowers from exact source and lawpack resources through the selected direct adapter into `echo.span-ir/v1`, preserving the explicit basis, create effect, create-if-absent intrinsic, typed failure arm, and semantic closure. | hello_echo_source_compiles_to_echo_target_ir_from_exact_lawpack_adapter | fixtures/lawpack/hello-echo/README.md | Target IR remains distinct from the later Echo executable package. | +| TIR-TP-035 | implemented | External request | TIR-REQ-001, TIR-REQ-008, TIR-REQ-017 | Workspace observation requests lower into `externalActionRequests` with exact capability closure and zero callable target steps or intrinsics; removing the operation from the closure makes canonical encoding fail closed. | workspace_observation_request_compiles_as_non_callable_data, request_operation_must_remain_in_core_and_target_capability_closure | crates/edict-syntax/tests/external_action_requests.rs | Echo owns later request admission, execution, and settlement. | ## Determinism Obligations diff --git a/editors/vscode/syntaxes/edict.tmLanguage.json b/editors/vscode/syntaxes/edict.tmLanguage.json index 41c0db4..5847d8e 100644 --- a/editors/vscode/syntaxes/edict.tmLanguage.json +++ b/editors/vscode/syntaxes/edict.tmLanguage.json @@ -66,7 +66,7 @@ }, "keywords": { "name": "keyword.control.edict", - "match": "\\b(?:package|use|type|enum|variant|intent|returns|profile|implements|basis|footprint|budget|where|let|return|require|guarantee|assert|if|then|else|for|in|bounded|yield|match|shape|lawpack|target|core|capability|as|digest|fn|const|true|false)\\b" + "match": "\\b(?:package|use|type|enum|variant|intent|returns|profile|implements|basis|footprint|budget|where|let|return|request|require|guarantee|assert|if|then|else|for|in|bounded|yield|match|shape|lawpack|target|core|capability|as|digest|fn|const|true|false)\\b" }, "types": { "name": "entity.name.type.edict", diff --git a/fixtures/core/canonical/README.md b/fixtures/core/canonical/README.md index a447f84..d9a0ca7 100644 --- a/fixtures/core/canonical/README.md +++ b/fixtures/core/canonical/README.md @@ -3,7 +3,7 @@ This directory contains reviewed Core artifact fixtures generated from the executable compiler and canonical encoder. -## Current Fixture +## Current Fixtures `bounded-hello` is generated from [`fixtures/lang/bounds/bounded-hello.edict`](../../lang/bounds/bounded-hello.edict) @@ -20,6 +20,12 @@ Artifacts: - `bounded-hello.core.sha256`: review rendering of the `edict.core.module/v1` digest. +`workspace-snapshot` is generated from +[`fixtures/lang/external-actions/workspace-snapshot.edict`](../../lang/external-actions/workspace-snapshot.edict) +using the deterministic `workspace.read` profile and `workspace.tiny` budget +facts. Its `.core.cbor` and `.core.sha256` files bind every external-request +field and exact capability closure into reviewed Core identity. + The digest preimage is the canonical CBOR encoding of: ```text diff --git a/fixtures/core/canonical/workspace-snapshot.core.cbor b/fixtures/core/canonical/workspace-snapshot.core.cbor new file mode 100644 index 0000000..62e9413 Binary files /dev/null and b/fixtures/core/canonical/workspace-snapshot.core.cbor differ diff --git a/fixtures/core/canonical/workspace-snapshot.core.sha256 b/fixtures/core/canonical/workspace-snapshot.core.sha256 new file mode 100644 index 0000000..3befdee --- /dev/null +++ b/fixtures/core/canonical/workspace-snapshot.core.sha256 @@ -0,0 +1 @@ +sha256:19fda44e4469ddcd69110dcca6e6209f96951ee06230161ca883bcc62bd85af8 diff --git a/fixtures/lang/external-actions/workspace-snapshot.edict b/fixtures/lang/external-actions/workspace-snapshot.edict new file mode 100644 index 0000000..e54b0fb --- /dev/null +++ b/fixtures/lang/external-actions/workspace-snapshot.edict @@ -0,0 +1,35 @@ +package examples.workspace_observer@1; + +use capability workspace.snapshot.observe@1 + digest "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + as snapshot; + +type ObserveInput = { + payload: Bytes, + scope: Bytes, + basis: Bytes, + maxSettlementBytes: U64, + maxAttempts: U32, +}; + +intent observe(input: ObserveInput) + returns ExternalActionRequest> + profile workspace.read + basis input.basis + budget <= workspace.tiny +{ + 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/provider-contracts/v1/edict-provider-contracts.cddl b/fixtures/provider-contracts/v1/edict-provider-contracts.cddl index 5df2ec6..f309b20 100644 --- a/fixtures/provider-contracts/v1/edict-provider-contracts.cddl +++ b/fixtures/provider-contracts/v1/edict-provider-contracts.cddl @@ -102,7 +102,7 @@ core-module = { } core-import = { - kind: "lawpack" / "target" / "core", + kind: "lawpack" / "target" / "core" / "capability", ref: resource-ref, ? alias: tstr, } @@ -111,7 +111,7 @@ core-import = { core-type = core-scalar-type / core-record-type / core-variant-type / core-option-type / core-list-type / core-map-type / - core-capability-ref-type + core-capability-ref-type / core-external-action-request-type core-scalar-type = core-bool-type / core-int-type / core-string-type / core-bytes-type / core-unit-type @@ -160,6 +160,10 @@ core-capability-ref-type = { kind: "CapabilityRef", item: core-type-ref, } +core-external-action-request-type = { + kind: "ExternalActionRequest", + settlement: core-type-ref, +} ; core-type-ref is defined in edict-common.cddl and assembled with this schema. @@ -324,7 +328,8 @@ core-block = { result: core-expr, } -core-node = let-node / require-node / effect-node / guard-node / branch-node / +core-node = let-node / require-node / effect-node / + external-action-request-node / guard-node / branch-node / for-node / match-node / proof-obligation-node let-node = { @@ -358,6 +363,26 @@ effect-node = { input: core-expr, obstructionMap: { * failure-ident => obstruction-arm }, } +external-action-request-node = { + kind: "externalActionRequest", + binding: local-ref, + operation: resource-ref, + inputType: core-type-ref, + settlementType: core-type-ref, + inputSchema: resource-ref, + settlementSchema: resource-ref, + input: core-expr, + authorityScope: core-expr, + basis: core-expr, + budget: external-action-budget, + reconciliationLaw: resource-ref, + state: "awaitingSettlement", + settlementAdmission: "schemaRequired", +} +external-action-budget = { + maxSettlementBytes: core-expr, + maxAttempts: core-expr, +} obstruction-arm = { binder: local-ref, value: core-expr, @@ -872,11 +897,13 @@ target-ir-legacy-artifact = { target-ir-semantic-closure = { sourceCore: target-ir-resource-ref, lawpacks: [* target-ir-resource-ref], + ? capabilities: [* target-ir-resource-ref], } target-ir-intent = { target-ir-intent-common, ? basis: core-expr, + ? externalActionRequests: [* target-ir-external-action-request], } target-ir-legacy-intent = { @@ -907,3 +934,20 @@ target-ir-step = { obstructionFailures: [* failure-ident], obstructionArms: { * failure-ident => obstruction-arm }, } + +target-ir-external-action-request = { + id: tstr, + binding: local-ref, + operation: target-ir-resource-ref, + inputType: core-type-ref, + settlementType: core-type-ref, + inputSchema: target-ir-resource-ref, + settlementSchema: target-ir-resource-ref, + input: core-expr, + authorityScope: core-expr, + basis: core-expr, + budget: external-action-budget, + reconciliationLaw: target-ir-resource-ref, + state: "awaitingSettlement", + settlementAdmission: "schemaRequired", +} diff --git a/fixtures/provider-contracts/v1/manifest.json b/fixtures/provider-contracts/v1/manifest.json index 0106222..ee9c0bc 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": "3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d70726f76696465722d636f6e7472616374732e6364646c0a3b2047656e6572617465642066726f6d2045646963742d6f776e65642041424920667261676d656e74732e20444f204e4f5420454449542e0a0a3b202d2d2d2065646963742d636f6d6d6f6e2e6364646c202d2d2d0a3b2065646963742d636f6d6d6f6e2e6364646c0a3b20536861726564204344444c20747970657320666f722074686520456469637420414249732c20646566696e6564204f4e4345206865726520736f20746865792063616e6e6f742064726966740a3b202845444943542d4142492d4e4f4455502d303031292e2054776f2067726f7570733a0a3b2020202d207265736f757263652d7265662c207368613235362d6469676573742c206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c0a3b2020202020636f72652d747970652d7265663a20617373656d626c656420776974682065646963742d7461726765742d70726f66696c652e6364646c20616e640a3b202020202065646963742d6c61777061636b2e6364646c20627920746865206275696c643b2074686f736520736368656d617320646f206e6f74207265646566696e65207468656d2e0a3b2020202d206f7065726174696f6e2d70726f66696c652c206f707469632d74656d706c6174652c2061706572747572652d726571756972656d656e7420616e6420746865697220726566733a0a3b2020202020636f6e73756d65642062792074686520436f72652f6f70746963206c61796572202865646963742d636f72652e6364646c2920616e64207265666572656e636564206279207468650a3b20202020206c616e67756167652f7461726765742d70726f66696c652073706563732e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a0a3b2041206e6f726d617469766520737562636f6d706f6e656e74207265666572656e636564206279206964656e7469747920706c7573206469676573742e204d616e696665737473206e657665720a3b20656d626564207468656972206f776e2073656c662d64696765737420696e20746865697220707265696d616765202845444943542d434f52452d53454c46484153482d303031292e0a7265736f757263652d726566203d207b2069643a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20446967657374732061726520617574686f726974617469766520617320747970656420627974652076616c7565732c206e657665722068657820737472696e67732e20526576696577204a534f4e0a3b2072656e64657273207468697320617320227368613235363a3c3634206c6f77657263617365206865783e22202845444943542d4449474553542d574952452d303031292e0a7368613235362d646967657374203d205b20616c676f726974686d3a2022736861323536222c2062797465733a2062737472202e73697a65203332205d0a0a3b2041206e616d6564206c6f772d6c6576656c206661696c75726520616e206566666563742063616e2072616973652e2054686520736f75726365206f62737472756374696f6e206d61700a3b2062696e64732069742028627920636f6f7264696e6174652920616e6420636f6e73747275637473206120747970656420646f6d61696e206f62737472756374696f6e2066726f6d206974730a3b207061796c6f6164202845444943542d4142492d4641494c5552452d4e414d45442d303031292e0a3b20416e20656666656374277320606566666563744661696c7572657360206c697374204d555354206861766520756e697175652060636f6f7264696e61746560733a2073696e6365207468650a3b206f62737472756374696f6e206d6170206973206b6579656420627920636f6f7264696e6174652c2074776f206661696c757265732073686172696e67206120636f6f7264696e61746520286576656e0a3b207769746820646966666572656e7420617574686f72697479436c6173732f7061796c6f61645479706529206d616b652065786861757374697665206d617070696e6720616e642062696e6465720a3b20747970696e6720616d626967756f757320616e64206172652072656a6563746564202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a3b0a3b2045666665637473206361727279207468656972206661696c757265732061732061206d617020607b206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d600a3b202873656520746865207461726765742f6c61777061636b2065666665637420736368656d6173292e20546865206661696c75726520636f6f7264696e61746520697320746865206d61700a3b204b45592c20736f206974206973206e6f7420726570656174656420696e2074686520626f647920616e642063616e6e6f74206469736167726565207769746820746865206b65792e0a6566666563742d6661696c7572652d626f6479203d207b0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164547970653a20636f72652d747970652d7265662c202020202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b2041206661696c75726520636f6f7264696e617465206d7573742062652061206261726520456469637420606964656e746020286c65747465722f756e64657273636f7265207468656e0a3b206c6574746572732f6469676974732f756e64657273636f7265732920414e44206d757374206e6f742062652061207265736572766564206b6579776f72642028652e672e2060656c7365602c0a3b20606261736973602c20607768657265602c206072657175697265602c2060666f72602c2060696660292e2054686520736f75726365206f62737472756374696f6e2d6d6170204c4853206f6e6c790a3b20616363657074732061206e6f6e2d6b6579776f726420606964656e74602c20736f20612068797068656e2f646f742f6b6579776f726420636f6f7264696e61746520776f756c642062650a3b204142492d76616c69642079657420696d706f737369626c6520746f206d617020657868617573746976656c7920696e20736f757263652e20546865207265676578206361707475726573207468650a3b206c65786963616c2073686170653b206b6579776f7264206578636c7573696f6e20697320616e206164646974696f6e616c2076616c69646174696f6e2072756c650a3b202845444943542d4142492d4641494c5552452d4944454e542d303031292e0a6661696c7572652d6964656e74203d2074737472202e72656765787020225b412d5a612d7a5f5d5b412d5a612d7a302d395f5d2a220a0a6566666563742d6b696e64203d20227265616422202f202263726561746522202f2022656e7375726522202f20227265706c61636522202f202264656c65746522202f0a202020202020202020202020202022617070656e6422202f202272656475636522202f202273656d616e7469632e656d697422202f2022637573746f6d220a0a617574686f726974792d636c617373203d2022646f6d61696e4d61707061626c6522202f20227061727469636970616e744f776e656422202f2022696e746567726974794661756c7422202f0a202020202020202020202020202020202020227265736f757263654661756c7422202f2022696e7465726e616c4661756c74220a0a636f72652d747970652d726566203d20747374722020203b2063616e6f6e6963616c20436f7265207479706520636f6f7264696e6174650a0a3b20416e206f7065726174696f6e2070726f66696c6520737570706c69657320746865206f707469632074656d706c617465206120436f726520696e74656e74207265736f6c766573206974730a3b206f707469634b696e642f626f756e646172794b696e642f737570706f7274506f6c6963792f6c6f7373446973706f736974696f6e2066726f6d2e205461726765742070726f66696c657320616e640a3b206c61777061636b73207075626c6973682074686573652061732061206d617020607b20636f6f7264696e617465203d3e206f7065726174696f6e2d70726f66696c65207d602c20736f207468650a3b20636f6f7264696e61746520697320746865204b45592c206e6f7420612076616c7565206669656c64202845444943542d4f505449432d54454d504c4154452d4f574e45522d3030312c0a3b2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65203d207b0a20206f7074696354656d706c6174653a206f707469632d74656d706c6174652c0a20206566666563745072656469636174653a20747374722c20202020202020202020203b20636f6f7264696e617465206f6620746865206f7065726174696f6e2d6d6f6465207072656469636174650a7d0a0a6f707469632d74656d706c617465203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a2020737570706f7274506f6c6963793a20747374722c202020202020202020202020203b2063616e6f6e6963616c20737570706f72742d706f6c69637920636f6f7264696e6174650a20206c6f7373446973706f736974696f6e3a20747374722c20202020202020202020203b2063616e6f6e6963616c206c6f73732d646973706f736974696f6e20636f6f7264696e6174650a20203f20626173697354656d706c6174653a20747374722c20202020202020202020203b206f7074696f6e616c206469676573742d6c6f636b65642062617369732074656d706c61746520636f6f72640a20203b2074686520617065727475726520726571756972656d656e7420746869732074656d706c61746520737570706c6965732e205265717569726564207768656e207468652074656d706c6174650a20203b2069732074686520736f75726365206f66206120436f7265206f707469632773206170657274757265526571756972656d656e742028692e652e2074686520696e74656e7420686173206e6f0a20203b20736f757263652060666f6f747072696e74203c3d202e2e2e60292c2073696e6365206170657274757265526571756972656d656e74206973206d616e6461746f727920696e20436f72650a20203b202845444943542d4f505449432d41504552545552452d5245462d303031292e0a20203f206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a7d0a0a3b206170657274757265526571756972656d656e742069732061207479706564207265666572656e63652c206e65766572206120667265652d666f726d20737472696e672e2041207265766965770a3b2072656e646572696e67206d61792073686f772069747320636f6f7264696e617465202845444943542d4f505449432d41504552545552452d5245462d303031292e0a61706572747572652d726571756972656d656e74203d20666f6f747072696e742d6365696c696e672d726566202f2061627374726163742d666f6f747072696e742d6f626c69676174696f6e2d7265660a666f6f747072696e742d6365696c696e672d726566203d207b206b696e643a2022666f6f747072696e744365696c696e67222c207265663a2074737472207d0a61627374726163742d666f6f747072696e742d6f626c69676174696f6e2d726566203d207b206b696e643a20226162737472616374466f6f747072696e744f626c69676174696f6e222c207265663a2074737472207d0a0a3b202d2d2d2065646963742d636f72652e6364646c202d2d2d0a3b2065646963742d636f72652e6364646c0a3b204e6f726d617469766520736368656d6120666f722074686520456469637420436f72652076312073656d616e746963206d6f64656c2e0a3b0a3b2053636f706520626f756e646172793a20746869732066696c6520646566696e657320436f7265206d65616e696e6720616e6420736368656d61207368617065206f6e6c792e20497420646f65730a3b206e6f7420646566696e6520612063616e6f6e6963616c20656e636f6465722c20436f7265206d6f64756c652068617368206669656c64732c20686173682066697874757265732c207461726765740a3b206c6f776572696e672c2061646d697373696f6e2062756e646c65732c206f72207461726765742d6f776e65642049522e0a0a636f72652d6d6f64756c65203d207b0a202061706956657273696f6e3a202265646963742e636f72652f7631222c0a2020636f6f7264696e6174653a20747374722c0a2020696d706f7274733a205b2a20636f72652d696d706f72745d2c0a202074797065733a207b202a2074737472203d3e20636f72652d74797065207d2c0a2020696e74656e74733a207b202b2074737472203d3e20636f72652d696e74656e74207d2c0a20207265717569726564436f72654361706162696c69746965733a205b2a20747374725d2c0a7d0a0a636f72652d696d706f7274203d207b0a20206b696e643a20226c61777061636b22202f202274617267657422202f2022636f7265222c0a20207265663a207265736f757263652d7265662c0a20203f20616c6961733a20747374722c0a7d0a0a3b202d2d2d207479706573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d74797065203d20636f72652d7363616c61722d74797065202f20636f72652d7265636f72642d74797065202f20636f72652d76617269616e742d74797065202f0a202020202020202020202020636f72652d6f7074696f6e2d74797065202f20636f72652d6c6973742d74797065202f20636f72652d6d61702d74797065202f0a202020202020202020202020636f72652d6361706162696c6974792d7265662d747970650a0a636f72652d7363616c61722d74797065203d20636f72652d626f6f6c2d74797065202f20636f72652d696e742d74797065202f20636f72652d737472696e672d74797065202f0a20202020202020202020202020202020202020636f72652d62797465732d74797065202f20636f72652d756e69742d747970650a0a636f72652d626f6f6c2d74797065203d207b206b696e643a2022426f6f6c22207d0a636f72652d756e69742d74797065203d207b206b696e643a2022556e697422207d0a636f72652d696e742d74797065203d207b0a20206b696e643a202249363422202f202255363422202f202249333222202f202255333222202f202249313622202f202255313622202f2022493822202f20225538222c0a7d0a636f72652d737472696e672d74797065203d207b0a20206b696e643a2022537472696e67222c0a20206d61783a2075696e742c0a202063616e6f6e6963616c3a2022756e69636f64652d7363616c61722d6e666322202f20227261772d75746638222c0a7d0a636f72652d62797465732d74797065203d207b0a20206b696e643a20224279746573222c0a20206d61783a2075696e742c0a7d0a636f72652d7265636f72642d74797065203d207b0a20206b696e643a20225265636f7264222c0a20206669656c64733a207b202a2074737472203d3e20636f72652d747970652d726566207d2c0a7d0a636f72652d76617269616e742d74797065203d207b0a20206b696e643a202256617269616e74222c0a202063617365733a207b202b2074737472203d3e2076617269616e742d636173652d626f6479207d2c0a7d0a76617269616e742d636173652d626f6479203d207b0a20203f207061796c6f61643a20636f72652d747970652d7265662c0a7d0a636f72652d6f7074696f6e2d74797065203d207b0a20206b696e643a20224f7074696f6e222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a636f72652d6c6973742d74797065203d207b0a20206b696e643a20224c697374222c0a20206974656d3a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6d61702d74797065203d207b0a20206b696e643a20224d6170222c0a20206b65793a20636f72652d747970652d7265662c0a202076616c75653a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6361706162696c6974792d7265662d74797065203d207b0a20206b696e643a20224361706162696c697479526566222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a0a3b20636f72652d747970652d72656620697320646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d612e0a0a3b202d2d2d207265666572656e63657320616e642076616c756573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a3b204c6f63616c206964656e7469747920697320616c7068612d737461626c652e20606964602069732074686520636f6d70696c65722d6f776e6564206c6f63616c20636f6f7264696e6174653b0a3b2060616c7068614e616d656020697320746865206e6f726d616c697a65642068756d616e2f6465627567206e616d652e20536f757263652062696e646572207370656c6c696e67206973206e6f740a3b206964656e746974792e0a6c6f63616c2d726566203d207b0a202069643a20747374722c0a2020616c7068614e616d653a20747374722c0a2020747970653a20636f72652d747970652d7265662c0a7d0a0a636f72652d76616c7565203d20636f72652d6e756c6c2d76616c7565202f20636f72652d626f6f6c2d76616c7565202f20636f72652d696e742d76616c7565202f0a20202020202020202020202020636f72652d737472696e672d76616c7565202f20636f72652d62797465732d76616c7565202f20636f72652d7265636f72642d76616c7565202f0a20202020202020202020202020636f72652d76617269616e742d76616c7565202f20636f72652d6c6973742d76616c7565202f20636f72652d6d61702d76616c7565202f0a20202020202020202020202020636f72652d6361706162696c6974792d76616c75650a0a636f72652d6e756c6c2d76616c7565203d207b206b696e643a20226e756c6c22207d0a636f72652d626f6f6c2d76616c7565203d207b206b696e643a2022626f6f6c222c2076616c75653a20626f6f6c207d0a636f72652d696e742d76616c7565203d207b206b696e643a2022696e74222c2077696474683a20747374722c2076616c75653a20696e74207d0a636f72652d737472696e672d76616c7565203d207b206b696e643a2022737472696e67222c2076616c75653a2074737472207d0a636f72652d62797465732d76616c7565203d207b206b696e643a20226279746573222c2076616c75653a2062737472207d0a636f72652d7265636f72642d76616c7565203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d76616c7565207d207d0a636f72652d76617269616e742d76616c7565203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d76616c75652c0a7d0a636f72652d6c6973742d76616c7565203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d76616c75655d207d0a636f72652d6d61702d76616c7565203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d76616c75652c2076616c75653a20636f72652d76616c75655d5d207d0a636f72652d6361706162696c6974792d76616c7565203d207b0a20206b696e643a20226361706162696c697479222c0a2020726563656970743a207368613235362d6469676573742c0a7d0a0a3b2045646963742d617574686f72656420707572652068656c70657273207573652061207075726520436f72652066756e6374696f6e20626f64792e2054686520626f64792063616e2062696e640a3b20707572652065787072657373696f6e7320616e642072657475726e20616e2065787072657373696f6e2c206275742069742063616e6e6f7420636f6e7461696e20436f7265206566666563742c0a3b2067756172642c206272616e63682c206c6f6f702c206d617463682d6e6f64652c206f722070726f6f662d6f626c69676174696f6e206e6f6465732e0a636f72652d666e2d626f6479203d207b0a2020706172616d733a205b2a206c6f63616c2d7265665d2c0a2020626f64793a20636f72652d707572652d626c6f636b2c0a7d0a0a636f72652d707572652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a202062696e64696e67733a205b2a20707572652d6c65742d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a707572652d6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a0a3b202d2d2d2065787072657373696f6e7320616e642070726564696361746573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d65787072203d206c6f63616c2d65787072202f20636f6e73742d65787072202f207265636f72642d65787072202f206669656c642d65787072202f0a20202020202020202020202076617269616e742d65787072202f206d617463682d65787072202f2063616c6c2d65787072202f206c6973742d65787072202f206d61702d65787072202f0a20202020202020202020202069662d657870720a0a6c6f63616c2d65787072203d207b206b696e643a20226c6f63616c222c207265663a206c6f63616c2d726566207d0a636f6e73742d65787072203d207b206b696e643a2022636f6e7374222c2076616c75653a20636f72652d76616c7565207d0a7265636f72642d65787072203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d65787072207d207d0a6669656c642d65787072203d207b206b696e643a20226669656c64222c20626173653a20636f72652d657870722c206669656c643a2074737472207d0a76617269616e742d65787072203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d657870722c0a7d0a6d617463682d65787072203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d61726d5d2c0a7d0a6d617463682d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d657870722c0a7d0a63616c6c2d65787072203d207b0a20206b696e643a202263616c6c222c0a202063616c6c65653a20747374722c0a202074797065417267733a205b2a20636f72652d747970652d7265665d2c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6c6973742d65787072203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d657870725d207d0a6d61702d65787072203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d657870722c2076616c75653a20636f72652d657870725d5d207d0a69662d65787072203d207b0a20206b696e643a20226966222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d657870722c0a2020656c73653a20636f72652d657870722c0a7d0a0a636f72652d707265646963617465203d20747275652d707265646963617465202f2066616c73652d707265646963617465202f206e6f742d707265646963617465202f0a2020202020202020202020202020202020616c6c2d707265646963617465202f20616e792d707265646963617465202f20636f6d706172652d707265646963617465202f0a202020202020202020202020202020202063616c6c2d707265646963617465202f206f62737472756374696f6e2d7072656469636174650a0a747275652d707265646963617465203d207b206b696e643a20227472756522207d0a66616c73652d707265646963617465203d207b206b696e643a202266616c736522207d0a6e6f742d707265646963617465203d207b206b696e643a20226e6f74222c2076616c75653a20636f72652d707265646963617465207d0a616c6c2d707265646963617465203d207b206b696e643a2022616c6c222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a616e792d707265646963617465203d207b206b696e643a2022616e79222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a636f6d706172652d707265646963617465203d207b0a20206b696e643a2022636f6d70617265222c0a20206f703a20223d3d22202f2022213d22202f20223c22202f20223c3d22202f20223e22202f20223e3d222c0a20206c6566743a20636f72652d657870722c0a202072696768743a20636f72652d657870722c0a7d0a63616c6c2d707265646963617465203d207b0a20206b696e643a202263616c6c222c0a20207072656469636174653a20747374722c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6f62737472756374696f6e2d707265646963617465203d207b0a20206b696e643a20226f62737472756374696f6e222c0a2020636f6f7264696e6174653a206661696c7572652d6964656e742c0a20207061796c6f61643a20636f72652d657870722c0a7d0a0a696e7075742d636f6e73747261696e74203d207b0a2020636f6f7264696e6174653a20747374722c0a2020736f757263653a2022776865726522202f2022636f6d70696c6572222c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a3b202d2d2d20696e74656e74732c20626c6f636b732c20616e64206e6f646573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d696e74656e74203d207b0a2020696e7075743a20636f72652d747970652d7265662c0a20206f75747075743a20636f72652d747970652d7265662c0a202072657175697265644f7065726174696f6e50726f66696c653a20747374722c0a20203f2062617369733a20636f72652d657870722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020626f64793a20636f72652d626c6f636b2c0a20203f206f707469633a20636f72652d6f707469632c0a7d0a0a636f72652d627564676574203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a636f72652d6f70746963203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a20206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a2020737570706f7274506f6c6963793a20747374722c0a20206c6f7373446973706f736974696f6e3a20747374722c0a7d0a0a636f72652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a20206e6f6465733a205b2a20636f72652d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a636f72652d6e6f6465203d206c65742d6e6f6465202f20726571756972652d6e6f6465202f206566666563742d6e6f6465202f2067756172642d6e6f6465202f206272616e63682d6e6f6465202f0a202020202020202020202020666f722d6e6f6465202f206d617463682d6e6f6465202f2070726f6f662d6f626c69676174696f6e2d6e6f64650a0a6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a726571756972652d6e6f6465203d207b0a20206b696e643a202272657175697265222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a726571756972652d6661696c7572652d61726d203d207465726d696e616c2d726571756972652d6661696c757265202f0a20202020202020202020202020202020202020202020636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c7572650a7465726d696e616c2d726571756972652d6661696c757265203d207b0a20206b696e643a20227465726d696e616c222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c757265203d207b0a20206b696e643a2022636f6e74696e75654f627374727563746564222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a6f62737472756374696f6e2d726561736f6e203d207b0a2020726561736f6e4b696e643a20747374722c0a20207061796c6f61643a207b202a2074737472203d3e20636f72652d65787072207d2c0a7d0a6566666563742d6e6f6465203d207b0a20206b696e643a2022656666656374222c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4d61703a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a6f62737472756374696f6e2d61726d203d207b0a202062696e6465723a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a67756172642d6e6f6465203d207b0a20206b696e643a20226775617264222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f62737472756374696f6e3a20636f72652d657870722c0a7d0a6272616e63682d6e6f6465203d207b0a20206b696e643a20226272616e6368222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d626c6f636b2c0a2020656c73653a20636f72652d626c6f636b2c0a7d0a666f722d6e6f6465203d207b0a20206b696e643a2022666f72222c0a202062696e6465723a206c6f63616c2d7265662c0a2020697465723a20636f72652d657870722c0a2020626f756e643a20636f72652d626f756e642c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a6d617463682d6e6f6465203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d626c6f636b2d61726d5d2c0a7d0a6d617463682d626c6f636b2d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a70726f6f662d6f626c69676174696f6e2d6e6f6465203d207b0a20206b696e643a202270726f6f66222c0a2020636f6f7264696e6174653a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a636f72652d626f756e64203d206c69746572616c2d626f756e64202f20636f6f7264696e6174652d626f756e640a6c69746572616c2d626f756e64203d207b206b696e643a20226c69746572616c222c2076616c75653a2075696e74207d0a636f6f7264696e6174652d626f756e64203d207b206b696e643a2022636f6f7264696e617465222c207265663a2074737472207d0a0a3b20536861726564207265736f757263652d7265662c207368613235362d6469676573742c206661696c7572652d6964656e742c2061706572747572652d726571756972656d656e742c20616e640a3b20636f72652d747970652d7265662061726520646566696e6564206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c2e0a0a3b202d2d2d2065646963742d6c61777061636b2e6364646c202d2d2d0a3b2065646963742d6c61777061636b2e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374206c61777061636b206d616e696665737420616e64206578706f727420737572666163652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e204a534f4e20696e207468652070726f73652073706563730a3b2069732061207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a6c61777061636b2d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e6c61777061636b2f7631222c0a202069643a20747374722c0a202076657273696f6e3a20747374722c0a20206163636570746564436f72654162693a205b2b20747374725d2c0a2020646570656e64656e636965733a205b2a206c61777061636b2d6465705d2c202020202020202020203b20616379636c69632c206469676573742d6c6f636b6564202845444943542d4c41575041434b2d4441472d303031290a20206578706f7274733a207265736f757263652d7265662c0a20203f2074617267657441646170746572733a205b2b207461726765742d616461707465725d2c2020203b207265717569726564206f6e6c7920696620616e792072756e74696d6520656666656374206578697374730a20203f2068656c706572436f6d706f6e656e743a2065786563757461626c652d636f6d706f6e656e742c203b2065786563757461626c652068656c70657273206361727279207468656972206f776e2073616e64626f782b6675656c0a202076657269666965723a2076657269666965722c202020202020202020202020202020202020202020203b20636c61737369666965643a206465636c61726174697665206f722065786563757461626c650a2020636f6d7061746962696c6974793a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b2041207665726966696572206973206569746865722061206465636c617261746976652072756c6573657420286e6f2072756e74696d6529206f7220616e2065786563757461626c650a3b20636f6d706f6e656e742e20416e2065786563757461626c65207665726966696572204d55535420636172727920697473206f776e2073616e64626f7820616e64206675656c206d6f64656c2c0a3b20736f2074686520736368656d6120656e666f726365732074686174206e6f2065786563757461626c6520636f6d706f6e656e74206973206c65667420756e626f756e6465640a3b202845444943542d4142492d56455249464945522d424f554e442d303031292e0a7665726966696572203d206465636c617261746976652d7665726966696572202f2065786563757461626c652d76657269666965720a6465636c617261746976652d7665726966696572203d207b20636c6173733a20226465636c61726174697665222c2072756c657365743a207265736f757263652d726566207d0a65786563757461626c652d7665726966696572203d207b0a2020636c6173733a202265786563757461626c65222c0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a3b20416e792065786563757461626c6520636f6d706f6e656e7420697320626f756e64656420627920697473206f776e2073616e64626f78202b206675656c206d6f64656c2e0a65786563757461626c652d636f6d706f6e656e74203d207b0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a6c61777061636b2d646570203d207b2069643a20747374722c2076657273696f6e3a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20416461707465722073656c656374696f6e206b65797320534f4c454c59206f666620746865206469676573742d6c6f636b65642060616363657074656454617267657450726f66696c65600a3b20286974732060696460206973207468652070726f66696c652069643b206974732060646967657374602070696e73207468652065786163742070726f66696c652f76657273696f6e292e2054686572650a3b20617265206e6f20696e646570656e64656e7420646973706c617920737472696e6773207468617420636f756c64206469736167726565207769746820746865206c6f636b2c20736f20610a3b207265736f6c7665722063616e6e6f742062696e6420616e206164617074657220746f206f6e6520746172676574207768696c6520746865206c6f636b2070726f76657320616e6f746865720a3b202845444943542d4c41575041434b2d414441505445522d54415247455449522d303031292e0a7461726765742d61646170746572203d207b0a2020616363657074656454617267657450726f66696c653a207265736f757263652d7265662c202020203b206469676573742d6c6f636b65642c20617574686f72697461746976652073656c6563746f720a2020616363657074656454617267657449723a207265736f757263652d7265662c2020202020202020203b206469676573742d6c6f636b65640a2020616461707465723a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d206578706f72742073757266616365202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a6c61777061636b2d6578706f727473203d207b0a202074797065733a205b2a206578706f727465642d747970655d2c0a2020636f6e7374616e74733a205b2a206578706f727465642d636f6e7374616e745d2c0a20207075726546756e6374696f6e733a205b2a20707572652d66756e6374696f6e5d2c0a2020656666656374733a205b2a2073656d616e7469632d6566666563745d2c0a20206f62737472756374696f6e733a205b2a206f62737472756374696f6e2d6465665d2c0a20203b206b65796564206279206f7065726174696f6e2d70726f66696c6520636f6f7264696e61746520e2869220756e697175656e65737320656e666f726365640a20203b202845444943542d4142492d4f5050524f46494c452d554e495155452d303031290a20203b206f7065726174696f6e2d70726f66696c65207265636f7264732074686973206c61777061636b206578706f72747320286f707469632074656d706c6174657320746861740a20203b2060696d706c656d656e7473602f6070726f66696c656020636c6175736573207265736f6c766520616761696e7374292e206f7065726174696f6e2d70726f66696c652069730a20203b20646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c20203b206b6579656420627920636f6f7264696e6174650a7d0a0a6578706f727465642d7479706520202020203d207b20636f6f7264696e6174653a20747374722c20646566696e6974696f6e3a20636f72652d747970652d726566207d0a6578706f727465642d636f6e7374616e74203d207b20636f6f7264696e6174653a20747374722c20747970653a20636f72652d747970652d7265662c2076616c75653a20616e79207d0a0a3b204120707572652068656c7065722069732061206469736372696d696e6174656420756e696f6e2062792060736f75726365602c20736f2074686520736368656d6120697473656c660a3b2067756172616e7465657320616e20696d706c656d656e746174696f6e20657869737473202845444943542d4c41575041434b2d505552452d494d504c2d303031293a0a3b2020202d20226564696374223a20617574686f72656420696e2045646963742f436f72653b2074686520436f726520626f6479206973206361727269656420696e6c696e6520286861736865640a3b20202020207769746820746865206578706f72742073757266616365292e2054686520736368656d61207265717569726573207468652060626f647960206669656c642e0a3b2020202d2022636f6d706f6e656e74223a20696d706c656d656e746564206f7574736964652045646963743b2063617272696573206e6f20696e6c696e6520626f647920616e6420696e73746561640a3b20202020206361727269657320697473206f776e206469676573742d6c6f636b65642060696d706c656d656e746174696f6e60202873616e64626f78202b206675656c292e20497420646f65730a3b20202020206e6f7420646570656e64206f6e20746865206f7074696f6e616c206d616e69666573742d6c6576656c2068656c706572436f6d706f6e656e742e0a707572652d66756e6374696f6e203d2065646963742d707572652d66756e6374696f6e202f20636f6d706f6e656e742d707572652d66756e6374696f6e0a0a707572652d66756e6374696f6e2d636f6d6d6f6e203d20280a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020706172616d6574657254797065733a205b2a20636f72652d747970652d7265665d2c2020202020203b20616c6c20626f756e6465640a202072657475726e547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020636f737454656d706c6174653a20747374722c0a202064657465726d696e69736d436c6173733a2022746f74616c22202f2022746f74616c2d776974682d74797065642d646961676e6f73746963222c0a290a0a65646963742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a20226564696374222c0a2020626f64793a20636f72652d666e2d626f64792c2020202020202020202020202020202020202020203b20696e6c696e652c20686173682d7369676e69666963616e740a7d0a0a636f6d706f6e656e742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a2022636f6d706f6e656e74222c0a20203b20746865206469676573742d6c6f636b656420636f6d706f6e656e7420696d706c656d656e74696e6720746869732068656c7065722e2052657175697265642061742074686520736368656d610a20203b206c6576656c20736f206120636f6d706f6e656e742068656c7065722063616e206e657665722076616c696461746520776974686f7574206120686173682d626f756e642c0a20203b2073616e64626f782b6675656c2d64657363726962656420696d706c656d656e746174696f6e202845444943542d4c41575041434b2d505552452d494d504c2d303031292e0a2020696d706c656d656e746174696f6e3a2065786563757461626c652d636f6d706f6e656e742c0a7d0a0a3b20636f72652d666e2d626f647920697320646566696e65642062792065646963742d636f72652e6364646c20616e6420617373656d626c656420776974682074686973206c61777061636b0a3b20736368656d612e2049742069732061207075726520436f72652066756e6374696f6e20626f64792c206e6f7420616e206566666563742d63617061626c6520636f72652d626c6f636b2e0a0a73656d616e7469632d656666656374203d207b0a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020696e707574547970653a20636f72652d747970652d7265662c2020202020202020202020202020203b20626f756e6465640a20206f7574707574547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020657865637574696f6e436c6173733a202270726f6f664f6e6c7922202f202272756e74696d65222c2020203b206f7274686f676f6e616c20746f207772697465436c6173730a20206566666563744b696e6448696e743a206566666563742d6b696e642c0a2020666f6f747072696e744f626c69676174696f6e3a20747374722c0a2020636f73744f626c69676174696f6e3a20747374722c0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c20203b206b6579656420627920636f6f7264696e6174653b20756e697175650a20206775617264537570706f72743a20626f6f6c2c0a7d0a0a6f62737472756374696f6e2d646566203d207b0a2020636f6f7264696e6174653a20747374722c0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164536368656d613a20636f72652d747970652d7265662c20202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d6c61777061636b2d616461707465722e6364646c202d2d2d0a3b2065646963742d6c61777061636b2d616461707465722e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f72206f6e6520646972656374206465636c61726174697665206c61777061636b2074617267657420616461707465722e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b2054686520656e636c6f73696e67206c61777061636b206d616e69666573742073656c6563747320746865206578616374207461726765742070726f66696c652c207461726765742049522c0a3b20616e642061646170746572207265736f75726365206469676573742e2054686f7365206964656e74697469657320617265206e6f7420726570656174656420686572652e0a0a6c61777061636b2d61646170746572203d207b0a202061706956657273696f6e3a202265646963742e6c61777061636b2d616461707465722f7631222c0a2020636c6173733a20226465636c61726174697665222c0a20206f7065726174696f6e50726f66696c65733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6f7065726174696f6e2d70726f66696c650a20207d2c0a2020656666656374496d706c656d656e746174696f6e733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6566666563740a20207d2c0a2020627564676574733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6275646765740a20207d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206c61777061636b206f7065726174696f6e2d70726f66696c6520636f6f7264696e617465732e0a6c61777061636b2d616461707465722d6f7065726174696f6e2d70726f66696c65203d207b0a2020636f72653a20747374722c0a202073656d616e746963456666656374733a205b2b20747374725d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206c61777061636b2073656d616e7469632d65666665637420636f6f7264696e617465732e20466f6f747072696e742c20636f73742c20616e640a3b206661696c757265206669656c6473206d7573742065786163746c792064697363686172676520746865206d61746368696e67206578706f72746564206566666563742e0a6c61777061636b2d616461707465722d656666656374203d207b0a2020746172676574496e7472696e7369633a20747374722c0a2020746172676574436f6e66696775726174696f6e3a207265736f757263652d7265662c0a20207772697465436c6173733a206c61777061636b2d616461707465722d77726974652d636c6173732c0a2020666f6f747072696e744f626c69676174696f6e3a20747374722c0a2020636f73744f626c69676174696f6e3a20747374722c0a20206661696c7572654d617070696e67733a207b202a206661696c7572652d6964656e74203d3e2074737472207d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206578706f7274656420636f73742d6f626c69676174696f6e20636f6f7264696e617465732e0a6c61777061636b2d616461707465722d627564676574203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a6c61777061636b2d616461707465722d77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f0a20202020202020202020202020202020202020202020202020202020202022617070656e6422202f20227265706c61636522202f202264656c65746522202f2022637573746f6d220a0a3b206661696c7572652d6964656e7420697320646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c2e0a0a3b202d2d2d2065646963742d7461726765742d70726f66696c652e6364646c202d2d2d0a3b2065646963742d7461726765742d70726f66696c652e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374207461726765742070726f66696c65206d616e69666573742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76310a3b202873656520535045435f636f6e74696e75756d2d636f6e74726163742d62756e646c652d76312e6d64292e204a534f4e20696e207468652070726f736520737065637320697320610a3b207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d613b2074686973204344444c206973207468652073696e676c6520736f757263650a3b206f66207472757468202845444943542d4142492d4e4f4455502d303031292e0a0a7461726765742d70726f66696c652d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652f7631222c0a202069643a20747374722c202020202020202020202020202020202020202020202020203b20652e672e20226563686f2e64706f220a202076657273696f6e3a20747374722c20202020202020202020202020202020202020203b20652e672e202231220a20206163636570746564436f72654162693a205b2b20747374725d2c20202020202020203b20652e672e205b2265646963742e636f72652f7631225d0a0a2020696e7472696e736963733a207265736f757263652d7265662c0a2020696e7472696e7369634e616d6573706163653a20747374722c0a20203b207075626c697368657320746869732070726f66696c652773206f7065726174696f6e2d70726f66696c65207265636f72647320286f707469632074656d706c6174657320746861740a20203b206070726f66696c65602f60696d706c656d656e74736020636c6175736573207265736f6c766520616761696e7374292e205265666572656e63657320616e0a20203b206f7065726174696f6e2d70726f66696c65732d646f63756d656e74202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207265736f757263652d7265662c0a2020666f6f747072696e74416c67656272613a207265736f757263652d7265662c0a2020636f7374416c67656272613a207265736f757263652d7265662c0a202074617267657449723a207265736f757263652d7265662c0a20206f62737472756374696f6e5461786f6e6f6d793a207265736f757263652d7265662c0a202076657269666965723a207265736f757263652d7265662c0a20206c6f77657265723a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a0a20203b206669656c647320746865206c616e67756167652073706563207265717569726573206f662065766572792070726f66696c650a202062756e646c6550726f66696c653a207265736f757263652d7265662c0a202067656e657261746564417274696661637450726f66696c65733a205b2a207265736f757263652d7265665d2c0a202063616e6f6e6963616c456e636f64696e6752756c65733a207265736f757263652d7265662c0a20203b20412070726f66696c65207468617420616363657074732074686520646972656374206465636c61726174697665206c61777061636b2d6164617074657220414249206e616d65732069740a20203b2065786163746c79206f6e63652e2050726f66696c6573207468617420646f206e6f7420636f6e73756d65206c61777061636b206164617074657273206c6561766520746869730a20203b206f7074696f6e616c20736c6f7420616273656e74206f7220656d7074792e0a20203f2061636365707465644c61777061636b416461707465724162693a205b5d202f205b2265646963742e6c61777061636b2d616461707465722f7631225d2c0a2020646961676e6f737469634162693a207265736f757263652d7265662c0a0a20203b206170706c69636174696f6e20646f637472696e650a20206170706c69636174696f6e4d6f64656c3a202261746f6d6963222c0a202072656164436f6e73697374656e63793a20226170706c69636174696f6e2d736e617073686f7422202f20747374722c0a202067756172644576616c756174696f6e3a2022707265636f6d6d69742d61746f6d696322202f20747374722c0a20206f62737472756374696f6e526f6c6c6261636b3a20226e6f2d76697369626c652d6566666563747322202f20747374722c0a20206d756c74695461726765743a20626f6f6c2c0a20203b207768657468657220746865207461726765742063616e206576616c7561746520707265636f6d6d697420706f7374636f6e646974696f6e20286067756172616e746565602920636865636b730a20203b20696e73696465207468652061746f6d6963206170706c69636174696f6e20756e6974202845444943542d5441524745542d504f5354434f4e442d303031290a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a0a202064657465726d696e6973746963457865637574696f6e3a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d6120627920746865206275696c640a3b202845444943542d4142492d4e4f4455502d303031292e205468657920617265206e6f74207265646566696e656420686572652e0a0a3b202d2d2d20696e7472696e736963207369676e6174757265202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e696665737427732060696e7472696e7369637360207265736f757263652d726566206973207468650a3b20696e7472696e7369632d7369676e617475726520636f7270757320646f63756d656e742062656c6f772e20497473206c61796f757420697320666978656420736f2074776f0a3b20696e646570656e64656e742070726f66696c65732076616c69646174652f686173682074686520636f72707573206964656e746963616c6c790a3b202845444943542d4142492d494e5452494e534943532d444f432d303031292e0a0a3b20696e7472696e736963732069732061204d4150206b6579656420627920636f6f7264696e6174652c20736f2074686520736368656d6120697473656c6620656e666f726365730a3b20636f6f7264696e61746520756e697175656e6573732e20412070726f766964657220726563656976657320746865207265736f6c76656420636f7270757320617320610a3b206469676573742d626f756e642073656d616e74696320696e70757420616e64207265736f6c76657320636f6f7264696e617465732077697468696e20746861742061727469666163742e0a3b2045616368206d6170206b6579204d55535420657175616c20697473207265636f726427732060636f6f7264696e61746560206669656c640a3b202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e696e7472696e736963732f7631222c0a2020696e7472696e736963733a207b202a2074737472203d3e20696e7472696e736963207d2c0a7d0a0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e6966657374277320606f7065726174696f6e50726f66696c657360207265736f757263652d7265662e0a3b206f7065726174696f6e2d70726f66696c65202f206f707469632d74656d706c6174652061726520646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c2e204b657965642062790a3b20636f6f7264696e61746520736f207265736f6c7574696f6e2063616e2774207069636b206265747765656e2074776f2073616d652d636f6f7264696e6174652070726f66696c65730a3b202845444943542d4142492d4f5050524f46494c452d534c4f542d3030312c2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e6f7065726174696f6e2d70726f66696c65732f7631222c0a202070726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c0a7d0a0a3b2041207479706564207072652d6c6f776572696e67207175657374696f6e20746861742063616e2062652070726f706f73656420627920576174736f6e206f7220616e206167656e7420616e640a3b20636865636b65642062792074686520636f6d70696c65722e2049742069732063616e6f6e6963616c2d43424f5220656e636f64656420756e6465720a3b206065646963742e6c6f776572696e672d726571756972656d656e74732f7631603b2074686520636f6d70696c657220636865636b7320746869732061727469666163742c206e6f74207468650a3b2070726f736520746861742070726f64756365642069742e0a6c6f776572696e672d726571756972656d656e7473203d207b0a202061706956657273696f6e3a202265646963742e6c6f776572696e672d726571756972656d656e74732f7631222c0a20206f7065726174696f6e50726f66696c653a20747374722c0a202073656d616e746963456666656374733a205b2a2073656d616e7469632d6566666563742d726571756972656d656e745d2c0a202072657175697265645772697465436c61737365733a205b2a2077726974652d636c6173735d2c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a202061746f6d69636974793a2061746f6d69636974792d726571756972656d656e742c0a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a20206f70746963436f6e74726163743a20747374722c0a7d0a0a73656d616e7469632d6566666563742d726571756972656d656e74203d207b0a2020636f6f7264696e6174653a20747374722c0a20207772697465436c6173733a2077726974652d636c6173732c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a7d0a0a77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a2020202020202020202020202020227265706c61636522202f202264656c65746522202f20747374720a67756172642d6b696e64203d2022707265636f6d6d69742d61746f6d696322202f20747374720a61746f6d69636974792d726571756972656d656e74203d202261746f6d696322202f20747374720a0a3b20412067656e75696e6520756e696f6e3a207075726520636f6e7374727563746f7273206361727279206e6f20656666656374206b696e64206f72206661696c757265733b206566666563740a3b20696e7472696e73696373206d757374202845444943542d5441524745542d494e5452494e5349432d434c4153532d303031292e2054686520736368656d6120656e666f7263657320746869732c0a3b206e6f74206120636f6d6d656e742e0a0a3b2054686520696e7472696e736963277320636f6f7264696e6174652069732074686520696e7472696e73696373206d6170204b45592c206e6f7420612076616c7565206669656c642c20736f207468650a3b206b657920616e6420636f6f7264696e6174652063616e206e65766572206469736167726565202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963203d20707572652d696e7472696e736963202f206566666563742d696e7472696e7369630a0a707572652d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a202270757265222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206775617264537570706f72743a2066616c73652c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20226e6f6e65222c0a7d0a0a6566666563742d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a2022656666656374222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206566666563744b696e643a206566666563742d6b696e642c0a20203b206d6170206b65796564206279206661696c75726520636f6f7264696e61746520286661696c7572652d6964656e74293b20746865206661696c75726520636f6f7264696e6174652069730a20203b20746865206b65792c206e6f7420612076616c7565206669656c642c20736f20756e697175656e657373206973207374727563747572616c0a20203b202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c0a20206775617264537570706f72743a20626f6f6c2c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f20227265706c61636522202f0a20202020202020202020202020202264656c65746522202f2022637573746f6d222c0a202063616e5061727469636970617465496e41746f6d696347756172643a20626f6f6c2c0a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d617574686f726974792d66616374732e6364646c202d2d2d0a3b2065646963742d617574686f726974792d66616374732e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f722074686520666972737420636f6d70696c65722d636f6e7465787420617574686f726974792d666163747320646f63756d656e742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20736f20736f757263652e6469676573742075736573207468650a3b20736861726564207368613235362d6469676573742074797065642076616c75652e204a534f4e2069732061207265766965772f696e7075742072656e646572696e673a206974730a3b20607368613235363a3c3634206865783e6020736f75726365206469676573742069732070726f6a656374656420746f205b60736861323536602c203332207261772062797465735d206f6e0a3b2074686520776972652c20616e64206974732066616374206172726179732070726f6a65637420746f2074686520636f6f7264696e6174652d6b65796564206d6170732062656c6f772e0a0a617574686f726974792d6661637473203d207b0a202061706956657273696f6e3a202265646963742e617574686f726974792d66616374732f7631222c0a2020736f757263653a20617574686f726974792d666163742d736f757263652c0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e20617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374207d2c0a20206566666563745772697465436c61737365733a207b202a2074737472203d3e20617574686f726974792d77726974652d636c617373207d2c0a2020627564676574733a207b202a2074737472203d3e20617574686f726974792d6275646765742d66616374207d2c0a7d0a0a617574686f726974792d666163742d736f75726365203d207b0a20206b696e643a20226c61777061636b22202f202274617267657450726f66696c65222c0a2020636f6f7264696e6174653a20747374722c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a3b20546865206d6170206b65792069732074686520736f75726365206f7065726174696f6e2d70726f66696c6520636f6f7264696e6174652e204974206973206e6f7420726570656174656420696e0a3b207468652076616c75652c20736f2061206b657920616e6420656d62656464656420636f6f7264696e6174652063616e6e6f742064697361677265652e20416c6c6f7765642077726974650a3b20636c61737365732061726520612063616e6f6e6963616c206d61702d7365743a2074686520636c6173732069732074686520756e69717565206b657920616e64206e756c6c206973207468650a3b20756e6974206d61726b65722e2043616e6f6e6963616c2043424f52206669786573206b6579206f7264657220776974686f75742061207365636f6e64206f72646572696e672072756c652e0a617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374203d207b0a2020636f72653a20747374722c0a2020616c6c6f7765645772697465436c61737365733a207b202a20617574686f726974792d77726974652d636c617373203d3e206e756c6c207d2c0a7d0a0a3b20546865206566666563745772697465436c6173736573206d6170206b6579206973207468652073656d616e7469632065666665637420636f6f7264696e6174652e2054686520627564676574730a3b206d6170206b65792069732074686520736f757263652062756467657420636f6f7264696e6174652e2043616e6f6e6963616c2043424f52206d61702d6b657920756e697175656e6573730a3b206d616b6573206475706c6963617465206661637420636f6f7264696e61746573207374727563747572616c6c7920756e726570726573656e7461626c652e0a617574686f726974792d6275646765742d66616374203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a3b20417574686f726974794661637473446f63756d656e7420763120696e74656e74696f6e616c6c792061636365707473206f6e6c792074686520777269746520636c6173736573207468650a3b2063757272656e7420636f6d70696c6572206d6f64656c2063616e20636f6e73756d652e2060637573746f6d602069732074686520736f6c6520763120637573746f6d207370656c6c696e673b0a3b20617262697472617279207461726765742d70726f66696c6520657874656e73696f6e20737472696e677320646f206e6f7420656e746572207468697320636f6d70696c657220706174682e0a617574686f726974792d77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a202020202020202020202020202020202020202020202020227265706c61636522202f202264656c65746522202f2022637573746f6d220a0a3b202d2d2d2065646963742d726573756c742d70726f6a656374696f6e2e6364646c202d2d2d0a3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d726573756c742d70726f6a656374696f6e2e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220636f6d70696c65722d6f776e6564206170706c69636174696f6e2d726573756c742070726f6a656374696f6e732e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a0a726573756c742d70726f6a656374696f6e203d207b0a2020736368656d613a202265646963742e726573756c742d70726f6a656374696f6e2f7631222c0a20206f7065726174696f6e436f6f7264696e6174653a20626f756e6465642d70726f6a656374696f6e2d746578742c0a20206f7574707574547970653a20626f756e6465642d70726f6a656374696f6e2d746578742c0a20206d61784f757470757442797465733a2075696e74202e677420302c0a202065787072657373696f6e3a20726573756c742d70726f6a656374696f6e2d657870722c0a7d0a0a726573756c742d70726f6a656374696f6e2d65787072203d20726573756c742d70726f6a656374696f6e2d7265636f7264202f20726573756c742d70726f6a656374696f6e2d736f757263650a0a726573756c742d70726f6a656374696f6e2d7265636f7264203d207b0a20206b696e643a20227265636f7264222c0a20203b2054686520726f6f74207265636f726420636f756e7473206173206f6e65206f66207468652052757374206465636f6465722773203235362065787072657373696f6e206e6f6465732e0a20203b204e657374656420616767726567617465206e6f646520636f756e742072656d61696e7320616e20617574686f7269746174697665206465636f64657220636865636b2e0a20206669656c64733a207b20302a32353520626f756e6465642d70726f6a656374696f6e2d74657874203d3e20726573756c742d70726f6a656374696f6e2d65787072207d2c0a7d0a0a726573756c742d70726f6a656374696f6e2d736f75726365203d207b0a20206b696e643a2022736f75726365222c0a2020736f757263653a20726573756c742d70726f6a656374696f6e2d736f757263652d6b696e642c0a20203b204d617463686573204d41585f524553554c545f50524f4a454354494f4e5f504154485f5345474d454e545320696e2065646963742d73796e7461782e0a2020706174683a205b302a333220626f756e6465642d70726f6a656374696f6e2d746578745d2c0a7d0a0a726573756c742d70726f6a656374696f6e2d736f757263652d6b696e64203d0a20207b206b696e643a20226170706c69636174696f6e496e70757422207d202f0a20207b0a202020206b696e643a20226361706162696c697479526573756c74222c0a202020207374657049643a20626f756e6465642d70726f6a656374696f6e2d746578742c0a20207d0a0a626f756e6465642d70726f6a656374696f6e2d74657874203d2074737472202e73697a652028312e2e31303234290a0a3b202d2d2d2065646963742d7461726765742d69722e6364646c202d2d2d0a3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d7461726765742d69722e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f72207468652045646963742d6f776e65642054617267657420495220617274696661637420656e76656c6f70652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20616e642065646963742d636f72652e6364646c2e2049740a3b2064656c696265726174656c792072657573657320436f72652065787072657373696f6e732c20707265646963617465732c20627564676574732c206c6f63616c207265666572656e6365732c0a3b206f62737472756374696f6e20726561736f6e732c20616e64206f62737472756374696f6e2061726d7320736f2074686520736368656d61206d617463686573207468652076616c75650a3b20656d6974746564206279207468652063616e6f6e6963616c2054617267657420495220656e636f64657220726174686572207468616e20726573746174696e672074686f73652074797065732e0a3b2049742064657363726962657320746865207374727563747572616c207368617065206f662076616c6964206c6f776572696e672d70726f6475636564206172746966616374732e205468650a3b206c6f776572696e6720616e6420656e636f64657220636f6e7472616374732073657061726174656c7920656e666f7263652073656d616e746963206964656e7469666965722072756c65730a3b20616e642063616e6f6e6963616c206f72646572696e672f64656475706c69636174696f6e20666f72207365742d6c696b652076616c7565732e0a0a3b2054617267657420495220656e636f64696e672072656a6563747320616e20656d707479207461726765742d70726f66696c6520636f6f7264696e617465206265666f72652062797465730a3b2065786973742c20736f207468697320726f6f74207469676874656e732074686520736861726564207374727563747572616c207265736f757263652d726566206163636f7264696e676c792e0a7461726765742d69722d7265736f757263652d726566203d207b0a202069643a2074737472202e7265676578702022283f73292e2b222c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a7461726765742d69722d6172746966616374203d207461726765742d69722d636c6f7365642d6172746966616374202f207461726765742d69722d6c65676163792d61727469666163740a0a7461726765742d69722d61727469666163742d636f6d6d6f6e203d20280a20206b696e643a202274617267657449724172746966616374222c0a2020646f6d61696e3a20747374722c0a202074617267657450726f66696c653a207461726765742d69722d7265736f757263652d7265662c0a2020736f75726365436f7265436f6f7264696e6174653a2074737472202e7265676578702022283f73292e2b222c0a290a0a7461726765742d69722d636c6f7365642d6172746966616374203d207b0a20207461726765742d69722d61727469666163742d636f6d6d6f6e2c0a202073656d616e746963436c6f737572653a207461726765742d69722d73656d616e7469632d636c6f737572652c0a2020696e74656e74733a207b202a2074737472203d3e207461726765742d69722d696e74656e74207d2c0a7d0a0a7461726765742d69722d6c65676163792d6172746966616374203d207b0a20207461726765742d69722d61727469666163742d636f6d6d6f6e2c0a2020696e74656e74733a207b202a2074737472203d3e207461726765742d69722d6c65676163792d696e74656e74207d2c0a7d0a0a7461726765742d69722d73656d616e7469632d636c6f73757265203d207b0a2020736f75726365436f72653a207461726765742d69722d7265736f757263652d7265662c0a20206c61777061636b733a205b2a207461726765742d69722d7265736f757263652d7265665d2c0a7d0a0a7461726765742d69722d696e74656e74203d207b0a20207461726765742d69722d696e74656e742d636f6d6d6f6e2c0a20203f2062617369733a20636f72652d657870722c0a7d0a0a7461726765742d69722d6c65676163792d696e74656e74203d207b0a20207461726765742d69722d696e74656e742d636f6d6d6f6e2c0a7d0a0a7461726765742d69722d696e74656e742d636f6d6d6f6e203d20280a20206f7065726174696f6e50726f66696c653a20747374722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020726571756972656d656e74733a205b2a207461726765742d69722d726571756972656d656e745d2c0a202073746570733a205b2a207461726765742d69722d737465705d2c0a2020726573756c743a20636f72652d657870722c0a290a0a7461726765742d69722d726571756972656d656e74203d207b0a202069643a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a0a7461726765742d69722d73746570203d207b0a202069643a20747374722c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020746172676574496e7472696e7369633a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4661696c757265733a205b2a206661696c7572652d6964656e745d2c0a20206f62737472756374696f6e41726d733a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a", - "rawSha256": "ff5405708185e0bcf33dac263000fcb772c79e21145ad69da7cad0692e1a9552" + "bytesHex": "3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d70726f76696465722d636f6e7472616374732e6364646c0a3b2047656e6572617465642066726f6d2045646963742d6f776e65642041424920667261676d656e74732e20444f204e4f5420454449542e0a0a3b202d2d2d2065646963742d636f6d6d6f6e2e6364646c202d2d2d0a3b2065646963742d636f6d6d6f6e2e6364646c0a3b20536861726564204344444c20747970657320666f722074686520456469637420414249732c20646566696e6564204f4e4345206865726520736f20746865792063616e6e6f742064726966740a3b202845444943542d4142492d4e4f4455502d303031292e2054776f2067726f7570733a0a3b2020202d207265736f757263652d7265662c207368613235362d6469676573742c206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c0a3b2020202020636f72652d747970652d7265663a20617373656d626c656420776974682065646963742d7461726765742d70726f66696c652e6364646c20616e640a3b202020202065646963742d6c61777061636b2e6364646c20627920746865206275696c643b2074686f736520736368656d617320646f206e6f74207265646566696e65207468656d2e0a3b2020202d206f7065726174696f6e2d70726f66696c652c206f707469632d74656d706c6174652c2061706572747572652d726571756972656d656e7420616e6420746865697220726566733a0a3b2020202020636f6e73756d65642062792074686520436f72652f6f70746963206c61796572202865646963742d636f72652e6364646c2920616e64207265666572656e636564206279207468650a3b20202020206c616e67756167652f7461726765742d70726f66696c652073706563732e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a0a3b2041206e6f726d617469766520737562636f6d706f6e656e74207265666572656e636564206279206964656e7469747920706c7573206469676573742e204d616e696665737473206e657665720a3b20656d626564207468656972206f776e2073656c662d64696765737420696e20746865697220707265696d616765202845444943542d434f52452d53454c46484153482d303031292e0a7265736f757263652d726566203d207b2069643a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20446967657374732061726520617574686f726974617469766520617320747970656420627974652076616c7565732c206e657665722068657820737472696e67732e20526576696577204a534f4e0a3b2072656e64657273207468697320617320227368613235363a3c3634206c6f77657263617365206865783e22202845444943542d4449474553542d574952452d303031292e0a7368613235362d646967657374203d205b20616c676f726974686d3a2022736861323536222c2062797465733a2062737472202e73697a65203332205d0a0a3b2041206e616d6564206c6f772d6c6576656c206661696c75726520616e206566666563742063616e2072616973652e2054686520736f75726365206f62737472756374696f6e206d61700a3b2062696e64732069742028627920636f6f7264696e6174652920616e6420636f6e73747275637473206120747970656420646f6d61696e206f62737472756374696f6e2066726f6d206974730a3b207061796c6f6164202845444943542d4142492d4641494c5552452d4e414d45442d303031292e0a3b20416e20656666656374277320606566666563744661696c7572657360206c697374204d555354206861766520756e697175652060636f6f7264696e61746560733a2073696e6365207468650a3b206f62737472756374696f6e206d6170206973206b6579656420627920636f6f7264696e6174652c2074776f206661696c757265732073686172696e67206120636f6f7264696e61746520286576656e0a3b207769746820646966666572656e7420617574686f72697479436c6173732f7061796c6f61645479706529206d616b652065786861757374697665206d617070696e6720616e642062696e6465720a3b20747970696e6720616d626967756f757320616e64206172652072656a6563746564202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a3b0a3b2045666665637473206361727279207468656972206661696c757265732061732061206d617020607b206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d600a3b202873656520746865207461726765742f6c61777061636b2065666665637420736368656d6173292e20546865206661696c75726520636f6f7264696e61746520697320746865206d61700a3b204b45592c20736f206974206973206e6f7420726570656174656420696e2074686520626f647920616e642063616e6e6f74206469736167726565207769746820746865206b65792e0a6566666563742d6661696c7572652d626f6479203d207b0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164547970653a20636f72652d747970652d7265662c202020202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b2041206661696c75726520636f6f7264696e617465206d7573742062652061206261726520456469637420606964656e746020286c65747465722f756e64657273636f7265207468656e0a3b206c6574746572732f6469676974732f756e64657273636f7265732920414e44206d757374206e6f742062652061207265736572766564206b6579776f72642028652e672e2060656c7365602c0a3b20606261736973602c20607768657265602c206072657175697265602c2060666f72602c2060696660292e2054686520736f75726365206f62737472756374696f6e2d6d6170204c4853206f6e6c790a3b20616363657074732061206e6f6e2d6b6579776f726420606964656e74602c20736f20612068797068656e2f646f742f6b6579776f726420636f6f7264696e61746520776f756c642062650a3b204142492d76616c69642079657420696d706f737369626c6520746f206d617020657868617573746976656c7920696e20736f757263652e20546865207265676578206361707475726573207468650a3b206c65786963616c2073686170653b206b6579776f7264206578636c7573696f6e20697320616e206164646974696f6e616c2076616c69646174696f6e2072756c650a3b202845444943542d4142492d4641494c5552452d4944454e542d303031292e0a6661696c7572652d6964656e74203d2074737472202e72656765787020225b412d5a612d7a5f5d5b412d5a612d7a302d395f5d2a220a0a6566666563742d6b696e64203d20227265616422202f202263726561746522202f2022656e7375726522202f20227265706c61636522202f202264656c65746522202f0a202020202020202020202020202022617070656e6422202f202272656475636522202f202273656d616e7469632e656d697422202f2022637573746f6d220a0a617574686f726974792d636c617373203d2022646f6d61696e4d61707061626c6522202f20227061727469636970616e744f776e656422202f2022696e746567726974794661756c7422202f0a202020202020202020202020202020202020227265736f757263654661756c7422202f2022696e7465726e616c4661756c74220a0a636f72652d747970652d726566203d20747374722020203b2063616e6f6e6963616c20436f7265207479706520636f6f7264696e6174650a0a3b20416e206f7065726174696f6e2070726f66696c6520737570706c69657320746865206f707469632074656d706c617465206120436f726520696e74656e74207265736f6c766573206974730a3b206f707469634b696e642f626f756e646172794b696e642f737570706f7274506f6c6963792f6c6f7373446973706f736974696f6e2066726f6d2e205461726765742070726f66696c657320616e640a3b206c61777061636b73207075626c6973682074686573652061732061206d617020607b20636f6f7264696e617465203d3e206f7065726174696f6e2d70726f66696c65207d602c20736f207468650a3b20636f6f7264696e61746520697320746865204b45592c206e6f7420612076616c7565206669656c64202845444943542d4f505449432d54454d504c4154452d4f574e45522d3030312c0a3b2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65203d207b0a20206f7074696354656d706c6174653a206f707469632d74656d706c6174652c0a20206566666563745072656469636174653a20747374722c20202020202020202020203b20636f6f7264696e617465206f6620746865206f7065726174696f6e2d6d6f6465207072656469636174650a7d0a0a6f707469632d74656d706c617465203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a2020737570706f7274506f6c6963793a20747374722c202020202020202020202020203b2063616e6f6e6963616c20737570706f72742d706f6c69637920636f6f7264696e6174650a20206c6f7373446973706f736974696f6e3a20747374722c20202020202020202020203b2063616e6f6e6963616c206c6f73732d646973706f736974696f6e20636f6f7264696e6174650a20203f20626173697354656d706c6174653a20747374722c20202020202020202020203b206f7074696f6e616c206469676573742d6c6f636b65642062617369732074656d706c61746520636f6f72640a20203b2074686520617065727475726520726571756972656d656e7420746869732074656d706c61746520737570706c6965732e205265717569726564207768656e207468652074656d706c6174650a20203b2069732074686520736f75726365206f66206120436f7265206f707469632773206170657274757265526571756972656d656e742028692e652e2074686520696e74656e7420686173206e6f0a20203b20736f757263652060666f6f747072696e74203c3d202e2e2e60292c2073696e6365206170657274757265526571756972656d656e74206973206d616e6461746f727920696e20436f72650a20203b202845444943542d4f505449432d41504552545552452d5245462d303031292e0a20203f206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a7d0a0a3b206170657274757265526571756972656d656e742069732061207479706564207265666572656e63652c206e65766572206120667265652d666f726d20737472696e672e2041207265766965770a3b2072656e646572696e67206d61792073686f772069747320636f6f7264696e617465202845444943542d4f505449432d41504552545552452d5245462d303031292e0a61706572747572652d726571756972656d656e74203d20666f6f747072696e742d6365696c696e672d726566202f2061627374726163742d666f6f747072696e742d6f626c69676174696f6e2d7265660a666f6f747072696e742d6365696c696e672d726566203d207b206b696e643a2022666f6f747072696e744365696c696e67222c207265663a2074737472207d0a61627374726163742d666f6f747072696e742d6f626c69676174696f6e2d726566203d207b206b696e643a20226162737472616374466f6f747072696e744f626c69676174696f6e222c207265663a2074737472207d0a0a3b202d2d2d2065646963742d636f72652e6364646c202d2d2d0a3b2065646963742d636f72652e6364646c0a3b204e6f726d617469766520736368656d6120666f722074686520456469637420436f72652076312073656d616e746963206d6f64656c2e0a3b0a3b2053636f706520626f756e646172793a20746869732066696c6520646566696e657320436f7265206d65616e696e6720616e6420736368656d61207368617065206f6e6c792e20497420646f65730a3b206e6f7420646566696e6520612063616e6f6e6963616c20656e636f6465722c20436f7265206d6f64756c652068617368206669656c64732c20686173682066697874757265732c207461726765740a3b206c6f776572696e672c2061646d697373696f6e2062756e646c65732c206f72207461726765742d6f776e65642049522e0a0a636f72652d6d6f64756c65203d207b0a202061706956657273696f6e3a202265646963742e636f72652f7631222c0a2020636f6f7264696e6174653a20747374722c0a2020696d706f7274733a205b2a20636f72652d696d706f72745d2c0a202074797065733a207b202a2074737472203d3e20636f72652d74797065207d2c0a2020696e74656e74733a207b202b2074737472203d3e20636f72652d696e74656e74207d2c0a20207265717569726564436f72654361706162696c69746965733a205b2a20747374725d2c0a7d0a0a636f72652d696d706f7274203d207b0a20206b696e643a20226c61777061636b22202f202274617267657422202f2022636f726522202f20226361706162696c697479222c0a20207265663a207265736f757263652d7265662c0a20203f20616c6961733a20747374722c0a7d0a0a3b202d2d2d207479706573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d74797065203d20636f72652d7363616c61722d74797065202f20636f72652d7265636f72642d74797065202f20636f72652d76617269616e742d74797065202f0a202020202020202020202020636f72652d6f7074696f6e2d74797065202f20636f72652d6c6973742d74797065202f20636f72652d6d61702d74797065202f0a202020202020202020202020636f72652d6361706162696c6974792d7265662d74797065202f20636f72652d65787465726e616c2d616374696f6e2d726571756573742d747970650a0a636f72652d7363616c61722d74797065203d20636f72652d626f6f6c2d74797065202f20636f72652d696e742d74797065202f20636f72652d737472696e672d74797065202f0a20202020202020202020202020202020202020636f72652d62797465732d74797065202f20636f72652d756e69742d747970650a0a636f72652d626f6f6c2d74797065203d207b206b696e643a2022426f6f6c22207d0a636f72652d756e69742d74797065203d207b206b696e643a2022556e697422207d0a636f72652d696e742d74797065203d207b0a20206b696e643a202249363422202f202255363422202f202249333222202f202255333222202f202249313622202f202255313622202f2022493822202f20225538222c0a7d0a636f72652d737472696e672d74797065203d207b0a20206b696e643a2022537472696e67222c0a20206d61783a2075696e742c0a202063616e6f6e6963616c3a2022756e69636f64652d7363616c61722d6e666322202f20227261772d75746638222c0a7d0a636f72652d62797465732d74797065203d207b0a20206b696e643a20224279746573222c0a20206d61783a2075696e742c0a7d0a636f72652d7265636f72642d74797065203d207b0a20206b696e643a20225265636f7264222c0a20206669656c64733a207b202a2074737472203d3e20636f72652d747970652d726566207d2c0a7d0a636f72652d76617269616e742d74797065203d207b0a20206b696e643a202256617269616e74222c0a202063617365733a207b202b2074737472203d3e2076617269616e742d636173652d626f6479207d2c0a7d0a76617269616e742d636173652d626f6479203d207b0a20203f207061796c6f61643a20636f72652d747970652d7265662c0a7d0a636f72652d6f7074696f6e2d74797065203d207b0a20206b696e643a20224f7074696f6e222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a636f72652d6c6973742d74797065203d207b0a20206b696e643a20224c697374222c0a20206974656d3a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6d61702d74797065203d207b0a20206b696e643a20224d6170222c0a20206b65793a20636f72652d747970652d7265662c0a202076616c75653a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6361706162696c6974792d7265662d74797065203d207b0a20206b696e643a20224361706162696c697479526566222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a636f72652d65787465726e616c2d616374696f6e2d726571756573742d74797065203d207b0a20206b696e643a202245787465726e616c416374696f6e52657175657374222c0a2020736574746c656d656e743a20636f72652d747970652d7265662c0a7d0a0a3b20636f72652d747970652d72656620697320646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d612e0a0a3b202d2d2d207265666572656e63657320616e642076616c756573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a3b204c6f63616c206964656e7469747920697320616c7068612d737461626c652e20606964602069732074686520636f6d70696c65722d6f776e6564206c6f63616c20636f6f7264696e6174653b0a3b2060616c7068614e616d656020697320746865206e6f726d616c697a65642068756d616e2f6465627567206e616d652e20536f757263652062696e646572207370656c6c696e67206973206e6f740a3b206964656e746974792e0a6c6f63616c2d726566203d207b0a202069643a20747374722c0a2020616c7068614e616d653a20747374722c0a2020747970653a20636f72652d747970652d7265662c0a7d0a0a636f72652d76616c7565203d20636f72652d6e756c6c2d76616c7565202f20636f72652d626f6f6c2d76616c7565202f20636f72652d696e742d76616c7565202f0a20202020202020202020202020636f72652d737472696e672d76616c7565202f20636f72652d62797465732d76616c7565202f20636f72652d7265636f72642d76616c7565202f0a20202020202020202020202020636f72652d76617269616e742d76616c7565202f20636f72652d6c6973742d76616c7565202f20636f72652d6d61702d76616c7565202f0a20202020202020202020202020636f72652d6361706162696c6974792d76616c75650a0a636f72652d6e756c6c2d76616c7565203d207b206b696e643a20226e756c6c22207d0a636f72652d626f6f6c2d76616c7565203d207b206b696e643a2022626f6f6c222c2076616c75653a20626f6f6c207d0a636f72652d696e742d76616c7565203d207b206b696e643a2022696e74222c2077696474683a20747374722c2076616c75653a20696e74207d0a636f72652d737472696e672d76616c7565203d207b206b696e643a2022737472696e67222c2076616c75653a2074737472207d0a636f72652d62797465732d76616c7565203d207b206b696e643a20226279746573222c2076616c75653a2062737472207d0a636f72652d7265636f72642d76616c7565203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d76616c7565207d207d0a636f72652d76617269616e742d76616c7565203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d76616c75652c0a7d0a636f72652d6c6973742d76616c7565203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d76616c75655d207d0a636f72652d6d61702d76616c7565203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d76616c75652c2076616c75653a20636f72652d76616c75655d5d207d0a636f72652d6361706162696c6974792d76616c7565203d207b0a20206b696e643a20226361706162696c697479222c0a2020726563656970743a207368613235362d6469676573742c0a7d0a0a3b2045646963742d617574686f72656420707572652068656c70657273207573652061207075726520436f72652066756e6374696f6e20626f64792e2054686520626f64792063616e2062696e640a3b20707572652065787072657373696f6e7320616e642072657475726e20616e2065787072657373696f6e2c206275742069742063616e6e6f7420636f6e7461696e20436f7265206566666563742c0a3b2067756172642c206272616e63682c206c6f6f702c206d617463682d6e6f64652c206f722070726f6f662d6f626c69676174696f6e206e6f6465732e0a636f72652d666e2d626f6479203d207b0a2020706172616d733a205b2a206c6f63616c2d7265665d2c0a2020626f64793a20636f72652d707572652d626c6f636b2c0a7d0a0a636f72652d707572652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a202062696e64696e67733a205b2a20707572652d6c65742d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a707572652d6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a0a3b202d2d2d2065787072657373696f6e7320616e642070726564696361746573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d65787072203d206c6f63616c2d65787072202f20636f6e73742d65787072202f207265636f72642d65787072202f206669656c642d65787072202f0a20202020202020202020202076617269616e742d65787072202f206d617463682d65787072202f2063616c6c2d65787072202f206c6973742d65787072202f206d61702d65787072202f0a20202020202020202020202069662d657870720a0a6c6f63616c2d65787072203d207b206b696e643a20226c6f63616c222c207265663a206c6f63616c2d726566207d0a636f6e73742d65787072203d207b206b696e643a2022636f6e7374222c2076616c75653a20636f72652d76616c7565207d0a7265636f72642d65787072203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d65787072207d207d0a6669656c642d65787072203d207b206b696e643a20226669656c64222c20626173653a20636f72652d657870722c206669656c643a2074737472207d0a76617269616e742d65787072203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d657870722c0a7d0a6d617463682d65787072203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d61726d5d2c0a7d0a6d617463682d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d657870722c0a7d0a63616c6c2d65787072203d207b0a20206b696e643a202263616c6c222c0a202063616c6c65653a20747374722c0a202074797065417267733a205b2a20636f72652d747970652d7265665d2c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6c6973742d65787072203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d657870725d207d0a6d61702d65787072203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d657870722c2076616c75653a20636f72652d657870725d5d207d0a69662d65787072203d207b0a20206b696e643a20226966222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d657870722c0a2020656c73653a20636f72652d657870722c0a7d0a0a636f72652d707265646963617465203d20747275652d707265646963617465202f2066616c73652d707265646963617465202f206e6f742d707265646963617465202f0a2020202020202020202020202020202020616c6c2d707265646963617465202f20616e792d707265646963617465202f20636f6d706172652d707265646963617465202f0a202020202020202020202020202020202063616c6c2d707265646963617465202f206f62737472756374696f6e2d7072656469636174650a0a747275652d707265646963617465203d207b206b696e643a20227472756522207d0a66616c73652d707265646963617465203d207b206b696e643a202266616c736522207d0a6e6f742d707265646963617465203d207b206b696e643a20226e6f74222c2076616c75653a20636f72652d707265646963617465207d0a616c6c2d707265646963617465203d207b206b696e643a2022616c6c222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a616e792d707265646963617465203d207b206b696e643a2022616e79222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a636f6d706172652d707265646963617465203d207b0a20206b696e643a2022636f6d70617265222c0a20206f703a20223d3d22202f2022213d22202f20223c22202f20223c3d22202f20223e22202f20223e3d222c0a20206c6566743a20636f72652d657870722c0a202072696768743a20636f72652d657870722c0a7d0a63616c6c2d707265646963617465203d207b0a20206b696e643a202263616c6c222c0a20207072656469636174653a20747374722c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6f62737472756374696f6e2d707265646963617465203d207b0a20206b696e643a20226f62737472756374696f6e222c0a2020636f6f7264696e6174653a206661696c7572652d6964656e742c0a20207061796c6f61643a20636f72652d657870722c0a7d0a0a696e7075742d636f6e73747261696e74203d207b0a2020636f6f7264696e6174653a20747374722c0a2020736f757263653a2022776865726522202f2022636f6d70696c6572222c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a3b202d2d2d20696e74656e74732c20626c6f636b732c20616e64206e6f646573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d696e74656e74203d207b0a2020696e7075743a20636f72652d747970652d7265662c0a20206f75747075743a20636f72652d747970652d7265662c0a202072657175697265644f7065726174696f6e50726f66696c653a20747374722c0a20203f2062617369733a20636f72652d657870722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020626f64793a20636f72652d626c6f636b2c0a20203f206f707469633a20636f72652d6f707469632c0a7d0a0a636f72652d627564676574203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a636f72652d6f70746963203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a20206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a2020737570706f7274506f6c6963793a20747374722c0a20206c6f7373446973706f736974696f6e3a20747374722c0a7d0a0a636f72652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a20206e6f6465733a205b2a20636f72652d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a636f72652d6e6f6465203d206c65742d6e6f6465202f20726571756972652d6e6f6465202f206566666563742d6e6f6465202f0a20202020202020202020202065787465726e616c2d616374696f6e2d726571756573742d6e6f6465202f2067756172642d6e6f6465202f206272616e63682d6e6f6465202f0a202020202020202020202020666f722d6e6f6465202f206d617463682d6e6f6465202f2070726f6f662d6f626c69676174696f6e2d6e6f64650a0a6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a726571756972652d6e6f6465203d207b0a20206b696e643a202272657175697265222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a726571756972652d6661696c7572652d61726d203d207465726d696e616c2d726571756972652d6661696c757265202f0a20202020202020202020202020202020202020202020636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c7572650a7465726d696e616c2d726571756972652d6661696c757265203d207b0a20206b696e643a20227465726d696e616c222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c757265203d207b0a20206b696e643a2022636f6e74696e75654f627374727563746564222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a6f62737472756374696f6e2d726561736f6e203d207b0a2020726561736f6e4b696e643a20747374722c0a20207061796c6f61643a207b202a2074737472203d3e20636f72652d65787072207d2c0a7d0a6566666563742d6e6f6465203d207b0a20206b696e643a2022656666656374222c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4d61703a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a65787465726e616c2d616374696f6e2d726571756573742d6e6f6465203d207b0a20206b696e643a202265787465726e616c416374696f6e52657175657374222c0a202062696e64696e673a206c6f63616c2d7265662c0a20206f7065726174696f6e3a207265736f757263652d7265662c0a2020696e707574547970653a20636f72652d747970652d7265662c0a2020736574746c656d656e74547970653a20636f72652d747970652d7265662c0a2020696e707574536368656d613a207265736f757263652d7265662c0a2020736574746c656d656e74536368656d613a207265736f757263652d7265662c0a2020696e7075743a20636f72652d657870722c0a2020617574686f7269747953636f70653a20636f72652d657870722c0a202062617369733a20636f72652d657870722c0a20206275646765743a2065787465726e616c2d616374696f6e2d6275646765742c0a20207265636f6e63696c696174696f6e4c61773a207265736f757263652d7265662c0a202073746174653a20226177616974696e67536574746c656d656e74222c0a2020736574746c656d656e7441646d697373696f6e3a2022736368656d615265717569726564222c0a7d0a65787465726e616c2d616374696f6e2d627564676574203d207b0a20206d6178536574746c656d656e7442797465733a20636f72652d657870722c0a20206d6178417474656d7074733a20636f72652d657870722c0a7d0a6f62737472756374696f6e2d61726d203d207b0a202062696e6465723a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a67756172642d6e6f6465203d207b0a20206b696e643a20226775617264222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f62737472756374696f6e3a20636f72652d657870722c0a7d0a6272616e63682d6e6f6465203d207b0a20206b696e643a20226272616e6368222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d626c6f636b2c0a2020656c73653a20636f72652d626c6f636b2c0a7d0a666f722d6e6f6465203d207b0a20206b696e643a2022666f72222c0a202062696e6465723a206c6f63616c2d7265662c0a2020697465723a20636f72652d657870722c0a2020626f756e643a20636f72652d626f756e642c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a6d617463682d6e6f6465203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d626c6f636b2d61726d5d2c0a7d0a6d617463682d626c6f636b2d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a70726f6f662d6f626c69676174696f6e2d6e6f6465203d207b0a20206b696e643a202270726f6f66222c0a2020636f6f7264696e6174653a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a636f72652d626f756e64203d206c69746572616c2d626f756e64202f20636f6f7264696e6174652d626f756e640a6c69746572616c2d626f756e64203d207b206b696e643a20226c69746572616c222c2076616c75653a2075696e74207d0a636f6f7264696e6174652d626f756e64203d207b206b696e643a2022636f6f7264696e617465222c207265663a2074737472207d0a0a3b20536861726564207265736f757263652d7265662c207368613235362d6469676573742c206661696c7572652d6964656e742c2061706572747572652d726571756972656d656e742c20616e640a3b20636f72652d747970652d7265662061726520646566696e6564206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c2e0a0a3b202d2d2d2065646963742d6c61777061636b2e6364646c202d2d2d0a3b2065646963742d6c61777061636b2e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374206c61777061636b206d616e696665737420616e64206578706f727420737572666163652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e204a534f4e20696e207468652070726f73652073706563730a3b2069732061207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a6c61777061636b2d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e6c61777061636b2f7631222c0a202069643a20747374722c0a202076657273696f6e3a20747374722c0a20206163636570746564436f72654162693a205b2b20747374725d2c0a2020646570656e64656e636965733a205b2a206c61777061636b2d6465705d2c202020202020202020203b20616379636c69632c206469676573742d6c6f636b6564202845444943542d4c41575041434b2d4441472d303031290a20206578706f7274733a207265736f757263652d7265662c0a20203f2074617267657441646170746572733a205b2b207461726765742d616461707465725d2c2020203b207265717569726564206f6e6c7920696620616e792072756e74696d6520656666656374206578697374730a20203f2068656c706572436f6d706f6e656e743a2065786563757461626c652d636f6d706f6e656e742c203b2065786563757461626c652068656c70657273206361727279207468656972206f776e2073616e64626f782b6675656c0a202076657269666965723a2076657269666965722c202020202020202020202020202020202020202020203b20636c61737369666965643a206465636c61726174697665206f722065786563757461626c650a2020636f6d7061746962696c6974793a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b2041207665726966696572206973206569746865722061206465636c617261746976652072756c6573657420286e6f2072756e74696d6529206f7220616e2065786563757461626c650a3b20636f6d706f6e656e742e20416e2065786563757461626c65207665726966696572204d55535420636172727920697473206f776e2073616e64626f7820616e64206675656c206d6f64656c2c0a3b20736f2074686520736368656d6120656e666f726365732074686174206e6f2065786563757461626c6520636f6d706f6e656e74206973206c65667420756e626f756e6465640a3b202845444943542d4142492d56455249464945522d424f554e442d303031292e0a7665726966696572203d206465636c617261746976652d7665726966696572202f2065786563757461626c652d76657269666965720a6465636c617261746976652d7665726966696572203d207b20636c6173733a20226465636c61726174697665222c2072756c657365743a207265736f757263652d726566207d0a65786563757461626c652d7665726966696572203d207b0a2020636c6173733a202265786563757461626c65222c0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a3b20416e792065786563757461626c6520636f6d706f6e656e7420697320626f756e64656420627920697473206f776e2073616e64626f78202b206675656c206d6f64656c2e0a65786563757461626c652d636f6d706f6e656e74203d207b0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a6c61777061636b2d646570203d207b2069643a20747374722c2076657273696f6e3a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20416461707465722073656c656374696f6e206b65797320534f4c454c59206f666620746865206469676573742d6c6f636b65642060616363657074656454617267657450726f66696c65600a3b20286974732060696460206973207468652070726f66696c652069643b206974732060646967657374602070696e73207468652065786163742070726f66696c652f76657273696f6e292e2054686572650a3b20617265206e6f20696e646570656e64656e7420646973706c617920737472696e6773207468617420636f756c64206469736167726565207769746820746865206c6f636b2c20736f20610a3b207265736f6c7665722063616e6e6f742062696e6420616e206164617074657220746f206f6e6520746172676574207768696c6520746865206c6f636b2070726f76657320616e6f746865720a3b202845444943542d4c41575041434b2d414441505445522d54415247455449522d303031292e0a7461726765742d61646170746572203d207b0a2020616363657074656454617267657450726f66696c653a207265736f757263652d7265662c202020203b206469676573742d6c6f636b65642c20617574686f72697461746976652073656c6563746f720a2020616363657074656454617267657449723a207265736f757263652d7265662c2020202020202020203b206469676573742d6c6f636b65640a2020616461707465723a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d206578706f72742073757266616365202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a6c61777061636b2d6578706f727473203d207b0a202074797065733a205b2a206578706f727465642d747970655d2c0a2020636f6e7374616e74733a205b2a206578706f727465642d636f6e7374616e745d2c0a20207075726546756e6374696f6e733a205b2a20707572652d66756e6374696f6e5d2c0a2020656666656374733a205b2a2073656d616e7469632d6566666563745d2c0a20206f62737472756374696f6e733a205b2a206f62737472756374696f6e2d6465665d2c0a20203b206b65796564206279206f7065726174696f6e2d70726f66696c6520636f6f7264696e61746520e2869220756e697175656e65737320656e666f726365640a20203b202845444943542d4142492d4f5050524f46494c452d554e495155452d303031290a20203b206f7065726174696f6e2d70726f66696c65207265636f7264732074686973206c61777061636b206578706f72747320286f707469632074656d706c6174657320746861740a20203b2060696d706c656d656e7473602f6070726f66696c656020636c6175736573207265736f6c766520616761696e7374292e206f7065726174696f6e2d70726f66696c652069730a20203b20646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c20203b206b6579656420627920636f6f7264696e6174650a7d0a0a6578706f727465642d7479706520202020203d207b20636f6f7264696e6174653a20747374722c20646566696e6974696f6e3a20636f72652d747970652d726566207d0a6578706f727465642d636f6e7374616e74203d207b20636f6f7264696e6174653a20747374722c20747970653a20636f72652d747970652d7265662c2076616c75653a20616e79207d0a0a3b204120707572652068656c7065722069732061206469736372696d696e6174656420756e696f6e2062792060736f75726365602c20736f2074686520736368656d6120697473656c660a3b2067756172616e7465657320616e20696d706c656d656e746174696f6e20657869737473202845444943542d4c41575041434b2d505552452d494d504c2d303031293a0a3b2020202d20226564696374223a20617574686f72656420696e2045646963742f436f72653b2074686520436f726520626f6479206973206361727269656420696e6c696e6520286861736865640a3b20202020207769746820746865206578706f72742073757266616365292e2054686520736368656d61207265717569726573207468652060626f647960206669656c642e0a3b2020202d2022636f6d706f6e656e74223a20696d706c656d656e746564206f7574736964652045646963743b2063617272696573206e6f20696e6c696e6520626f647920616e6420696e73746561640a3b20202020206361727269657320697473206f776e206469676573742d6c6f636b65642060696d706c656d656e746174696f6e60202873616e64626f78202b206675656c292e20497420646f65730a3b20202020206e6f7420646570656e64206f6e20746865206f7074696f6e616c206d616e69666573742d6c6576656c2068656c706572436f6d706f6e656e742e0a707572652d66756e6374696f6e203d2065646963742d707572652d66756e6374696f6e202f20636f6d706f6e656e742d707572652d66756e6374696f6e0a0a707572652d66756e6374696f6e2d636f6d6d6f6e203d20280a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020706172616d6574657254797065733a205b2a20636f72652d747970652d7265665d2c2020202020203b20616c6c20626f756e6465640a202072657475726e547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020636f737454656d706c6174653a20747374722c0a202064657465726d696e69736d436c6173733a2022746f74616c22202f2022746f74616c2d776974682d74797065642d646961676e6f73746963222c0a290a0a65646963742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a20226564696374222c0a2020626f64793a20636f72652d666e2d626f64792c2020202020202020202020202020202020202020203b20696e6c696e652c20686173682d7369676e69666963616e740a7d0a0a636f6d706f6e656e742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a2022636f6d706f6e656e74222c0a20203b20746865206469676573742d6c6f636b656420636f6d706f6e656e7420696d706c656d656e74696e6720746869732068656c7065722e2052657175697265642061742074686520736368656d610a20203b206c6576656c20736f206120636f6d706f6e656e742068656c7065722063616e206e657665722076616c696461746520776974686f7574206120686173682d626f756e642c0a20203b2073616e64626f782b6675656c2d64657363726962656420696d706c656d656e746174696f6e202845444943542d4c41575041434b2d505552452d494d504c2d303031292e0a2020696d706c656d656e746174696f6e3a2065786563757461626c652d636f6d706f6e656e742c0a7d0a0a3b20636f72652d666e2d626f647920697320646566696e65642062792065646963742d636f72652e6364646c20616e6420617373656d626c656420776974682074686973206c61777061636b0a3b20736368656d612e2049742069732061207075726520436f72652066756e6374696f6e20626f64792c206e6f7420616e206566666563742d63617061626c6520636f72652d626c6f636b2e0a0a73656d616e7469632d656666656374203d207b0a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020696e707574547970653a20636f72652d747970652d7265662c2020202020202020202020202020203b20626f756e6465640a20206f7574707574547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020657865637574696f6e436c6173733a202270726f6f664f6e6c7922202f202272756e74696d65222c2020203b206f7274686f676f6e616c20746f207772697465436c6173730a20206566666563744b696e6448696e743a206566666563742d6b696e642c0a2020666f6f747072696e744f626c69676174696f6e3a20747374722c0a2020636f73744f626c69676174696f6e3a20747374722c0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c20203b206b6579656420627920636f6f7264696e6174653b20756e697175650a20206775617264537570706f72743a20626f6f6c2c0a7d0a0a6f62737472756374696f6e2d646566203d207b0a2020636f6f7264696e6174653a20747374722c0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164536368656d613a20636f72652d747970652d7265662c20202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d6c61777061636b2d616461707465722e6364646c202d2d2d0a3b2065646963742d6c61777061636b2d616461707465722e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f72206f6e6520646972656374206465636c61726174697665206c61777061636b2074617267657420616461707465722e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b2054686520656e636c6f73696e67206c61777061636b206d616e69666573742073656c6563747320746865206578616374207461726765742070726f66696c652c207461726765742049522c0a3b20616e642061646170746572207265736f75726365206469676573742e2054686f7365206964656e74697469657320617265206e6f7420726570656174656420686572652e0a0a6c61777061636b2d61646170746572203d207b0a202061706956657273696f6e3a202265646963742e6c61777061636b2d616461707465722f7631222c0a2020636c6173733a20226465636c61726174697665222c0a20206f7065726174696f6e50726f66696c65733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6f7065726174696f6e2d70726f66696c650a20207d2c0a2020656666656374496d706c656d656e746174696f6e733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6566666563740a20207d2c0a2020627564676574733a207b0a202020202a2074737472203d3e206c61777061636b2d616461707465722d6275646765740a20207d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206c61777061636b206f7065726174696f6e2d70726f66696c6520636f6f7264696e617465732e0a6c61777061636b2d616461707465722d6f7065726174696f6e2d70726f66696c65203d207b0a2020636f72653a20747374722c0a202073656d616e746963456666656374733a205b2b20747374725d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206c61777061636b2073656d616e7469632d65666665637420636f6f7264696e617465732e20466f6f747072696e742c20636f73742c20616e640a3b206661696c757265206669656c6473206d7573742065786163746c792064697363686172676520746865206d61746368696e67206578706f72746564206566666563742e0a6c61777061636b2d616461707465722d656666656374203d207b0a2020746172676574496e7472696e7369633a20747374722c0a2020746172676574436f6e66696775726174696f6e3a207265736f757263652d7265662c0a20207772697465436c6173733a206c61777061636b2d616461707465722d77726974652d636c6173732c0a2020666f6f747072696e744f626c69676174696f6e3a20747374722c0a2020636f73744f626c69676174696f6e3a20747374722c0a20206661696c7572654d617070696e67733a207b202a206661696c7572652d6964656e74203d3e2074737472207d2c0a7d0a0a3b204b657973206172652063616e6f6e6963616c206578706f7274656420636f73742d6f626c69676174696f6e20636f6f7264696e617465732e0a6c61777061636b2d616461707465722d627564676574203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a6c61777061636b2d616461707465722d77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f0a20202020202020202020202020202020202020202020202020202020202022617070656e6422202f20227265706c61636522202f202264656c65746522202f2022637573746f6d220a0a3b206661696c7572652d6964656e7420697320646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c2e0a0a3b202d2d2d2065646963742d7461726765742d70726f66696c652e6364646c202d2d2d0a3b2065646963742d7461726765742d70726f66696c652e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374207461726765742070726f66696c65206d616e69666573742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76310a3b202873656520535045435f636f6e74696e75756d2d636f6e74726163742d62756e646c652d76312e6d64292e204a534f4e20696e207468652070726f736520737065637320697320610a3b207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d613b2074686973204344444c206973207468652073696e676c6520736f757263650a3b206f66207472757468202845444943542d4142492d4e4f4455502d303031292e0a0a7461726765742d70726f66696c652d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652f7631222c0a202069643a20747374722c202020202020202020202020202020202020202020202020203b20652e672e20226563686f2e64706f220a202076657273696f6e3a20747374722c20202020202020202020202020202020202020203b20652e672e202231220a20206163636570746564436f72654162693a205b2b20747374725d2c20202020202020203b20652e672e205b2265646963742e636f72652f7631225d0a0a2020696e7472696e736963733a207265736f757263652d7265662c0a2020696e7472696e7369634e616d6573706163653a20747374722c0a20203b207075626c697368657320746869732070726f66696c652773206f7065726174696f6e2d70726f66696c65207265636f72647320286f707469632074656d706c6174657320746861740a20203b206070726f66696c65602f60696d706c656d656e74736020636c6175736573207265736f6c766520616761696e7374292e205265666572656e63657320616e0a20203b206f7065726174696f6e2d70726f66696c65732d646f63756d656e74202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207265736f757263652d7265662c0a2020666f6f747072696e74416c67656272613a207265736f757263652d7265662c0a2020636f7374416c67656272613a207265736f757263652d7265662c0a202074617267657449723a207265736f757263652d7265662c0a20206f62737472756374696f6e5461786f6e6f6d793a207265736f757263652d7265662c0a202076657269666965723a207265736f757263652d7265662c0a20206c6f77657265723a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a0a20203b206669656c647320746865206c616e67756167652073706563207265717569726573206f662065766572792070726f66696c650a202062756e646c6550726f66696c653a207265736f757263652d7265662c0a202067656e657261746564417274696661637450726f66696c65733a205b2a207265736f757263652d7265665d2c0a202063616e6f6e6963616c456e636f64696e6752756c65733a207265736f757263652d7265662c0a20203b20412070726f66696c65207468617420616363657074732074686520646972656374206465636c61726174697665206c61777061636b2d6164617074657220414249206e616d65732069740a20203b2065786163746c79206f6e63652e2050726f66696c6573207468617420646f206e6f7420636f6e73756d65206c61777061636b206164617074657273206c6561766520746869730a20203b206f7074696f6e616c20736c6f7420616273656e74206f7220656d7074792e0a20203f2061636365707465644c61777061636b416461707465724162693a205b5d202f205b2265646963742e6c61777061636b2d616461707465722f7631225d2c0a2020646961676e6f737469634162693a207265736f757263652d7265662c0a0a20203b206170706c69636174696f6e20646f637472696e650a20206170706c69636174696f6e4d6f64656c3a202261746f6d6963222c0a202072656164436f6e73697374656e63793a20226170706c69636174696f6e2d736e617073686f7422202f20747374722c0a202067756172644576616c756174696f6e3a2022707265636f6d6d69742d61746f6d696322202f20747374722c0a20206f62737472756374696f6e526f6c6c6261636b3a20226e6f2d76697369626c652d6566666563747322202f20747374722c0a20206d756c74695461726765743a20626f6f6c2c0a20203b207768657468657220746865207461726765742063616e206576616c7561746520707265636f6d6d697420706f7374636f6e646974696f6e20286067756172616e746565602920636865636b730a20203b20696e73696465207468652061746f6d6963206170706c69636174696f6e20756e6974202845444943542d5441524745542d504f5354434f4e442d303031290a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a0a202064657465726d696e6973746963457865637574696f6e3a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d6120627920746865206275696c640a3b202845444943542d4142492d4e4f4455502d303031292e205468657920617265206e6f74207265646566696e656420686572652e0a0a3b202d2d2d20696e7472696e736963207369676e6174757265202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e696665737427732060696e7472696e7369637360207265736f757263652d726566206973207468650a3b20696e7472696e7369632d7369676e617475726520636f7270757320646f63756d656e742062656c6f772e20497473206c61796f757420697320666978656420736f2074776f0a3b20696e646570656e64656e742070726f66696c65732076616c69646174652f686173682074686520636f72707573206964656e746963616c6c790a3b202845444943542d4142492d494e5452494e534943532d444f432d303031292e0a0a3b20696e7472696e736963732069732061204d4150206b6579656420627920636f6f7264696e6174652c20736f2074686520736368656d6120697473656c6620656e666f726365730a3b20636f6f7264696e61746520756e697175656e6573732e20412070726f766964657220726563656976657320746865207265736f6c76656420636f7270757320617320610a3b206469676573742d626f756e642073656d616e74696320696e70757420616e64207265736f6c76657320636f6f7264696e617465732077697468696e20746861742061727469666163742e0a3b2045616368206d6170206b6579204d55535420657175616c20697473207265636f726427732060636f6f7264696e61746560206669656c640a3b202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e696e7472696e736963732f7631222c0a2020696e7472696e736963733a207b202a2074737472203d3e20696e7472696e736963207d2c0a7d0a0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e6966657374277320606f7065726174696f6e50726f66696c657360207265736f757263652d7265662e0a3b206f7065726174696f6e2d70726f66696c65202f206f707469632d74656d706c6174652061726520646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c2e204b657965642062790a3b20636f6f7264696e61746520736f207265736f6c7574696f6e2063616e2774207069636b206265747765656e2074776f2073616d652d636f6f7264696e6174652070726f66696c65730a3b202845444943542d4142492d4f5050524f46494c452d534c4f542d3030312c2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e6f7065726174696f6e2d70726f66696c65732f7631222c0a202070726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c0a7d0a0a3b2041207479706564207072652d6c6f776572696e67207175657374696f6e20746861742063616e2062652070726f706f73656420627920576174736f6e206f7220616e206167656e7420616e640a3b20636865636b65642062792074686520636f6d70696c65722e2049742069732063616e6f6e6963616c2d43424f5220656e636f64656420756e6465720a3b206065646963742e6c6f776572696e672d726571756972656d656e74732f7631603b2074686520636f6d70696c657220636865636b7320746869732061727469666163742c206e6f74207468650a3b2070726f736520746861742070726f64756365642069742e0a6c6f776572696e672d726571756972656d656e7473203d207b0a202061706956657273696f6e3a202265646963742e6c6f776572696e672d726571756972656d656e74732f7631222c0a20206f7065726174696f6e50726f66696c653a20747374722c0a202073656d616e746963456666656374733a205b2a2073656d616e7469632d6566666563742d726571756972656d656e745d2c0a202072657175697265645772697465436c61737365733a205b2a2077726974652d636c6173735d2c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a202061746f6d69636974793a2061746f6d69636974792d726571756972656d656e742c0a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a20206f70746963436f6e74726163743a20747374722c0a7d0a0a73656d616e7469632d6566666563742d726571756972656d656e74203d207b0a2020636f6f7264696e6174653a20747374722c0a20207772697465436c6173733a2077726974652d636c6173732c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a7d0a0a77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a2020202020202020202020202020227265706c61636522202f202264656c65746522202f20747374720a67756172642d6b696e64203d2022707265636f6d6d69742d61746f6d696322202f20747374720a61746f6d69636974792d726571756972656d656e74203d202261746f6d696322202f20747374720a0a3b20412067656e75696e6520756e696f6e3a207075726520636f6e7374727563746f7273206361727279206e6f20656666656374206b696e64206f72206661696c757265733b206566666563740a3b20696e7472696e73696373206d757374202845444943542d5441524745542d494e5452494e5349432d434c4153532d303031292e2054686520736368656d6120656e666f7263657320746869732c0a3b206e6f74206120636f6d6d656e742e0a0a3b2054686520696e7472696e736963277320636f6f7264696e6174652069732074686520696e7472696e73696373206d6170204b45592c206e6f7420612076616c7565206669656c642c20736f207468650a3b206b657920616e6420636f6f7264696e6174652063616e206e65766572206469736167726565202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963203d20707572652d696e7472696e736963202f206566666563742d696e7472696e7369630a0a707572652d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a202270757265222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206775617264537570706f72743a2066616c73652c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20226e6f6e65222c0a7d0a0a6566666563742d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a2022656666656374222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206566666563744b696e643a206566666563742d6b696e642c0a20203b206d6170206b65796564206279206661696c75726520636f6f7264696e61746520286661696c7572652d6964656e74293b20746865206661696c75726520636f6f7264696e6174652069730a20203b20746865206b65792c206e6f7420612076616c7565206669656c642c20736f20756e697175656e657373206973207374727563747572616c0a20203b202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c0a20206775617264537570706f72743a20626f6f6c2c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f20227265706c61636522202f0a20202020202020202020202020202264656c65746522202f2022637573746f6d222c0a202063616e5061727469636970617465496e41746f6d696347756172643a20626f6f6c2c0a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d617574686f726974792d66616374732e6364646c202d2d2d0a3b2065646963742d617574686f726974792d66616374732e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f722074686520666972737420636f6d70696c65722d636f6e7465787420617574686f726974792d666163747320646f63756d656e742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20736f20736f757263652e6469676573742075736573207468650a3b20736861726564207368613235362d6469676573742074797065642076616c75652e204a534f4e2069732061207265766965772f696e7075742072656e646572696e673a206974730a3b20607368613235363a3c3634206865783e6020736f75726365206469676573742069732070726f6a656374656420746f205b60736861323536602c203332207261772062797465735d206f6e0a3b2074686520776972652c20616e64206974732066616374206172726179732070726f6a65637420746f2074686520636f6f7264696e6174652d6b65796564206d6170732062656c6f772e0a0a617574686f726974792d6661637473203d207b0a202061706956657273696f6e3a202265646963742e617574686f726974792d66616374732f7631222c0a2020736f757263653a20617574686f726974792d666163742d736f757263652c0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e20617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374207d2c0a20206566666563745772697465436c61737365733a207b202a2074737472203d3e20617574686f726974792d77726974652d636c617373207d2c0a2020627564676574733a207b202a2074737472203d3e20617574686f726974792d6275646765742d66616374207d2c0a7d0a0a617574686f726974792d666163742d736f75726365203d207b0a20206b696e643a20226c61777061636b22202f202274617267657450726f66696c65222c0a2020636f6f7264696e6174653a20747374722c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a3b20546865206d6170206b65792069732074686520736f75726365206f7065726174696f6e2d70726f66696c6520636f6f7264696e6174652e204974206973206e6f7420726570656174656420696e0a3b207468652076616c75652c20736f2061206b657920616e6420656d62656464656420636f6f7264696e6174652063616e6e6f742064697361677265652e20416c6c6f7765642077726974650a3b20636c61737365732061726520612063616e6f6e6963616c206d61702d7365743a2074686520636c6173732069732074686520756e69717565206b657920616e64206e756c6c206973207468650a3b20756e6974206d61726b65722e2043616e6f6e6963616c2043424f52206669786573206b6579206f7264657220776974686f75742061207365636f6e64206f72646572696e672072756c652e0a617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374203d207b0a2020636f72653a20747374722c0a2020616c6c6f7765645772697465436c61737365733a207b202a20617574686f726974792d77726974652d636c617373203d3e206e756c6c207d2c0a7d0a0a3b20546865206566666563745772697465436c6173736573206d6170206b6579206973207468652073656d616e7469632065666665637420636f6f7264696e6174652e2054686520627564676574730a3b206d6170206b65792069732074686520736f757263652062756467657420636f6f7264696e6174652e2043616e6f6e6963616c2043424f52206d61702d6b657920756e697175656e6573730a3b206d616b6573206475706c6963617465206661637420636f6f7264696e61746573207374727563747572616c6c7920756e726570726573656e7461626c652e0a617574686f726974792d6275646765742d66616374203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a3b20417574686f726974794661637473446f63756d656e7420763120696e74656e74696f6e616c6c792061636365707473206f6e6c792074686520777269746520636c6173736573207468650a3b2063757272656e7420636f6d70696c6572206d6f64656c2063616e20636f6e73756d652e2060637573746f6d602069732074686520736f6c6520763120637573746f6d207370656c6c696e673b0a3b20617262697472617279207461726765742d70726f66696c6520657874656e73696f6e20737472696e677320646f206e6f7420656e746572207468697320636f6d70696c657220706174682e0a617574686f726974792d77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a202020202020202020202020202020202020202020202020227265706c61636522202f202264656c65746522202f2022637573746f6d220a0a3b202d2d2d2065646963742d726573756c742d70726f6a656374696f6e2e6364646c202d2d2d0a3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d726573756c742d70726f6a656374696f6e2e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220636f6d70696c65722d6f776e6564206170706c69636174696f6e2d726573756c742070726f6a656374696f6e732e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a0a726573756c742d70726f6a656374696f6e203d207b0a2020736368656d613a202265646963742e726573756c742d70726f6a656374696f6e2f7631222c0a20206f7065726174696f6e436f6f7264696e6174653a20626f756e6465642d70726f6a656374696f6e2d746578742c0a20206f7574707574547970653a20626f756e6465642d70726f6a656374696f6e2d746578742c0a20206d61784f757470757442797465733a2075696e74202e677420302c0a202065787072657373696f6e3a20726573756c742d70726f6a656374696f6e2d657870722c0a7d0a0a726573756c742d70726f6a656374696f6e2d65787072203d20726573756c742d70726f6a656374696f6e2d7265636f7264202f20726573756c742d70726f6a656374696f6e2d736f757263650a0a726573756c742d70726f6a656374696f6e2d7265636f7264203d207b0a20206b696e643a20227265636f7264222c0a20203b2054686520726f6f74207265636f726420636f756e7473206173206f6e65206f66207468652052757374206465636f6465722773203235362065787072657373696f6e206e6f6465732e0a20203b204e657374656420616767726567617465206e6f646520636f756e742072656d61696e7320616e20617574686f7269746174697665206465636f64657220636865636b2e0a20206669656c64733a207b20302a32353520626f756e6465642d70726f6a656374696f6e2d74657874203d3e20726573756c742d70726f6a656374696f6e2d65787072207d2c0a7d0a0a726573756c742d70726f6a656374696f6e2d736f75726365203d207b0a20206b696e643a2022736f75726365222c0a2020736f757263653a20726573756c742d70726f6a656374696f6e2d736f757263652d6b696e642c0a20203b204d617463686573204d41585f524553554c545f50524f4a454354494f4e5f504154485f5345474d454e545320696e2065646963742d73796e7461782e0a2020706174683a205b302a333220626f756e6465642d70726f6a656374696f6e2d746578745d2c0a7d0a0a726573756c742d70726f6a656374696f6e2d736f757263652d6b696e64203d0a20207b206b696e643a20226170706c69636174696f6e496e70757422207d202f0a20207b0a202020206b696e643a20226361706162696c697479526573756c74222c0a202020207374657049643a20626f756e6465642d70726f6a656374696f6e2d746578742c0a20207d0a0a626f756e6465642d70726f6a656374696f6e2d74657874203d2074737472202e73697a652028312e2e31303234290a0a3b202d2d2d2065646963742d7461726765742d69722e6364646c202d2d2d0a3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d7461726765742d69722e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f72207468652045646963742d6f776e65642054617267657420495220617274696661637420656e76656c6f70652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20616e642065646963742d636f72652e6364646c2e2049740a3b2064656c696265726174656c792072657573657320436f72652065787072657373696f6e732c20707265646963617465732c20627564676574732c206c6f63616c207265666572656e6365732c0a3b206f62737472756374696f6e20726561736f6e732c20616e64206f62737472756374696f6e2061726d7320736f2074686520736368656d61206d617463686573207468652076616c75650a3b20656d6974746564206279207468652063616e6f6e6963616c2054617267657420495220656e636f64657220726174686572207468616e20726573746174696e672074686f73652074797065732e0a3b2049742064657363726962657320746865207374727563747572616c207368617065206f662076616c6964206c6f776572696e672d70726f6475636564206172746966616374732e205468650a3b206c6f776572696e6720616e6420656e636f64657220636f6e7472616374732073657061726174656c7920656e666f7263652073656d616e746963206964656e7469666965722072756c65730a3b20616e642063616e6f6e6963616c206f72646572696e672f64656475706c69636174696f6e20666f72207365742d6c696b652076616c7565732e0a0a3b2054617267657420495220656e636f64696e672072656a6563747320616e20656d707479207461726765742d70726f66696c6520636f6f7264696e617465206265666f72652062797465730a3b2065786973742c20736f207468697320726f6f74207469676874656e732074686520736861726564207374727563747572616c207265736f757263652d726566206163636f7264696e676c792e0a7461726765742d69722d7265736f757263652d726566203d207b0a202069643a2074737472202e7265676578702022283f73292e2b222c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a7461726765742d69722d6172746966616374203d207461726765742d69722d636c6f7365642d6172746966616374202f207461726765742d69722d6c65676163792d61727469666163740a0a7461726765742d69722d61727469666163742d636f6d6d6f6e203d20280a20206b696e643a202274617267657449724172746966616374222c0a2020646f6d61696e3a20747374722c0a202074617267657450726f66696c653a207461726765742d69722d7265736f757263652d7265662c0a2020736f75726365436f7265436f6f7264696e6174653a2074737472202e7265676578702022283f73292e2b222c0a290a0a7461726765742d69722d636c6f7365642d6172746966616374203d207b0a20207461726765742d69722d61727469666163742d636f6d6d6f6e2c0a202073656d616e746963436c6f737572653a207461726765742d69722d73656d616e7469632d636c6f737572652c0a2020696e74656e74733a207b202a2074737472203d3e207461726765742d69722d696e74656e74207d2c0a7d0a0a7461726765742d69722d6c65676163792d6172746966616374203d207b0a20207461726765742d69722d61727469666163742d636f6d6d6f6e2c0a2020696e74656e74733a207b202a2074737472203d3e207461726765742d69722d6c65676163792d696e74656e74207d2c0a7d0a0a7461726765742d69722d73656d616e7469632d636c6f73757265203d207b0a2020736f75726365436f72653a207461726765742d69722d7265736f757263652d7265662c0a20206c61777061636b733a205b2a207461726765742d69722d7265736f757263652d7265665d2c0a20203f206361706162696c69746965733a205b2a207461726765742d69722d7265736f757263652d7265665d2c0a7d0a0a7461726765742d69722d696e74656e74203d207b0a20207461726765742d69722d696e74656e742d636f6d6d6f6e2c0a20203f2062617369733a20636f72652d657870722c0a20203f2065787465726e616c416374696f6e52657175657374733a205b2a207461726765742d69722d65787465726e616c2d616374696f6e2d726571756573745d2c0a7d0a0a7461726765742d69722d6c65676163792d696e74656e74203d207b0a20207461726765742d69722d696e74656e742d636f6d6d6f6e2c0a7d0a0a7461726765742d69722d696e74656e742d636f6d6d6f6e203d20280a20206f7065726174696f6e50726f66696c653a20747374722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020726571756972656d656e74733a205b2a207461726765742d69722d726571756972656d656e745d2c0a202073746570733a205b2a207461726765742d69722d737465705d2c0a2020726573756c743a20636f72652d657870722c0a290a0a7461726765742d69722d726571756972656d656e74203d207b0a202069643a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a0a7461726765742d69722d73746570203d207b0a202069643a20747374722c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020746172676574496e7472696e7369633a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4661696c757265733a205b2a206661696c7572652d6964656e745d2c0a20206f62737472756374696f6e41726d733a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a0a7461726765742d69722d65787465726e616c2d616374696f6e2d72657175657374203d207b0a202069643a20747374722c0a202062696e64696e673a206c6f63616c2d7265662c0a20206f7065726174696f6e3a207461726765742d69722d7265736f757263652d7265662c0a2020696e707574547970653a20636f72652d747970652d7265662c0a2020736574746c656d656e74547970653a20636f72652d747970652d7265662c0a2020696e707574536368656d613a207461726765742d69722d7265736f757263652d7265662c0a2020736574746c656d656e74536368656d613a207461726765742d69722d7265736f757263652d7265662c0a2020696e7075743a20636f72652d657870722c0a2020617574686f7269747953636f70653a20636f72652d657870722c0a202062617369733a20636f72652d657870722c0a20206275646765743a2065787465726e616c2d616374696f6e2d6275646765742c0a20207265636f6e63696c696174696f6e4c61773a207461726765742d69722d7265736f757263652d7265662c0a202073746174653a20226177616974696e67536574746c656d656e74222c0a2020736574746c656d656e7441646d697373696f6e3a2022736368656d615265717569726564222c0a7d0a", + "rawSha256": "4936a5c13f61647f710c99cbdfb347ef180d9ccf4b9db75bfcee68e394fdc480" }, "contracts": [ {"contract": "authority-facts", "rootRule": "authority-facts"}, diff --git a/fixtures/target-ir/canonical/workspace-snapshot.target-ir.cbor b/fixtures/target-ir/canonical/workspace-snapshot.target-ir.cbor new file mode 100644 index 0000000..47198d0 Binary files /dev/null and b/fixtures/target-ir/canonical/workspace-snapshot.target-ir.cbor differ diff --git a/fixtures/target-ir/canonical/workspace-snapshot.target-ir.sha256 b/fixtures/target-ir/canonical/workspace-snapshot.target-ir.sha256 new file mode 100644 index 0000000..3f3540f --- /dev/null +++ b/fixtures/target-ir/canonical/workspace-snapshot.target-ir.sha256 @@ -0,0 +1 @@ +sha256:e55e2c015cfa26ddddf7b957c4d1a8c03627cdfac6699bc5831e286d362986d0 diff --git a/grammars/textmate/edict.tmLanguage.json b/grammars/textmate/edict.tmLanguage.json index 41c0db4..5847d8e 100644 --- a/grammars/textmate/edict.tmLanguage.json +++ b/grammars/textmate/edict.tmLanguage.json @@ -66,7 +66,7 @@ }, "keywords": { "name": "keyword.control.edict", - "match": "\\b(?:package|use|type|enum|variant|intent|returns|profile|implements|basis|footprint|budget|where|let|return|require|guarantee|assert|if|then|else|for|in|bounded|yield|match|shape|lawpack|target|core|capability|as|digest|fn|const|true|false)\\b" + "match": "\\b(?:package|use|type|enum|variant|intent|returns|profile|implements|basis|footprint|budget|where|let|return|request|require|guarantee|assert|if|then|else|for|in|bounded|yield|match|shape|lawpack|target|core|capability|as|digest|fn|const|true|false)\\b" }, "types": { "name": "entity.name.type.edict", diff --git a/xtask/src/goldens.rs b/xtask/src/goldens.rs index 038870c..b96161f 100644 --- a/xtask/src/goldens.rs +++ b/xtask/src/goldens.rs @@ -129,11 +129,18 @@ struct CoreGoldenCase { digest: &'static str, } -const CORE_GOLDEN_CASES: &[CoreGoldenCase] = &[CoreGoldenCase { - source: "fixtures/lang/bounds/bounded-hello.edict", - bytes: "fixtures/core/canonical/bounded-hello.core.cbor", - digest: "fixtures/core/canonical/bounded-hello.core.sha256", -}]; +const CORE_GOLDEN_CASES: &[CoreGoldenCase] = &[ + CoreGoldenCase { + source: "fixtures/lang/bounds/bounded-hello.edict", + bytes: "fixtures/core/canonical/bounded-hello.core.cbor", + digest: "fixtures/core/canonical/bounded-hello.core.sha256", + }, + CoreGoldenCase { + source: "fixtures/lang/external-actions/workspace-snapshot.edict", + bytes: "fixtures/core/canonical/workspace-snapshot.core.cbor", + digest: "fixtures/core/canonical/workspace-snapshot.core.sha256", + }, +]; pub(crate) fn core_goldens(root: &Path, mode: CoreGoldenMode) -> Result<(), String> { for case in CORE_GOLDEN_CASES { @@ -181,6 +188,7 @@ fn check_or_write_core_golden( fn core_golden_context() -> CompilerContext { CompilerContext::new() .with_operation_profile("hello.readOnly", "continuum.profile.read-only/v1") + .with_operation_profile("workspace.read", "continuum.profile.read-only/v1") .with_budget( "hello.tinyBudget", CoreBudget { @@ -189,6 +197,14 @@ fn core_golden_context() -> CompilerContext { max_output_bytes: 1024, }, ) + .with_budget( + "workspace.tiny", + CoreBudget { + max_steps: 512, + max_allocated_bytes: 256 * 1024, + max_output_bytes: 128 * 1024, + }, + ) } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -201,6 +217,7 @@ pub(crate) enum TargetIrGoldenMode { enum TargetIrGoldenKind { Echo, Gitwarp, + ExternalRequest, } #[derive(Debug)] @@ -221,6 +238,11 @@ const TARGET_IR_GOLDEN_CASES: &[TargetIrGoldenCase] = &[ bytes: "fixtures/target-ir/canonical/gitwarp-append.target-ir.cbor", digest: "fixtures/target-ir/canonical/gitwarp-append.target-ir.sha256", }, + TargetIrGoldenCase { + kind: TargetIrGoldenKind::ExternalRequest, + bytes: "fixtures/target-ir/canonical/workspace-snapshot.target-ir.cbor", + digest: "fixtures/target-ir/canonical/workspace-snapshot.target-ir.sha256", + }, ]; const TARGET_IR_ECHO_SOURCE: &str = "package a.b@1;\n\ @@ -250,6 +272,9 @@ const TARGET_IR_GITWARP_SOURCE: &str = "package a.git@1;\n\ return { id: receipt.id };\n\ }"; +const TARGET_IR_EXTERNAL_REQUEST_SOURCE: &str = + include_str!("../../fixtures/lang/external-actions/workspace-snapshot.edict"); + pub(crate) fn target_ir_goldens(root: &Path, mode: TargetIrGoldenMode) -> Result<(), String> { for case in TARGET_IR_GOLDEN_CASES { check_or_write_target_ir_golden(root, case, mode)?; @@ -302,6 +327,11 @@ fn target_ir_golden_artifact(case: &TargetIrGoldenCase) -> Result ( + TARGET_IR_EXTERNAL_REQUEST_SOURCE, + target_ir_external_request_context(), + target_ir_external_request_facts(), + ), }; let module = parse_module(source) .map_err(|err| format!("parse {:?} Target IR golden source: {err}", case.kind))?; @@ -346,6 +376,19 @@ fn target_ir_gitwarp_context() -> CompilerContext { ) } +fn target_ir_external_request_context() -> CompilerContext { + CompilerContext::new() + .with_operation_profile("workspace.read", "continuum.profile.read-only/v1") + .with_budget( + "workspace.tiny", + CoreBudget { + max_steps: 512, + max_allocated_bytes: 256 * 1024, + max_output_bytes: 128 * 1024, + }, + ) +} + fn target_ir_echo_facts() -> TargetIrLoweringFacts { TargetIrLoweringFacts { target_profile: ResourceRef { @@ -380,6 +423,19 @@ fn target_ir_gitwarp_facts() -> TargetIrLoweringFacts { } } +fn target_ir_external_request_facts() -> TargetIrLoweringFacts { + TargetIrLoweringFacts { + target_profile: ResourceRef { + coordinate: ECHO_DPO_TARGET_PROFILE.to_owned(), + digest: Some(digest_text('3')), + }, + target_ir_domain: ECHO_SPAN_IR_DOMAIN.to_owned(), + operation_profiles: vec!["continuum.profile.read-only/v1".to_owned()], + obstruction_coordinates: Vec::new(), + effect_lowerings: Vec::new(), + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum BundleGoldenMode { Check, diff --git a/xtask/src/tests.rs b/xtask/src/tests.rs index 4a5b1c5..f7efca2 100644 --- a/xtask/src/tests.rs +++ b/xtask/src/tests.rs @@ -22,9 +22,9 @@ use wit_parser::{ use super::contract_check::{check_links, check_topic, contract_check}; use super::goldens::{ - authority_facts_goldens, bundle_goldens, cli_binary_path_from_cargo_metadata, + authority_facts_goldens, bundle_goldens, cli_binary_path_from_cargo_metadata, core_goldens, target_ir_goldens, target_profile_resource_goldens, AuthorityFactsGoldenMode, BundleGoldenMode, - TargetIrGoldenMode, TargetProfileResourceGoldenMode, + CoreGoldenMode, TargetIrGoldenMode, TargetProfileResourceGoldenMode, }; use super::lawpack_goldens::{lawpack_goldens, LawpackGoldenMode}; use super::provider_contract_pack::{ @@ -57,6 +57,12 @@ fn bundle_digest_goldens_match_assembly() { .expect("bundle digest goldens match assembly output"); } +#[test] +fn core_goldens_match_executable_encoder() { + core_goldens(&repo_root().expect("repo root"), CoreGoldenMode::Check) + .expect("Core goldens match executable encoder output"); +} + #[test] fn target_ir_goldens_match_executable_encoder() { target_ir_goldens(&repo_root().expect("repo root"), TargetIrGoldenMode::Check) @@ -1031,7 +1037,7 @@ fn textmate_grammar_covers_public_highlight_roles() { let type_regex = textmate_repository_match(&grammar, "types"); let identifier_regex = textmate_repository_match(&grammar, "identifiers"); let source = "package use type enum variant intent returns profile implements basis \ - footprint budget where let return require guarantee assert if then else for in \ + footprint budget where let return request require guarantee assert if then else for in \ bounded yield match shape lawpack target core capability as digest fn const true \ false HelloInput input = == != < <= > >= + - * / % ! && || => -> :: ... ; : , . @ \ ( ) { } [ ] \"text\" 123";