From 9f1a11e0358caeb03339f0035333f8a49a2a814a Mon Sep 17 00:00:00 2001 From: James Ross Date: Mon, 20 Jul 2026 22:35:26 -0700 Subject: [PATCH 1/4] Refactor: Rename Intent to Action workspace-wide Follows the Kitten architecture where 'Action' replaces 'Intent' as the Edict-compiled graph rewrite rule, avoiding collision with teleological Kitten 'Intent'. --- ARCHITECTURE.md | 2 +- CHANGELOG.md | 16 +- EDICT.md | 116 ++++----- README.md | 34 +-- ROADMAP.md | 4 +- crates/edict-cli/src/main.rs | 38 +-- crates/edict-cli/tests/jsonl_cli.rs | 12 +- .../tests/invocation.rs | 2 +- .../tests/provider_contract_pack.rs | 26 +- .../edict-provider-schema/tests/registry.rs | 4 +- crates/edict-syntax/src/ast.rs | 14 +- crates/edict-syntax/src/canonical.rs | 44 ++-- crates/edict-syntax/src/compiler.rs | 234 +++++++++--------- crates/edict-syntax/src/core_ir.rs | 6 +- crates/edict-syntax/src/highlight.rs | 2 +- crates/edict-syntax/src/lib.rs | 12 +- crates/edict-syntax/src/parser.rs | 34 +-- crates/edict-syntax/src/semantic.rs | 68 ++--- crates/edict-syntax/src/target_ir.rs | 118 ++++----- crates/edict-syntax/tests/authority_facts.rs | 12 +- .../tests/authority_facts_cbor.rs | 12 +- .../edict-syntax/tests/canonical_encoding.rs | 34 +-- crates/edict-syntax/tests/common/mod.rs | 16 +- crates/edict-syntax/tests/compiler_spine.rs | 74 +++--- crates/edict-syntax/tests/contract_bundle.rs | 10 +- .../tests/core_golden_fixtures.rs | 14 +- .../edict-syntax/tests/parse_control_flow.rs | 24 +- crates/edict-syntax/tests/parse_greeting.rs | 36 +-- crates/edict-syntax/tests/parse_hello.rs | 28 +-- crates/edict-syntax/tests/parse_keywords.rs | 2 +- crates/edict-syntax/tests/parse_loops.rs | 4 +- crates/edict-syntax/tests/parse_negative.rs | 2 +- .../tests/parse_review_regressions.rs | 24 +- crates/edict-syntax/tests/parse_variants.rs | 26 +- .../edict-syntax/tests/provider_invocation.rs | 2 +- .../edict-syntax/tests/provider_lowering.rs | 4 +- .../edict-syntax/tests/semantic_validation.rs | 46 ++-- crates/edict-syntax/tests/target_ir.rs | 144 +++++------ ...untime-neutral-edict-sha-lock-assurance.md | 4 +- docs/REQUIREMENTS.md | 4 +- docs/RETRO_phase1-parser.md | 2 +- docs/SPEC_edict-language-v1.md | 138 +++++------ docs/SPEC_edict-lawpack-abi-v1.md | 4 +- docs/SPEC_edict-target-profile-abi-v1.md | 2 +- docs/TECHNICAL_EXPLANATION.md | 48 ++-- docs/abi/edict-common.cddl | 4 +- docs/abi/edict-core.cddl | 6 +- docs/abi/edict-target-ir.cddl | 4 +- docs/audit/2026-06-28_code-quality.md | 2 +- .../audit/2026-06-28_documentation-quality.md | 6 +- docs/design/authority-fact-governance.md | 2 +- docs/design/canonical-target-ir-v0.11.md | 6 +- docs/design/obstruction-strands-v0.md | 6 +- docs/releases/v0.11.0-alpha.1.md | 2 +- docs/releases/v0.2.0-alpha.1.md | 2 +- docs/releases/v0.8.0-alpha.1.md | 2 +- docs/releases/v0.9.0-alpha.1.md | 6 +- docs/topics/compiler-spine/README.md | 4 +- docs/topics/compiler-spine/test-plan.md | 4 +- docs/topics/core-ir/README.md | 6 +- docs/topics/core-ir/canonical-encoding.md | 2 +- docs/topics/core-ir/test-plan.md | 12 +- docs/topics/developer-tooling/test-plan.md | 4 +- docs/topics/obstruction-strands/README.md | 2 +- docs/topics/semantic-validation/README.md | 18 +- docs/topics/semantic-validation/test-plan.md | 18 +- docs/topics/syntax/README.md | 2 +- docs/topics/syntax/test-plan.md | 4 +- docs/topics/target-ir/test-plan.md | 4 +- editors/vscode/syntaxes/edict.tmLanguage.json | 2 +- .../assembly/bounded-hello.bundle-digests.txt | 4 +- fixtures/cli/01-source-ok/request.jsonl | 2 +- .../cli/05-path-input-ok/inputs/hello.edict | 2 +- .../06-directory-expansion-ok/inputs/a.edict | 2 +- .../inputs/nested/b.edict | 2 +- .../cli/07-path-list-ok/inputs/first.edict | 2 +- .../cli/07-path-list-ok/inputs/second.edict | 2 +- .../cli/08-glob-expansion-ok/inputs/x.edict | 2 +- .../cli/08-glob-expansion-ok/inputs/y.edict | 2 +- fixtures/cli/09-shape-source-ok/request.jsonl | 2 +- .../core/canonical/bounded-hello.core.cbor | Bin 1478 -> 1478 bytes .../core/canonical/bounded-hello.core.sha256 | 2 +- ...imal.fields => core-action-minimal.fields} | 2 +- .../accepted/core-module-minimal.fields | 2 +- ... core-action-unknown-verified-mode.fields} | 2 +- ...lds => core-module-missing-actions.fields} | 0 fixtures/lang/bounds/bounded-hello.edict | 2 +- fixtures/lang/effects/conditional-blob.edict | 2 +- fixtures/lang/effects/read-greeting.edict | 2 +- fixtures/lang/tooling/highlight-smoke.edict | 2 +- fixtures/lang/types/color-match.edict | 2 +- .../v0/stale-basis/README.md | 2 +- .../v0/stale-basis/source.edict | 2 +- .../v1/edict-provider-contracts.cddl | 14 +- fixtures/provider-contracts/v1/manifest.json | 4 +- fixtures/providers/components/inventory.json | 4 +- .../components/lowerer.component.wasm | Bin 28007 -> 28007 bytes .../canonical/echo-effectful.target-ir.cbor | Bin 848 -> 848 bytes .../canonical/echo-effectful.target-ir.sha256 | 2 +- .../canonical/gitwarp-append.target-ir.cbor | Bin 1108 -> 1108 bytes .../canonical/gitwarp-append.target-ir.sha256 | 2 +- grammars/textmate/edict.tmLanguage.json | 2 +- grammars/tree-sitter-edict/grammar.js | 10 +- grammars/tree-sitter-edict/src/grammar.json | 10 +- .../tree-sitter-edict/src/node-types.json | 10 +- .../test/corpus/current-subset.txt | 62 ++--- xtask/src/goldens.rs | 4 +- xtask/src/tests.rs | 10 +- 108 files changed, 905 insertions(+), 905 deletions(-) rename fixtures/core/schema/accepted/{core-intent-minimal.fields => core-action-minimal.fields} (91%) rename fixtures/core/schema/rejected/{core-intent-unknown-verified-mode.fields => core-action-unknown-verified-mode.fields} (92%) rename fixtures/core/schema/rejected/{core-module-missing-intents.fields => core-module-missing-actions.fields} (100%) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 2fe856b..2f8bada 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -65,7 +65,7 @@ Module map: | `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. | +| `core_ir` | Runtime-neutral Core module, action, expression, budget, import, and obstruction 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. | diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e6a210..c57bc0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -356,14 +356,14 @@ versions still track specification maturity rather than a released product. targets and unsupported Core nodes reject with stable target-lowering failure kinds before any artifact is emitted. Target IR preserves effect result bindings, effect inputs, obstruction failure keys, obstruction arm values, and - intent result expressions. Target IR lowering facts can also be derived from + action result expressions. Target IR lowering facts can also be derived from selected native lowerability results, keeping target artifact paths tied to the lowerability report's target profile, operation profile, and selected native effect support. The lowerer rejects unsupported Core ABI versions, unsupported Core capability flags, undigested target-profile references, non-Echo target intrinsics, operation profiles unsupported by the - selected target facts, and intents with no target-owned steps before Target IR - emission. Target IR intents also preserve Core input constraints and Core + selected target facts, and actions with no target-owned steps before Target IR + emission. Target IR actions also preserve Core input constraints and Core evaluation budgets so supported artifacts do not drop preconditions or evaluation limits. Added the second supported target slice: `gitwarp.ref_crdt@1` can lower the same supported effectful Core shape into a @@ -574,8 +574,8 @@ versions still track specification maturity rather than a released product. - **Phase 2 — source-AST semantic validation (`edict-syntax`).** Added `validate_module`, stable `SemanticErrorKind` categories, deterministic tests, and a semantic-validation topic shelf for checks that do not require Core IR: - bounded runtime `String`/`Bytes`, intent operation-mode/budget/basis - requiredness, duplicate singleton intent clauses, module namespace collision + bounded runtime `String`/`Bytes`, action operation-mode/budget/basis + requiredness, duplicate singleton action clauses, module namespace collision checks, and scoped binder shadowing checks. - **Topic shelf pilot (`docs/topics/syntax/`).** Added the first current-truth topic chapter and verification matrix for the Phase 1 syntax front end, @@ -587,7 +587,7 @@ versions still track specification maturity rather than a released product. recursive-descent parser for the `edict.implementation/minimal-v1` surface. Now parses: package/imports (shape/lawpack/target/core, optional `digest`); `type` records and refined scalars; `enum` declarations; `variant` types with - optional payloads; `intent`s with their clauses; `let`/`return`; calls and + optional payloads; `action`s with their clauses; `let`/`return`; calls and type-calls (`echo.ref(...)`); effect statements with single- and map-form `else` obstruction handlers; `require`/`guarantee`/`assert`; the full `if` family (ternary `if … then … else …`, effectful branch-yield in @@ -681,7 +681,7 @@ versions still track specification maturity rather than a released product. - Self-review nits: dropped an unused WIT import; de-duplicated the `basis`-requiredness wording; locked `edict-common.cddl` in `spec.lock.json`; corrected the `edict-common.cddl` header. -- Second-order ripples from the above (Codex + CodeRabbit round): an intent may +- Second-order ripples from the above (Codex + CodeRabbit round): an action may carry **both** `profile` and `implements` (was wrongly "exactly one"); pure expressions may call **pure** target/lawpack constructors (only effect intrinsics forbidden); integer-literal propagation reaches binary operands; @@ -708,7 +708,7 @@ versions still track specification maturity rather than a released product. non-fixtures; `effectFailures` coordinates must be unique per effect. - **jedit appendix brought to clause-conformance** (it is the intended first real-world use case): added correct `basis` clauses to all 12 rope-package and - structural-history intents; the Product Text Buffer Optic sketch remains the + structural-history actions; the Product Text Buffer Optic sketch remains the one deliberate non-v1 example (uses rejected `invoke`/`use capability` to show design pressure). Appendix note rewritten accordingly. diff --git a/EDICT.md b/EDICT.md index 53c3b82..0a2ce62 100644 --- a/EDICT.md +++ b/EDICT.md @@ -29,9 +29,9 @@ Edict is a secure, restricted, deterministic programming language (a DSL) built around a single organizing idea: **what your code is allowed to do should be verified by the compiler, not left to trust** [claim:C01, confidence:1.0]. In an ordinary runtime, a function inherits the full authority of its host process — it can read any table, call any endpoint, write any file, regardless of what its name promises. Edict names this gap **FIDLAR** — *"Footprints Ignored; Developer Lies About Risk"* — the mismatch between a function's *declared* authority (its name and signature) and its *actual* authority (whatever the process can reach) [claim:C02, confidence:1.0]. -Edict's answer is to replace the function with an **intent**: an operation that must declare exactly what it reads, what it writes, what it costs, how it can fail, and which digest-locked "law" governs it — and the compiler proves all of it before anything is allowed to run [claim:C03, confidence:1.0]. The output of compilation is not a binary but a **contract bundle**: a cryptographically sealed, participant-neutral artifact whose identity is a SHA-256 hash over every layer of the compilation [claim:C04, confidence:0.98]. +Edict's answer is to replace the function with an **action**: an operation that must declare exactly what it reads, what it writes, what it costs, how it can fail, and which digest-locked "law" governs it — and the compiler proves all of it before anything is allowed to run [claim:C03, confidence:1.0]. The output of compilation is not a binary but a **contract bundle**: a cryptographically sealed, participant-neutral artifact whose identity is a SHA-256 hash over every layer of the compilation [claim:C04, confidence:0.98]. -The project matters most in the age of autonomous AI agents. When an agent runs a conventional function, its real capability is "anything the process was granted." Edict's formal execution lane for agents is `continuum.lane.lawful-autonomous/v1`, cheekily codenamed **YOLO — "You Only Lawfully Operate"**: an agent may execute autonomously *only after* its intent compiles, its bundle is sealed and hash-locked, an assurance engine certifies it, and a participant runtime's policy admits it [claim:C05, confidence:0.97]. +The project matters most in the age of autonomous AI agents. When an agent runs a conventional function, its real capability is "anything the process was granted." Edict's formal execution lane for agents is `continuum.lane.lawful-autonomous/v1`, cheekily codenamed **YOLO — "You Only Lawfully Operate"**: an agent may execute autonomously *only after* its action compiles, its bundle is sealed and hash-locked, an assurance engine certifies it, and a participant runtime's policy admits it [claim:C05, confidence:0.97]. At the original evidence baseline, Edict was at `v0.11.0-alpha.1`. The language front end, semantic validation, a compiler spine to Core IR, canonical byte encodings and digests, target-profile validation, lowerability checking, two Target IR backends, contract-bundle assembly, and Gate C admission-boundary checks all existed as tested Rust code, while runtime execution, participant policy, and any executable WASM sandbox were explicit non-claims. The current tree adds a capability-denied Wasmtime host specifically for external provider lowerer and verifier components; target-runtime execution and participant policy remain unimplemented [claim:C06, confidence:1.0]. At that baseline, the repository was remarkably young and fast-moving: first commit 2026-06-17, 458 commits and eleven published alpha releases in roughly three weeks, with 359 test functions and a zero-tolerance lint posture (`unsafe_code = "forbid"`, clippy `pedantic` at `deny`) [claim:C07, confidence:0.95]. @@ -54,7 +54,7 @@ type HelloReading = { message: String, }; -intent sayHello(input: HelloInput) +action sayHello(input: HelloInput) returns HelloReading profile hello.readOnly basis none @@ -71,9 +71,9 @@ Things worth noticing even in a hello world: | Line | What it shows | | --- | --- | | `use lawpack ... digest "sha256:..."` | Every imported authority is pinned to an exact SHA-256 digest. Dependencies cannot change silently without changing the program's identity [claim:C09, confidence:1.0]. | -| `String` | Scalars carry compile-time bounds. Unbounded `String`/`Bytes` are rejected — the compiler can compute the intent's maximum memory footprint before admission [claim:C10, confidence:0.95]. | -| `profile hello.readOnly` | The intent claims an operation profile; the compiler checks the body against the profile's allowed write classes and fails compilation on a mismatch [claim:C11, confidence:0.95]. | -| `basis none` | Every intent must declare its causal anchor — the point of history it reads from — even when that anchor is explicitly "none" [claim:C12, confidence:0.95]. | +| `String` | Scalars carry compile-time bounds. Unbounded `String`/`Bytes` are rejected — the compiler can compute the action's maximum memory footprint before admission [claim:C10, confidence:0.95]. | +| `profile hello.readOnly` | The action claims an operation profile; the compiler checks the body against the profile's allowed write classes and fails compilation on a mismatch [claim:C11, confidence:0.95]. | +| `basis none` | Every action must declare its causal anchor — the point of history it reads from — even when that anchor is explicitly "none" [claim:C12, confidence:0.95]. | | `budget <= hello.tinyBudget` | Cost is a declared, checkable ceiling, not an afterthought [claim:C13, confidence:0.95]. | | `where input.name != ""` | Pure input refinements are part of the type-checked contract and are hash-significant in Core IR [claim:C14, confidence:0.9]. | | String concatenation | Even `"hello, " + input.name` is bounds-checked: the result's max length is the *sum* of the operands' maxima [claim:C15, confidence:0.9]. | @@ -100,16 +100,16 @@ Edict describes itself as "a restricted deterministic source language for lawful | Phrase | Meaning | | --- | --- | -| *restricted* | Not Turing-complete by design: no unbounded loops, no recursion, no dynamic dispatch surprises. Every intent's cost can be computed and capped statically [claim:C18, confidence:0.95]. | +| *restricted* | Not Turing-complete by design: no unbounded loops, no recursion, no dynamic dispatch surprises. Every action's cost can be computed and capped statically [claim:C18, confidence:0.95]. | | *deterministic* | No wall clock, no randomness, no ambient environment, no I/O, no locale, no nondeterministic iteration. Byte-identical inputs produce byte-identical outputs [claim:C19, confidence:0.9]. | | *lawful* | Every effect is imported from a digest-locked **lawpack** (domain rules) or **target profile** (runtime capabilities). There are no ambient effects [claim:C20, confidence:0.9]. | -| *optics over witnessed causal history* | An intent is modeled as a **WARP optic** — in the theory, a five-part control structure `Ψ = (Ω observer plan, χ aperture, ρ claim-lowering surface, Π admissibility law, Λ retention contract)` that performs one act: *slice* a bounded window of causal history, *lower* the claims inside it under a law, *witness* the result, and *retain* a compact hash-sealed boundary shell (a *hologram*). An Edict intent is a source-language encoding of exactly that tuple: aperture/footprint (χ over the slice), profile and lawpack law (Π), budget-bounded observer (Ω), and the contract bundle as the retained shell (Λ's output) [claim:T01, confidence:0.85]. The vocabulary comes from AION Paper VII and the Observer Geometry series [claim:C21, confidence:0.95]. | +| *optics over witnessed causal history* | An action is modeled as a **WARP optic** — in the theory, a five-part control structure `Ψ = (Ω observer plan, χ aperture, ρ claim-lowering surface, Π admissibility law, Λ retention contract)` that performs one act: *slice* a bounded window of causal history, *lower* the claims inside it under a law, *witness* the result, and *retain* a compact hash-sealed boundary shell (a *hologram*). An Edict action is a source-language encoding of exactly that tuple: aperture/footprint (χ over the slice), profile and lawpack law (Π), budget-bounded observer (Ω), and the contract bundle as the retained shell (Λ's output) [claim:T01, confidence:0.85]. The vocabulary comes from AION Paper VII and the Observer Geometry series [claim:C21, confidence:0.95]. | ### 3.2 The pipeline at a glance ```mermaid flowchart LR - A["1. Write Intent
(declare inputs, outputs, budgets)"] -->|Compile and prove| B["2. Cryptographic Seal
(Core IR + hash-locked bundle)"] + A["1. Write Action
(declare inputs, outputs, budgets)"] -->|Compile and prove| B["2. Cryptographic Seal
(Core IR + hash-locked bundle)"] B -->|Submit bundle| C["3. Participant Admission
(inspect the nutrition label)"] C -->|Execute safely| D["4. Sandboxed Runtime
(enforced limits, auto-rollback)"] ``` @@ -117,7 +117,7 @@ flowchart LR
Caption: Edict in ten seconds - 1. An author (human or AI agent) writes an *intent* declaring its inputs, outputs, effects, budget, and failure vocabulary. + 1. An author (human or AI agent) writes an *action* declaring its inputs, outputs, effects, budget, and failure vocabulary. 2. The compiler proves the declarations and produces a sealed, hash-identified contract bundle. 3. A participant runtime inspects the bundle's machine-generated "nutrition label" and admits or rejects it under its own policy. 4. If admitted, the runtime executes it inside a sandbox with enforced budgets and atomic rollback on obstruction. @@ -169,7 +169,7 @@ The current Rust workspace has five members with a strict dependency direction: ### 4.1 The language surface -The v1 grammar admits one `package` declaration, then imports, then declarations (`type`, `enum`, `const`, `fn`, `intent` — with `const`/`fn` deferred in the current implementation), where an intent carries at-most-once clauses `profile`, `implements`, `basis`, `where`, `footprint`, `budget` [claim:C25, confidence:0.85]. The statement set is `let`, `assert`, `require`, `guarantee`, `record`, `if`, `for`, effect statements, and `return`; locals are immutable and there is no assignment statement [claim:C26, confidence:0.85]. +The v1 grammar admits one `package` declaration, then imports, then declarations (`type`, `enum`, `const`, `fn`, `action` — with `const`/`fn` deferred in the current implementation), where an action carries at-most-once clauses `profile`, `implements`, `basis`, `where`, `footprint`, `budget` [claim:C25, confidence:0.85]. The statement set is `let`, `assert`, `require`, `guarantee`, `record`, `if`, `for`, effect statements, and `return`; locals are immutable and there is no assignment statement [claim:C26, confidence:0.85]. The type system is bounded everywhere: scalars are `Bool`, `I32`/`I64`/`U32`/`U64`, `String`, `Bytes`, `Digest`, `Unit` (no floats); `String` and `Bytes` are refined scalar forms; `List` and `Map` require finite maxima; `Option` provides optionality; integer literal width and signedness are hash-significant [claim:C27, confidence:0.85]. `len(String)` counts Unicode scalars and `len(Bytes)` counts bytes, with canonicalization applied before measuring [claim:C28, confidence:0.8]. @@ -220,11 +220,11 @@ Compilation is deliberately staged, and tests prove the stages do not collapse i parse -> validate_surface -> resolve -> type_check -> lower_core -> canonicalize ``` -Surface validation (`validate_surface`) is context-free over the AST and enforces seven stable rules: `UnboundedScalar`, `MissingOperationMode`, `MissingBudget`, `MissingBasis`, `DuplicateIntentClause`, `DuplicateName`, `ShadowedName` [claim:C41, confidence:0.95]. The compiler proper (`resolve_module → type_check → lower_core`, or `compile_to_core` in one call) consumes a `CompilerContext` of *authority facts* — operation profiles, profile write-class allowances, effect write classes, and budgets — and refuses to invent any fact it was not given: a missing fact is a `MissingContextFact` error, and an effect whose write class is outside the profile's allowance is a `ProfileEffectMismatch` [claim:C42, confidence:0.95]. Those facts can be loaded from digest-bound JSON authority-facts files whose sources must be `lawpack` or `targetProfile` identities locked to a SHA-256 digest, with stable failure kinds for malformed, non-digest-locked, or conflicting facts [claim:C43, confidence:0.95]. +Surface validation (`validate_surface`) is context-free over the AST and enforces seven stable rules: `UnboundedScalar`, `MissingOperationMode`, `MissingBudget`, `MissingBasis`, `DuplicateActionClause`, `DuplicateName`, `ShadowedName` [claim:C41, confidence:0.95]. The compiler proper (`resolve_module → type_check → lower_core`, or `compile_to_core` in one call) consumes a `CompilerContext` of *authority facts* — operation profiles, profile write-class allowances, effect write classes, and budgets — and refuses to invent any fact it was not given: a missing fact is a `MissingContextFact` error, and an effect whose write class is outside the profile's allowance is a `ProfileEffectMismatch` [claim:C42, confidence:0.95]. Those facts can be loaded from digest-bound JSON authority-facts files whose sources must be `lawpack` or `targetProfile` identities locked to a SHA-256 digest, with stable failure kinds for malformed, non-digest-locked, or conflicting facts [claim:C43, confidence:0.95]. ### 4.5 Core IR and canonical bytes -Core IR (`edict.core/v1`) is the runtime-neutral heart: a `CoreModule` holds imports, types, and intents in `BTreeMap`s (deterministic order), and each `CoreIntent` carries input/output types, the required operation profile, typed input constraints, a three-part evaluation budget (`maxSteps`, `maxAllocatedBytes`, `maxOutputBytes`), and a body of `Let`/`Require`/`Effect` nodes [claim:C44, confidence:0.95]. Local names are **alpha-normalized**: the compiler synthesizes position-derived ids (`arg.0`/`$arg0`, `local.{n}`/`$local{n}`, `obstruction.{n}`) so your variable names never influence the hash [claim:C45, confidence:0.95]. Source spans are deliberately stripped: if they were preserved, adding a comment would shift spans, change the Core bytes, and mutate the contract's cryptographic identity — the "formatting identity hazard" [claim:C46, confidence:0.95]. +Core IR (`edict.core/v1`) is the runtime-neutral heart: a `CoreModule` holds imports, types, and actions in `BTreeMap`s (deterministic order), and each `CoreAction` carries input/output types, the required operation profile, typed input constraints, a three-part evaluation budget (`maxSteps`, `maxAllocatedBytes`, `maxOutputBytes`), and a body of `Let`/`Require`/`Effect` nodes [claim:C44, confidence:0.95]. Local names are **alpha-normalized**: the compiler synthesizes position-derived ids (`arg.0`/`$arg0`, `local.{n}`/`$local{n}`, `obstruction.{n}`) so your variable names never influence the hash [claim:C45, confidence:0.95]. Source spans are deliberately stripped: if they were preserved, adding a comment would shift spans, change the Core bytes, and mutate the contract's cryptographic identity — the "formatting identity hazard" [claim:C46, confidence:0.95]. Canonical encoding is a strict CBOR subset (`edict.canonical-cbor/v1`): definite lengths only, minimal integer encodings, map keys sorted by their *encoded bytes*, duplicate keys rejected — and, unusually, canonicality is enforced on *decode* as well: the decoder re-encodes what it read and requires an exact byte match, so a non-canonical artifact cannot even be read as valid [claim:C47, confidence:0.95]. Canonical Core, Target IR, and bundle digests are domain-separated by hashing the canonical encoding of a frame `["edict.digest/v1", "", ]`, with domains like `edict.core.module/v1` and `edict.target-ir.artifact/v1`, and digests travel as typed `[algorithm, bytes]` pairs — hex strings like `sha256:...` are review-display forms, never hash inputs [claim:C48, confidence:0.95]. Admission-request digests intentionally use a separate labeled, length-prefixed framing rather than canonical CBOR [claim:C61, confidence:0.9]. @@ -232,7 +232,7 @@ Canonical encoding is a strict CBOR subset (`edict.canonical-cbor/v1`): definite Edict Core contains no storage nouns, so executing it requires **lowering** through a declared, digest-locked **target profile**. A v1 `TargetProfileManifest` declares its intrinsics, operation profiles, footprint and cost algebras, obstruction taxonomy, verifier, lowerer, sandbox, fuel model, and a fixed v1 application doctrine: `atomic` application, `application-snapshot` read consistency, `precommit-atomic` guard evaluation, and `no-visible-effects` rollback [claim:C49, confidence:0.95]. -Before lowering, a typed **lowerability check** classifies each intent as `Native`, `Adapted`, or `Unsupported`. V1 is strict: an effect is either natively supported by a target intrinsic or discharged by *exactly one direct* lawpack adapter — adapter chains and composite discharge are rejected outright as future v2 work, ambiguity is an error, and unsupported means a loud compiler error rather than a silent approximation [claim:C50, confidence:0.95]. +Before lowering, a typed **lowerability check** classifies each action as `Native`, `Adapted`, or `Unsupported`. V1 is strict: an effect is either natively supported by a target intrinsic or discharged by *exactly one direct* lawpack adapter — adapter chains and composite discharge are rejected outright as future v2 work, ambiguity is an error, and unsupported means a loud compiler error rather than a silent approximation [claim:C50, confidence:0.95]. Two Target IR backends exist today [claim:C51, confidence:0.95]: @@ -293,16 +293,16 @@ The repository's process is unusually explicit. Release gates require that "No c ### TLDR -**Edict is a small, deliberately weak programming language whose compiler produces cryptographically sealed "operation contracts" instead of executables.** You write an *intent* that says "here is exactly what I read, write, spend, and how I can fail, under these hash-pinned rules." The compiler proves every claim, strips away anything cosmetic, and hashes what remains into an identity that changes if *anything meaningful* changes. A runtime later reads that contract like a nutrition label and decides — under its own policy — whether to run it. Nothing hidden can influence execution: no clock, no randomness, no host state, no silent dependency upgrades. At the original `v0.11.0-alpha.1` baseline, the language, compiler-to-Core, canonical hashing, two lowering backends, bundle assembly, and admission-boundary *checks* were real, tested Rust; the current tree also has a constrained provider-component host, while the target runtime and policy engines remain the next acts [claim:C06, confidence:1.0]. +**Edict is a small, deliberately weak programming language whose compiler produces cryptographically sealed "operation contracts" instead of executables.** You write an *action* that says "here is exactly what I read, write, spend, and how I can fail, under these hash-pinned rules." The compiler proves every claim, strips away anything cosmetic, and hashes what remains into an identity that changes if *anything meaningful* changes. A runtime later reads that contract like a nutrition label and decides — under its own policy — whether to run it. Nothing hidden can influence execution: no clock, no randomness, no host state, no silent dependency upgrades. At the original `v0.11.0-alpha.1` baseline, the language, compiler-to-Core, canonical hashing, two lowering backends, bundle assembly, and admission-boundary *checks* were real, tested Rust; the current tree also has a constrained provider-component host, while the target runtime and policy engines remain the next acts [claim:C06, confidence:1.0]. ### Glossary | Terminology | Definition | Remarks | | --- | --- | --- | | **FIDLAR** | "Footprints Ignored; Developer Lies About Risk" — the gap between what a function's name promises and what its process can actually do | The founding villain of the project; also an invariant (I-007 "FIDLAR Rejection") [claim:C02, confidence:1.0] | -| **Intent** | Edict's unit of execution: an operation declaring bounded inputs/outputs, basis, budget, footprint, profile, and failure mapping | Replaces "function"; modeled as an *optic* [claim:C03, confidence:1.0] | -| **Aperture** | The bounded set of state an intent may read or write; in Observer Geometry terms, the *projection* — what history detail is visible to the observer | Reaching outside it is a compile-time rejection [claim:C76, confidence:0.9]. Theory keeps aperture (what is visible) orthogonal to basis (how it is expressed / where it is judged) [claim:T02, confidence:0.8] | -| **Basis** | The causal-history anchor an intent reads from — theory calls this the *bounded basis* an admission is "judged against under explicit law," resolved to a coordinate in causal history | Must be declared, even as `basis none`; evaluated in the pure pre-body environment [claim:C12, confidence:0.9]. In the Continuum API this is the Coordinate, with determinism laws applying to the *resolved* coordinate receipt, not the request [claim:T03, confidence:0.8] | +| **Action** | Edict's unit of execution: an operation declaring bounded inputs/outputs, basis, budget, footprint, profile, and failure mapping | Replaces "function"; modeled as an *optic* [claim:C03, confidence:1.0] | +| **Aperture** | The bounded set of state an action may read or write; in Observer Geometry terms, the *projection* — what history detail is visible to the observer | Reaching outside it is a compile-time rejection [claim:C76, confidence:0.9]. Theory keeps aperture (what is visible) orthogonal to basis (how it is expressed / where it is judged) [claim:T02, confidence:0.8] | +| **Basis** | The causal-history anchor an action reads from — theory calls this the *bounded basis* an admission is "judged against under explicit law," resolved to a coordinate in causal history | Must be declared, even as `basis none`; evaluated in the pure pre-body environment [claim:C12, confidence:0.9]. In the Continuum API this is the Coordinate, with determinism laws applying to the *resolved* coordinate receipt, not the request [claim:T03, confidence:0.8] | | **Footprint** | The read/write/delete contact set of an operation — descended from the delete/use sets of double-pushout graph rewriting, where footprint disjointness is the formal test for safe concurrency | "Delete–use interference is the only sin"; footprint honesty (computed ≤ declared) is Edict invariant I-006 [claim:T04, confidence:0.85] | | **WARP graph** | The substrate state object: a recursively nested graph where every node and edge can carry another WARP graph, bottoming out at atoms | "Graphs all the way down — but with a floor." Paper VII then demotes it: the graph is a coordinate chart over history, not the territory [claim:T05, confidence:0.85] | | **Hologram / boundary shell** | A witness-bearing boundary artifact sufficient to verify, replay, transport, or selectively reveal a slice of history without exposing its interior | The contract bundle is Edict's hologram; its direct theoretical ancestor is the content-addressed Boundary Transition Record (BTR) [claim:T06, confidence:0.85] | @@ -336,7 +336,7 @@ mindmap Ambient authority Agent blast radius Language - Intents as optics + Actions as optics Bounded types A-normal form effects Typed obstructions @@ -370,7 +370,7 @@ mindmap
Caption: Edict concept map - The project decomposes into six conceptual clusters. **Problem** motivates everything: functions lie about their footprint. **Language** is what authors touch: intents with bounded types, sequenced effects, and typed failure. **Compilation** turns source into a runtime-neutral Core IR using only externally supplied, digest-bound facts. **Identity** makes every artifact hash-stable and mutation-sensitive. **Targets** map neutral Core onto real runtimes without contaminating it. **Trust** is the outer loop: sealed bundles, machine-readable labels, participant admission, and adversarial assurance. + The project decomposes into six conceptual clusters. **Problem** motivates everything: functions lie about their footprint. **Language** is what authors touch: actions with bounded types, sequenced effects, and typed failure. **Compilation** turns source into a runtime-neutral Core IR using only externally supplied, digest-bound facts. **Identity** makes every artifact hash-stable and mutation-sensitive. **Targets** map neutral Core onto real runtimes without contaminating it. **Trust** is the outer loop: sealed bundles, machine-readable labels, participant admission, and adversarial assurance.
@@ -392,8 +392,8 @@ flowchart TD F -->|can| T3["users.deleteAll()"] F -->|can| T4["network / filesystem"] end - subgraph EdictWorld["Edict intent"] - I["readGreeting intent"] -->|declared and proven| A["exactly: read Greeting by id"] + subgraph EdictWorld["Edict action"] + I["readGreeting action"] -->|declared and proven| A["exactly: read Greeting by id"] I -.->|compile error| X["anything else"] end ``` @@ -402,14 +402,14 @@ flowchart TD Caption: Ambient authority vs declared-and-proven authority 1. The conventional function's name says "read a message thread," but the process grants it reach into every table, the network, and the filesystem — the FIDLAR gap. - 2. The Edict intent must declare its exact read (a `Greeting` node by id) and the compiler proves the body stays within that declaration. + 2. The Edict action must declare its exact read (a `Greeting` node by id) and the compiler proves the body stays within that declaration. 3. Any attempt to reach further is not caught at runtime — it fails to *compile*, so the artifact never exists [claim:C03, confidence:1.0].
-#### 5.2 Level 1 — The language: writing an intent +#### 5.2 Level 1 — The language: writing an action -An intent looks like a function but reads like a contract. Here is the effectful fixture that actually compiles in the repo today [claim:C82, confidence:1.0]: +An action looks like a function but reads like a contract. Here is the effectful fixture that actually compiles in the repo today [claim:C82, confidence:1.0]: ```graphql package examples.greeting@1; @@ -418,7 +418,7 @@ use shape "schemas/greeting.graphql" as shape; use lawpack greeting.optics@1 digest "sha256:00000000...00000000" as greetingLaw; use target echo.dpo@1 digest "sha256:11111111...11111111" as echo; -intent readGreeting(input: shape.ReadGreetingInput) +action readGreeting(input: shape.ReadGreetingInput) returns shape.GreetingReading profile echo.readOnly basis input.greetingId @@ -441,7 +441,7 @@ intent readGreeting(input: shape.ReadGreetingInput) | --- | --- | --- | | `use shape "..."` | GraphQL type definitions — Edict compiles *against* your schema rather than owning it | Path-referenced | | `use lawpack ...` | Domain law: pure helpers, effect signatures, obstruction types, budgets | Yes, mandatory in bundles | -| `use target ...` | The runtime profile this intent lowers through | Yes, mandatory in bundles | +| `use target ...` | The runtime profile this action lowers through | Yes, mandatory in bundles | A fourth kind, `use capability`, exists in product sketches but is *rejected* by the v1 parser as unsupported syntax — a nice example of the project encoding its own deferrals as errors rather than silence [claim:C83, confidence:0.95]. @@ -520,7 +520,7 @@ classDiagram coordinate imports: Vec~CoreImport~ types: BTreeMap - intents: BTreeMap + actions: BTreeMap required_core_capabilities } class CoreImport { @@ -533,7 +533,7 @@ classDiagram digest: Option~sha256~ is_digest_locked() } - class CoreIntent { + class CoreAction { input / output required_operation_profile input_constraints @@ -571,9 +571,9 @@ classDiagram CoreModule --> CoreImport CoreImport --> ResourceRef - CoreModule --> CoreIntent - CoreIntent --> CoreBudget - CoreIntent --> CoreBlock + CoreModule --> CoreAction + CoreAction --> CoreBudget + CoreAction --> CoreBlock CoreBlock --> CoreNode CoreNode --> EffectNode EffectNode --> CoreObstructionArm @@ -587,7 +587,7 @@ classDiagram | --- | --- | | `CoreModule` | The hashable unit; `BTreeMap` collections give deterministic iteration order for free [claim:C44, confidence:0.95] | | `ResourceRef` | Every external reference carries a coordinate plus an optional digest; `is_digest_locked()` demands a valid `sha256:` review digest | - | `CoreIntent` | Carries the contract (profile, constraints, budget) alongside the body | + | `CoreAction` | Carries the contract (profile, constraints, budget) alongside the body | | `CoreBudget` | The portable three-axis budget: steps, peak allocated bytes, output bytes [claim:C86, confidence:0.9] | | `CoreNode::Effect` | The semantic effect node: a binding, an effect coordinate, one input, and a `BTreeMap` obstruction map | | `LocalRef` | Alpha-normalized: ids are position-derived (`arg.0`, `local.3`, `obstruction.1`), so renaming a source variable cannot change the hash [claim:C45, confidence:0.95] | @@ -628,7 +628,7 @@ Core IR deliberately contains no storage nouns — "Core contains laws of physic ```mermaid flowchart TD - E["Semantic effect required by intent"] --> N{"Native intrinsic
in target profile?"} + E["Semantic effect required by action"] --> N{"Native intrinsic
in target profile?"} N -->|"exactly one, guards satisfied"| NAT["Native"] N -->|"multiple match"| AMB1["Error: AmbiguousNativeSupport"] N -->|none| A{"Direct lawpack adapter
for this target?"} @@ -648,7 +648,7 @@ flowchart TD -The two shipped backends make the neutrality concrete: the same Core module lowers to `echo.span-ir/v1` for the Echo runtime and to `gitwarp.commit-reducer-ir/v1` for git-warp — and the git-warp profile honestly declares it does not support `require` guards, so intents using them refuse to lower rather than degrade [claim:C51, confidence:0.95]. +The two shipped backends make the neutrality concrete: the same Core module lowers to `echo.span-ir/v1` for the Echo runtime and to `gitwarp.commit-reducer-ir/v1` for git-warp — and the git-warp profile honestly declares it does not support `require` guards, so actions using them refuse to lower rather than degrade [claim:C51, confidence:0.95]. #### 5.6 Level 5 — Trust: bundles, admission, and the YOLO lane @@ -808,13 +808,13 @@ flowchart LR P7["Paper VII
WARP optic 5-tuple,
commitment / folding / revelation"] P8["Paper VIII / Continuum
admission, four outcomes,
evidence posture"] OG1["OG-I
projection, basis,
degeneracy, budgets"] - OG2["OG-II
suffix transport,
intent-loss floor"] + OG2["OG-II
suffix transport,
action-loss floor"] OG3["OG-III
support ledger,
witness debt, holonomy"] end subgraph Edict["Edict constructs"] FP["footprint clause +
write-class checks"] CB["contract bundle +
digest pinning"] - INT["intent = optic
(profile, budget, basis, law)"] + INT["action = optic
(profile, budget, basis, law)"] ADM["Gate C admission +
typed obstructions"] BAS["basis clause +
aperture + budget"] OBS["obstruction strands"] @@ -835,11 +835,11 @@ flowchart LR | Theory source | What it defines | The Edict construct it grounds | | --- | --- | --- | | Paper II (ticks) | Read/write (delete/use) footprints as the formal independence test for safe concurrency; tick confluence; deterministic scheduler; descriptive receipts | The `footprint` clause, write-class checking, and determinism-as-contract. FIDLAR's "Footprints Ignored" is a literal Paper II accusation, not a metaphor [claim:T04, confidence:0.85] | - | Paper III (holography) | The bulk/boundary split: full history reconstructible from initial state + tick patches; the prescriptive patch ("what happened") vs descriptive receipt ("why") split; content-addressed Boundary Transition Records | The contract bundle (Edict's BTR/hologram), the hash ladder, and the intent/witness envelope separation. The "anti-tautology" — holography holds only when patches pin every choice — is the formal reason Edict digest-locks lawpacks, targets, and bases [claim:T06, confidence:0.85] [claim:T11, confidence:0.8] | - | Paper VII (optics) | The WARP optic five-tuple; the one act at every scale; the commitment/folding/revelation plane split; "there is no graph" | The intent as optic; the revelation-vs-affect intent classes; runtime-neutral Core ("no storage nouns" because storage is a *reading*, not a substrate) [claim:T01, confidence:0.85] | + | Paper III (holography) | The bulk/boundary split: full history reconstructible from initial state + tick patches; the prescriptive patch ("what happened") vs descriptive receipt ("why") split; content-addressed Boundary Transition Records | The contract bundle (Edict's BTR/hologram), the hash ladder, and the action/witness envelope separation. The "anti-tautology" — holography holds only when patches pin every choice — is the formal reason Edict digest-locks lawpacks, targets, and bases [claim:T06, confidence:0.85] [claim:T11, confidence:0.8] | + | Paper VII (optics) | The WARP optic five-tuple; the one act at every scale; the commitment/folding/revelation plane split; "there is no graph" | The action as optic; the revelation-vs-affect action classes; runtime-neutral Core ("no storage nouns" because storage is a *reading*, not a substrate) [claim:T01, confidence:0.85] | | Paper VIII (Continuum) | Admission as "a runtime-owned, witnessed act judged against a bounded basis under explicit law"; four canonical outcomes `Derived / Plural / Conflict / Obstruction`; evidence posture lattice; obstruction witnesses as "causal teaching artifacts" for agents | Gate C, typed obstruction outcomes, the participant-neutral bundle, and the whole YOLO lane posture [claim:T09, confidence:0.85] [claim:T12, confidence:0.8] | | OG-I | Projection (aperture) vs basis (native expression) vs accumulation as orthogonal observer axes; degeneracy; budget elasticity | The `basis` and `budget` clauses; why aperture and basis are separate declarations [claim:T02, confidence:0.8] | - | OG-II | Suffix transport without rollback; the last-write-wins "intent-recovery insufficiency floor" — silent merges permanently erase intent | Obstruction strands: preserving a blocked attempt as first-class causal material is exactly how a system avoids the insufficiency floor [claim:T13, confidence:0.8] | + | OG-II | Suffix transport without rollback; the last-write-wins "action-recovery insufficiency floor" — silent merges permanently erase action | Obstruction strands: preserving a blocked attempt as first-class causal material is exactly how a system avoids the insufficiency floor [claim:T13, confidence:0.8] | | OG-III | The four-compartment support ledger (carried/declared-lost/blocked/refuting); witness holonomy (loop defect); witness debt; hallucination as structural overreport | The Aperture Ledger's support-carried/lost/blocked/refuting fields and witness-debt vocabulary in the Edict spec [claim:T07, confidence:0.85] [claim:T08, confidence:0.85] | @@ -850,7 +850,7 @@ Three consequences of the theory are worth spelling out, because they sharpen cl 2. **Determinism is conditional, and the conditions are exactly what Edict pins.** In Paper II, a state admits many candidate rewrites; determinism holds once the batch (scheduler policy) is fixed. In the Continuum API, determinism laws apply to the *resolved* coordinate, not the request. Edict's obsessive digest-locking of lawpacks, target profiles, bases, and compiler facts is the language-level enforcement of those conditions — the "anti-tautology" that a sealed artifact only means replay if its boundary is sufficient and stable [claim:T11, confidence:0.8] [claim:T03, confidence:0.8]. 3. **Obstruction is pedagogy, not just failure.** Continuum frames the machine-readable obstruction witness as a "causal teaching artifact" that an agent uses to refine its next attempt. That reframes Edict's typed obstructions and obstruction strands: they are the feedback channel of the lawful-autonomous loop, which is why they must be typed, structured, and preserved rather than collapsed into exceptions [claim:T12, confidence:0.8]. -There is also real Edict source circulating in the theory corpus itself — a `task.edit_document@1` intent with a `require jim.basisFresh(input.basis) else ... StaleBase` optimistic-concurrency guard and a typed `EditReceipt | EditObstruction` return union — confirming that the README's aspirational `createEntry` example reflects a syntax already being exercised in cross-project design work [claim:T14, confidence:0.8]. +There is also real Edict source circulating in the theory corpus itself — a `task.edit_document@1` action with a `require jim.basisFresh(input.basis) else ... StaleBase` optimistic-concurrency guard and a typed `EditReceipt | EditObstruction` return union — confirming that the README's aspirational `createEntry` example reflects a syntax already being exercised in cross-project design work [claim:T14, confidence:0.8]. --- @@ -869,7 +869,7 @@ Beyond the headline design, the codebase is full of small, sharp decisions worth | 7 | **Nesting is load-bearing in preimages** — tests assert `DigestList([A,B])` hashes differently from `[Digest(A), Digest(B)]` [claim:C56, confidence:0.95] | Digest preimages are typed structures, not concatenated bytes; classic length-extension-style ambiguities are structurally impossible | | 8 | **Coordinate changes move digests even with identical bytes** — a toolchain's *identity* is part of the release preimage [claim:C56, confidence:0.95] | "Same bytes, different claimed origin" is a detectable supply-chain event | | 9 | **Requirements must precede effects** — Target IR rejects any `require` placed after a step or reading a step output [claim:C52, confidence:0.9] | Guards are provably pre-effect; no interleaved check-then-act ambiguity survives lowering | -| 10 | **`basis none` must be said out loud** — every intent declares its causal anchor, even the empty one [claim:C12, confidence:0.9] | Absence of history-dependence is a claim, not a default | +| 10 | **`basis none` must be said out loud** — every action declares its causal anchor, even the empty one [claim:C12, confidence:0.9] | Absence of history-dependence is a claim, not a default | | 11 | **The repo audits itself in-tree** — dated code-quality, documentation-quality, and ship-readiness audits live in `docs/audit/` [claim:C99, confidence:0.9] | Ship-readiness is versioned evidence, like everything else here | | 12 | **Docs are executable** — topic-shelf READMEs are wired in as doctests, and `cargo xtask verify` checks golden bytes, digests, topic contracts, and link hygiene in one gate [claim:C100, confidence:0.9] | Documentation drift fails the build instead of accumulating | | 13 | **Spec aphorisms** — "A theorem the IR cannot represent is a wish"; "Core contains laws of physics, not furniture"; "A prose ABI is not yet an ABI" [claim:C88, confidence:0.85] | The specs read like they were written to be quoted — and each aphorism encodes a real invariant | @@ -935,7 +935,7 @@ Most file citations pin the original report baseline, git sha `56f82ec` (`56f82e | --- | --- | --- | --- | --- | | C01 | Edict is a secure DSL where the compiler, not trust, verifies what code may do | `README.md#3@56f82ec`, `README.md#5@56f82ec` | 1.0 | Read directly | | C02 | FIDLAR = "Footprints Ignored; Developer Lies About Risk"; also invariant I-007 | `README.md#62@56f82ec`, `docs/SPEC_edict-language-v1.md#986@56f82ec` | 1.0 | Spec never expands the acronym; README does | -| C03 | An intent declares reads, writes, cost, failure modes, and governing law; compiler verifies before admission | `README.md#107-118@56f82ec` | 1.0 | | +| C03 | An action declares reads, writes, cost, failure modes, and governing law; compiler verifies before admission | `README.md#107-118@56f82ec` | 1.0 | | | C04 | Compilation output is a contract bundle whose identity is a hash over every layer | `README.md#255-283@56f82ec`, `crates/edict-syntax/src/contract_bundle.rs#428-452@56f82ec` | 0.98 | | | C05 | YOLO = "You Only Lawfully Operate", codename for `continuum.lane.lawful-autonomous/v1`; six-step precondition list for autonomous execution | `README.md#353-381@56f82ec`, `docs/SPEC_edict-language-v1.md#135@56f82ec` | 0.97 | Spec lane coordinate verified at section level | | C06 | At the baseline, v0.11.0-alpha.1 implemented the front end through bundle assembly and had no executable WASM sandbox; the current tree adds a capability-denied Wasmtime host for provider components, while target-runtime execution and participant policy remain unimplemented | `README.md#16-25@56f82ec`, `README.md#624-643@56f82ec`, `ARCHITECTURE.md#109-121@c75c3f5`, `ARCHITECTURE.md#187-193@c75c3f5`, `ARCHITECTURE.md#218-229@c75c3f5` | 1.0 | Historical baseline plus explicit current-tree correction | @@ -944,7 +944,7 @@ Most file citations pin the original report baseline, git sha `56f82ec` (`56f82e | C09 | Imports are pinned to SHA-256 digests; drift changes identity | `README.md#193-196@56f82ec`, `crates/edict-syntax/src/parser.rs#163-168@56f82ec` | 1.0 | Parser enforces `sha256:` + 64 hex | | C10 | Unbounded String/Bytes rejected; bounds enable max-memory computation | `crates/edict-syntax/src/semantic.rs#311-320@56f82ec`, `docs/TECHNICAL_EXPLANATION.md#641-647@56f82ec` | 0.95 | | | C11 | Profile claims are checked against effect write classes; mismatch fails compilation | `crates/edict-syntax/src/compiler.rs#1047-1087@56f82ec`, `README.md#199-204@56f82ec` | 0.95 | | -| C12 | Every intent must declare a basis; `basis none` is the explicit no-basis form; basis is evaluated pure | `crates/edict-syntax/src/semantic.rs#253-259@56f82ec`, `crates/edict-syntax/src/parser.rs#818-824@56f82ec`, `docs/SPEC_edict-language-v1.md#1785@56f82ec` | 0.95 | | +| C12 | Every action must declare a basis; `basis none` is the explicit no-basis form; basis is evaluated pure | `crates/edict-syntax/src/semantic.rs#253-259@56f82ec`, `crates/edict-syntax/src/parser.rs#818-824@56f82ec`, `docs/SPEC_edict-language-v1.md#1785@56f82ec` | 0.95 | | | C13 | Budget is a mandatory declared ceiling | `crates/edict-syntax/src/semantic.rs#246-252@56f82ec` | 0.95 | | | C14 | `where` predicates become typed input constraints, hash-significant in Core | `crates/edict-syntax/src/compiler.rs#658-680@56f82ec`, `docs/SPEC_edict-language-v1.md#1763@56f82ec` | 0.9 | | | C15 | String concatenation bound = sum of operand maxima | `crates/edict-syntax/src/compiler.rs#1695-1725@56f82ec`, `docs/SPEC_edict-language-v1.md#1597@56f82ec` | 0.9 | | @@ -957,7 +957,7 @@ Most file citations pin the original report baseline, git sha `56f82ec` (`56f82e | C22 | Stack layering: GraphQL → Wesley → Edict → Core → targets → Continuum → runtimes | `README.md#453-470@56f82ec` | 0.95 | | | C23 | Current five-member workspace adds provider schema and Wasmtime host crates with one-way dependency boundaries | `Cargo.toml#1-15@c75c3f5`, `ARCHITECTURE.md#8-25@c75c3f5` | 1.0 | Current workspace map | | C24 | edict-syntax's breadth is acknowledged; layered crate split planned (not a rename) | `ARCHITECTURE.md#23-45@56f82ec`, `docs/design/crate-scope-v0.11.md#1@56f82ec` | 1.0 | | -| C25 | Grammar: package, imports, type/enum/const/fn/intent; at-most-once intent clauses | `docs/SPEC_edict-language-v1.md#1329@56f82ec`, `crates/edict-syntax/src/ast.rs#173-182@56f82ec` | 0.85 | `const`/`fn` deferred in impl per `crates/edict-syntax/src/lib.rs#31-37@56f82ec` | +| C25 | Grammar: package, imports, type/enum/const/fn/action; at-most-once action clauses | `docs/SPEC_edict-language-v1.md#1329@56f82ec`, `crates/edict-syntax/src/ast.rs#173-182@56f82ec` | 0.85 | `const`/`fn` deferred in impl per `crates/edict-syntax/src/lib.rs#31-37@56f82ec` | | C26 | Statement set: let/assert/require/guarantee/record/if/for/effect/return; locals immutable | `docs/SPEC_edict-language-v1.md#1329@56f82ec`, `docs/SPEC_edict-language-v1.md#1700@56f82ec`, `crates/edict-syntax/src/ast.rs#193-246@56f82ec` | 0.85 | `record` statement not yet parsed in impl | | C27 | Type system: Bool/I32/I64/U32/U64/String/Bytes/Digest/Unit, no floats; refined scalars; bounded List/Map; Option; hash-significant int widths | `docs/SPEC_edict-language-v1.md#1578@56f82ec`, `docs/SPEC_edict-language-v1.md#1272@56f82ec`, `crates/edict-syntax/src/ast.rs#122-144@56f82ec` | 0.85 | | | C28 | len(String) counts Unicode scalars; len(Bytes) counts bytes (EDICT-LANG-LEN-001) | `docs/SPEC_edict-language-v1.md#1597@56f82ec` | 0.8 | | @@ -976,7 +976,7 @@ Most file citations pin the original report baseline, git sha `56f82ec` (`56f82e | C41 | validate_surface enforces 7 stable rules | `crates/edict-syntax/src/semantic.rs#13-22@56f82ec` | 0.95 | | | C42 | CompilerContext holds 4 fact maps; MissingContextFact / ProfileEffectMismatch semantics | `crates/edict-syntax/src/compiler.rs#56-66@56f82ec`, `crates/edict-syntax/src/compiler.rs#1047-1087@56f82ec` | 0.95 | | | C43 | Authority facts: digest-bound JSON, lawpack/targetProfile sources, 9 stable failure kinds, conflict detection | `crates/edict-syntax/src/authority_facts.rs#70-81@56f82ec`, `crates/edict-syntax/src/authority_facts.rs#275-282@56f82ec`, `crates/edict-syntax/src/authority_facts.rs#351-370@56f82ec` | 0.95 | | -| C44 | CoreModule/CoreIntent structure with BTreeMaps and three-part budget | `crates/edict-syntax/src/core_ir.rs#13-21@56f82ec`, `crates/edict-syntax/src/core_ir.rs#189-205@56f82ec` | 0.95 | | +| C44 | CoreModule/CoreAction structure with BTreeMaps and three-part budget | `crates/edict-syntax/src/core_ir.rs#13-21@56f82ec`, `crates/edict-syntax/src/core_ir.rs#189-205@56f82ec` | 0.95 | | | C45 | Alpha-normalized locals: arg.0/$arg0, local.n, obstruction.n | `crates/edict-syntax/src/core_ir.rs#108-114@56f82ec`, `crates/edict-syntax/src/compiler.rs#636-640@56f82ec`, `crates/edict-syntax/src/compiler.rs#1685-1693@56f82ec` | 0.95 | | | C46 | Spans stripped from Core IR to avoid the formatting identity hazard | `docs/TECHNICAL_EXPLANATION.md#666-683@56f82ec`, `crates/edict-syntax/src/token.rs#10-16@56f82ec` | 0.95 | | | C47 | Canonical CBOR: minimal ints, byte-sorted map keys, duplicate rejection, decode-side canonicality enforcement | `crates/edict-syntax/src/canonical.rs#1056-1075@56f82ec`, `crates/edict-syntax/src/canonical.rs#1107-1136@56f82ec`, `crates/edict-syntax/src/canonical.rs#424-451@56f82ec` | 0.95 | | @@ -1046,7 +1046,7 @@ Citations prefixed `agy:` refer to files in the `agy-readings` corpus at commit | Claim ID | Claim | Citation(s) | Confidence | Notes | | --- | --- | --- | --- | --- | -| T01 | A WARP optic is a five-part control structure Ψ = (observer plan Ω, aperture χ, claim-lowering surface ρ, admissibility law Π, retention contract Λ) performing "Slice. Lower. Witness. Retain."; an Edict intent encodes that tuple | `agy:paper-7-summary.md#28@25ff542`, `agy:braid-optics-design.md#15-18@25ff542`, `agy:onramp-aion-07-its-all-optics.md#29-39@25ff542` | 0.85 | Five-tuple corroborated in two documents; component letter-assignments vary slightly between them | +| T01 | A WARP optic is a five-part control structure Ψ = (observer plan Ω, aperture χ, claim-lowering surface ρ, admissibility law Π, retention contract Λ) performing "Slice. Lower. Witness. Retain."; an Edict action encodes that tuple | `agy:paper-7-summary.md#28@25ff542`, `agy:braid-optics-design.md#15-18@25ff542`, `agy:onramp-aion-07-its-all-optics.md#29-39@25ff542` | 0.85 | Five-tuple corroborated in two documents; component letter-assignments vary slightly between them | | T02 | Observer Geometry keeps projection/aperture (what is visible), basis (native expression vocabulary), and accumulation (aggregation over history) as orthogonal observer axes; degeneracy = how many histories collapse to one trace | `agy:og-1-summary.md#18-20@25ff542`, `agy:observer-geometry-overview.md#27-37@25ff542` | 0.8 | Edict's `basis` clause uses the Paper VIII "bounded basis" sense (see T03), not the OG-I trace-vocabulary sense — a naming subtlety worth a docs note | | T03 | Admission is "a runtime-owned, witnessed act judged against a bounded basis under explicit law"; determinism laws apply to the resolved coordinate (receipt), not the request | `agy:paper-8-summary.md#21@25ff542`, `agy:WARP-CONTINUUM-APIs.md#148-158@25ff542` | 0.8 | | | T04 | Footprints originate in Paper II's double-pushout rewriting as delete/use sets; disjoint footprints are the formal independence test enabling confluent parallel execution ("delete–use interference is the only sin") | `agy:paper-2-summary.md#15@25ff542`, `agy:onramp-aion-02-ticks.md#62-73@25ff542`, `agy:og-2-summary.md#17@25ff542` | 0.85 | Grounds Edict invariant I-006 (footprint honesty) and gives FIDLAR's "Footprints Ignored" its technical bite | @@ -1056,10 +1056,10 @@ Citations prefixed `agy:` refer to files in the `agy-readings` corpus at commit | T08 | Witness debt = the gap between emitted report and justified status; hallucination is structurally defined as overreporting beyond carried support | `agy:og-3-summary.md#23-34@25ff542`, `agy:observer-geometry-overview.md#45@25ff542` | 0.85 | Formally checked in Coq/Rocq per the summaries | | T09 | Continuum defines four canonical admission/lowering outcomes — Derived, Plural, Conflict, Obstruction — with plurality and conflict as first-class honest results; the Continuum app API mirrors this as `Accepted/Plural/Conflict/Obstruction` and `Observed/Plural/Conflict/Obstructed` | `agy:paper-8-summary.md#30@25ff542`, `agy:aion-overview.md#65@25ff542`, `agy:onramp-aion-07-its-all-optics.md#33@25ff542`, `agy:WARP-CONTINUUM-APIs.md#559@25ff542` | 0.85 | Corroborated in four documents; resolves this report's original "pluralize" evidence gap | | T10 | AION's core inversion: history is the primary immutable artifact; current state is a materialized view projected from it | `agy:aion-overview.md#8@25ff542` | 0.85 | | -| T11 | Computational holography (bulk reconstructible from initial state + tick patches) holds only when patches are sufficient and stable — every choice pinned, nothing outside the declared boundary ("the anti-tautology") — which is the formal justification for Edict's digest pinning; Paper III also predicts "what happened" and "why" become *intent* and *witness* in separate envelopes | `agy:onramp-aion-03-holography.md#53-75@25ff542`, `agy:paper-3-summary.md#20-21@25ff542` | 0.8 | | +| T11 | Computational holography (bulk reconstructible from initial state + tick patches) holds only when patches are sufficient and stable — every choice pinned, nothing outside the declared boundary ("the anti-tautology") — which is the formal justification for Edict's digest pinning; Paper III also predicts "what happened" and "why" become *action* and *witness* in separate envelopes | `agy:onramp-aion-03-holography.md#53-75@25ff542`, `agy:paper-3-summary.md#20-21@25ff542` | 0.8 | | | T12 | Agents interacting through Continuum receive machine-readable "obstruction witnesses" as "causal teaching artifacts" to refine behavior | `agy:paper-8-summary.md#37@25ff542` | 0.8 | Reframes Edict obstructions as the feedback channel of the lawful-autonomous loop | -| T13 | OG-II proves silent last-write-wins merges create a permanent "intent-recovery insufficiency floor"; only preserving conflict/authored acts as first-class objects avoids it | `agy:og-2-summary.md#29@25ff542`, `agy:observer-geometry-overview.md#38@25ff542` | 0.8 | The theoretical case for Edict's obstruction strands | -| T14 | Real Edict source exists in cross-project design docs: a `task.edit_document@1` intent with pinned digests, `budget <=`, a `require jim.basisFresh(input.basis) else ... StaleBase` guard, effect-level `else` obstructions, and a typed `EditReceipt` | `EditObstruction` return union | `agy:agy/continuum-receipts.md#12-46@25ff542` | 0.8 | Matches the README's aspirational `createEntry` syntax family | +| T13 | OG-II proves silent last-write-wins merges create a permanent "action-recovery insufficiency floor"; only preserving conflict/authored acts as first-class objects avoids it | `agy:og-2-summary.md#29@25ff542`, `agy:observer-geometry-overview.md#38@25ff542` | 0.8 | The theoretical case for Edict's obstruction strands | +| T14 | Real Edict source exists in cross-project design docs: a `task.edit_document@1` action with pinned digests, `budget <=`, a `require jim.basisFresh(input.basis) else ... StaleBase` guard, effect-level `else` obstructions, and a typed `EditReceipt` | `EditObstruction` return union | `agy:agy/continuum-receipts.md#12-46@25ff542` | 0.8 | Matches the README's aspirational `createEntry` syntax family | | T15 | The PROVE-IT evidence ladder runs L0 (agent assertion) → L1 (signed receipt) → L2 (history inclusion proof, "minimum serious alpha requirement") → L3 (holographic witness capsule) → L4 (challenge replay) → L5 (proof-carrying transition); the "buildable Continuum" rests on L2+L3+signature | `agy:agy/continuum-receipts.md#57-84@25ff542` | 0.8 | | | T16 | The evidence posture lattice spans origin, proof strength, access, and completeness dimensions; Paper VI defines FULL/ZK/OPAQUE provenance tiers; Paper VII's commitment/folding/revelation split enables "perfect provenance and real privacy at the same time" | `agy:paper-8-summary.md#32@25ff542`, `agy:paper-6-summary.md#28@25ff542`, `agy:onramp-aion-07-its-all-optics.md#94-96@25ff542` | 0.75 | Speculative as an *Edict roadmap* claim; well-attested as theory | @@ -1095,14 +1095,14 @@ Citations prefixed `agy:` refer to files in the `agy-readings` corpus at commit **The bundle is a hologram — and calling it a "receipt" anywhere would be a category error.** The theory keeps three artifacts rigorously apart: the descriptive *receipt* (why, audit-grade), the prescriptive *patch* (what, replay-grade), and the sealed *hologram/BTR* (boundary, transport-grade). Edict's code already respects this (bundles exclude admission artifacts; receipts are admission-side), but as prose accumulates, that three-way distinction is the easiest one to blur. It might deserve its own topic shelf. -**Obstruction strands are OG-II's theorem wearing work clothes.** The "intent-recovery insufficiency floor" — silent merges permanently erase intent — is, to me, the single most practical result in the whole corpus, and `require ... else continue obstructed { reason: ... }` is its direct engineering consequence: never collapse an authored attempt, preserve it as repairable causal material. I'd surface that lineage in the obstruction-strands design note; it upgrades the feature from "nice error handling" to "provably necessary." +**Obstruction strands are OG-II's theorem wearing work clothes.** The "action-recovery insufficiency floor" — silent merges permanently erase action — is, to me, the single most practical result in the whole corpus, and `require ... else continue obstructed { reason: ... }` is its direct engineering consequence: never collapse an authored attempt, preserve it as repairable causal material. I'd surface that lineage in the obstruction-strands design note; it upgrades the feature from "nice error handling" to "provably necessary." ### B.3 Ideas and brainstorms 1. **Moriarty is measuring holonomy — say so.** OG-III's loop defect measures how evidence degrades around a translation loop. Moriarty's hash-impact matrix is exactly a discrete holonomy measurement over the artifact graph: mutate, propagate, and check which digests move. Formalizing the matrix as "digest holonomy must be zero on semantics-preserving loops and provably nonzero on semantic mutations" would give Moriarty a crisp mathematical spec — and might surface canonicalization bugs as *loop defects* rather than ad-hoc test failures. 2. **Watson should carry a support ledger.** If Watson is ever implemented over an LLM, OG-III hands you its compliance test for free: every explanation is an emitted report, so require Watson to attach the support ledger (carried/lost/blocked/refuting) for each remedial claim, and reject explanations with nonzero witness debt. "The explainer may not hallucinate" becomes a checkable property, not a hope. A `watson.explanation/v1` schema with mandatory per-claim support references would be a striking first. 3. **Nutrition labels could advertise witness debt = 0.** The label already reports profile, budget, footprint, obstructions. Adding an evidence-posture line (origin / proof strength / access / completeness, per the Paper VIII lattice) and an explicit "witness debt: none — every claim below is compiler-derived" would make the label's own trustworthiness self-describing. -4. **Budget elasticity as a v2 budget refinement.** OG-I models observers with *budget elasticity* — how much more they recover as budgets grow. Edict budgets today are scalar ceilings. An elasticity-aware budget ("this intent degrades gracefully: at half budget it returns a coarser reading") would let participants admit operations at negotiated resolution instead of binary accept/reject. That pairs naturally with the accept-with-lowered-ceilings outcome already in the admission spec. +4. **Budget elasticity as a v2 budget refinement.** OG-I models observers with *budget elasticity* — how much more they recover as budgets grow. Edict budgets today are scalar ceilings. An elasticity-aware budget ("this action degrades gracefully: at half budget it returns a coarser reading") would let participants admit operations at negotiated resolution instead of binary accept/reject. That pairs naturally with the accept-with-lowered-ceilings outcome already in the admission spec. 5. **Adopt the four-outcome coproduct at the Edict admission boundary.** The repo's admission spec models accept/reject/lower; the theory's `Derived | Plural | Conflict | Obstruction` is richer and already leaked into the README. When v0.12's admission harness lands, aligning the receipt decision taxonomy with the Continuum coproduct early would avoid a painful migration later — exhaustive-match handling of all four outcomes is exactly the kind of law (`Exhaustive Admission`) the Continuum API notes already state. 6. **The two-lowerer trial is a rulial-distance experiment.** Paper IV defines translation cost between observers; two independent lowerers producing byte-identical Target IR is a demonstration that the semantic digest sits at rulial distance zero between two implementations. Framing conformance that way suggests a generalization: *measure* the divergence when they disagree (where in the artifact, under which mutation class) instead of just failing — a diagnostic gold mine for canonicalization work. 7. **Obstruction strands will eventually meet Paper VI.** If agents' blocked attempts are preserved as first-class causal material, then strands are behavioral records of an agent's decision-making — precisely the material provenance sovereignty exists to protect. A future where strand payloads support ZK/OPAQUE tiers ("prove the strand exists and is repairable, without revealing the attempt's content") feels less like speculation and more like an inevitable v3 issue. @@ -1134,7 +1134,7 @@ printf '%s\n%s\n' \ ### Lab 2 — Sloppy source, three typed diagnostics (exit 1) -Feed it an inline source with an unbounded `String` and an intent missing its `budget` and `basis` clauses: +Feed it an inline source with an unbounded `String` and an action missing its `budget` and `basis` clauses: ```graphql package examples.broken@1; @@ -1143,7 +1143,7 @@ type Sloppy = { name: String, // unbounded — not allowed }; -intent greet(input: Sloppy) +action greet(input: Sloppy) returns Sloppy profile hello.readOnly { @@ -1161,7 +1161,7 @@ All three violations are reported in one pass, on stderr, with byte spans: ``` The spans are structured but not yet surgical insertion points. In this -transcript, both missing-clause diagnostics share the broad intent-body span +transcript, both missing-clause diagnostics share the broad action-body span `64..168`; consumers should not assume that every missing clause receives a distinct, exact source location. @@ -1197,7 +1197,7 @@ Ask the unreleased `project` operation for Core IR *without* supplying authority Supply the facts in `compilerContext` — a read-only profile for `hello.readOnly` and a `{maxSteps: 64, maxAllocatedBytes: 4096, maxOutputBytes: 1024}` budget for `hello.tinyBudget` — and the same request yields a live Core review and a canonical digest: ```json -{"schema":"edict.projection.core/v1","state":"available","digest":"sha256:f8243875a77aa3cc8f02529e2c8b18e183ed6b02ad5f6fd6d0795c6ad532ce85","review":{"apiVersion":"edict.core/v1","coordinate":"examples.hello@1","intents":{"sayHello":{"body":{"locals":[{"alphaName":"$arg0","id":"arg.0","ty":"examples.hello@1.HelloInput"},{"alphaName":"$local0","id":"local.0","ty":"String"}],"...":"..."}}}}} +{"schema":"edict.projection.core/v1","state":"available","digest":"sha256:f8243875a77aa3cc8f02529e2c8b18e183ed6b02ad5f6fd6d0795c6ad532ce85","review":{"apiVersion":"edict.core/v1","coordinate":"examples.hello@1","actions":{"sayHello":{"body":{"locals":[{"alphaName":"$arg0","id":"arg.0","ty":"examples.hello@1.HelloInput"},{"alphaName":"$local0","id":"local.0","ty":"String"}],"...":"..."}}}}} ``` Two details in that review are worth savoring. The locals are the alpha-normalized `$arg0` / `$local0` — your variable names are already gone. And the concatenation `"hello, " + input.name` was typed `String`: the compiler summed the literal's length (7) and the input's bound (256). Bounds are arithmetic, not annotations. @@ -1230,7 +1230,7 @@ use target @ digest "sha256:<64hex>" as ; type = { field: Type, ... }; // record | variant | ref enum { Case1, Case2 } -intent (input: ) +action (input: ) returns // may be a union: Reading | Obstruction profile . // and/or implements basis | basis none // mandatory @@ -1268,7 +1268,7 @@ 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` | -| Semantic (7) | `UnboundedScalar`, `MissingOperationMode`, `MissingBudget`, `MissingBasis`, `DuplicateIntentClause`, `DuplicateName`, `ShadowedName` | +| Semantic (7) | `UnboundedScalar`, `MissingOperationMode`, `MissingBudget`, `MissingBasis`, `DuplicateActionClause`, `DuplicateName`, `ShadowedName` | | Compiler (10) | `SurfaceValidation`, `MissingContextFact`, `UnsupportedSourceShape`, `UnresolvedType`, `UnknownField`, `TypeMismatch`, `ExpectedPredicate`, `ProfileEffectMismatch`, `DuplicateObstructionFailure`, `DuplicateObstructionPayloadField` | | CLI exit codes | `0` ok · `1` compiler/validation diagnostics · `2` invalid CLI input | diff --git a/README.md b/README.md index 1b63740..d88252a 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,14 @@ Unlike general-purpose runtimes where code has unrestricted access to the filesy ```mermaid flowchart LR - A["1. Write Intent\n(Declare inputs, outputs, & budgets)"] -->|Compile & Prove| B["2. Cryptographic Seal\n(Core IR + Hash-Locked Manifest)"] + A["1. Write Action\n(Declare inputs, outputs, & budgets)"] -->|Compile & Prove| B["2. Cryptographic Seal\n(Core IR + Hash-Locked Manifest)"] B -->|Submit Bundle| C["3. Participant Admission\n(Inspect 'Nutrition Label')"] C -->|Execute Safely| D["4. WASM Sandbox\n(Enforced limits & auto-rollback)"] ``` > **Shipping today vs. envisioned.** This diagram is the full architecture. The > current alpha (`v0.11.0-alpha.1`) implements stage 1 and the front half of -> stage 2 — writing intents and **compiling + validating** them through the +> stage 2 — writing actions and **compiling + validating** them through the > `edict check` CLI and `edict_syntax` library, plus canonical Core IR, > canonical Target IR artifact bytes, and participant-neutral contract-bundle > assembly with reviewed digest goldens. Participant admission, runtime @@ -104,7 +104,7 @@ Edict is a restricted deterministic language where the compiler enforces what an operation is actually allowed to do — not as a convention, but as a verified contract. -You write an *intent*. The intent declares: +You write an *action*. The action declares: - **What it reads** — exactly which data it is allowed to inspect, bounded - **What it writes** — exactly which effects it may produce, with typed outcomes @@ -124,7 +124,7 @@ do. --- -## What An Intent Looks Like +## What An Action Looks Like The smallest useful Edict program is deliberately boring: @@ -141,7 +141,7 @@ type HelloReading = { message: String, }; -intent sayHello(input: HelloInput) +action sayHello(input: HelloInput) returns HelloReading profile hello.readOnly budget <= hello.tinyBudget @@ -152,14 +152,14 @@ intent sayHello(input: HelloInput) } ``` -No ambient access. No clock. No filesystem. No network. No database. This intent +No ambient access. No clock. No filesystem. No network. No database. This action compiles to a Core IR with zero runtime effects. The current compiler-spine can prove the pure subset and can reject known profile/effect write-class conflicts from deterministic compiler context facts, including facts loaded from explicit authority-facts files. Loading full lawpack and target-profile manifests remains future target/lawpack work. -A more realistic intent — one that actually reads from a backing store — looks like +A more realistic action — one that actually reads from a backing store — looks like this: ```graphql @@ -169,7 +169,7 @@ use shape "schemas/greeting.graphql" as shape; use lawpack greeting.optics@1 digest "sha256:b4e..." as greeting; use target echo.dpo@1 digest "sha256:c9f..." as echo; -intent readGreeting(input: shape.ReadGreetingInput) +action readGreeting(input: shape.ReadGreetingInput) returns shape.GreetingReading profile echo.readOnly budget <= greeting.readGreetingBudget @@ -194,7 +194,7 @@ Walk through this line by line: cryptographic hash*. You can't silently upgrade a law package and keep the same bundle identity. Version drift is visible by construction. - `use target echo.dpo@1 digest "sha256:..."` — imports the target runtime profile - under which this intent will execute. Edict Core is runtime-neutral; it lowers + under which this action will execute. Edict Core is runtime-neutral; it lowers to a specific target through an explicitly declared, hash-locked profile. - `profile echo.readOnly` — a claim the current compiler can check when the caller supplies deterministic profile and effect write-class facts, including @@ -243,8 +243,8 @@ let barValue = bar.create({ id: input.id }) let z = hash(fooValue, barValue); ``` -The explicit form makes the intent's effect structure legible to the compiler, to -tooling, and to human reviewers. When an agent submits an Edict intent for +The explicit form makes the action's effect structure legible to the compiler, to +tooling, and to human reviewers. When an agent submits an Edict action for admission, a participant can read the effect sequence directly from the Core IR — not by running the code, but by inspecting the artifact. @@ -252,7 +252,7 @@ not by running the code, but by inspecting the artifact. ## What The Compiler Produces -An Edict intent doesn't compile to a binary that you run. It compiles to a +An Edict action doesn't compile to a binary that you run. It compiles to a **contract bundle** — a structured, participant-neutral artifact that can be inspected, admitted, and executed by a runtime that accepts it. @@ -363,7 +363,7 @@ checked the footprint. Nobody verified the law. The operation just ran. The YOLO lane is the alternative. An agent may execute autonomously *only after*: -1. Its intent is expressed in Edict source. +1. Its action is expressed in Edict source. 2. The source compiles to a Core IR with verified footprint, budget, and effects. 3. The Core lowers to a target-specific IR through a declared, hash-locked target profile. @@ -408,10 +408,10 @@ returns `null`, or crashes. The failure is an escape hatch from the type system. Callers catch it or don't; nothing enforces that they handle it correctly. In Edict, effects that can fail must declare typed *obstructions* — named, -structured failure outcomes that are part of the intent's return type: +structured failure outcomes that are part of the action's return type: ```graphql -intent createEntry(input: shape.EntryInput) +action createEntry(input: shape.EntryInput) returns shape.EntryReceipt | history.EntryObstruction profile history.readWrite budget <= history.createEntryBudget @@ -435,10 +435,10 @@ intent createEntry(input: shape.EntryInput) `StaleBase` and `Conflict` aren't exceptions. They're expected domain outcomes that callers can match, reason about, and respond to. The participant runtime that -evaluates this intent knows, before execution, exactly what failure vocabulary the +evaluates this action knows, before execution, exactly what failure vocabulary the operation can produce. -This matters for agents in particular. An agent that submits an Edict intent and +This matters for agents in particular. An agent that submits an Edict action and receives `history.EntryObstruction.StaleBase` knows exactly what happened and can decide how to respond — refresh its basis and retry, escalate, or abandon — without inspecting a stack trace or parsing an error message. diff --git a/ROADMAP.md b/ROADMAP.md index aee9202..95dd224 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -382,7 +382,7 @@ Scope: Exit gates: -- At least one effectful intent compiles from source through typed Core with +- At least one effectful action compiles from source through typed Core with deterministic fixtures. Met by PR #63. - Unsupported effectful forms fail with stable diagnostics before lowering. Met by PR #63. @@ -433,7 +433,7 @@ Scope: Exit gates: -- One source intent can compile to Core and lower to a concrete target IR +- One source action can compile to Core and lower to a concrete target IR artifact for the selected Echo or git-warp profile. - Lowerability evidence and target IR generation agree on the same fact set. - Unsupported target features fail loudly rather than falling back silently. diff --git a/crates/edict-cli/src/main.rs b/crates/edict-cli/src/main.rs index 53a1b20..bafbde7 100644 --- a/crates/edict-cli/src/main.rs +++ b/crates/edict-cli/src/main.rs @@ -15,7 +15,7 @@ use edict_syntax::{ CoreBudget, CoreExpr, CoreImport, CoreNode, CoreObstructionArm, CoreObstructionReason, CorePredicate, CoreRequireFailureArm, CoreType, CoreValue, HighlightRole, InputConstraint, InputConstraintSource, ParseError, ResourceRef, SemanticError, Span, TargetEffectLowering, - TargetIrArtifact, TargetIrIntent, TargetIrLoweringFacts, TargetIrRequireFailure, + TargetIrAction, TargetIrArtifact, TargetIrLoweringFacts, TargetIrRequireFailure, TargetIrRequirement, TargetIrStep, TargetLoweringFailure, TargetLoweringFailureKind, WriteClass, }; @@ -1102,20 +1102,20 @@ fn core_review(core: &edict_syntax::CoreModule) -> Value { .iter() .map(|(name, ty)| (name.clone(), core_type_review(ty))) .collect::>(), - "intents": core.intents + "actions": core.actions .iter() - .map(|(name, intent)| { + .map(|(name, action)| { (name.clone(), json!({ - "input": intent.input, - "output": intent.output, - "requiredOperationProfile": intent.required_operation_profile, - "inputConstraints": intent + "input": action.input, + "output": action.output, + "requiredOperationProfile": action.required_operation_profile, + "inputConstraints": action .input_constraints .iter() .map(input_constraint_review) .collect::>(), - "coreEvaluationBudget": core_budget_review(&intent.core_evaluation_budget), - "body": core_block_review(&intent.body), + "coreEvaluationBudget": core_budget_review(&action.core_evaluation_budget), + "body": core_block_review(&action.body), })) }) .collect::>(), @@ -1128,29 +1128,29 @@ fn target_ir_review(artifact: &TargetIrArtifact) -> Value { "domain": artifact.domain, "targetProfile": resource_ref_review(&artifact.target_profile), "sourceCoreCoordinate": artifact.source_core_coordinate, - "intents": artifact.intents + "actions": artifact.actions .iter() - .map(|(name, intent)| (name.clone(), target_ir_intent_review(intent))) + .map(|(name, action)| (name.clone(), target_ir_action_review(action))) .collect::>(), }) } -fn target_ir_intent_review(intent: &TargetIrIntent) -> Value { +fn target_ir_action_review(action: &TargetIrAction) -> Value { json!({ - "operationProfile": intent.operation_profile, - "inputConstraints": intent + "operationProfile": action.operation_profile, + "inputConstraints": action .input_constraints .iter() .map(input_constraint_review) .collect::>(), - "coreEvaluationBudget": core_budget_review(&intent.core_evaluation_budget), - "requirements": intent + "coreEvaluationBudget": core_budget_review(&action.core_evaluation_budget), + "requirements": action .requirements .iter() .map(target_ir_requirement_review) .collect::>(), - "steps": intent.steps.iter().map(target_ir_step_review).collect::>(), - "result": core_expr_review(&intent.result), + "steps": action.steps.iter().map(target_ir_step_review).collect::>(), + "result": core_expr_review(&action.result), }) } @@ -1410,7 +1410,7 @@ fn compare_op_name(op: edict_syntax::CompareOp) -> &'static str { fn target_lowering_failure_review(failure: &TargetLoweringFailure) -> Value { json!({ "kind": target_lowering_failure_kind_name(failure.kind), - "intent": failure.intent, + "action": failure.action, "nodeIndex": failure.node_index, "detail": failure.detail, }) diff --git a/crates/edict-cli/tests/jsonl_cli.rs b/crates/edict-cli/tests/jsonl_cli.rs index 8ada918..d8779d9 100644 --- a/crates/edict-cli/tests/jsonl_cli.rs +++ b/crates/edict-cli/tests/jsonl_cli.rs @@ -19,7 +19,7 @@ type HelloInput = { name: String, }; -intent sayHello(input: HelloInput) +action sayHello(input: HelloInput) returns HelloInput profile hello.readOnly basis none @@ -35,7 +35,7 @@ type Input = { id: String, }; type Receipt = { id: String, }; type Output = { id: String, }; -intent replaceThing(input: Input) +action replaceThing(input: Input) returns Output profile p.effectful basis none @@ -1050,7 +1050,7 @@ fn assert_syntax_projection(stdout: &[Value]) { assert!( spans.iter().any(|span| { span.get("role").and_then(Value::as_str) == Some("keyword") - && span.get("lexeme").and_then(Value::as_str) == Some("intent") + && span.get("lexeme").and_then(Value::as_str) == Some("action") }), "syntax spans must expose editor roles over dirty source text" ); @@ -1087,8 +1087,8 @@ fn assert_available_core_projection(stdout: &[Value], expected_core_digest: &str Some("edict.core/v1") ); assert!( - core.pointer("/review/intents/replaceThing").is_some(), - "Core review must include the lowered intent" + core.pointer("/review/actions/replaceThing").is_some(), + "Core review must include the lowered action" ); } @@ -1118,7 +1118,7 @@ fn assert_available_target_ir_projection(stdout: &[Value], expected_target_diges ); assert_eq!( target_ir - .pointer("/review/intents/replaceThing/steps/0/targetIntrinsic") + .pointer("/review/actions/replaceThing/steps/0/targetIntrinsic") .and_then(Value::as_str), Some("echo.dpo@1.replace") ); diff --git a/crates/edict-provider-host-wasmtime/tests/invocation.rs b/crates/edict-provider-host-wasmtime/tests/invocation.rs index 42885cd..3bcc55c 100644 --- a/crates/edict-provider-host-wasmtime/tests/invocation.rs +++ b/crates/edict-provider-host-wasmtime/tests/invocation.rs @@ -30,7 +30,7 @@ const SCHEMA: &[u8] = br#" artifact = null / { kind: "targetIrArtifact", domain: tstr, - intents: { * tstr => any }, + actions: { * tstr => any }, targetProfile: { * tstr => any }, sourceCoreCoordinate: tstr, } diff --git a/crates/edict-provider-schema/tests/provider_contract_pack.rs b/crates/edict-provider-schema/tests/provider_contract_pack.rs index 9c142d7..0531ec8 100644 --- a/crates/edict-provider-schema/tests/provider_contract_pack.rs +++ b/crates/edict-provider-schema/tests/provider_contract_pack.rs @@ -14,7 +14,7 @@ use edict_syntax::{ canonical_target_profile_contract_resources, decode_canonical_cbor, digest_target_profile_contract_resource, encode_target_ir_artifact, CanonicalValue, CoreBudget, CoreExpr, CoreObstructionReason, CorePredicate, CoreValue, - ProviderArtifactSchemaValidationErrorKind, ResourceRef, TargetIrArtifact, TargetIrIntent, + ProviderArtifactSchemaValidationErrorKind, ResourceRef, TargetIrAction, TargetIrArtifact, TargetIrRequireFailure, TargetIrRequirement, TargetProfileContractResource, AUTHORITY_FACTS_API_VERSION, CORE_MODULE_DIGEST_DOMAIN, PROVIDER_LAWPACK_ARTIFACT_DOMAIN, TARGET_IR_ARTIFACT_DIGEST_DOMAIN, TARGET_PROFILE_API_VERSION, @@ -210,28 +210,28 @@ fn target_ir_root_matches_reference_encoder() { let mut malformed_nested = decode_canonical_cbor(TARGET_IR_FIXTURE).expect("fixture is canonical"); - let CanonicalValue::Map(intents) = map_value_mut(&mut malformed_nested, "intents") else { - panic!("Target IR intents must be a map"); + let CanonicalValue::Map(actions) = map_value_mut(&mut malformed_nested, "actions") else { + panic!("Target IR actions must be a map"); }; - let intent = &mut intents + let action = &mut actions .first_mut() - .expect("reviewed Target IR has an intent") + .expect("reviewed Target IR has an action") .1; - *map_value_mut(intent, "steps") = CanonicalValue::Null; + *map_value_mut(action, "steps") = CanonicalValue::Null; assert_eq!( pack.validate_domain(TARGET_IR_ARTIFACT_DIGEST_DOMAIN, &malformed_nested), Err(ProviderArtifactSchemaValidationErrorKind::SchemaMismatch) ); let mut malformed_requirement = encoded_target_ir_with_requirements(); - let CanonicalValue::Map(intents) = map_value_mut(&mut malformed_requirement, "intents") else { - panic!("Target IR intents must be a map"); + let CanonicalValue::Map(actions) = map_value_mut(&mut malformed_requirement, "actions") else { + panic!("Target IR actions must be a map"); }; - let intent = &mut intents + let action = &mut actions .first_mut() - .expect("encoded Target IR has an intent") + .expect("encoded Target IR has an action") .1; - let CanonicalValue::Array(requirements) = map_value_mut(intent, "requirements") else { + let CanonicalValue::Array(requirements) = map_value_mut(action, "requirements") else { panic!("Target IR requirements must be an array"); }; let requirement = requirements @@ -557,9 +557,9 @@ fn encoded_target_ir_with_coordinate(coordinate: &str) -> CanonicalValue { digest: Some(format!("sha256:{}", "1".repeat(64))), }, source_core_coordinate: "example.core@1".to_owned(), - intents: BTreeMap::from([( + actions: BTreeMap::from([( "apply".to_owned(), - TargetIrIntent { + TargetIrAction { operation_profile: "example.operation/v1".to_owned(), input_constraints: Vec::new(), core_evaluation_budget: CoreBudget { diff --git a/crates/edict-provider-schema/tests/registry.rs b/crates/edict-provider-schema/tests/registry.rs index a20979c..1e4a3ea 100644 --- a/crates/edict-provider-schema/tests/registry.rs +++ b/crates/edict-provider-schema/tests/registry.rs @@ -159,7 +159,7 @@ fn effectful_core_with_obstruction_map() -> CanonicalValue { )]), ), ]); - let intent = map(&[ + let action = map(&[ ("input", CanonicalValue::Text("Unit".to_owned())), ("output", CanonicalValue::Text("Unit".to_owned())), ( @@ -195,7 +195,7 @@ fn effectful_core_with_obstruction_map() -> CanonicalValue { ), ("imports", CanonicalValue::Array(Vec::new())), ("types", map(&[])), - ("intents", map(&[("apply", intent)])), + ("actions", map(&[("apply", action)])), ( "requiredCoreCapabilities", CanonicalValue::Array(Vec::new()), diff --git a/crates/edict-syntax/src/ast.rs b/crates/edict-syntax/src/ast.rs index a2c73e6..edce02f 100644 --- a/crates/edict-syntax/src/ast.rs +++ b/crates/edict-syntax/src/ast.rs @@ -52,7 +52,7 @@ pub struct Import { pub enum Decl { Type(TypeDecl), Enum(EnumDecl), - Intent(IntentDecl), + Action(ActionDecl), } /// `enum Name { CASE, CASE, ... }` — a closed set of payload-free cases. @@ -150,13 +150,13 @@ pub struct ScalarRefine { pub canonical: Option, } -/// `intent name(params) returns Ty { body }` +/// `action name(params) returns Ty { body }` #[derive(Debug, Clone, PartialEq, Eq)] -pub struct IntentDecl { +pub struct ActionDecl { pub name: String, pub params: Vec, pub returns: TypeRef, - pub clauses: Vec, + pub clauses: Vec, pub body: Block, pub span: Span, } @@ -169,9 +169,9 @@ pub struct Param { pub span: Span, } -/// An intent clause (order-independent). +/// An action clause (order-independent). #[derive(Debug, Clone, PartialEq, Eq)] -pub enum IntentClause { +pub enum ActionClause { Profile(Vec), Implements(Vec), /// `basis none` or `basis `. @@ -188,7 +188,7 @@ pub struct Block { pub span: Span, } -/// A statement inside an intent body (minimal-v1 subset). +/// A statement inside an action body (minimal-v1 subset). #[derive(Debug, Clone, PartialEq, Eq)] pub enum Stmt { Let { diff --git a/crates/edict-syntax/src/canonical.rs b/crates/edict-syntax/src/canonical.rs index 65075b9..3aae5e1 100644 --- a/crates/edict-syntax/src/canonical.rs +++ b/crates/edict-syntax/src/canonical.rs @@ -12,12 +12,12 @@ use std::str; use sha2::{Digest, Sha256}; use crate::core_ir::{ - CompareOp, CoreBlock, CoreBudget, CoreExpr, CoreImport, CoreIntent, CoreModule, CoreNode, + CompareOp, CoreAction, CoreBlock, CoreBudget, CoreExpr, CoreImport, CoreModule, CoreNode, CoreObstructionArm, CorePredicate, CoreType, CoreValue, InputConstraint, InputConstraintSource, LocalRef, ResourceRef, }; use crate::target_ir::{ - TargetIrArtifact, TargetIrIntent, TargetIrRequireFailure, TargetIrRequirement, TargetIrStep, + TargetIrAction, TargetIrArtifact, TargetIrRequireFailure, TargetIrRequirement, TargetIrStep, }; /// Canonical encoding profile for Core artifacts. @@ -481,12 +481,12 @@ fn target_ir_artifact_value(artifact: &TargetIrArtifact) -> Result Result Result { +fn target_ir_action_value(action: &TargetIrAction) -> Result { Ok(map([ - ("operationProfile", text(&intent.operation_profile)), + ("operationProfile", text(&action.operation_profile)), ( "inputConstraints", - sorted_array_results(intent.input_constraints.iter().map(input_constraint_value))?, + sorted_array_results(action.input_constraints.iter().map(input_constraint_value))?, ), ( "coreEvaluationBudget", - core_budget_value(&intent.core_evaluation_budget), + core_budget_value(&action.core_evaluation_budget), ), ( "requirements", - array_results(intent.requirements.iter().map(target_ir_requirement_value))?, + array_results(action.requirements.iter().map(target_ir_requirement_value))?, ), ( "steps", - array_results(intent.steps.iter().map(target_ir_step_value))?, + array_results(action.steps.iter().map(target_ir_step_value))?, ), - ("result", core_expr_value(&intent.result)?), + ("result", core_expr_value(&action.result)?), ])) } @@ -608,12 +608,12 @@ fn core_module_value(module: &CoreModule) -> Result CanonicalValue { } } -fn core_intent_value(intent: &CoreIntent) -> Result { +fn core_action_value(action: &CoreAction) -> Result { Ok(map([ - ("input", text(&intent.input)), - ("output", text(&intent.output)), + ("input", text(&action.input)), + ("output", text(&action.output)), ( "requiredOperationProfile", - text(&intent.required_operation_profile), + text(&action.required_operation_profile), ), ( "inputConstraints", - sorted_array_results(intent.input_constraints.iter().map(input_constraint_value))?, + sorted_array_results(action.input_constraints.iter().map(input_constraint_value))?, ), ( "coreEvaluationBudget", - core_budget_value(&intent.core_evaluation_budget), + core_budget_value(&action.core_evaluation_budget), ), - ("body", core_block_value(&intent.body)?), + ("body", core_block_value(&action.body)?), ])) } diff --git a/crates/edict-syntax/src/compiler.rs b/crates/edict-syntax/src/compiler.rs index 61d1513..a249b13 100644 --- a/crates/edict-syntax/src/compiler.rs +++ b/crates/edict-syntax/src/compiler.rs @@ -6,12 +6,12 @@ use std::collections::{BTreeMap, BTreeSet}; use crate::ast::{ - BinOp, Block, BoundRef, Decl, ElseClause, Expr, FieldDecl, Import, ImportKind, IntentClause, - IntentDecl, Module, ObstructionArm, ObstructionHandler, ObstructionTarget, RecordEntry, + ActionClause, ActionDecl, BinOp, Block, BoundRef, Decl, ElseClause, Expr, FieldDecl, Import, + ImportKind, Module, ObstructionArm, ObstructionHandler, ObstructionTarget, RecordEntry, RequireElseArm, ScalarRefine, Stmt, TypeDecl, TypeExpr, TypeRef, YieldBlock, }; use crate::core_ir::{ - CompareOp, CoreBlock, CoreBudget, CoreExpr, CoreImport, CoreImportKind, CoreIntent, CoreModule, + CompareOp, CoreAction, CoreBlock, CoreBudget, CoreExpr, CoreImport, CoreImportKind, CoreModule, CoreNode, CoreObstructionArm, CoreObstructionReason, CorePredicate, CoreRequireFailureArm, CoreType, CoreValue, InputConstraint, InputConstraintSource, LocalRef, ResourceRef, CORE_API_VERSION, @@ -123,7 +123,7 @@ pub struct ResolvedModule { pub imports: Vec, pub effect_write_classes: BTreeMap, pub types: Vec, - pub intents: Vec, + pub actions: Vec, } /// Resolved type declaration. @@ -133,14 +133,14 @@ pub struct ResolvedTypeDecl { pub source: TypeDecl, } -/// Resolved intent declaration. +/// Resolved action declaration. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct ResolvedIntent { +pub struct ResolvedAction { pub name: String, pub profile: String, pub allowed_write_classes: Option>, pub budget: CoreBudget, - pub source: IntentDecl, + pub source: ActionDecl, } /// Module after type checking and Core-ready local normalization. @@ -149,12 +149,12 @@ pub struct TypedModule { pub coordinate: String, pub imports: Vec, pub types: BTreeMap, - pub intents: Vec, + pub actions: Vec, } -/// Typed intent boundary consumed by `lower_core`. +/// Typed action boundary consumed by `lower_core`. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct TypedIntent { +pub struct TypedAction { pub name: String, pub input: String, pub output: String, @@ -202,7 +202,7 @@ pub fn resolve_module( let coordinate = package_coordinate(&module.package.path, &module.package.version); let imports = resolve_imports(&module.imports, &mut errors); let mut types = Vec::new(); - let mut intents = Vec::new(); + let mut actions = Vec::new(); for decl in &module.decls { match decl { @@ -216,9 +216,9 @@ pub fn resolve_module( "enum declarations are not in the initial lowerable subset", decl.span, )), - Decl::Intent(intent) => { - if let Some(resolved) = resolve_intent(intent, context, &mut errors) { - intents.push(resolved); + Decl::Action(action) => { + if let Some(resolved) = resolve_action(action, context, &mut errors) { + actions.push(resolved); } } } @@ -231,7 +231,7 @@ pub fn resolve_module( imports, effect_write_classes: context.effect_write_classes.clone(), types, - intents, + actions, }, ) } @@ -252,19 +252,19 @@ pub fn type_check(resolved: &ResolvedModule) -> Result Result> { - let intents = typed - .intents + let actions = typed + .actions .iter() - .map(|intent| { + .map(|action| { ( - intent.name.clone(), - CoreIntent { - input: intent.input.clone(), - output: intent.output.clone(), - required_operation_profile: intent.profile.clone(), - input_constraints: intent.input_constraints.clone(), - core_evaluation_budget: intent.budget.clone(), - body: intent.body.clone(), + action.name.clone(), + CoreAction { + input: action.input.clone(), + output: action.output.clone(), + required_operation_profile: action.profile.clone(), + input_constraints: action.input_constraints.clone(), + core_evaluation_budget: action.budget.clone(), + body: action.body.clone(), }, ) }) @@ -274,7 +274,7 @@ pub fn lower_core(typed: &TypedModule) -> Result> coordinate: typed.coordinate.clone(), imports: typed.imports.clone(), types: typed.types.clone(), - intents, + actions, required_core_capabilities: Vec::new(), }) } @@ -317,17 +317,17 @@ fn core_import_kind(kind: ImportKind) -> Option { } } -fn resolve_intent( - intent: &IntentDecl, +fn resolve_action( + action: &ActionDecl, context: &CompilerContext, errors: &mut Vec, -) -> Option { +) -> Option { let mut profile = None; let mut allowed_write_classes = None; let mut budget = None; - for clause in &intent.clauses { + for clause in &action.clauses { match clause { - IntentClause::Profile(path) => { + ActionClause::Profile(path) => { let key = path_key(path); match context.operation_profiles.get(&key) { Some(value) => { @@ -337,36 +337,36 @@ fn resolve_intent( } None => errors.push(missing_context_fact( format!("operation profile `{key}` has no compiler context fact"), - intent.span, + action.span, )), } } - IntentClause::Budget(path) => { + ActionClause::Budget(path) => { let key = path_key(path); match context.budgets.get(&key) { Some(value) => budget = Some(value.clone()), None => errors.push(missing_context_fact( format!("budget `{key}` has no compiler context fact"), - intent.span, + action.span, )), } } - IntentClause::Implements(_) => errors.push(error( + ActionClause::Implements(_) => errors.push(error( CompilerStage::Resolve, CompilerErrorKind::UnsupportedSourceShape, - "`implements` intents are outside the initial lowerable subset", - intent.span, + "`implements` actions are outside the initial lowerable subset", + action.span, )), - IntentClause::Basis(_) | IntentClause::Footprint(_) | IntentClause::Where(_) => {} + ActionClause::Basis(_) | ActionClause::Footprint(_) | ActionClause::Where(_) => {} } } - Some(ResolvedIntent { - name: intent.name.clone(), + Some(ResolvedAction { + name: action.name.clone(), profile: profile?, allowed_write_classes, budget: budget?, - source: intent.clone(), + source: action.clone(), }) } @@ -463,17 +463,17 @@ impl<'a> TypeChecker<'a> { fn check(mut self) -> Result> { self.check_types(); - let mut intents = Vec::new(); - for intent in &self.resolved.intents { - if let Some(typed) = self.check_intent(intent) { - intents.push(typed); + let mut actions = Vec::new(); + for action in &self.resolved.actions { + if let Some(typed) = self.check_action(action) { + actions.push(typed); } } let module = TypedModule { coordinate: self.resolved.coordinate.clone(), imports: self.resolved.imports.clone(), types: self.core_types, - intents, + actions, }; finish(self.errors, module) } @@ -605,13 +605,13 @@ impl<'a> TypeChecker<'a> { }) } - fn check_intent(&mut self, intent: &ResolvedIntent) -> Option { - let source = &intent.source; + fn check_action(&mut self, action: &ResolvedAction) -> Option { + let source = &action.source; if source.params.len() != 1 { self.errors.push(error( CompilerStage::TypeCheck, CompilerErrorKind::UnsupportedSourceShape, - "initial Core lowering supports exactly one intent parameter", + "initial Core lowering supports exactly one action parameter", source.span, )); return None; @@ -619,7 +619,7 @@ impl<'a> TypeChecker<'a> { if !source .clauses .iter() - .any(|clause| matches!(clause, IntentClause::Basis(None))) + .any(|clause| matches!(clause, ActionClause::Basis(None))) { self.errors.push(error( CompilerStage::TypeCheck, @@ -641,14 +641,14 @@ impl<'a> TypeChecker<'a> { let mut locals = vec![input_binding.clone()]; let mut env = BTreeMap::from([(param.name.clone(), (input_binding.clone(), input_shape))]); let input_constraints = self.input_constraints(source, &env); - let body = self.check_body(intent, &output_shape, &mut env, &mut locals)?; + let body = self.check_body(action, &output_shape, &mut env, &mut locals)?; - Some(TypedIntent { - name: intent.name.clone(), + Some(TypedAction { + name: action.name.clone(), input: input_binding.ty.clone(), output: output_shape.coord, - profile: intent.profile.clone(), - budget: intent.budget.clone(), + profile: action.profile.clone(), + budget: action.budget.clone(), input_binding, input_constraints, body, @@ -657,13 +657,13 @@ impl<'a> TypeChecker<'a> { fn input_constraints( &mut self, - intent: &IntentDecl, + action: &ActionDecl, env: &BTreeMap, ) -> Vec { let mut out = Vec::new(); let mut index = 0usize; - for clause in &intent.clauses { - if let IntentClause::Where(predicates) = clause { + for clause in &action.clauses { + if let ActionClause::Where(predicates) = clause { for predicate in predicates { if let Some(predicate) = self.check_predicate(predicate, env) { out.push(InputConstraint { @@ -681,16 +681,16 @@ impl<'a> TypeChecker<'a> { fn check_body( &mut self, - intent: &ResolvedIntent, + action: &ResolvedAction, output_shape: &TypeShape, env: &mut BTreeMap, locals: &mut Vec, ) -> Option { - let source = &intent.source; + let source = &action.source; let mut state = BodyState::default(); for stmt in &source.body.stmts { - self.check_body_stmt(intent, output_shape, stmt, env, locals, &mut state); + self.check_body_stmt(action, output_shape, stmt, env, locals, &mut state); } if let Some(result) = state.result { @@ -703,7 +703,7 @@ impl<'a> TypeChecker<'a> { self.errors.push(error( CompilerStage::TypeCheck, CompilerErrorKind::TypeMismatch, - "intent body must return a value", + "action body must return a value", source.span, )); None @@ -712,7 +712,7 @@ impl<'a> TypeChecker<'a> { fn check_body_stmt( &mut self, - intent: &ResolvedIntent, + action: &ResolvedAction, output_shape: &TypeShape, stmt: &Stmt, env: &mut BTreeMap, @@ -727,7 +727,7 @@ impl<'a> TypeChecker<'a> { els, span, } => self.check_let_stmt( - intent, + action, LetStatement { name, ty: ty.as_ref(), @@ -750,12 +750,12 @@ impl<'a> TypeChecker<'a> { CompilerStage::TypeCheck, CompilerErrorKind::TypeMismatch, "return value does not match declared output type", - intent.source.span, + action.source.span, )); } } Stmt::Effect { call, span, .. } => { - if self.check_effect_profile(intent, call, *span) { + if self.check_effect_profile(action, call, *span) { self.unsupported_stmt(*span, "effect statement"); } } @@ -945,28 +945,28 @@ impl<'a> TypeChecker<'a> { fn check_let_stmt( &mut self, - intent: &ResolvedIntent, + action: &ResolvedAction, stmt: LetStatement<'_>, env: &mut BTreeMap, locals: &mut Vec, state: &mut BodyState, ) { if let Some(handler) = stmt.handler { - self.check_effectful_let(intent, stmt, handler, env, locals, state); + self.check_effectful_let(action, stmt, handler, env, locals, state); } else { - self.check_pure_let(intent, stmt, env, locals, state); + self.check_pure_let(action, stmt, env, locals, state); } } fn check_pure_let( &mut self, - intent: &ResolvedIntent, + action: &ResolvedAction, stmt: LetStatement<'_>, env: &mut BTreeMap, locals: &mut Vec, state: &mut BodyState, ) { - if !self.check_known_effect_profiles(intent, stmt.value) { + if !self.check_known_effect_profiles(action, stmt.value) { return; } let Some(value) = self.check_expr(stmt.value, env) else { @@ -1008,14 +1008,14 @@ impl<'a> TypeChecker<'a> { fn check_effectful_let( &mut self, - intent: &ResolvedIntent, + action: &ResolvedAction, stmt: LetStatement<'_>, handler: &ObstructionHandler, env: &mut BTreeMap, locals: &mut Vec, state: &mut BodyState, ) { - if !self.check_effect_profile(intent, stmt.value, stmt.span) { + if !self.check_effect_profile(action, stmt.value, stmt.span) { return; } let Some(binding_shape) = self.effect_binding_shape(stmt.ty, stmt.span) else { @@ -1044,7 +1044,7 @@ impl<'a> TypeChecker<'a> { env.insert(stmt.name.to_owned(), (local, binding_shape)); } - fn check_effect_profile(&mut self, intent: &ResolvedIntent, call: &Expr, span: Span) -> bool { + fn check_effect_profile(&mut self, action: &ResolvedAction, call: &Expr, span: Span) -> bool { let Some(effect) = effect_coordinate(call) else { self.unsupported_stmt(span, "effect call"); return false; @@ -1058,13 +1058,13 @@ impl<'a> TypeChecker<'a> { )); return false; }; - let Some(allowed_write_classes) = &intent.allowed_write_classes else { + let Some(allowed_write_classes) = &action.allowed_write_classes else { self.errors.push(error( CompilerStage::TypeCheck, CompilerErrorKind::MissingContextFact, format!( "operation profile `{}` has no write-class compiler context fact", - intent.profile + action.profile ), span, )); @@ -1078,7 +1078,7 @@ impl<'a> TypeChecker<'a> { CompilerErrorKind::ProfileEffectMismatch, format!( "effect `{effect}` requires write class {write_class:?}, which profile `{}` does not allow", - intent.profile + action.profile ), span, )); @@ -1231,11 +1231,11 @@ impl<'a> TypeChecker<'a> { }) } - fn check_known_effect_profiles(&mut self, intent: &ResolvedIntent, expr: &Expr) -> bool { + fn check_known_effect_profiles(&mut self, action: &ResolvedAction, expr: &Expr) -> bool { let mut accepted = true; if let Some(effect) = effect_coordinate(expr) { if self.resolved.effect_write_classes.contains_key(&effect) { - accepted &= self.check_effect_profile(intent, expr, expr_span(expr)); + accepted &= self.check_effect_profile(action, expr, expr_span(expr)); } } match expr { @@ -1245,32 +1245,32 @@ impl<'a> TypeChecker<'a> { | Expr::Bool { .. } | Expr::Digest { .. } => {} Expr::Field { base, .. } => { - accepted &= self.check_known_effect_profiles(intent, base); + accepted &= self.check_known_effect_profiles(action, base); } Expr::Call { callee, args, .. } => { - accepted &= self.check_known_effect_profiles(intent, callee); + accepted &= self.check_known_effect_profiles(action, callee); for arg in args { - accepted &= self.check_known_effect_profiles(intent, arg); + accepted &= self.check_known_effect_profiles(action, arg); } } Expr::Unary { operand, .. } => { - accepted &= self.check_known_effect_profiles(intent, operand); + accepted &= self.check_known_effect_profiles(action, operand); } Expr::Binary { lhs, rhs, .. } => { - accepted &= self.check_known_effect_profiles(intent, lhs); - accepted &= self.check_known_effect_profiles(intent, rhs); + accepted &= self.check_known_effect_profiles(action, lhs); + accepted &= self.check_known_effect_profiles(action, rhs); } Expr::Record { entries, .. } => { for entry in entries { - accepted &= self.check_known_effect_profiles_in_record_entry(intent, entry); + accepted &= self.check_known_effect_profiles_in_record_entry(action, entry); } } Expr::If { cond, then, els, .. } => { - accepted &= self.check_known_effect_profiles(intent, cond); - accepted &= self.check_known_effect_profiles(intent, then); - accepted &= self.check_known_effect_profiles(intent, els); + accepted &= self.check_known_effect_profiles(action, cond); + accepted &= self.check_known_effect_profiles(action, then); + accepted &= self.check_known_effect_profiles(action, els); } Expr::IfYield { pred, @@ -1278,21 +1278,21 @@ impl<'a> TypeChecker<'a> { else_block, .. } => { - accepted &= self.check_known_effect_profiles(intent, pred); - accepted &= self.check_known_effect_profiles_in_yield_block(intent, then_block); - accepted &= self.check_known_effect_profiles_in_yield_block(intent, else_block); + accepted &= self.check_known_effect_profiles(action, pred); + accepted &= self.check_known_effect_profiles_in_yield_block(action, then_block); + accepted &= self.check_known_effect_profiles_in_yield_block(action, else_block); } Expr::VariantLit { payload, .. } => { if let Some(payload) = payload { - accepted &= self.check_known_effect_profiles(intent, payload); + accepted &= self.check_known_effect_profiles(action, payload); } } Expr::Match { scrutinee, arms, .. } => { - accepted &= self.check_known_effect_profiles(intent, scrutinee); + accepted &= self.check_known_effect_profiles(action, scrutinee); for arm in arms { - accepted &= self.check_known_effect_profiles(intent, &arm.body); + accepted &= self.check_known_effect_profiles(action, &arm.body); } } } @@ -1301,12 +1301,12 @@ impl<'a> TypeChecker<'a> { fn check_known_effect_profiles_in_record_entry( &mut self, - intent: &ResolvedIntent, + action: &ResolvedAction, entry: &RecordEntry, ) -> bool { match entry { RecordEntry::Field { value, .. } | RecordEntry::Spread(value) => { - self.check_known_effect_profiles(intent, value) + self.check_known_effect_profiles(action, value) } RecordEntry::Shorthand { .. } => true, } @@ -1314,32 +1314,32 @@ impl<'a> TypeChecker<'a> { fn check_known_effect_profiles_in_yield_block( &mut self, - intent: &ResolvedIntent, + action: &ResolvedAction, block: &YieldBlock, ) -> bool { let mut accepted = true; for stmt in &block.stmts { - accepted &= self.check_known_effect_profiles_in_stmt(intent, stmt); + accepted &= self.check_known_effect_profiles_in_stmt(action, stmt); } - accepted &= self.check_known_effect_profiles(intent, &block.value); + accepted &= self.check_known_effect_profiles(action, &block.value); accepted } fn check_known_effect_profiles_in_block( &mut self, - intent: &ResolvedIntent, + action: &ResolvedAction, block: &Block, ) -> bool { let mut accepted = true; for stmt in &block.stmts { - accepted &= self.check_known_effect_profiles_in_stmt(intent, stmt); + accepted &= self.check_known_effect_profiles_in_stmt(action, stmt); } accepted } fn check_known_effect_profiles_in_stmt( &mut self, - intent: &ResolvedIntent, + action: &ResolvedAction, stmt: &Stmt, ) -> bool { match stmt { @@ -1347,45 +1347,45 @@ impl<'a> TypeChecker<'a> { value, els, span, .. } => { if els.is_some() { - self.check_effect_profile(intent, value, *span) + self.check_effect_profile(action, value, *span) } else { - self.check_known_effect_profiles(intent, value) + self.check_known_effect_profiles(action, value) } } - Stmt::Effect { call, span, .. } => self.check_effect_profile(intent, call, *span), + Stmt::Effect { call, span, .. } => self.check_effect_profile(action, call, *span), Stmt::Require { predicate, .. } | Stmt::Guarantee { predicate, .. } - | Stmt::Assert { predicate, .. } => self.check_known_effect_profiles(intent, predicate), + | Stmt::Assert { predicate, .. } => self.check_known_effect_profiles(action, predicate), Stmt::If { cond, then_block, els, .. } => { - let mut accepted = self.check_known_effect_profiles(intent, cond); - accepted &= self.check_known_effect_profiles_in_block(intent, then_block); + let mut accepted = self.check_known_effect_profiles(action, cond); + accepted &= self.check_known_effect_profiles_in_block(action, then_block); if let Some(els) = els { - accepted &= self.check_known_effect_profiles_in_else(intent, els); + accepted &= self.check_known_effect_profiles_in_else(action, els); } accepted } Stmt::For { iter, body, .. } => { - let mut accepted = self.check_known_effect_profiles(intent, iter); - accepted &= self.check_known_effect_profiles_in_block(intent, body); + let mut accepted = self.check_known_effect_profiles(action, iter); + accepted &= self.check_known_effect_profiles_in_block(action, body); accepted } - Stmt::Return { value, .. } => self.check_known_effect_profiles(intent, value), + Stmt::Return { value, .. } => self.check_known_effect_profiles(action, value), } } fn check_known_effect_profiles_in_else( &mut self, - intent: &ResolvedIntent, + action: &ResolvedAction, els: &ElseClause, ) -> bool { match els { - ElseClause::Block(block) => self.check_known_effect_profiles_in_block(intent, block), - ElseClause::If(stmt) => self.check_known_effect_profiles_in_stmt(intent, stmt), + ElseClause::Block(block) => self.check_known_effect_profiles_in_block(action, block), + ElseClause::If(stmt) => self.check_known_effect_profiles_in_stmt(action, stmt), } } diff --git a/crates/edict-syntax/src/core_ir.rs b/crates/edict-syntax/src/core_ir.rs index bde995f..d1bb10f 100644 --- a/crates/edict-syntax/src/core_ir.rs +++ b/crates/edict-syntax/src/core_ir.rs @@ -18,7 +18,7 @@ pub struct CoreModule { pub coordinate: String, pub imports: Vec, pub types: BTreeMap, - pub intents: BTreeMap, + pub actions: BTreeMap, pub required_core_capabilities: Vec, } @@ -195,9 +195,9 @@ pub struct CoreBudget { pub max_output_bytes: u64, } -/// Core intent shape for the initial lowerer. +/// Core action shape for the initial lowerer. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct CoreIntent { +pub struct CoreAction { pub input: String, pub output: String, pub required_operation_profile: String, diff --git a/crates/edict-syntax/src/highlight.rs b/crates/edict-syntax/src/highlight.rs index 1ed34b1..62c7ae7 100644 --- a/crates/edict-syntax/src/highlight.rs +++ b/crates/edict-syntax/src/highlight.rs @@ -107,7 +107,7 @@ fn is_highlight_keyword(text: &str) -> bool { | "type" | "enum" | "variant" - | "intent" + | "action" | "returns" | "profile" | "implements" diff --git a/crates/edict-syntax/src/lib.rs b/crates/edict-syntax/src/lib.rs index 94f7310..130446b 100644 --- a/crates/edict-syntax/src/lib.rs +++ b/crates/edict-syntax/src/lib.rs @@ -2,13 +2,13 @@ //! //! Scope is `edict.implementation/minimal-v1` (see SPEC - Edict Language v1). //! Phase 1 parses: package and imports; `type` records and refined scalars; -//! `enum` declarations and `variant` types; `intent`s with their clauses; +//! `enum` declarations and `variant` types; `action`s with their clauses; //! `let`/`return`/`require`/`guarantee`/`assert`; the `if` family; bounded //! `for`; calls and type-calls; variant-literal constructors; and `match`. //! Phase 2 currently exposes the `validate_surface` compiler stage for //! source-AST constraints that do not require import resolution, resolved typing, //! target/lawpack facts, or Core IR: bounded runtime `String`/`Bytes`, required -//! intent operation-mode/budget/basis clauses, duplicate singleton intent +//! action operation-mode/budget/basis clauses, duplicate singleton action //! clauses, module namespace collisions, and source binder shadowing. //! Phase 3 begins the executable compiler spine with `resolve_module`, //! `type_check`, `lower_core`, and `compile_to_core`, currently covering the @@ -114,8 +114,8 @@ pub use canonical::{ }; pub use compiler::{ compile_to_core, lower_core, resolve_module, type_check, CompilerContext, CompilerError, - CompilerErrorKind, CompilerStage, ResolvedIntent, ResolvedModule, ResolvedTypeDecl, - TypedIntent, TypedModule, + CompilerErrorKind, CompilerStage, ResolvedAction, ResolvedModule, ResolvedTypeDecl, + TypedAction, TypedModule, }; pub use contract_bundle::{ assemble_contract_bundle, assemble_contract_bundle_from_target_ir, @@ -128,7 +128,7 @@ pub use contract_bundle::{ SourceArtifactRef, SuppliedDigest, SuppliedTargetIrResource, CONTRACT_BUNDLE_API_VERSION, }; pub use core_ir::{ - CompareOp, CoreBlock, CoreBudget, CoreExpr, CoreImport, CoreImportKind, CoreIntent, CoreModule, + CompareOp, CoreAction, CoreBlock, CoreBudget, CoreExpr, CoreImport, CoreImportKind, CoreModule, CoreNode, CoreObstructionArm, CoreObstructionReason, CorePredicate, CoreRequireFailureArm, CoreType, CoreValue, InputConstraint, InputConstraintSource, LocalRef, ResourceRef, CORE_API_VERSION, @@ -180,7 +180,7 @@ pub use provider_lowering::{ }; pub use semantic::{validate_module, validate_surface, SemanticError, SemanticErrorKind}; pub use target_ir::{ - lower_to_target_ir, TargetEffectLowering, TargetIrArtifact, TargetIrIntent, + lower_to_target_ir, TargetEffectLowering, TargetIrAction, TargetIrArtifact, TargetIrLoweringFacts, TargetIrRequireFailure, TargetIrRequirement, TargetIrStep, TargetLoweringFailure, TargetLoweringFailureKind, TargetLoweringReport, TargetLoweringStatus, ECHO_DPO_TARGET_PROFILE, ECHO_SPAN_IR_DOMAIN, GITWARP_COMMIT_REDUCER_IR_DOMAIN, diff --git a/crates/edict-syntax/src/parser.rs b/crates/edict-syntax/src/parser.rs index d82face..087b886 100644 --- a/crates/edict-syntax/src/parser.rs +++ b/crates/edict-syntax/src/parser.rs @@ -4,8 +4,8 @@ //! 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, + ActionClause, ActionDecl, BinOp, Block, BoundRef, ContinueObstructedArm, Decl, ElseClause, + EnumDecl, Expr, FieldConstraint, FieldDecl, Import, ImportKind, MatchArm, Module, ObstructionArm, ObstructionHandler, ObstructionTarget, PackageRef, Param, RecordEntry, RequireElseArm, ScalarRefine, Stmt, TypeDecl, TypeExpr, TypeRef, UnOp, VariantCase, YieldBlock, }; @@ -126,7 +126,7 @@ fn is_keyword(s: &str) -> bool { | "type" | "enum" | "variant" - | "intent" + | "action" | "returns" | "profile" | "implements" @@ -525,11 +525,11 @@ impl Parser { Ok(Decl::Type(self.type_decl()?)) } else if self.at_kw("enum") { Ok(Decl::Enum(self.enum_decl()?)) - } else if self.at_kw("intent") { - Ok(Decl::Intent(self.intent_decl()?)) + } else if self.at_kw("action") { + Ok(Decl::Action(self.action_decl()?)) } else { self.err(format!( - "expected `type`, `enum`, or `intent` declaration, found {:?}", + "expected `type`, `enum`, or `action` declaration, found {:?}", self.peek() )) } @@ -781,11 +781,11 @@ impl Parser { Ok(Some(max)) } - // --- intents --- + // --- actions --- - fn intent_decl(&mut self) -> Result { + fn action_decl(&mut self) -> Result { let start = self.peek_span().start; - self.expect_kw("intent")?; + self.expect_kw("action")?; let name = self.ident()?; self.expect(&TokenKind::LParen)?; let mut params = Vec::new(); @@ -811,39 +811,39 @@ impl Parser { loop { if self.at_kw("profile") { self.idx += 1; - clauses.push(IntentClause::Profile(self.path()?)); + clauses.push(ActionClause::Profile(self.path()?)); } else if self.at_kw("implements") { self.idx += 1; - clauses.push(IntentClause::Implements(self.path()?)); + clauses.push(ActionClause::Implements(self.path()?)); } else if self.at_kw("basis") { self.idx += 1; if self.eat_kw("none") { - clauses.push(IntentClause::Basis(None)); + clauses.push(ActionClause::Basis(None)); } else { - clauses.push(IntentClause::Basis(Some(self.expr()?))); + clauses.push(ActionClause::Basis(Some(self.expr()?))); } } else if self.at_kw("footprint") { self.idx += 1; self.expect(&TokenKind::Le)?; - clauses.push(IntentClause::Footprint(self.path()?)); + clauses.push(ActionClause::Footprint(self.path()?)); } else if self.at_kw("budget") { self.idx += 1; self.expect(&TokenKind::Le)?; - clauses.push(IntentClause::Budget(self.path()?)); + clauses.push(ActionClause::Budget(self.path()?)); } else if self.at_kw("where") { self.idx += 1; let mut preds = vec![self.expr()?]; while self.eat(&TokenKind::Comma) { preds.push(self.expr()?); } - clauses.push(IntentClause::Where(preds)); + clauses.push(ActionClause::Where(preds)); } else { break; } } let body = self.block()?; - Ok(IntentDecl { + Ok(ActionDecl { name, params, returns, diff --git a/crates/edict-syntax/src/semantic.rs b/crates/edict-syntax/src/semantic.rs index ce2425b..92df25e 100644 --- a/crates/edict-syntax/src/semantic.rs +++ b/crates/edict-syntax/src/semantic.rs @@ -4,7 +4,7 @@ use std::collections::BTreeSet; use crate::ast::{ - Block, Decl, ElseClause, Expr, IntentClause, IntentDecl, Module, ObstructionHandler, + ActionClause, ActionDecl, Block, Decl, ElseClause, Expr, Module, ObstructionHandler, ObstructionTarget, RecordEntry, RequireElseArm, Stmt, TypeExpr, TypeRef, YieldBlock, }; use crate::token::Span; @@ -16,7 +16,7 @@ pub enum SemanticErrorKind { MissingOperationMode, MissingBudget, MissingBasis, - DuplicateIntentClause, + DuplicateActionClause, DuplicateName, ShadowedName, } @@ -36,7 +36,7 @@ impl SemanticErrorKind { SemanticErrorKind::MissingOperationMode => "MissingOperationMode", SemanticErrorKind::MissingBudget => "MissingBudget", SemanticErrorKind::MissingBasis => "MissingBasis", - SemanticErrorKind::DuplicateIntentClause => "DuplicateIntentClause", + SemanticErrorKind::DuplicateActionClause => "DuplicateActionClause", SemanticErrorKind::DuplicateName => "DuplicateName", SemanticErrorKind::ShadowedName => "ShadowedName", } @@ -81,9 +81,9 @@ pub fn validate_surface(module: &Module) -> Result<(), Vec> { match decl { Decl::Type(decl) => validate_type_expr(&decl.body, decl.span, &mut errors), Decl::Enum(_) => {} - Decl::Intent(intent) => { + Decl::Action(action) => { let mut names = NameEnv::new(protected_names.clone()); - validate_intent(intent, &mut names, &mut errors); + validate_action(action, &mut names, &mut errors); } } } @@ -114,7 +114,7 @@ fn collect_module_names(module: &Module, errors: &mut Vec) -> BTr match decl { Decl::Type(decl) => record_module_name(&mut names, &decl.name, decl.span, errors), Decl::Enum(decl) => record_module_name(&mut names, &decl.name, decl.span, errors), - Decl::Intent(decl) => record_module_name(&mut names, &decl.name, decl.span, errors), + Decl::Action(decl) => record_module_name(&mut names, &decl.name, decl.span, errors), } } names @@ -198,7 +198,7 @@ impl NameEnv { } } -fn validate_intent(intent: &IntentDecl, names: &mut NameEnv, errors: &mut Vec) { +fn validate_action(action: &ActionDecl, names: &mut NameEnv, errors: &mut Vec) { let mut profile = None; let mut implements = None; let mut basis = None; @@ -206,29 +206,29 @@ fn validate_intent(intent: &IntentDecl, names: &mut NameEnv, errors: &mut Vec { - record_singleton("profile", intent.span, &mut profile, errors); + ActionClause::Profile(_) => { + record_singleton("profile", action.span, &mut profile, errors); } - IntentClause::Implements(_) => { - record_singleton("implements", intent.span, &mut implements, errors); + ActionClause::Implements(_) => { + record_singleton("implements", action.span, &mut implements, errors); } - IntentClause::Basis(expr) => { - record_singleton("basis", intent.span, &mut basis, errors); + ActionClause::Basis(expr) => { + record_singleton("basis", action.span, &mut basis, errors); if let Some(expr) = expr { validate_expr(expr, names, errors); } } - IntentClause::Footprint(_) => { - record_singleton("footprint", intent.span, &mut footprint, errors); + ActionClause::Footprint(_) => { + record_singleton("footprint", action.span, &mut footprint, errors); } - IntentClause::Budget(_) => record_singleton("budget", intent.span, &mut budget, errors), - IntentClause::Where(predicates) => { + ActionClause::Budget(_) => record_singleton("budget", action.span, &mut budget, errors), + ActionClause::Where(predicates) => { for predicate in predicates { validate_expr(predicate, names, errors); } @@ -239,30 +239,30 @@ fn validate_intent(intent: &IntentDecl, names: &mut NameEnv, errors: &mut Vec, ) { if slot.replace(()).is_some() { - // Intent clauses do not currently preserve their own spans, so duplicate - // clause diagnostics report at intent granularity. + // Action clauses do not currently preserve their own spans, so duplicate + // clause diagnostics report at action granularity. errors.push(error( - SemanticErrorKind::DuplicateIntentClause, - format!("intent contains duplicate `{name}` clause"), + SemanticErrorKind::DuplicateActionClause, + format!("action contains duplicate `{name}` clause"), span, )); } @@ -572,8 +572,8 @@ mod semantic_error_kind_codes { (SemanticErrorKind::MissingBudget, "MissingBudget"), (SemanticErrorKind::MissingBasis, "MissingBasis"), ( - SemanticErrorKind::DuplicateIntentClause, - "DuplicateIntentClause", + SemanticErrorKind::DuplicateActionClause, + "DuplicateActionClause", ), (SemanticErrorKind::DuplicateName, "DuplicateName"), (SemanticErrorKind::ShadowedName, "ShadowedName"), diff --git a/crates/edict-syntax/src/target_ir.rs b/crates/edict-syntax/src/target_ir.rs index 4d910b0..e3c8fd3 100644 --- a/crates/edict-syntax/src/target_ir.rs +++ b/crates/edict-syntax/src/target_ir.rs @@ -7,7 +7,7 @@ use std::collections::{BTreeMap, BTreeSet}; use crate::core_ir::{ - CoreBudget, CoreExpr, CoreIntent, CoreModule, CoreNode, CoreObstructionArm, + CoreAction, CoreBudget, CoreExpr, CoreModule, CoreNode, CoreObstructionArm, CoreObstructionReason, CorePredicate, CoreRequireFailureArm, InputConstraint, LocalRef, ResourceRef, CORE_API_VERSION, }; @@ -74,7 +74,7 @@ impl TargetIrLoweringFacts { if report.status != LowerabilityStatus::Native { return Err(TargetLoweringFailure { kind: TargetLoweringFailureKind::UnsupportedLowerabilityReport, - intent: None, + action: None, node_index: None, detail: format!("{:?}", report.status), }); @@ -82,7 +82,7 @@ impl TargetIrLoweringFacts { if target_profile.coordinate != report.target_profile { return Err(TargetLoweringFailure { kind: TargetLoweringFailureKind::UnsupportedTargetProfile, - intent: None, + action: None, node_index: None, detail: target_profile.coordinate, }); @@ -90,7 +90,7 @@ impl TargetIrLoweringFacts { if !target_profile.is_digest_locked() { return Err(TargetLoweringFailure { kind: TargetLoweringFailureKind::UndigestedTargetProfile, - intent: None, + action: None, node_index: None, detail: target_profile .digest @@ -159,7 +159,7 @@ pub enum TargetLoweringFailureKind { #[derive(Debug, Clone, PartialEq, Eq)] pub struct TargetLoweringFailure { pub kind: TargetLoweringFailureKind, - pub intent: Option, + pub action: Option, pub node_index: Option, pub detail: String, } @@ -176,11 +176,11 @@ pub struct TargetIrArtifact { pub domain: String, pub target_profile: ResourceRef, pub source_core_coordinate: String, - pub intents: BTreeMap, + pub actions: BTreeMap, } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct TargetIrIntent { +pub struct TargetIrAction { pub operation_profile: String, pub input_constraints: Vec, pub core_evaluation_budget: CoreBudget, @@ -244,17 +244,17 @@ pub fn lower_to_target_ir( effect_lowerings: &effect_lowerings, }; let mut failures = Vec::new(); - let mut intents = BTreeMap::new(); + let mut actions = BTreeMap::new(); - for (intent_name, intent) in &core.intents { - let lowered = lower_intent( - intent_name, - intent, + for (action_name, action) in &core.actions { + let lowered = lower_action( + action_name, + action, &operation_profiles, &context, &mut failures, ); - intents.insert(intent_name.clone(), lowered); + actions.insert(action_name.clone(), lowered); } if failures.is_empty() { @@ -264,7 +264,7 @@ pub fn lower_to_target_ir( domain: facts.target_ir_domain.clone(), target_profile: facts.target_profile.clone(), source_core_coordinate: core.coordinate.clone(), - intents, + actions, }), failures, } @@ -280,7 +280,7 @@ fn validate_target_selection( else { return Err(vec![TargetLoweringFailure { kind: TargetLoweringFailureKind::UnsupportedTargetProfile, - intent: None, + action: None, node_index: None, detail: facts.target_profile.coordinate.clone(), }]); @@ -288,7 +288,7 @@ fn validate_target_selection( if !facts.target_profile.is_digest_locked() { return Err(vec![TargetLoweringFailure { kind: TargetLoweringFailureKind::UndigestedTargetProfile, - intent: None, + action: None, node_index: None, detail: facts .target_profile @@ -300,7 +300,7 @@ fn validate_target_selection( if facts.target_ir_domain != target_selection.target_ir_domain { return Err(vec![TargetLoweringFailure { kind: TargetLoweringFailureKind::UnsupportedTargetIrDomain, - intent: None, + action: None, node_index: None, detail: facts.target_ir_domain.clone(), }]); @@ -312,17 +312,17 @@ fn validate_core_module(core: &CoreModule) -> Vec { if core.api_version != CORE_API_VERSION { return vec![TargetLoweringFailure { kind: TargetLoweringFailureKind::UnsupportedCoreAbi, - intent: None, + action: None, node_index: None, detail: core.api_version.clone(), }]; } - if core.intents.is_empty() { + if core.actions.is_empty() { return vec![TargetLoweringFailure { kind: TargetLoweringFailureKind::NoTargetSteps, - intent: None, + action: None, node_index: None, - detail: "core module has no target-owned intents".to_owned(), + detail: "core module has no target-owned actions".to_owned(), }]; } let floating_imports = core @@ -331,7 +331,7 @@ fn validate_core_module(core: &CoreModule) -> Vec { .filter(|import| !import.resource.is_digest_locked()) .map(|import| TargetLoweringFailure { kind: TargetLoweringFailureKind::UndigestedCoreImport, - intent: None, + action: None, node_index: None, detail: import.resource.coordinate.clone(), }) @@ -343,49 +343,49 @@ fn validate_core_module(core: &CoreModule) -> Vec { .iter() .map(|capability| TargetLoweringFailure { kind: TargetLoweringFailureKind::UnsupportedCoreCapability, - intent: None, + action: None, node_index: None, detail: capability.clone(), }) .collect() } -fn lower_intent( - intent_name: &str, - intent: &CoreIntent, +fn lower_action( + action_name: &str, + action: &CoreAction, operation_profiles: &BTreeSet<&str>, context: &TargetLoweringContext<'_>, failures: &mut Vec, -) -> TargetIrIntent { - if !operation_profiles.contains(intent.required_operation_profile.as_str()) { +) -> TargetIrAction { + if !operation_profiles.contains(action.required_operation_profile.as_str()) { failures.push(TargetLoweringFailure { kind: TargetLoweringFailureKind::MissingOperationProfile, - intent: Some(intent_name.to_owned()), + action: Some(action_name.to_owned()), node_index: None, - detail: intent.required_operation_profile.clone(), + detail: action.required_operation_profile.clone(), }); } - let mut state = IntentLoweringState::default(); - for (node_index, node) in intent.body.nodes.iter().enumerate() { - lower_node(intent_name, node_index, node, context, &mut state, failures); + let mut state = ActionLoweringState::default(); + for (node_index, node) in action.body.nodes.iter().enumerate() { + lower_node(action_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() && action.body.nodes.is_empty() { failures.push(TargetLoweringFailure { kind: TargetLoweringFailureKind::NoTargetSteps, - intent: Some(intent_name.to_owned()), + action: Some(action_name.to_owned()), node_index: None, - detail: "intent has no target-owned steps".to_owned(), + detail: "action has no target-owned steps".to_owned(), }); } - TargetIrIntent { - operation_profile: intent.required_operation_profile.clone(), - input_constraints: intent.input_constraints.clone(), - core_evaluation_budget: intent.core_evaluation_budget.clone(), + TargetIrAction { + operation_profile: action.required_operation_profile.clone(), + input_constraints: action.input_constraints.clone(), + core_evaluation_budget: action.core_evaluation_budget.clone(), requirements: state.requirements, steps: state.steps, - result: intent.body.result.clone(), + result: action.body.result.clone(), } } @@ -396,18 +396,18 @@ struct TargetLoweringContext<'a> { } #[derive(Default)] -struct IntentLoweringState { +struct ActionLoweringState { requirements: Vec, steps: Vec, step_outputs: BTreeSet, } fn lower_node( - intent_name: &str, + action_name: &str, node_index: usize, node: &CoreNode, context: &TargetLoweringContext<'_>, - state: &mut IntentLoweringState, + state: &mut ActionLoweringState, failures: &mut Vec, ) { match node { @@ -419,7 +419,7 @@ fn lower_node( } => { let steps_before = state.steps.len(); lower_effect_node( - intent_name, + action_name, node_index, EffectNodeParts { binding, @@ -437,12 +437,12 @@ fn lower_node( } CoreNode::Let { .. } => failures.push(TargetLoweringFailure { kind: TargetLoweringFailureKind::UnsupportedCoreNode, - intent: Some(intent_name.to_owned()), + action: Some(action_name.to_owned()), node_index: Some(node_index), detail: "let".to_owned(), }), CoreNode::Require { predicate, arm } => lower_require_node( - intent_name, + action_name, node_index, predicate, arm, @@ -454,18 +454,18 @@ fn lower_node( } fn lower_require_node( - intent_name: &str, + action_name: &str, node_index: usize, predicate: &CorePredicate, arm: &CoreRequireFailureArm, context: &TargetLoweringContext<'_>, - state: &mut IntentLoweringState, + state: &mut ActionLoweringState, failures: &mut Vec, ) { if !context.target_selection.supports_requirements { failures.push(TargetLoweringFailure { kind: TargetLoweringFailureKind::UnsupportedTargetFeature, - intent: Some(intent_name.to_owned()), + action: Some(action_name.to_owned()), node_index: Some(node_index), detail: "obstruction_requirement".to_owned(), }); @@ -474,7 +474,7 @@ fn lower_require_node( if require_references_step_output(predicate, arm, &state.step_outputs) { failures.push(TargetLoweringFailure { kind: TargetLoweringFailureKind::UnsupportedTargetFeature, - intent: Some(intent_name.to_owned()), + action: Some(action_name.to_owned()), node_index: Some(node_index), detail: "obstruction_requirement_step_output_dependency".to_owned(), }); @@ -483,14 +483,14 @@ fn lower_require_node( if !state.steps.is_empty() { failures.push(TargetLoweringFailure { kind: TargetLoweringFailureKind::UnsupportedTargetFeature, - intent: Some(intent_name.to_owned()), + action: Some(action_name.to_owned()), node_index: Some(node_index), detail: "obstruction_requirement_after_target_step".to_owned(), }); return; } state.requirements.push(TargetIrRequirement { - id: format!("{}.require.{}", intent_name, state.requirements.len()), + id: format!("{}.require.{}", action_name, state.requirements.len()), predicate: predicate.clone(), on_failure: target_ir_require_failure(arm), }); @@ -571,7 +571,7 @@ struct EffectNodeParts<'a> { } fn lower_effect_node( - intent_name: &str, + action_name: &str, node_index: usize, node: EffectNodeParts<'_>, context: &TargetLoweringContext<'_>, @@ -590,7 +590,7 @@ fn lower_effect_node( { failures.push(TargetLoweringFailure { kind: TargetLoweringFailureKind::UnsupportedTargetIntrinsic, - intent: Some(intent_name.to_owned()), + action: Some(action_name.to_owned()), node_index: Some(node_index), detail: lowering.target_intrinsic.clone(), }); @@ -606,7 +606,7 @@ fn lower_effect_node( failures.extend(unsupported_obstructions.into_iter().map(|failure| { TargetLoweringFailure { kind: TargetLoweringFailureKind::MissingObstruction, - intent: Some(intent_name.to_owned()), + action: Some(action_name.to_owned()), node_index: Some(node_index), detail: failure, } @@ -614,7 +614,7 @@ fn lower_effect_node( return; } steps.push(TargetIrStep { - id: format!("{}.step.{}", intent_name, steps.len()), + id: format!("{}.step.{}", action_name, steps.len()), binding: node.binding.clone(), effect: node.effect.to_owned(), target_intrinsic: lowering.target_intrinsic.clone(), @@ -625,13 +625,13 @@ fn lower_effect_node( } [] => failures.push(TargetLoweringFailure { kind: TargetLoweringFailureKind::MissingEffectLowering, - intent: Some(intent_name.to_owned()), + action: Some(action_name.to_owned()), node_index: Some(node_index), detail: node.effect.to_owned(), }), _ => failures.push(TargetLoweringFailure { kind: TargetLoweringFailureKind::AmbiguousEffectLowering, - intent: Some(intent_name.to_owned()), + action: Some(action_name.to_owned()), node_index: Some(node_index), detail: node.effect.to_owned(), }), diff --git a/crates/edict-syntax/tests/authority_facts.rs b/crates/edict-syntax/tests/authority_facts.rs index add9d27..5f41e7c 100644 --- a/crates/edict-syntax/tests/authority_facts.rs +++ b/crates/edict-syntax/tests/authority_facts.rs @@ -29,15 +29,15 @@ fn file_backed_authority_facts_compile_bounded_hello() { .expect("authority facts load"); let module = edict_syntax::parse_module(BOUNDED_HELLO).expect("fixture parses"); let core = compile_to_core(&module, &context).expect("loaded facts compile fixture"); - let intent = core.intents.get("sayHello").expect("compiled intent"); + let action = core.actions.get("sayHello").expect("compiled action"); assert_eq!( - intent.required_operation_profile, + action.required_operation_profile, "continuum.profile.read-only/v1" ); - assert_eq!(intent.core_evaluation_budget.max_steps, 64); - assert_eq!(intent.core_evaluation_budget.max_allocated_bytes, 4096); - assert_eq!(intent.core_evaluation_budget.max_output_bytes, 1024); + assert_eq!(action.core_evaluation_budget.max_steps, 64); + assert_eq!(action.core_evaluation_budget.max_allocated_bytes, 4096); + assert_eq!(action.core_evaluation_budget.max_output_bytes, 1024); } #[test] @@ -56,7 +56,7 @@ fn file_backed_authority_facts_reject_write_effect_profile_mismatch() { "package a.b@1;\n\ type Input = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.readOnly\n\ basis none\n\ budget <= p.tiny {\n\ diff --git a/crates/edict-syntax/tests/authority_facts_cbor.rs b/crates/edict-syntax/tests/authority_facts_cbor.rs index 7359f04..d6b7438 100644 --- a/crates/edict-syntax/tests/authority_facts_cbor.rs +++ b/crates/edict-syntax/tests/authority_facts_cbor.rs @@ -12,7 +12,7 @@ const EFFECTFUL_SOURCE: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Receipt = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.effectful\n\ basis none\n\ budget <= p.tiny {\n\ @@ -32,15 +32,15 @@ fn canonical_authority_facts_decode_into_existing_compiler_context() { let context = compiler_context_from_authority_facts(&[decoded]).expect("facts merge"); let module = edict_syntax::parse_module(EFFECTFUL_SOURCE).expect("fixture parses"); let core = compile_to_core(&module, &context).expect("decoded facts compile fixture"); - let intent = core.intents.get("t").expect("compiled intent"); + let action = core.actions.get("t").expect("compiled action"); assert_eq!( - intent.required_operation_profile, + action.required_operation_profile, "continuum.profile.write/v1" ); - assert_eq!(intent.core_evaluation_budget.max_steps, 8); - assert_eq!(intent.core_evaluation_budget.max_allocated_bytes, 1024); - assert_eq!(intent.core_evaluation_budget.max_output_bytes, 256); + assert_eq!(action.core_evaluation_budget.max_steps, 8); + assert_eq!(action.core_evaluation_budget.max_allocated_bytes, 1024); + assert_eq!(action.core_evaluation_budget.max_output_bytes, 256); } #[test] diff --git a/crates/edict-syntax/tests/canonical_encoding.rs b/crates/edict-syntax/tests/canonical_encoding.rs index c985b0d..532be0a 100644 --- a/crates/edict-syntax/tests/canonical_encoding.rs +++ b/crates/edict-syntax/tests/canonical_encoding.rs @@ -27,11 +27,11 @@ fn canonical_core_bytes_are_independent_of_map_construction_order() { .rev() .map(|(name, ty)| (name.clone(), ty.clone())) .collect(); - reordered.intents = core - .intents + reordered.actions = core + .actions .iter() .rev() - .map(|(name, intent)| (name.clone(), intent.clone())) + .map(|(name, action)| (name.clone(), action.clone())) .collect(); assert_eq!( @@ -46,9 +46,9 @@ fn canonical_core_bytes_change_when_core_meaning_changes() { let core = compile_to_core(&module, &hello_context()).expect("fixture compiles to Core"); let mut changed = core.clone(); changed - .intents + .actions .get_mut("sayHello") - .expect("intent exists") + .expect("action exists") .core_evaluation_budget .max_steps += 1; @@ -62,17 +62,17 @@ fn canonical_core_bytes_change_when_core_meaning_changes() { fn canonical_core_bytes_change_when_effect_coordinate_changes() { let mut core = bounded_hello_core(); let binding = core - .intents + .actions .get("sayHello") - .expect("intent exists") + .expect("action exists") .body .locals .last() .expect("local exists") .clone(); - core.intents + core.actions .get_mut("sayHello") - .expect("intent exists") + .expect("action exists") .body .nodes .push(CoreNode::Effect { @@ -85,9 +85,9 @@ fn canonical_core_bytes_change_when_effect_coordinate_changes() { }); let mut changed = core.clone(); let CoreNode::Effect { effect, .. } = changed - .intents + .actions .get_mut("sayHello") - .expect("intent exists") + .expect("action exists") .body .nodes .last_mut() @@ -108,9 +108,9 @@ fn canonical_core_bytes_change_when_local_identity_changes() { let core = bounded_hello_core(); let mut changed = core.clone(); changed - .intents + .actions .get_mut("sayHello") - .expect("intent exists") + .expect("action exists") .body .locals .first_mut() @@ -247,9 +247,9 @@ fn canonical_core_bytes_treat_required_capabilities_as_a_set() { fn canonical_core_bytes_are_independent_of_input_constraint_order() { let module = parse_module(BOUNDED_HELLO).expect("fixture parses"); let mut core = compile_to_core(&module, &hello_context()).expect("fixture compiles to Core"); - core.intents + core.actions .get_mut("sayHello") - .expect("intent exists") + .expect("action exists") .input_constraints .push(InputConstraint { coordinate: "compiler.0".to_owned(), @@ -258,9 +258,9 @@ fn canonical_core_bytes_are_independent_of_input_constraint_order() { }); let mut reordered = core.clone(); reordered - .intents + .actions .get_mut("sayHello") - .expect("intent exists") + .expect("action exists") .input_constraints .reverse(); diff --git a/crates/edict-syntax/tests/common/mod.rs b/crates/edict-syntax/tests/common/mod.rs index 89560bf..ad4d1b3 100644 --- a/crates/edict-syntax/tests/common/mod.rs +++ b/crates/edict-syntax/tests/common/mod.rs @@ -5,18 +5,18 @@ //! test uses every helper, hence the crate-level `dead_code` allowance. #![allow(dead_code)] -use edict_syntax::ast::{Decl, IntentDecl, Module}; +use edict_syntax::ast::{ActionDecl, Decl, Module}; use edict_syntax::{compile_to_core, parse_module, CompilerContext, CoreBudget, CoreModule}; /// Shared source fixture for the initial pure local-record compiler slice. pub const BOUNDED_HELLO: &str = include_str!("../../../../fixtures/lang/bounds/bounded-hello.edict"); -/// Wrap a statement sequence in a minimal, well-formed intent body. +/// Wrap a statement sequence in a minimal, well-formed action body. pub fn body(stmts: &str) -> String { format!( "package a.b@1;\n\ - intent t(input: shape.In) returns shape.Out basis none budget <= p.b {{\n\ + action t(input: shape.In) returns shape.Out basis none budget <= p.b {{\n\ {stmts}\n\ }}" ) @@ -47,10 +47,10 @@ pub fn bounded_hello_core() -> CoreModule { compile_to_core(&module, &hello_context()).expect("bounded-hello fixture compiles to Core") } -/// The first declaration as an intent. -pub fn intent_of(m: &Module) -> &IntentDecl { - let Decl::Intent(intent) = &m.decls[0] else { - panic!("decl 0 is an intent"); +/// The first declaration as an action. +pub fn action_of(m: &Module) -> &ActionDecl { + let Decl::Action(action) = &m.decls[0] else { + panic!("decl 0 is an action"); }; - intent + action } diff --git a/crates/edict-syntax/tests/compiler_spine.rs b/crates/edict-syntax/tests/compiler_spine.rs index 963b36e..c32056b 100644 --- a/crates/edict-syntax/tests/compiler_spine.rs +++ b/crates/edict-syntax/tests/compiler_spine.rs @@ -18,7 +18,7 @@ const EFFECTFUL_REPLACE: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Receipt = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.effectful\n\ basis none\n\ budget <= p.tiny {\n\ @@ -29,7 +29,7 @@ const EFFECTFUL_REPLACE: &str = "package a.b@1;\n\ const EFFECTFUL_BRANCH_YIELD: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.effectful\n\ basis none\n\ budget <= p.tiny {\n\ @@ -45,7 +45,7 @@ const DUPLICATE_OBSTRUCTION_FAILURE: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Receipt = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.effectful\n\ basis none\n\ budget <= p.tiny {\n\ @@ -60,7 +60,7 @@ const ORDERED_OBSTRUCTION_FAILURES: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Receipt = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.effectful\n\ basis none\n\ budget <= p.tiny {\n\ @@ -75,7 +75,7 @@ const REVERSED_OBSTRUCTION_FAILURES: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Receipt = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.effectful\n\ basis none\n\ budget <= p.tiny {\n\ @@ -89,7 +89,7 @@ const REVERSED_OBSTRUCTION_FAILURES: &str = "package a.b@1;\n\ const TERMINAL_REQUIRE_OBSTRUCTION: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.read\n\ basis none\n\ budget <= p.tiny {\n\ @@ -99,7 +99,7 @@ const TERMINAL_REQUIRE_OBSTRUCTION: &str = "package a.b@1;\n\ const TERMINAL_REQUIRE_WITH_REASON_PAYLOAD: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.read\n\ basis none\n\ budget <= p.tiny {\n\ @@ -109,7 +109,7 @@ const TERMINAL_REQUIRE_WITH_REASON_PAYLOAD: &str = "package a.b@1;\n\ const CONTINUE_OBSTRUCTED_REQUIRE: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.read\n\ basis none\n\ budget <= p.tiny {\n\ @@ -184,26 +184,26 @@ fn bounded_hello_compiles_to_initial_core() { } ); - let intent = core.intents.get("sayHello").expect("sayHello intent"); + let action = core.actions.get("sayHello").expect("sayHello action"); assert_eq!( - intent.required_operation_profile, + action.required_operation_profile, "continuum.profile.read-only/v1" ); - assert_eq!(intent.core_evaluation_budget.max_steps, 64); - assert_eq!(intent.input_constraints.len(), 1); + assert_eq!(action.core_evaluation_budget.max_steps, 64); + assert_eq!(action.input_constraints.len(), 1); assert!(matches!( - &intent.input_constraints[0].predicate, + &action.input_constraints[0].predicate, CorePredicate::Compare { .. } )); - assert_eq!(intent.body.locals.len(), 2); - assert_eq!(intent.body.locals[0].id, "arg.0"); - assert_eq!(intent.body.locals[0].alpha_name, "$arg0"); - assert_eq!(intent.body.locals[1].id, "local.0"); - assert_eq!(intent.body.locals[1].alpha_name, "$local0"); - assert_eq!(intent.body.nodes.len(), 1); - assert!(matches!(intent.body.nodes[0], CoreNode::Let { .. })); - assert!(matches!(intent.body.result, CoreExpr::Record { .. })); + assert_eq!(action.body.locals.len(), 2); + assert_eq!(action.body.locals[0].id, "arg.0"); + assert_eq!(action.body.locals[0].alpha_name, "$arg0"); + assert_eq!(action.body.locals[1].id, "local.0"); + assert_eq!(action.body.locals[1].alpha_name, "$local0"); + assert_eq!(action.body.nodes.len(), 1); + assert!(matches!(action.body.nodes[0], CoreNode::Let { .. })); + assert!(matches!(action.body.result, CoreExpr::Record { .. })); } #[test] @@ -212,18 +212,18 @@ fn compiler_spine_exposes_distinct_stage_boundaries() { let resolved = resolve_module(&module, &hello_context()).expect("resolve stage"); assert_eq!(resolved.coordinate, "examples.hello@1"); assert_eq!( - resolved.intents[0].profile, + resolved.actions[0].profile, "continuum.profile.read-only/v1" ); let typed = type_check(&resolved).expect("type-check stage"); assert_eq!( - typed.intents[0].input_binding.ty, + typed.actions[0].input_binding.ty, "examples.hello@1.HelloInput" ); let core = lower_core(&typed).expect("lower Core stage"); - assert!(core.intents.contains_key("sayHello")); + assert!(core.actions.contains_key("sayHello")); } #[test] @@ -249,7 +249,7 @@ fn missing_context_facts_reject_in_resolve_stage() { fn unresolved_local_types_reject_in_type_check_stage() { let module = parse_module( "package a.b@1;\n\ - intent t(input: MissingInput) returns MissingOutput\n\ + action t(input: MissingInput) returns MissingOutput\n\ profile p.read\n\ basis none\n\ budget <= p.tiny {\n\ @@ -284,7 +284,7 @@ fn unresolved_record_field_types_reject_in_type_check_stage() { let module = parse_module( "package a.b@1;\n\ type Box = { value: MissingValue, };\n\ - intent t(input: Box) returns Box\n\ + action t(input: Box) returns Box\n\ profile p.read\n\ basis none\n\ budget <= p.tiny {\n\ @@ -350,7 +350,7 @@ fn read_only_profile_rejects_write_effect_body() { "package a.b@1;\n\ type Input = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.readOnly\n\ basis none\n\ budget <= p.tiny {\n\ @@ -393,7 +393,7 @@ fn read_only_profile_rejects_write_effect_let_without_else() { "package a.b@1;\n\ type Input = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.readOnly\n\ basis none\n\ budget <= p.tiny {\n\ @@ -431,7 +431,7 @@ fn read_only_profile_rejects_write_effect_let_without_else() { } #[test] -fn effectful_write_intent_lowers_to_typed_core_from_file_backed_facts() { +fn effectful_write_action_lowers_to_typed_core_from_file_backed_facts() { let dir = temp_case_dir("effectful-write"); let target = write_json( &dir, @@ -444,20 +444,20 @@ fn effectful_write_intent_lowers_to_typed_core_from_file_backed_facts() { .expect("authority facts load"); let module = parse_module(EFFECTFUL_REPLACE).expect("effectful source parses"); let core = compile_to_core(&module, &context).expect("effectful source compiles to Core"); - let intent = core.intents.get("t").expect("compiled effectful intent"); + let action = core.actions.get("t").expect("compiled effectful action"); assert_eq!( - intent.required_operation_profile, + action.required_operation_profile, "continuum.profile.write/v1" ); - assert_eq!(intent.body.nodes.len(), 1); + assert_eq!(action.body.nodes.len(), 1); let CoreNode::Effect { binding, effect, input, obstruction_map, - } = &intent.body.nodes[0] + } = &action.body.nodes[0] else { panic!("effectful source lowers to a semantic effect node"); }; @@ -765,7 +765,7 @@ fn continue_obstructed_reason_rejects_local_expression() { let source = "package a.b@1;\n\ type Input = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.read\n\ basis none\n\ budget <= p.tiny {\n\ @@ -825,9 +825,9 @@ fn compile_pure_source(source: &str) -> edict_syntax::CoreModule { } fn only_require_node(core: &edict_syntax::CoreModule) -> &CoreNode { - let intent = core.intents.get("t").expect("compiled intent"); - assert_eq!(intent.body.nodes.len(), 1); - &intent.body.nodes[0] + let action = core.actions.get("t").expect("compiled action"); + assert_eq!(action.body.nodes.len(), 1); + &action.body.nodes[0] } fn assert_reason<'a>( diff --git a/crates/edict-syntax/tests/contract_bundle.rs b/crates/edict-syntax/tests/contract_bundle.rs index 15cce8f..863e773 100644 --- a/crates/edict-syntax/tests/contract_bundle.rs +++ b/crates/edict-syntax/tests/contract_bundle.rs @@ -380,7 +380,7 @@ mod contract_bundle_assembly { type Input = { id: String, };\n\ type Receipt = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.effectful\n\ basis none\n\ budget <= p.tiny {\n\ @@ -608,9 +608,9 @@ mod contract_bundle_assembly { let mut changed_input = assembly_from_target_ir_input(); changed_input .target_ir_artifact - .intents + .actions .get_mut("t") - .expect("intent t") + .expect("action t") .core_evaluation_budget .max_steps += 1; let changed = assembled_from_target_ir(changed_input); @@ -716,9 +716,9 @@ mod contract_bundle_assembly { assert_semantic_mutation_changes("Core semantic change", |input| { input .core_module - .intents + .actions .get_mut("sayHello") - .expect("intent exists") + .expect("action exists") .core_evaluation_budget .max_steps += 1; }); diff --git a/crates/edict-syntax/tests/core_golden_fixtures.rs b/crates/edict-syntax/tests/core_golden_fixtures.rs index 0e0e42d..34c6b59 100644 --- a/crates/edict-syntax/tests/core_golden_fixtures.rs +++ b/crates/edict-syntax/tests/core_golden_fixtures.rs @@ -51,11 +51,11 @@ fn core_module_digest_is_stable_for_equivalent_core_ordering() { .rev() .map(|(name, ty)| (name.clone(), ty.clone())) .collect(); - reordered.intents = core - .intents + reordered.actions = core + .actions .iter() .rev() - .map(|(name, intent)| (name.clone(), intent.clone())) + .map(|(name, action)| (name.clone(), action.clone())) .collect(); assert_eq!( @@ -69,9 +69,9 @@ fn core_module_digest_changes_when_core_meaning_changes() { let core = bounded_hello_core(); let mut changed = core.clone(); changed - .intents + .actions .get_mut("sayHello") - .expect("intent exists") + .expect("action exists") .core_evaluation_budget .max_steps += 1; @@ -86,9 +86,9 @@ fn core_module_digest_changes_when_local_identity_changes() { let core = bounded_hello_core(); let mut changed = core.clone(); changed - .intents + .actions .get_mut("sayHello") - .expect("intent exists") + .expect("action exists") .body .locals .first_mut() diff --git a/crates/edict-syntax/tests/parse_control_flow.rs b/crates/edict-syntax/tests/parse_control_flow.rs index 5245561..7890880 100644 --- a/crates/edict-syntax/tests/parse_control_flow.rs +++ b/crates/edict-syntax/tests/parse_control_flow.rs @@ -5,7 +5,7 @@ //! Grammar: `if-expr`, `effect-branch-expr`, `if-stmt` (SPEC Edict Language v1). mod common; -use common::{body, intent_of}; +use common::{action_of, body}; use edict_syntax::ast::{ElseClause, Expr, Stmt}; use edict_syntax::parse_module; @@ -15,8 +15,8 @@ const BLOB: &str = include_str!("../../../fixtures/lang/effects/conditional-blob fn pure_ternary_parses_as_let_value() { let src = body(" let x = if input.n == 0 then input.lo else input.hi;\n return { x };"); let m = parse_module(&src).expect("ternary parses"); - let intent = intent_of(&m); - let Stmt::Let { value, els, .. } = &intent.body.stmts[0] else { + let action = action_of(&m); + let Stmt::Let { value, els, .. } = &action.body.stmts[0] else { panic!("stmt 0 is a let"); }; assert!(els.is_none(), "a ternary let has no effect-else handler"); @@ -28,11 +28,11 @@ fn ternary_is_usable_in_nested_expression_position() { // The ternary sits at the top of `expr`, so it nests inside a call arg. let src = body(" let x = f(if input.a then input.b else input.c);\n return { x };"); let m = parse_module(&src).expect("nested ternary parses"); - let intent = intent_of(&m); + let action = action_of(&m); let Stmt::Let { value: Expr::Call { args, .. }, .. - } = &intent.body.stmts[0] + } = &action.body.stmts[0] else { panic!("rhs is a call"); }; @@ -54,7 +54,7 @@ fn branch_yield_parses_only_as_let_rhs() { \x20 return { blob };", ); let m = parse_module(&src).expect("branch-yield parses"); - let intent = intent_of(&m); + let action = action_of(&m); let Stmt::Let { value: Expr::IfYield { @@ -63,7 +63,7 @@ fn branch_yield_parses_only_as_let_rhs() { .. }, .. - } = &intent.body.stmts[0] + } = &action.body.stmts[0] else { panic!("rhs is a branch-yield"); }; @@ -105,8 +105,8 @@ fn if_statement_with_else_if_chain_parses() { \x20 }", ); let m = parse_module(&src).expect("if/else-if/else parses"); - let intent = intent_of(&m); - let Stmt::If { els, .. } = &intent.body.stmts[0] else { + let action = action_of(&m); + let Stmt::If { els, .. } = &action.body.stmts[0] else { panic!("stmt 0 is an if"); }; // first else is a chained `else if` → ElseClause::If wrapping another Stmt::If @@ -128,13 +128,13 @@ fn if_statement_with_else_if_chain_parses() { #[test] fn conditional_blob_fixture_parses() { let m = parse_module(BLOB).expect("conditional-blob fixture parses"); - let intent = intent_of(&m); + let action = action_of(&m); // body: let initialBytes; let initialBlob = ; return - assert_eq!(intent.body.stmts.len(), 3); + assert_eq!(action.body.stmts.len(), 3); let Stmt::Let { value: Expr::IfYield { else_block, .. }, .. - } = &intent.body.stmts[1] + } = &action.body.stmts[1] else { panic!("stmt 1 is a branch-yield let"); }; diff --git a/crates/edict-syntax/tests/parse_greeting.rs b/crates/edict-syntax/tests/parse_greeting.rs index f214d0c..f520af3 100644 --- a/crates/edict-syntax/tests/parse_greeting.rs +++ b/crates/edict-syntax/tests/parse_greeting.rs @@ -5,7 +5,7 @@ //! record fields. Fixture: `fixtures/lang/effects/read-greeting.edict`. use edict_syntax::ast::{ - Decl, Expr, ImportKind, IntentClause, ObstructionHandler, RecordEntry, Stmt, + ActionClause, Decl, Expr, ImportKind, ObstructionHandler, RecordEntry, Stmt, }; use edict_syntax::parse_module; @@ -28,16 +28,16 @@ fn read_greeting_parses() { assert_eq!(m.imports[2].kind, ImportKind::Target); assert_eq!(m.imports[2].alias, "echo"); - let Decl::Intent(intent) = &m.decls[0] else { - panic!("decl 0 is an intent") + let Decl::Action(action) = &m.decls[0] else { + panic!("decl 0 is an action") }; - assert_eq!(intent.name, "readGreeting"); - assert!(matches!(&intent.clauses[1], IntentClause::Basis(Some(_)))); + assert_eq!(action.name, "readGreeting"); + assert!(matches!(&action.clauses[1], ActionClause::Basis(Some(_)))); // body: let (call w/ type-args), let (call w/ effect-else), return record(2) - assert_eq!(intent.body.stmts.len(), 3); + assert_eq!(action.body.stmts.len(), 3); - let Stmt::Let { value, els, .. } = &intent.body.stmts[0] else { + let Stmt::Let { value, els, .. } = &action.body.stmts[0] else { panic!("stmt 0 is let") }; assert!(els.is_none()); @@ -54,7 +54,7 @@ fn read_greeting_parses() { ); assert_eq!(args.len(), 1); - let Stmt::Let { els, .. } = &intent.body.stmts[1] else { + let Stmt::Let { els, .. } = &action.body.stmts[1] else { panic!("stmt 1 is let") }; assert!( @@ -62,7 +62,7 @@ fn read_greeting_parses() { "read() else " ); - let Stmt::Return { value, .. } = &intent.body.stmts[2] else { + let Stmt::Return { value, .. } = &action.body.stmts[2] else { panic!("stmt 2 is return") }; let Expr::Record { entries, .. } = value else { @@ -77,20 +77,20 @@ fn generic_call_vs_comparison_disambiguation() { // `a < b` is comparison; `f(x)` is a type-call. Both must parse, in the // same body, without the `<` heuristic misfiring. let src = "package a.b@1;\n\ - intent t(input: shape.In) returns shape.Out basis none budget <= p.b {\n\ + action t(input: shape.In) returns shape.Out basis none budget <= p.b {\n\ let cmp = input.lo < input.hi;\n\ let made = echo.ref(input.id);\n\ return { cmp, made };\n\ }"; let m = parse_module(src).expect("both forms parse"); - let Decl::Intent(intent) = &m.decls[0] else { - panic!("intent") + let Decl::Action(action) = &m.decls[0] else { + panic!("action") }; - let Stmt::Let { value: cmp, .. } = &intent.body.stmts[0] else { + let Stmt::Let { value: cmp, .. } = &action.body.stmts[0] else { panic!("let cmp") }; assert!(matches!(cmp, Expr::Binary { .. }), "`<` here is comparison"); - let Stmt::Let { value: made, .. } = &intent.body.stmts[1] else { + let Stmt::Let { value: made, .. } = &action.body.stmts[1] else { panic!("let made") }; assert!( @@ -102,7 +102,7 @@ fn generic_call_vs_comparison_disambiguation() { #[test] fn obstruction_map_with_binders_and_payload_parses() { let src = "package a.b@1;\n\ - intent t(input: shape.In) returns shape.Out basis none budget <= p.b {\n\ + action t(input: shape.In) returns shape.Out basis none budget <= p.b {\n\ let blob = blobRef.ensure(input.candidate)\n\ else {\n\ mismatch(fault) => rope.TextBlobHashConflict({ observed: fault.existing }),\n\ @@ -111,13 +111,13 @@ fn obstruction_map_with_binders_and_payload_parses() { return { blob };\n\ }"; let m = parse_module(src).expect("obstruction map parses"); - let Decl::Intent(intent) = &m.decls[0] else { - panic!("intent") + let Decl::Action(action) = &m.decls[0] else { + panic!("action") }; let Stmt::Let { els: Some(ObstructionHandler::Map(arms)), .. - } = &intent.body.stmts[0] + } = &action.body.stmts[0] else { panic!("let with obstruction map"); }; diff --git a/crates/edict-syntax/tests/parse_hello.rs b/crates/edict-syntax/tests/parse_hello.rs index bf7b1b0..8f8b7c8 100644 --- a/crates/edict-syntax/tests/parse_hello.rs +++ b/crates/edict-syntax/tests/parse_hello.rs @@ -4,7 +4,7 @@ //! `EDICT-LANG-BOUNDS-001`). This is the first real-world parse target. use edict_syntax::ast::{ - BoundRef, Decl, Expr, ImportKind, IntentClause, RecordEntry, ScalarRefine, Stmt, TypeExpr, + ActionClause, BoundRef, Decl, Expr, ImportKind, RecordEntry, ScalarRefine, Stmt, TypeExpr, TypeRef, }; use edict_syntax::{parse_module, ParseErrorKind}; @@ -54,23 +54,23 @@ fn bounded_hello_parses() { }; assert_eq!(hello_reading.name, "HelloReading"); - // intent sayHello(...) ... - let Decl::Intent(intent) = &module.decls[2] else { - panic!("decl 2 is an intent") + // action sayHello(...) ... + let Decl::Action(action) = &module.decls[2] else { + panic!("decl 2 is an action") }; - assert_eq!(intent.name, "sayHello"); - assert_eq!(intent.params.len(), 1); - assert_eq!(intent.params[0].name, "input"); + assert_eq!(action.name, "sayHello"); + assert_eq!(action.params.len(), 1); + assert_eq!(action.params[0].name, "input"); // clauses: profile, basis none, budget, where - assert!(matches!(&intent.clauses[0], IntentClause::Profile(p) if p == &["hello", "readOnly"])); - assert!(matches!(&intent.clauses[1], IntentClause::Basis(None))); - assert!(matches!(&intent.clauses[2], IntentClause::Budget(b) if b == &["hello", "tinyBudget"])); - assert!(matches!(&intent.clauses[3], IntentClause::Where(w) if w.len() == 1)); + assert!(matches!(&action.clauses[0], ActionClause::Profile(p) if p == &["hello", "readOnly"])); + assert!(matches!(&action.clauses[1], ActionClause::Basis(None))); + assert!(matches!(&action.clauses[2], ActionClause::Budget(b) if b == &["hello", "tinyBudget"])); + assert!(matches!(&action.clauses[3], ActionClause::Where(w) if w.len() == 1)); // body: let message = "hello, " + input.name; return { message }; - assert_eq!(intent.body.stmts.len(), 2); - let Stmt::Let { name, value, .. } = &intent.body.stmts[0] else { + assert_eq!(action.body.stmts.len(), 2); + let Stmt::Let { name, value, .. } = &action.body.stmts[0] else { panic!("stmt 0 is let") }; assert_eq!(name, "message"); @@ -79,7 +79,7 @@ fn bounded_hello_parses() { "concat is a binary expr" ); - let Stmt::Return { value, .. } = &intent.body.stmts[1] else { + let Stmt::Return { value, .. } = &action.body.stmts[1] else { panic!("stmt 1 is return") }; let Expr::Record { entries, .. } = value else { diff --git a/crates/edict-syntax/tests/parse_keywords.rs b/crates/edict-syntax/tests/parse_keywords.rs index 78d8a64..af16bb5 100644 --- a/crates/edict-syntax/tests/parse_keywords.rs +++ b/crates/edict-syntax/tests/parse_keywords.rs @@ -9,7 +9,7 @@ use edict_syntax::{parse_module, ParseErrorKind}; fn keywords_are_rejected_as_bare_values() { for kw in [ "then", "else", "yield", "for", "in", "bounded", "return", "let", "where", "require", - "assert", "intent", "type", "enum", + "assert", "action", "type", "enum", ] { let src = body(&format!(" let x = {kw};\n return {{ x }};")); let err = parse_module(&src).expect_err("keyword must reject as bare value"); diff --git a/crates/edict-syntax/tests/parse_loops.rs b/crates/edict-syntax/tests/parse_loops.rs index d9ece2f..b9b9ba2 100644 --- a/crates/edict-syntax/tests/parse_loops.rs +++ b/crates/edict-syntax/tests/parse_loops.rs @@ -15,8 +15,8 @@ fn first_stmt(src: &str) -> Stmt { .into_iter() .next() .and_then(|d| match d { - edict_syntax::ast::Decl::Intent(i) => i.body.stmts.into_iter().next(), - _ => panic!("decl 0 is an intent"), + edict_syntax::ast::Decl::Action(i) => i.body.stmts.into_iter().next(), + _ => panic!("decl 0 is an action"), }) .expect("a statement") } diff --git a/crates/edict-syntax/tests/parse_negative.rs b/crates/edict-syntax/tests/parse_negative.rs index 17fdb98..99c34a9 100644 --- a/crates/edict-syntax/tests/parse_negative.rs +++ b/crates/edict-syntax/tests/parse_negative.rs @@ -15,7 +15,7 @@ fn rejects(src: &str, kind: ParseErrorKind) { #[test] fn reserved_future_decls_are_rejected_at_top_level() { // `migration` / `projection` are reserved for future syntax; as v1 top-level - // declarations they are not `type`/`intent` and must reject. + // declarations they are not `type`/`action` and must reject. rejects( "package a.b@1;\nmigration M = {};", ParseErrorKind::ExpectedToken, diff --git a/crates/edict-syntax/tests/parse_review_regressions.rs b/crates/edict-syntax/tests/parse_review_regressions.rs index 7a89754..1e3035d 100644 --- a/crates/edict-syntax/tests/parse_review_regressions.rs +++ b/crates/edict-syntax/tests/parse_review_regressions.rs @@ -4,7 +4,7 @@ //! API and assert stable AST/error identities. mod common; -use common::{body, intent_of}; +use common::{action_of, body}; use edict_syntax::ast::{BinOp, BoundRef, Decl, Expr, RequireElseArm, Stmt, TypeExpr, TypeRef}; use edict_syntax::token::IntSuffix; use edict_syntax::{parse_module, ParseErrorKind}; @@ -20,8 +20,8 @@ fn reject_kind(src: &str, kind: ParseErrorKind) { fn first_let_value(src: &str) -> Expr { let m = parse_module(src).expect("module parses"); - let intent = intent_of(&m); - let Stmt::Let { value, .. } = &intent.body.stmts[0] else { + let action = action_of(&m); + let Stmt::Let { value, .. } = &action.body.stmts[0] else { panic!("first statement is a let"); }; value.clone() @@ -204,7 +204,7 @@ fn reserved_words_are_rejected_in_all_binder_positions() { ParseErrorKind::ReservedKeyword, ); reject_kind( - "package a.b@1;\nintent t(false: shape.In) returns shape.Out basis none budget <= p.b { return { false }; }", + "package a.b@1;\naction t(false: shape.In) returns shape.Out basis none budget <= p.b { return { false }; }", ParseErrorKind::ReservedKeyword, ); reject_kind( @@ -245,8 +245,8 @@ fn require_statement_parses_terminal_obstruction_source_shape() { " require input.ok else domain.Blocked({ reason: input.reason });\n return { input };", )) .expect("require statement parses"); - let intent = intent_of(&m); - let Stmt::Require { predicate, arm, .. } = &intent.body.stmts[0] else { + let action = action_of(&m); + let Stmt::Require { predicate, arm, .. } = &action.body.stmts[0] else { panic!("first statement is a require"); }; @@ -267,8 +267,8 @@ fn continue_obstructed_source_arm_parses() { " require input.ok else continue obstructed { reason: jim.EditObstruction.StaleBase, basis: input.basis };\n return { input };", )) .expect("continue-obstructed require arm parses"); - let intent = intent_of(&m); - let Stmt::Require { predicate, arm, .. } = &intent.body.stmts[0] else { + let action = action_of(&m); + let Stmt::Require { predicate, arm, .. } = &action.body.stmts[0] else { panic!("first statement is a require"); }; @@ -326,8 +326,8 @@ fn helper_shaped_continue_in_obstructed_strand_is_terminal() { " require input.ok else continueInObstructedStrand({ reason: jim.EditObstruction.StaleBase });\n return { input };", )) .expect("helper-shaped obstruction constructor remains terminal"); - let intent = intent_of(&m); - let Stmt::Require { arm, .. } = &intent.body.stmts[0] else { + let action = action_of(&m); + let Stmt::Require { arm, .. } = &action.body.stmts[0] else { panic!("first statement is a require"); }; let RequireElseArm::Terminal(obstruction) = arm else { @@ -342,8 +342,8 @@ fn helper_shaped_continue_in_obstructed_strand_is_terminal() { fn stale_basis_obstruction_strand_fixture_parses() { let m = parse_module(STALE_BASIS_OBSTRUCTION_STRAND) .expect("stale-basis obstruction strand fixture parses"); - let intent = intent_of(&m); - let Stmt::Require { arm, .. } = &intent.body.stmts[0] else { + let action = action_of(&m); + let Stmt::Require { arm, .. } = &action.body.stmts[0] else { panic!("first fixture statement is a require"); }; let RequireElseArm::ContinueObstructed(obstruction) = arm else { diff --git a/crates/edict-syntax/tests/parse_variants.rs b/crates/edict-syntax/tests/parse_variants.rs index 8849f68..9d774e4 100644 --- a/crates/edict-syntax/tests/parse_variants.rs +++ b/crates/edict-syntax/tests/parse_variants.rs @@ -40,10 +40,10 @@ fn variant_type_with_and_without_payloads_parses() { } fn first_let_value(src: &str) -> Expr { - let Decl::Intent(intent) = parse_ok(src).decls.into_iter().next().expect("a decl") else { - panic!("decl 0 is an intent"); + let Decl::Action(action) = parse_ok(src).decls.into_iter().next().expect("a decl") else { + panic!("decl 0 is an action"); }; - let Stmt::Let { value, .. } = intent.body.stmts.into_iter().next().expect("a stmt") else { + let Stmt::Let { value, .. } = action.body.stmts.into_iter().next().expect("a stmt") else { panic!("stmt 0 is a let"); }; value @@ -52,7 +52,7 @@ fn first_let_value(src: &str) -> Expr { #[test] fn variant_literal_with_payload_parses() { let src = "package a.b@1;\n\ - intent t(input: shape.In) returns shape.Out basis none budget <= p.b {\n\ + action t(input: shape.In) returns shape.Out basis none budget <= p.b {\n\ let x = shape.Paint::Solid(input.rgb);\n\ return { x };\n\ }"; @@ -73,7 +73,7 @@ fn variant_literal_with_payload_parses() { #[test] fn variant_literal_without_payload_parses() { let src = "package a.b@1;\n\ - intent t(input: shape.In) returns shape.Out basis none budget <= p.b {\n\ + action t(input: shape.In) returns shape.Out basis none budget <= p.b {\n\ let x = shape.Paint::Transparent;\n\ return { x };\n\ }"; @@ -87,7 +87,7 @@ fn variant_literal_without_payload_parses() { #[test] fn match_expr_with_binders_parses() { let src = "package a.b@1;\n\ - intent t(input: shape.In) returns shape.Out basis none budget <= p.b {\n\ + action t(input: shape.In) returns shape.Out basis none budget <= p.b {\n\ let label = match input.paint {\n\ Solid(rgb) => rgb.hex,\n\ Named(name) => name.text,\n\ @@ -108,7 +108,7 @@ fn match_expr_with_binders_parses() { #[test] fn empty_match_is_rejected() { let src = "package a.b@1;\n\ - intent t(input: shape.In) returns shape.Out basis none budget <= p.b {\n\ + action t(input: shape.In) returns shape.Out basis none budget <= p.b {\n\ let x = match input.paint { };\n\ return { x };\n\ }"; @@ -121,17 +121,17 @@ fn empty_match_is_rejected() { #[test] fn palette_fixture_parses() { let m = parse_module(PALETTE).expect("color-match fixture parses"); - // enum, variant type, intent + // enum, variant type, action assert!(matches!(m.decls[0], Decl::Enum(_))); assert!(matches!(m.decls[1], Decl::Type(_))); - assert!(matches!(m.decls[2], Decl::Intent(_))); - let Decl::Intent(intent) = &m.decls[2] else { - panic!("decl 2 is an intent"); + assert!(matches!(m.decls[2], Decl::Action(_))); + let Decl::Action(action) = &m.decls[2] else { + panic!("decl 2 is an action"); }; let Stmt::Let { value: Expr::Match { arms, .. }, .. - } = &intent.body.stmts[0] + } = &action.body.stmts[0] else { panic!("stmt 0 is a match let"); }; @@ -139,7 +139,7 @@ fn palette_fixture_parses() { let Stmt::Let { value: Expr::VariantLit { case, .. }, .. - } = &intent.body.stmts[1] + } = &action.body.stmts[1] else { panic!("stmt 1 is a variant-literal let"); }; diff --git a/crates/edict-syntax/tests/provider_invocation.rs b/crates/edict-syntax/tests/provider_invocation.rs index eeae86b..5cc8761 100644 --- a/crates/edict-syntax/tests/provider_invocation.rs +++ b/crates/edict-syntax/tests/provider_invocation.rs @@ -81,7 +81,7 @@ impl ProviderArtifactSchemaValidator for TestArtifactSchemas { map_value(entries, "sourceCoreCoordinate"), Some(CanonicalValue::Text(_)) ) - && matches!(map_value(entries, "intents"), Some(CanonicalValue::Map(_))); + && matches!(map_value(entries, "actions"), Some(CanonicalValue::Map(_))); if !target_ir_shape_is_valid { return Err(ProviderArtifactSchemaValidationErrorKind::SchemaMismatch); } diff --git a/crates/edict-syntax/tests/provider_lowering.rs b/crates/edict-syntax/tests/provider_lowering.rs index 4c48104..1b9b259 100644 --- a/crates/edict-syntax/tests/provider_lowering.rs +++ b/crates/edict-syntax/tests/provider_lowering.rs @@ -20,7 +20,7 @@ const EFFECTFUL_REPLACE: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Receipt = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.effectful\n\ basis none\n\ budget <= p.tiny {\n\ @@ -33,7 +33,7 @@ const GITWARP_APPEND_EVENT: &str = "package a.git@1;\n\ type Input = { id: String, };\n\ type Receipt = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.gitwarp\n\ basis none\n\ budget <= p.tiny\n\ diff --git a/crates/edict-syntax/tests/semantic_validation.rs b/crates/edict-syntax/tests/semantic_validation.rs index a127988..802d53e 100644 --- a/crates/edict-syntax/tests/semantic_validation.rs +++ b/crates/edict-syntax/tests/semantic_validation.rs @@ -37,7 +37,7 @@ fn validate_module_remains_surface_stage_compatibility_alias() { fn surface_validation_defers_import_and_name_resolution() { let module = parse_module( "package a.b@1;\n\ - intent t(input: missing.Input) returns missing.Output\n\ + action t(input: missing.Input) returns missing.Output\n\ profile missing.readOnly\n\ basis input.id\n\ budget <= missing.budget {\n\ @@ -54,7 +54,7 @@ fn surface_validation_defers_import_and_name_resolution() { fn surface_validation_defers_contextual_typing_and_loop_bound_proof() { let module = parse_module( "package a.b@1;\n\ - intent t(input: shape.In) returns shape.Out\n\ + action t(input: shape.In) returns shape.Out\n\ profile shape.readOnly\n\ basis none\n\ budget <= shape.tinyBudget {\n\ @@ -74,7 +74,7 @@ fn surface_validation_defers_contextual_typing_and_loop_bound_proof() { fn surface_validation_defers_obstruction_exhaustiveness() { let module = parse_module( "package a.b@1;\n\ - intent t(input: shape.In) returns shape.Out\n\ + action t(input: shape.In) returns shape.Out\n\ profile shape.readWrite\n\ basis none\n\ budget <= shape.tinyBudget {\n\ @@ -125,10 +125,10 @@ fn unbounded_runtime_scalars_are_rejected_in_declaration_type_surfaces() { } #[test] -fn unbounded_runtime_scalars_are_rejected_in_intent_and_expression_surfaces() { +fn unbounded_runtime_scalars_are_rejected_in_action_and_expression_surfaces() { let kinds = semantic_kinds( "package a.b@1;\n\ - intent t(input: String) returns Bytes\n\ + action t(input: String) returns Bytes\n\ profile shape.readOnly\n\ basis none\n\ budget <= shape.tinyBudget {\n\ @@ -149,10 +149,10 @@ fn unbounded_runtime_scalars_are_rejected_in_intent_and_expression_surfaces() { } #[test] -fn intent_required_clauses_are_validated() { +fn action_required_clauses_are_validated() { let kinds = semantic_kinds( "package a.b@1;\n\ - intent t(input: shape.In) returns shape.Out {\n\ + action t(input: shape.In) returns shape.Out {\n\ return { input };\n\ }", ); @@ -170,7 +170,7 @@ fn intent_required_clauses_are_validated() { fn duplicate_implements_and_footprint_clauses_are_rejected() { let kinds = semantic_kinds( "package a.b@1;\n\ - intent t(input: shape.In) returns shape.Out\n\ + action t(input: shape.In) returns shape.Out\n\ profile shape.readOnly\n\ implements shape.reader\n\ implements shape.writer\n\ @@ -184,8 +184,8 @@ fn duplicate_implements_and_footprint_clauses_are_rejected() { assert_eq!( kinds, vec![ - SemanticErrorKind::DuplicateIntentClause, - SemanticErrorKind::DuplicateIntentClause, + SemanticErrorKind::DuplicateActionClause, + SemanticErrorKind::DuplicateActionClause, ] ); } @@ -195,7 +195,7 @@ fn profile_or_implements_satisfies_operation_mode() { for clause in ["profile shape.readOnly", "implements shape.reader"] { let src = format!( "package a.b@1;\n\ - intent t(input: shape.In) returns shape.Out\n\ + action t(input: shape.In) returns shape.Out\n\ {clause}\n\ basis none\n\ budget <= shape.tinyBudget {{\n\ @@ -208,10 +208,10 @@ fn profile_or_implements_satisfies_operation_mode() { } #[test] -fn duplicate_singleton_intent_clauses_are_rejected() { +fn duplicate_singleton_action_clauses_are_rejected() { let kinds = semantic_kinds( "package a.b@1;\n\ - intent t(input: shape.In) returns shape.Out\n\ + action t(input: shape.In) returns shape.Out\n\ profile shape.readOnly\n\ profile shape.readWrite\n\ basis none\n\ @@ -224,9 +224,9 @@ fn duplicate_singleton_intent_clauses_are_rejected() { assert_eq!( kinds, vec![ - SemanticErrorKind::DuplicateIntentClause, - SemanticErrorKind::DuplicateIntentClause, - SemanticErrorKind::DuplicateIntentClause, + SemanticErrorKind::DuplicateActionClause, + SemanticErrorKind::DuplicateActionClause, + SemanticErrorKind::DuplicateActionClause, ] ); } @@ -238,7 +238,7 @@ fn module_namespace_collisions_are_rejected() { use shape \"schemas/a.graphql\" as Input;\n\ type Input = { id: String, };\n\ enum Status { Ready }\n\ - intent Status(input: shape.In) returns shape.Out\n\ + action Status(input: shape.In) returns shape.Out\n\ profile shape.readOnly\n\ basis none\n\ budget <= shape.tinyBudget {\n\ @@ -260,7 +260,7 @@ fn local_binders_cannot_shadow_visible_names() { "package a.b@1;\n\ use shape \"schemas/a.graphql\" as shape;\n\ type Input = { id: String, };\n\ - intent t(shape: Input) returns Input\n\ + action t(shape: Input) returns Input\n\ profile shape.readOnly\n\ basis none\n\ budget <= shape.tinyBudget {\n\ @@ -281,7 +281,7 @@ fn local_binders_cannot_shadow_visible_names() { fn branch_and_loop_binders_are_scoped() { let module = parse_module( "package a.b@1;\n\ - intent t(input: shape.In) returns shape.Out\n\ + action t(input: shape.In) returns shape.Out\n\ profile shape.readOnly\n\ basis none\n\ budget <= shape.tinyBudget {\n\ @@ -305,7 +305,7 @@ fn branch_and_loop_binders_are_scoped() { fn branch_yield_binders_are_scoped() { let module = parse_module( "package a.b@1;\n\ - intent t(input: shape.In) returns shape.Out\n\ + action t(input: shape.In) returns shape.Out\n\ profile shape.readOnly\n\ basis none\n\ budget <= shape.tinyBudget {\n\ @@ -329,7 +329,7 @@ fn expression_binders_cannot_shadow_visible_names() { let kinds = semantic_kinds( "package a.b@1;\n\ type Input = { id: String, };\n\ - intent t(input: Input) returns Input\n\ + action t(input: Input) returns Input\n\ profile shape.readOnly\n\ basis none\n\ budget <= shape.tinyBudget {\n\ @@ -345,7 +345,7 @@ fn clause_expression_binders_see_parameters() { let kinds = semantic_kinds( "package a.b@1;\n\ type Input = { id: String, };\n\ - intent t(input: Input) returns Input\n\ + action t(input: Input) returns Input\n\ profile shape.readOnly\n\ basis none\n\ budget <= shape.tinyBudget\n\ @@ -360,7 +360,7 @@ fn clause_expression_binders_see_parameters() { fn obstruction_map_binders_cannot_shadow_visible_names() { let kinds = semantic_kinds( "package a.b@1;\n\ - intent t(input: shape.In) returns shape.Out\n\ + action t(input: shape.In) returns shape.Out\n\ profile shape.readWrite\n\ basis none\n\ budget <= shape.tinyBudget {\n\ diff --git a/crates/edict-syntax/tests/target_ir.rs b/crates/edict-syntax/tests/target_ir.rs index 00cbf73..488e6c5 100644 --- a/crates/edict-syntax/tests/target_ir.rs +++ b/crates/edict-syntax/tests/target_ir.rs @@ -22,7 +22,7 @@ const EFFECTFUL_REPLACE: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Receipt = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.effectful\n\ basis none\n\ budget <= p.tiny {\n\ @@ -34,7 +34,7 @@ const CHAINED_EFFECT_RESULTS: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Receipt = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.effectful\n\ basis none\n\ budget <= p.tiny {\n\ @@ -48,7 +48,7 @@ const GITWARP_APPEND_EVENT: &str = "package a.git@1;\n\ type Input = { id: String, };\n\ type Receipt = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.gitwarp\n\ basis none\n\ budget <= p.tiny\n\ @@ -60,7 +60,7 @@ const GITWARP_APPEND_EVENT: &str = "package a.git@1;\n\ const ECHO_CONTINUE_OBSTRUCTED_REQUIRE: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.effectful\n\ basis none\n\ budget <= p.tiny {\n\ @@ -73,7 +73,7 @@ const ECHO_CONTINUE_OBSTRUCTED_REQUIRE: &str = "package a.b@1;\n\ const ECHO_TERMINAL_REQUIRE: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.effectful\n\ basis none\n\ budget <= p.tiny {\n\ @@ -84,7 +84,7 @@ const ECHO_EFFECT_OUTPUT_DEPENDENT_REQUIRE: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Receipt = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.effectful\n\ basis none\n\ budget <= p.tiny {\n\ @@ -100,7 +100,7 @@ const ECHO_POST_STEP_INPUT_REQUIRE: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Receipt = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.effectful\n\ basis none\n\ budget <= p.tiny {\n\ @@ -115,7 +115,7 @@ const ECHO_POST_STEP_INPUT_REQUIRE: &str = "package a.b@1;\n\ const GITWARP_CONTINUE_OBSTRUCTED_REQUIRE: &str = "package a.git@1;\n\ type Input = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.gitwarp\n\ basis none\n\ budget <= p.tiny {\n\ @@ -374,13 +374,13 @@ fn supported_effectful_core_lowers_to_echo_span_ir() { assert_eq!(artifact.domain, ECHO_SPAN_IR_DOMAIN); assert_eq!(artifact.target_profile.coordinate, ECHO_DPO_TARGET_PROFILE); assert_eq!(artifact.source_core_coordinate, "a.b@1"); - assert_eq!(artifact.intents.len(), 1); + assert_eq!(artifact.actions.len(), 1); - let intent = artifact.intents.get("t").expect("lowered intent t"); - assert_eq!(intent.operation_profile, "continuum.profile.write/v1"); - assert_eq!(intent.steps.len(), 1); + let action = artifact.actions.get("t").expect("lowered action t"); + assert_eq!(action.operation_profile, "continuum.profile.write/v1"); + assert_eq!(action.steps.len(), 1); - let step = &intent.steps[0]; + let step = &action.steps[0]; assert_eq!(step.id, "t.step.0"); assert_eq!(step.effect, "target.replace"); assert_eq!(step.target_intrinsic, "echo.dpo@1.replace"); @@ -411,7 +411,7 @@ fn lowerability_native_support_feeds_echo_target_lowering() { let artifact = report .artifact .expect("native lowerability feeds target IR"); - let step = &artifact.intents.get("t").expect("intent t").steps[0]; + let step = &artifact.actions.get("t").expect("action t").steps[0]; assert_eq!(step.effect, "target.replace"); assert_eq!(step.target_intrinsic, "echo.dpo@1.replace"); } @@ -432,24 +432,24 @@ fn supported_gitwarp_core_lowers_to_commit_reducer_ir() { ); assert_eq!(artifact.source_core_coordinate, "a.git@1"); - let intent = artifact.intents.get("t").expect("lowered intent t"); - assert_eq!(intent.operation_profile, "continuum.profile.append/v1"); + let action = artifact.actions.get("t").expect("lowered action t"); + assert_eq!(action.operation_profile, "continuum.profile.append/v1"); assert_eq!( - intent.core_evaluation_budget, + action.core_evaluation_budget, CoreBudget { max_steps: 13, max_allocated_bytes: 2048, max_output_bytes: 512, } ); - assert_eq!(intent.input_constraints.len(), 1); + assert_eq!(action.input_constraints.len(), 1); assert!(matches!( - intent.input_constraints[0].predicate, + action.input_constraints[0].predicate, CorePredicate::Compare { .. } )); - assert_eq!(intent.steps.len(), 1); + assert_eq!(action.steps.len(), 1); - let step = &intent.steps[0]; + let step = &action.steps[0]; assert_eq!(step.effect, "gitwarp.appendEvent"); assert_eq!(step.target_intrinsic, "gitwarp.ref_crdt@1.appendEvent"); assert_eq!(step.obstruction_failures, vec!["conflict".to_owned()]); @@ -460,8 +460,8 @@ fn supported_gitwarp_core_lowers_to_commit_reducer_ir() { }; assert_eq!(field, "id"); - let CoreExpr::Record { fields } = &intent.result else { - panic!("git-warp intent result is preserved structurally"); + let CoreExpr::Record { fields } = &action.result else { + panic!("git-warp action result is preserved structurally"); }; assert!(fields.contains_key("id")); } @@ -484,7 +484,7 @@ fn lowerability_native_support_feeds_gitwarp_target_lowering() { let artifact = report .artifact .expect("native git-warp lowerability feeds target IR"); - let step = &artifact.intents.get("t").expect("intent t").steps[0]; + let step = &artifact.actions.get("t").expect("action t").steps[0]; assert_eq!(step.effect, "gitwarp.appendEvent"); assert_eq!(step.target_intrinsic, "gitwarp.ref_crdt@1.appendEvent"); } @@ -492,11 +492,11 @@ fn lowerability_native_support_feeds_gitwarp_target_lowering() { #[test] fn echo_target_ir_contains_obstruction_requirement_payload() { let artifact = effectful_artifact(ECHO_CONTINUE_OBSTRUCTED_REQUIRE); - let intent = artifact.intents.get("t").expect("lowered intent t"); + let action = artifact.actions.get("t").expect("lowered action t"); - assert!(intent.steps.is_empty()); - assert_eq!(intent.requirements.len(), 1); - let requirement = &intent.requirements[0]; + assert!(action.steps.is_empty()); + assert_eq!(action.requirements.len(), 1); + let requirement = &action.requirements[0]; assert_eq!(requirement.id, "t.require.0"); assert_eq!(requirement.predicate, CorePredicate::True); @@ -521,8 +521,8 @@ fn terminal_and_preserved_requirements_are_target_ir_distinct() { ); let preserved = effectful_artifact(&preserved_source); - let terminal_requirement = &terminal.intents.get("t").expect("intent t").requirements[0]; - let preserved_requirement = &preserved.intents.get("t").expect("intent t").requirements[0]; + let terminal_requirement = &terminal.actions.get("t").expect("action t").requirements[0]; + let preserved_requirement = &preserved.actions.get("t").expect("action t").requirements[0]; assert!(matches!( terminal_requirement.on_failure, TargetIrRequireFailure::Terminal { .. } @@ -652,7 +652,7 @@ fn lowerability_bridge_carries_only_selected_native_effect() { let artifact = report .artifact .expect("unselected native support does not make target lowering ambiguous"); - let step = &artifact.intents.get("t").expect("intent t").steps[0]; + let step = &artifact.actions.get("t").expect("action t").steps[0]; assert_eq!(step.target_intrinsic, "echo.dpo@1.replace"); } @@ -679,7 +679,7 @@ fn lowerability_bridge_deduplicates_identical_native_effect_selection() { let artifact = report .artifact .expect("duplicate selected effect still lowers once"); - let step = &artifact.intents.get("t").expect("intent t").steps[0]; + let step = &artifact.actions.get("t").expect("action t").steps[0]; assert_eq!(step.target_intrinsic, "echo.dpo@1.replace"); } @@ -702,7 +702,7 @@ fn unused_duplicate_effect_lowerings_do_not_reject_supported_effect() { let artifact = report .artifact .expect("unused duplicate lowerings do not block supported effect"); - let step = &artifact.intents.get("t").expect("intent t").steps[0]; + let step = &artifact.actions.get("t").expect("action t").steps[0]; assert_eq!(step.effect, "target.replace"); assert_eq!(step.target_intrinsic, "echo.dpo@1.replace"); } @@ -800,7 +800,7 @@ fn obstruction_arm_values_are_preserved_in_echo_span_ir() { let artifact = effectful_artifact( &EFFECTFUL_REPLACE.replace("domain.WriteRejected", "domain.WriteDifferentlyRejected"), ); - let arm = &artifact.intents.get("t").expect("intent t").steps[0].obstruction_arms["rejected"]; + let arm = &artifact.actions.get("t").expect("action t").steps[0].obstruction_arms["rejected"]; let CoreExpr::Call { callee, @@ -816,14 +816,14 @@ fn obstruction_arm_values_are_preserved_in_echo_span_ir() { } #[test] -fn intent_result_is_preserved_in_echo_span_ir() { +fn action_result_is_preserved_in_echo_span_ir() { let artifact = effectful_artifact( &EFFECTFUL_REPLACE.replace("return { id: input.id };", "return { id: receipt.id };"), ); - let result = &artifact.intents.get("t").expect("intent t").result; + let result = &artifact.actions.get("t").expect("action t").result; let CoreExpr::Record { fields } = result else { - panic!("intent result is preserved as a record expression"); + panic!("action result is preserved as a record expression"); }; let CoreExpr::Field { base, field } = &fields["id"] else { panic!("result id field is preserved as a field expression"); @@ -833,26 +833,26 @@ fn intent_result_is_preserved_in_echo_span_ir() { } #[test] -fn intent_constraints_and_budget_are_preserved_in_echo_span_ir() { +fn action_constraints_and_budget_are_preserved_in_echo_span_ir() { let constrained_source = EFFECTFUL_REPLACE.replace( "budget <= p.tiny {", "budget <= p.tiny\n where input.id != \"\" {", ); let artifact = effectful_artifact(&constrained_source); - let intent = artifact.intents.get("t").expect("intent t"); + let action = artifact.actions.get("t").expect("action t"); assert_eq!( - intent.core_evaluation_budget, + action.core_evaluation_budget, CoreBudget { max_steps: 8, max_allocated_bytes: 1024, max_output_bytes: 256, } ); - assert_eq!(intent.input_constraints.len(), 1); - assert_eq!(intent.input_constraints[0].coordinate, "where.0"); + assert_eq!(action.input_constraints.len(), 1); + assert_eq!(action.input_constraints[0].coordinate, "where.0"); assert!(matches!( - intent.input_constraints[0].predicate, + action.input_constraints[0].predicate, CorePredicate::Compare { .. } )); } @@ -860,13 +860,13 @@ fn intent_constraints_and_budget_are_preserved_in_echo_span_ir() { #[test] fn effect_result_bindings_are_preserved_in_echo_span_ir() { let artifact = effectful_artifact(CHAINED_EFFECT_RESULTS); - let intent = artifact.intents.get("t").expect("intent t"); + let action = artifact.actions.get("t").expect("action t"); - assert_eq!(intent.steps.len(), 2); - assert_eq!(intent.steps[0].binding.id, "local.0"); - assert_eq!(intent.steps[1].binding.id, "local.1"); + assert_eq!(action.steps.len(), 2); + assert_eq!(action.steps[0].binding.id, "local.0"); + assert_eq!(action.steps[1].binding.id, "local.1"); - let CoreExpr::Field { base, field } = &intent.steps[1].input else { + let CoreExpr::Field { base, field } = &action.steps[1].input else { panic!("second effect input reads from first effect result"); }; assert_eq!(field, "id"); @@ -1009,11 +1009,11 @@ fn unsupported_obstruction_key_rejects_without_artifact() { } #[test] -fn empty_target_step_intents_reject_without_artifact() { +fn empty_target_step_actions_reject_without_artifact() { let mut core = effectful_core(); - core.intents + core.actions .get_mut("t") - .expect("intent t") + .expect("action t") .body .nodes .clear(); @@ -1031,7 +1031,7 @@ fn empty_target_step_intents_reject_without_artifact() { #[test] fn empty_core_modules_reject_without_artifact() { let mut core = effectful_core(); - core.intents.clear(); + core.actions.clear(); let report = lower_to_target_ir(&core, &echo_facts()); @@ -1157,9 +1157,9 @@ fn target_ir_artifact_canonicalization_ignores_equivalent_construction_order() { predicate: CorePredicate::True, }; - let left_intent = left.intents.get_mut("t").expect("intent t"); - left_intent.input_constraints.push(extra_constraint.clone()); - let left_step = left_intent.steps.get_mut(0).expect("step 0"); + let left_action = left.actions.get_mut("t").expect("action t"); + left_action.input_constraints.push(extra_constraint.clone()); + let left_step = left_action.steps.get_mut(0).expect("step 0"); let conflict_arm = left_step .obstruction_arms .get("conflict") @@ -1170,9 +1170,9 @@ fn target_ir_artifact_canonicalization_ignores_equivalent_construction_order() { .insert("retry".to_owned(), conflict_arm.clone()); left_step.obstruction_failures = vec!["retry".to_owned(), "conflict".to_owned()]; - let right_intent = right.intents.get_mut("t").expect("intent t"); - right_intent.input_constraints.insert(0, extra_constraint); - let right_step = right_intent.steps.get_mut(0).expect("step 0"); + let right_action = right.actions.get_mut("t").expect("action t"); + right_action.input_constraints.insert(0, extra_constraint); + let right_step = right_action.steps.get_mut(0).expect("step 0"); let mut rebuilt_arms = BTreeMap::new(); rebuilt_arms.insert("retry".to_owned(), conflict_arm); rebuilt_arms.insert( @@ -1201,9 +1201,9 @@ fn target_ir_step_order_changes_digest() { let baseline = effectful_artifact(CHAINED_EFFECT_RESULTS); let mut reordered = baseline.clone(); reordered - .intents + .actions .get_mut("t") - .expect("intent t") + .expect("action t") .steps .reverse(); @@ -1222,9 +1222,9 @@ fn target_ir_digest_moves_for_artifact_semantic_mutations() { assert_target_ir_digest_changes(&baseline, "source Core coordinate", |artifact| { artifact.source_core_coordinate = "a.changed@1".to_owned(); }); - assert_target_ir_digest_changes(&baseline, "intent name", |artifact| { - let intent = artifact.intents.remove("t").expect("intent t"); - artifact.intents.insert("renamed".to_owned(), intent); + assert_target_ir_digest_changes(&baseline, "action name", |artifact| { + let action = artifact.actions.remove("t").expect("action t"); + artifact.actions.insert("renamed".to_owned(), action); }); assert_target_ir_digest_changes(&baseline, "effect coordinate", |artifact| { target_step_mut(artifact).effect = "target.replace.changed".to_owned(); @@ -1242,9 +1242,9 @@ fn target_ir_digest_moves_for_artifact_semantic_mutations() { }); assert_target_ir_digest_changes(&baseline, "input constraint", |artifact| { artifact - .intents + .actions .get_mut("t") - .expect("intent t") + .expect("action t") .input_constraints .push(InputConstraint { coordinate: "compiler.0".to_owned(), @@ -1254,14 +1254,14 @@ fn target_ir_digest_moves_for_artifact_semantic_mutations() { }); assert_target_ir_digest_changes(&baseline, "Core evaluation budget", |artifact| { artifact - .intents + .actions .get_mut("t") - .expect("intent t") + .expect("action t") .core_evaluation_budget .max_steps += 1; }); assert_target_ir_digest_changes(&baseline, "result expression", |artifact| { - artifact.intents.get_mut("t").expect("intent t").result = + artifact.actions.get_mut("t").expect("action t").result = CoreExpr::Const(CoreValue::String("changed".to_owned())); }); } @@ -1322,9 +1322,9 @@ fn assert_target_ir_digest_changes( fn target_step_mut(artifact: &mut TargetIrArtifact) -> &mut edict_syntax::TargetIrStep { artifact - .intents + .actions .get_mut("t") - .expect("intent t") + .expect("action t") .steps .get_mut(0) .expect("step 0") @@ -1332,9 +1332,9 @@ fn target_step_mut(artifact: &mut TargetIrArtifact) -> &mut edict_syntax::Target fn requirement_mut(artifact: &mut TargetIrArtifact) -> &mut edict_syntax::TargetIrRequirement { artifact - .intents + .actions .get_mut("t") - .expect("intent t") + .expect("action t") .requirements .get_mut(0) .expect("requirement 0") diff --git a/docs/DESIGN_runtime-neutral-edict-sha-lock-assurance.md b/docs/DESIGN_runtime-neutral-edict-sha-lock-assurance.md index ea3badf..a35ae89 100644 --- a/docs/DESIGN_runtime-neutral-edict-sha-lock-assurance.md +++ b/docs/DESIGN_runtime-neutral-edict-sha-lock-assurance.md @@ -401,7 +401,7 @@ contract graft.structural_history v1 { use lawpack history.optics@1 as history; use target echo.dpo@1 as echo; - intent recordGitWarpImportBatch(input: RecordGitWarpImportBatchInput) + action recordGitWarpImportBatch(input: RecordGitWarpImportBatchInput) returns RecordGitWarpImportBatchReceipt profile echo.createOnly budget <= history.recordBatchBudget @@ -438,7 +438,7 @@ Portable semantic Edict targets an abstract lawpack and can lower to multiple ru contract graft.structural_history v1 { use lawpack history.optics@1 as history; - intent recordGitWarpImportBatch(input: RecordGitWarpImportBatchInput) + action recordGitWarpImportBatch(input: RecordGitWarpImportBatchInput) returns RecordGitWarpImportBatchReceipt implements history.recordEntry budget <= history.recordBatchBudget diff --git a/docs/REQUIREMENTS.md b/docs/REQUIREMENTS.md index b40f440..0bf3501 100644 --- a/docs/REQUIREMENTS.md +++ b/docs/REQUIREMENTS.md @@ -100,7 +100,7 @@ but owned by a follow-up issue; no fixtures until its dependency lands). | EDICT-LANG-OPTION-REFINE-001 | Only Option refinement (isSome/unwrap), lexical and variable-specific; no general narrowing | Language | `lang/refine/isSome` | `lang/refine/flows-through-helper` | spec | | EDICT-LANG-BOUND-VIOLATION-001 | Runtime value violating a proven static bound is integrity/internal fault, never silent truncation or resourceFault | Language | `lang/bound/integrity-fault` | `lang/bound/silent-truncate` | spec | | EDICT-CORE-GUARD-CATEGORY-001 | CoreGuard is targetAtomic + always carries obstruction; verifier proofs are CoreProofObligation nodes | Language | `core/guard/atomic-obstruction` | `core/guard/obstructionless` | spec | -| EDICT-CORE-PREIMAGE-LIST-001 | Canonicalization rules carry a positive exhaustive Core-intent preimage inclusion list | Language | `core/preimage/inclusion-list` | `core/preimage/scavenger-hunt` | spec | +| EDICT-CORE-PREIMAGE-LIST-001 | Canonicalization rules carry a positive exhaustive Core-action preimage inclusion list | Language | `core/preimage/inclusion-list` | `core/preimage/scavenger-hunt` | spec | | EDICT-OPTIC-TEMPLATE-OWNER-001 | operation-profile optic template has a canonical shape in edict-common.cddl, exported via ABIs | Target/Lawpack | `optic/template/owned` | `optic/template/undefined` | spec | | EDICT-OPTIC-APERTURE-REF-001 | `apertureRequirement` is a typed reference (footprintCeiling/abstractFootprintObligation), not a string | Language | `optic/aperture/typed-ref` | `optic/aperture/string` | spec | | EDICT-LANG-CAPABILITIES-SPLIT-001 | requiredSourceCapabilities (compiler) vs requiredCoreCapabilities (hash-significant Core field) | Language/Bundle | `lang/caps/split` | `lang/caps/conflated` | spec | @@ -110,7 +110,7 @@ but owned by a follow-up issue; no fixtures until its dependency lands). | EDICT-LAWPACK-ADAPTER-DIRECT-001 | v1 direct adaptation only; exactly one adapter per (effect,target); no chained legalization/fixed-point | Lawpack | `lawpack/adapter/direct-one` | `lawpack/adapter/chained` | spec | | EDICT-LANG-TARGETBUDGET-HASH-001 | targetBudget costAlgebra ref + ceiling both hash-significant; ceiling meaningless without its algebra | Language | `lang/targetbudget/both-hashed` | `lang/targetbudget/algebra-unhashed` | spec | | EDICT-LANG-OBSTRUCT-EMPTY-001 | Bare obstruction-target normalizes to ObstructionConstruct with empty `{}` payload | Language | `lang/obstruct/bare-empty` | `lang/obstruct/bare-undefined` | spec | -| EDICT-LANG-INTENT-CLAUSES-001 | Intent clause requiredness (at least one of profile/implements — both allowed; budget; basis-unless-template); omission parseable but semantically rejected | Language | `lang/clauses/required-present` | `lang/clauses/missing-budget` | spec | +| EDICT-LANG-ACTION-CLAUSES-001 | Action clause requiredness (at least one of profile/implements — both allowed; budget; basis-unless-template); omission parseable but semantically rejected | Language | `lang/clauses/required-present` | `lang/clauses/missing-budget` | spec | | EDICT-OPMODE-AUTHORITY-001 | Operation-mode predicates defined authoritatively in Target Profile ABI; language spec mirror must not diverge | Target/Language | `opmode/mirror-matches` | `opmode/mirror-diverges` | spec | | EDICT-ABI-OPPROFILE-SLOT-001 | Target profiles + lawpacks publish operation-profile records via a hash-locked ABI slot | Target/Lawpack | `abi/opprofile/published` | `abi/opprofile/no-slot` | spec | | EDICT-LAWPACK-PURE-IMPL-001 | Exported pure helper needs a hash-bound implementation (edict body or component+sandbox+fuel) | Lawpack | `lawpack/pure/has-impl` | `lawpack/pure/signature-only` | spec | diff --git a/docs/RETRO_phase1-parser.md b/docs/RETRO_phase1-parser.md index 5cfb7b8..fa330d4 100644 --- a/docs/RETRO_phase1-parser.md +++ b/docs/RETRO_phase1-parser.md @@ -25,7 +25,7 @@ Parsed surface, by grammar production: | --- | --- | | Module | `package`, `use shape/lawpack/target/core` (+ optional `digest`) | | Types | `type` records, refined `String`/`Bytes`, `Option`/`List`/`Map`/`CapabilityRef`, `enum`, `variant` (with payloads) | -| Intents | params, `returns`, `profile`/`implements`/`basis`/`footprint`/`budget`/`where` clauses | +| Actions | params, `returns`, `profile`/`implements`/`basis`/`footprint`/`budget`/`where` clauses | | Statements | `let` (+ effect-`else`), `return`, `require`, `guarantee`, `assert`, `if`/`else if`/`else`, bounded `for`, effect statements | | Expressions | full precedence chain; calls + type-calls; field access; records (incl. shorthand + spread); booleans; digest literals; ternary `if … then … else`; branch-yield; variant literals (`::`); `match` | | Obstructions | single-target and full map-form `else { failure(binder) => target, … }` | diff --git a/docs/SPEC_edict-language-v1.md b/docs/SPEC_edict-language-v1.md index d5cebda..3bf2412 100644 --- a/docs/SPEC_edict-language-v1.md +++ b/docs/SPEC_edict-language-v1.md @@ -69,7 +69,7 @@ the boundaries that must not leak back into language meaning: ## Decision Summary Edict v1 is a restricted deterministic source language for lawful optics over -witnessed causal history. An Edict intent is an optic-shaped operation +witnessed causal history. An Edict action is an optic-shaped operation specification: it declares a bounded aperture, typed input and output, imported law and target authorities, inferred effects, proof obligations, obstruction mappings, support posture, and target-owned application requirements. @@ -102,7 +102,7 @@ Edict inherits its motivating shape from Observer Geometry and WARP optics: lowers it under an admissibility law, and retains a holographic boundary shell. -Therefore an Edict intent is not only a typed function from input to output. It +Therefore an Edict action is not only a typed function from input to output. It is an authored optic candidate: ```text @@ -116,14 +116,14 @@ obstruction mappings, canonical Core IR, and bundle evidence. Future syntax may make basis, aperture, support ledgers, degeneracy posture, and holographic witness shells more explicit, but v1 must not contradict those concepts. -Intent classes: +Action classes: -- **revelation optic:** read-only or observer intent that projects a bounded +- **revelation optic:** read-only or observer action that projects a bounded aperture into a reading without authoring history; -- **affect/reintegration optic:** write intent that proposes effects against a +- **affect/reintegration optic:** write action that proposes effects against a basis and carries the guards, support obligations, and obstruction vocabulary needed for participant admission; -- **semantic lawpack optic:** storage-neutral intent that lowers through a +- **semantic lawpack optic:** storage-neutral action that lowers through a digest-locked lawpack into a concrete target profile. Target profiles interpret Edict Core optics into runtime-owned execution @@ -274,9 +274,9 @@ Edict has four separable layers. ### 1. Edict Source Human and agent authored syntax. It declares packages, imports, types, pure -functions, intents, assertions, and target intrinsic calls. Migration and +functions, actions, assertions, and target intrinsic calls. Migration and projection syntax is reserved for future versions; v1 expresses migrations as -ordinary intents with a migration profile. +ordinary actions with a migration profile. ### 2. Edict Core IR @@ -738,7 +738,7 @@ unit. Writes become visible only if all reads, guards, effects, budgets, resource checks, and guarantees succeed. Any obstruction or platform-owned failure aborts the application without -externally visible partial writes. If an intent appears to need effects across +externally visible partial writes. If an action appears to need effects across multiple physical systems, it must target a composite target profile that owns the coordination and atomicity semantics. @@ -894,7 +894,7 @@ must carry the same source artifact digest, Core IR digest, target profile, footprint, cost budget, verifier result, generated artifacts, admission posture, and obstruction taxonomy as write operations. -Observer source sugar, if accepted by a frontend, must desugar to an `intent` +Observer source sugar, if accepted by a frontend, must desugar to an `action` with a read-only source claim. The read-only claim remains inferential: writes, appends, runtime-materialized semantic records, unbounded reads, or unchecked costs reject the bundle. @@ -985,7 +985,7 @@ admission and scheduling precision. ### I-007 FIDLAR Rejection -An Edict intent must not receive raw runtime mutation authority in the +An Edict action must not receive raw runtime mutation authority in the lawful-autonomous lane. Privileged/native host extensions must use a separate trust lane and must not claim compile-time footprint honesty unless they lower to checked target IR with inferred effects. @@ -999,7 +999,7 @@ must emit a mismatch diagnostic unless the author updates the lock. ### I-009 Canonical Names -Package, type, function, intent, field, profile, and import names normalize to +Package, type, function, action, field, profile, and import names normalize to canonical coordinates. The canonical coordinate, not local alias spelling, is used in Core IR hashes. @@ -1135,7 +1135,7 @@ changing the identity of the enclosed contract bundle ### I-029 Atomic Application -A lawful-autonomous v1 intent lowers to one target-owned atomic application +A lawful-autonomous v1 action lowers to one target-owned atomic application unit. Runtime reads and guards observe one target-defined application snapshot. All writes become visible atomically. Any obstruction, resource failure, budget failure, failed runtime precondition, or failed precommit guarantee leaves @@ -1143,14 +1143,14 @@ externally visible target state unchanged. ### I-030 Single Runtime Effect Domain -An intent may lower to effects owned by at most one runtime target profile. +An action may lower to effects owned by at most one runtime target profile. Lawpack semantic effects may lower into that target. Cross-target application requires a composite target profile that owns coordination, obstruction, and atomicity semantics. -### I-031 Intent Optic Structure +### I-031 Action Optic Structure -Every Edict intent is an optic-shaped lawful operation specification. Core +Every Edict action is an optic-shaped lawful operation specification. Core carries a **minimal normative optic contract** — `opticKind`, `basis`, `boundaryKind` (`projection`/`affect`), aperture/footprint requirement, `supportPolicy`, and `lossDisposition` (see Optic Contract under Edict Core IR). @@ -1189,7 +1189,7 @@ type RecordGitWarpImportBatchReceipt = { batchId: Digest, }; -intent recordGitWarpImportBatch(input: RecordGitWarpImportBatchInput) +action recordGitWarpImportBatch(input: RecordGitWarpImportBatchInput) returns RecordGitWarpImportBatchReceipt profile echo.createOnly basis input.basisId @@ -1223,7 +1223,7 @@ package graft.structural_history@1; use lawpack history.optics@1 digest "sha256:..." as history; -intent recordGitWarpImportBatch(input: RecordGitWarpImportBatchInput) +action recordGitWarpImportBatch(input: RecordGitWarpImportBatchInput) returns RecordGitWarpImportBatchReceipt implements history.recordEntry basis input.basisId @@ -1355,7 +1355,7 @@ declaration = type-decl | enum-decl | const-decl | fn-decl - | intent-decl ; + | action-decl ; bound-ref = int-lit | qual-ident ; @@ -1398,10 +1398,10 @@ pure-statement = let-stmt | assert-stmt | return-stmt ; -intent-decl = "intent" , ident , "(" , param-list? , ")" , +action-decl = "action" , ident , "(" , param-list? , ")" , "returns" , type-ref , - intent-clause* , block ; -intent-clause = profile-clause + action-clause* , block ; +action-clause = profile-clause | implements-clause | basis-clause | where-clause @@ -1517,13 +1517,13 @@ Semantic grammar rules: accepted as v1 declarations. - Keywords are reserved as bare identifiers but may appear after `.` as member names, so `ref.ensure(value)` and `history.event.record(value)` are legal. -- Each intent may contain at most one `profile`, one `implements`, one `basis`, +- Each action may contain at most one `profile`, one `implements`, one `basis`, one `footprint`, and one `budget` clause. -- The grammar accepts `intent-clause*`, but clause **requiredness** is a semantic +- The grammar accepts `action-clause*`, but clause **requiredness** is a semantic rule; omitting a required clause is parseable but rejected in semantic - validation (`EDICT-LANG-INTENT-CLAUSES-001`). Required: **at least one** of + validation (`EDICT-LANG-ACTION-CLAUSES-001`). Required: **at least one** of `profile` (operation mode on a target) or `implements` (a portable law profile - the intent satisfies) — an intent may carry **both** (e.g. `implements` a law + the action satisfies) — an action may carry **both** (e.g. `implements` a law profile while declaring a `profile` operation mode); a `budget` clause; and a `basis` clause (see next bullet). Optional: `footprint` (a declared ceiling; the computed footprint is inferred regardless) and `where`. @@ -1613,7 +1613,7 @@ Only `String` may pin a `canonical=` policy; `Bytes` carries `max` only. max-only refinement), because bytes are measured and hashed raw and must not be normalized (`EDICT-LANG-BYTES-NOCANON-001`). -Boundedness is expressible **everywhere** a type appears: intent parameters, +Boundedness is expressible **everywhere** a type appears: action parameters, return types, type aliases, record fields, function parameters and returns, and imported-effect arguments. A naked, unbounded runtime `String` or `Bytes` value is rejected in the lawful-autonomous lane because its output cost cannot be @@ -1670,7 +1670,7 @@ lawful-autonomous locked bundle. ## Expressions Edict expressions are pure unless the expression is an imported effect call -in A-normal effect position inside an intent body. Pure expressions may: +in A-normal effect position inside an action body. Pure expressions may: - construct records, variants, lists, and maps; - read local variables and operation inputs; @@ -1699,7 +1699,7 @@ Pure expressions may not: ## Statements -Intent bodies are statement-oriented so effect ordering is explicit. The v1 +Action bodies are statement-oriented so effect ordering is explicit. The v1 statement set is deliberately small: - `let` binds an immutable local. @@ -1774,7 +1774,7 @@ application unit begins. - Domain-specific checks against runtime state belong in `require` (with its typed obstruction), never in `where`. -Multiple `where` clauses merge conjunctively. The Core intent carries the +Multiple `where` clauses merge conjunctively. The Core action carries the **typed predicate trees** themselves in `inputConstraints` (not merely a validator coordinate), because the predicates are hash-significant: changing `where input.repo != ""` to a different input predicate must change the semantic @@ -1785,10 +1785,10 @@ non-hash sidecar. ## Basis Expressions A `basis` expression is evaluated in the **pure pre-body environment** -(`EDICT-LANG-BASIS-PURE-001`). It may reference intent parameters, constants, +(`EDICT-LANG-BASIS-PURE-001`). It may reference action parameters, constants, `CapabilityRef` values, and total digest-locked pure functions over those values. It must **not** reference target state, imported effects, effect -results, or locals bound in the intent body. A profile- or lawpack-supplied +results, or locals bound in the action body. A profile- or lawpack-supplied basis template obeys the same restriction. This protects the central doctrine: the basis is what the operation *says* it @@ -2154,7 +2154,7 @@ not source syntax and not target IR. "imports": [], "types": [], "functions": [], - "intents": [], + "actions": [], "canonicalizationProfile": { "id": "edict.canonical-cbor/v1", "digest": "sha256:..." @@ -2183,12 +2183,12 @@ Core IR digest. Lowerer and verifier component digests are bundle fields, not Core fields: a different conforming lowerer must not change the semantic Core IR digest (`EDICT-CORE-NOPACKAGING-001`). -### Core Intent Shape +### Core Action Shape ```json { "name": "recordGitWarpImportBatch", - "coordinate": "graft.structural_history@1.intent.recordGitWarpImportBatch", + "coordinate": "graft.structural_history@1.action.recordGitWarpImportBatch", "input": "RecordGitWarpImportBatchInput", "output": "RecordGitWarpImportBatchReceipt", "optic": { @@ -2227,12 +2227,12 @@ digest (`EDICT-CORE-NOPACKAGING-001`). } ``` -Hash-significant intent fields are the optic contract, the +Hash-significant action fields are the optic contract, the `requiredOperationProfile` **requirement**, target authorities, law profiles, the typed `inputConstraints` predicate trees (not a validator reference), Core/target budgets, and the body. (The module-level `requiredCoreCapabilities` field is also hash-significant; see Core Module Shape.) The following are **not** -in the Core intent preimage: +in the Core action preimage: - `verifiedOperationMode` — a verifier-report field, not a Core claim. Core states the requirement; the verifier proves the verdict @@ -2246,7 +2246,7 @@ in the Core intent preimage: ### Optic Contract -Per I-031, every intent carries a minimal normative optic contract in Core. Each +Per I-031, every action carries a minimal normative optic contract in Core. Each field has exactly one deterministic source of truth (`EDICT-OPTIC-SOURCE-001`): @@ -2406,7 +2406,7 @@ Core IR canonicalization must: the `coordinate` is authoritative (I-009), and the `name` is review metadata; - fail if any imported digest is unresolved. -The Core intent preimage **includes**, positively and exhaustively +The Core action preimage **includes**, positively and exhaustively (`EDICT-CORE-PREIMAGE-LIST-001`): - `coordinate`; @@ -2538,7 +2538,7 @@ Diagnostic shape: "start": { "line": 12, "column": 3 }, "end": { "line": 12, "column": 54 } }, - "coordinate": "graft.structural_history@1.intent.recordGitWarpImportBatch", + "coordinate": "graft.structural_history@1.action.recordGitWarpImportBatch", "repair": { "kind": "adjust-footprint-bound-or-remove-effect", "details": {} @@ -2652,7 +2652,7 @@ are present. - Add parser conformance fixtures for valid and invalid syntax. - Add parser fixtures for branch-yield conditional effects, effect `else` obstruction mapping, `budget <=`, bound refs, spread literals, - order-independent intent clauses, and rejected `migration`/`projection` + order-independent action clauses, and rejected `migration`/`projection` declarations. - Add Core IR canonicalization fixtures with exact expected hashes. - Add relapse fixtures proving no graph built-ins exist in Core. @@ -2769,7 +2769,7 @@ These are important but are not parser or language freeze prerequisites: - Declared footprint underclaim rejects compilation. - FIDLAR raw callbacks are impossible in normal syntax. - Target lowering produces profile-owned target IR. -- Target lowering preserves the intent optic structure: basis, aperture or +- Target lowering preserves the action optic structure: basis, aperture or footprint, projection or affect boundary, support posture, guards, obstruction mappings, cost bounds, and canonical artifact identity. - Support loss, degeneracy, footprint overlap, and witness debt cannot be @@ -2781,8 +2781,8 @@ These are important but are not parser or language freeze prerequisites: - GraphQL source-profile imports without digest-locked bounds reject locked-bundle production. Participant policy cannot supply missing Core type bounds. -- Read-only intent containing a write effect rejects. -- Read-only intent containing a runtime-materialized semantic append/log effect +- Read-only action containing a write effect rejects. +- Read-only action containing a runtime-materialized semantic append/log effect rejects. - Query and observer operations are first-class lawful-autonomous operations with Core IR digest, target profile, footprint, budget, verifier result, @@ -2809,7 +2809,7 @@ These are important but are not parser or language freeze prerequisites: - Runtime reads, guards, writes, resource checks, and guarantees lower to one target-owned atomic application unit with no externally visible partial writes on obstruction. -- An intent may emit runtime effects for only one target profile unless it uses +- An action may emit runtime effects for only one target profile unless it uses a composite target profile that owns coordination and atomicity. - Codename terms reject from hash-significant canonical coordinates. - Branch-yield conditional effect values lower to structured Core branches with @@ -2822,7 +2822,7 @@ These are important but are not parser or language freeze prerequisites: laundered into author-defined domain obstructions. - Source `budget <=` clauses lower to Core budgets and reject underclaimed cost. - Loop, list, map, and field bounds accept literal or digest-locked bound refs. -- Intent clauses are order-independent with unique profile/implements/footprint +- Action clauses are order-independent with unique profile/implements/footprint and budget clauses. - `migration` and `projection` are reserved and rejected by the v1 parser. - `some`, `none`, and `default` are prelude functions with pinned semantics. @@ -2844,7 +2844,7 @@ These are important but are not parser or language freeze prerequisites: - Authoritative hash input is `edict.canonical-cbor/v1`; canonical JSON is review/debug rendering. - First non-Echo target is `kv.transactional@1`; event-log follows later. -- Migrations start as ordinary intents with a migration profile. +- Migrations start as ordinary actions with a migration profile. - Target profile manifests are data; lowerers and verifiers are locked, sandboxed executable components satisfying the manifest. - Portable semantic records may exist only as sugar for lawpack semantic @@ -2861,7 +2861,7 @@ These are important but are not parser or language freeze prerequisites: costs, obstructions, target requirements, and admission requirements are inlined or summarized by verifier-approved bundle evidence. - A future version may add first-class `migration`, `projection`, and `observer` - declarations after ordinary intents and observer desugaring prove the + declarations after ordinary actions and observer desugaring prove the semantics. - A future version may loosen map-key restrictions for imported types with explicitly declared canonical key semantics. @@ -2878,16 +2878,16 @@ These are important but are not parser or language freeze prerequisites: - Do not require GraphQL for native Edict authoring. - Do not make HOLMES, Watson, or Moriarty runtime authorities. -## Appendix A: jedit Intent Stress Test +## Appendix A: jedit Action Stress Test > [!NOTE] > jedit is the intended first real-world use case, so these are kept > **clause-conformant**, not left to rot. The rope-package and structural-history -> intents below carry the clauses v1 requires (including `basis`, per -> `EDICT-LANG-INTENT-CLAUSES-001`) and are fixture candidates. The one +> actions below carry the clauses v1 requires (including `basis`, per +> `EDICT-LANG-ACTION-CLAUSES-001`) and are fixture candidates. The one > deliberate exception is the **Product Text Buffer Optic Sketch**, which uses > `use capability` and `invoke` — **explicitly rejected v1 grammar** — to expose -> design pressure; it is a negative/illustrative sketch, not a conforming intent. +> design pressure; it is a negative/illustrative sketch, not a conforming action. ### Source Anchors @@ -2920,7 +2920,7 @@ Two adjacent jedit contract surfaces also exist: This appendix treats `rope.graphql` as the primary stress surface because it is the installed generated jedit package. Consistent with the note above: the -rope-package and structural-history intents are clause-conformant **fixture +rope-package and structural-history actions are clause-conformant **fixture candidates**, while the product text-buffer optic surface is the one deliberate **non-v1 sketch** (it exposes capability invocation/composition and opaque handles via the rejected `invoke`/`use capability` forms). "Stress fixture" here @@ -2972,7 +2972,7 @@ use shape "contracts/jedit/rope.graphql" as shape; use lawpack jedit.rope@1 as rope; use target echo.dpo@1 as echo; -intent createBufferWorldline(input: shape.CreateBufferWorldlineInput) +action createBufferWorldline(input: shape.CreateBufferWorldlineInput) returns shape.CreateBufferWorldlineResult profile echo.createOnly basis none @@ -3059,7 +3059,7 @@ intent createBufferWorldline(input: shape.CreateBufferWorldlineInput) }; } -intent replaceRangeAsTick(input: shape.ReplaceRangeAsTickInput) +action replaceRangeAsTick(input: shape.ReplaceRangeAsTickInput) returns shape.ReplaceRangeAsTickResult profile echo.boundaryReplacementLens basis input.baseHeadId @@ -3170,7 +3170,7 @@ intent replaceRangeAsTick(input: shape.ReplaceRangeAsTickInput) }; } -intent createCheckpoint(input: shape.CreateCheckpointInput) +action createCheckpoint(input: shape.CreateCheckpointInput) returns shape.CreateCheckpointResult profile echo.createOnly basis input.worldlineId @@ -3206,7 +3206,7 @@ intent createCheckpoint(input: shape.CreateCheckpointInput) }; } -intent worldlineSnapshot(input: shape.WorldlineSnapshotInput) +action worldlineSnapshot(input: shape.WorldlineSnapshotInput) returns shape.WorldlineSnapshot profile echo.readOnly basis input.worldlineId @@ -3232,7 +3232,7 @@ intent worldlineSnapshot(input: shape.WorldlineSnapshotInput) }; } -intent textWindow(input: shape.TextWindowInput) +action textWindow(input: shape.TextWindowInput) returns shape.TextWindowReading profile echo.readOnly basis input.worldlineId @@ -3294,7 +3294,7 @@ use shape "contracts/jedit/text-buffer-optic.graphql" as optic; use lawpack jedit.text_buffer_optic@1 as textBuffer; use capability jedit.rope_contract@1 as rope; -intent createBuffer(input: optic.CreateBufferInput) +action createBuffer(input: optic.CreateBufferInput) returns optic.CreateBufferPayload profile textBuffer.productCreate { @@ -3315,7 +3315,7 @@ intent createBuffer(input: optic.CreateBufferInput) }; } -intent replaceRange(input: optic.ReplaceRangeInput) +action replaceRange(input: optic.ReplaceRangeInput) returns optic.ReplaceRangePayload profile textBuffer.productEdit { @@ -3340,7 +3340,7 @@ intent replaceRange(input: optic.ReplaceRangeInput) }; } -intent textWindow( +action textWindow( readBasis: optic.ReadBasisHandle, input: optic.TextWindowInput ) @@ -3379,7 +3379,7 @@ package jedit.structural_history@1; use shape "contracts/jedit/structural-history.graphql" as historyShape; use lawpack jedit.structural_history@1 as history; -intent createTextHistory(input: historyShape.CreateTextHistoryInput) +action createTextHistory(input: historyShape.CreateTextHistoryInput) returns historyShape.CreateTextHistoryPayload implements history.createTextHistory basis none @@ -3396,7 +3396,7 @@ intent createTextHistory(input: historyShape.CreateTextHistoryInput) return history.createdPayload(input); } -intent replaceTextRange(input: historyShape.ReplaceTextRangeInput) +action replaceTextRange(input: historyShape.ReplaceTextRangeInput) returns historyShape.ReplaceTextRangePayload implements history.replaceTextRange basis input.baseRevisionId @@ -3418,7 +3418,7 @@ intent replaceTextRange(input: historyShape.ReplaceTextRangeInput) return history.replacePayload(input); } -intent openTextEditGroup(input: historyShape.OpenTextEditGroupInput) +action openTextEditGroup(input: historyShape.OpenTextEditGroupInput) returns historyShape.TextEditGroupPayload implements history.openTextEditGroup basis input.historyId @@ -3432,7 +3432,7 @@ intent openTextEditGroup(input: historyShape.OpenTextEditGroupInput) return history.openGroupPayload(input); } -intent includeTextEventInOpenGroup( +action includeTextEventInOpenGroup( input: historyShape.IncludeTextEventInOpenGroupInput ) returns historyShape.TextEditGroupPayload @@ -3449,7 +3449,7 @@ intent includeTextEventInOpenGroup( return history.includeEventPayload(input); } -intent closeTextEditGroup(input: historyShape.CloseTextEditGroupInput) +action closeTextEditGroup(input: historyShape.CloseTextEditGroupInput) returns historyShape.TextEditGroupPayload implements history.closeTextEditGroup basis input.historyId @@ -3463,7 +3463,7 @@ intent closeTextEditGroup(input: historyShape.CloseTextEditGroupInput) return history.closeGroupPayload(input); } -intent createTextCheckpoint(input: historyShape.CreateTextCheckpointInput) +action createTextCheckpoint(input: historyShape.CreateTextCheckpointInput) returns historyShape.CreateTextCheckpointPayload implements history.createTextCheckpoint basis input.revisionId @@ -3480,7 +3480,7 @@ intent createTextCheckpoint(input: historyShape.CreateTextCheckpointInput) return history.checkpointPayload(input); } -intent textHistorySnapshot(input: historyShape.TextHistorySnapshotInput) +action textHistorySnapshot(input: historyShape.TextHistorySnapshotInput) returns historyShape.TextHistorySnapshotReading implements history.textHistorySnapshot profile history.readOnly @@ -3508,7 +3508,7 @@ dedicated language repository. It is not Echo-shaped, but it should still lower to Echo when an Echo target lawpack exists. It may also lower naturally to an event-log target without graph semantics. -The snapshot intent uses a semantic query effect because a pure lawpack helper +The snapshot action uses a semantic query effect because a pure lawpack helper cannot observe history state. If a future lawpack exposes a proof-only plan constructor, it must be named and classified as a non-runtime symbolic plan; it must not smuggle state-dependent output through a pure helper. diff --git a/docs/SPEC_edict-lawpack-abi-v1.md b/docs/SPEC_edict-lawpack-abi-v1.md index 2dea741..d149d54 100644 --- a/docs/SPEC_edict-lawpack-abi-v1.md +++ b/docs/SPEC_edict-lawpack-abi-v1.md @@ -61,7 +61,7 @@ exports: expression position. It may never read host state, allocate without bound, or reach a target through a side door (`EDICT-LAWPACK-PURE-001`). - An `effect` emits a semantic fact. It is callable only in A-normal effect - position inside an intent body, exactly like a target intrinsic. + position inside an action body, exactly like a target intrinsic. A pure helper that touches runtime state, even transitively, is a relapse and must reject locked-bundle production. @@ -264,7 +264,7 @@ adapter: guard attachment rules ``` -A portable semantic intent compiles for a target **only** when the lawpack +A portable semantic action compiles for a target **only** when the lawpack supplies an adapter for that target profile. Absent an adapter, this is a **compiler/lowering error**, not an admission-class error: no valid target artifact exists yet, so admission never enters the picture. It is never a silent diff --git a/docs/SPEC_edict-target-profile-abi-v1.md b/docs/SPEC_edict-target-profile-abi-v1.md index 8f6002b..a265407 100644 --- a/docs/SPEC_edict-target-profile-abi-v1.md +++ b/docs/SPEC_edict-target-profile-abi-v1.md @@ -237,7 +237,7 @@ externally visible target state unchanged. ## Runtime Effect Domain -An Edict intent may lower to effects owned by at most one runtime target profile. +An Edict action may lower to effects owned by at most one runtime target profile. Lawpack semantic effects may lower into that same target. Cross-target application requires a composite target profile that explicitly owns coordination, obstruction classification, and atomicity semantics. diff --git a/docs/TECHNICAL_EXPLANATION.md b/docs/TECHNICAL_EXPLANATION.md index d88561c..3ff1577 100644 --- a/docs/TECHNICAL_EXPLANATION.md +++ b/docs/TECHNICAL_EXPLANATION.md @@ -29,7 +29,7 @@ mindmap Recursive Descent Precedence Climbing AST Module - Intents + Actions Bounded Aperture Declared Basis Explicit Budgets @@ -61,16 +61,16 @@ The Edict ecosystem employs specialized terminology derived from capabilities-ba | Term | Definition | | :--- | :--- | | **FIDLAR** | *Footprints Ignored; Developer Lies About Risk*. The security gap between a function's declared purpose (e.g., its name or signature) and its actual authority (whatever the host process can access). Edict eliminates FIDLAR by statically verifying and cryptographically sealing code authority. | -| **Intent** | The primary unit of execution in Edict. Unlike a traditional function, an intent is an optic-shaped operation specification that declares its bounded input, output, basis, budget, footprint bounds, and governing law. | -| **Aperture** | The bounded set of state and capabilities that an intent is permitted to read or write. Accessing anything outside the declared aperture triggers a compile-time rejection. | +| **Action** | The primary unit of execution in Edict. Unlike a traditional function, an action is an optic-shaped operation specification that declares its bounded input, output, basis, budget, footprint bounds, and governing law. | +| **Aperture** | The bounded set of state and capabilities that an action is permitted to read or write. Accessing anything outside the declared aperture triggers a compile-time rejection. | | **Basis** | The causal history reference point or timeline anchor used to resolve projections. It represents the point of state from which the execution begins. | | **Lawpack** | A cryptographically locked, authority-free package of pure helper functions, typed constants, semantic effect signatures, and target adapters. Lawpacks represent domain rules and are pinned by SHA-256 hashes. | | **Target Profile / DPO** | A Deterministic Process Object specification defining the execution environment's runtime intrinsics, verifier rules, footprint/cost algebras, and target-lowering properties (e.g., `echo.dpo`). | -| **Core IR** | A runtime-neutral, canonical representation of compiled Edict intents. It has no side-channel APIs, no file system, and no networking, and is completely deterministic. | +| **Core IR** | A runtime-neutral, canonical representation of compiled Edict actions. It has no side-channel APIs, no file system, and no networking, and is completely deterministic. | | **Contract Bundle** | A participant-neutral, CBOR-encoded package of compiled Core IR, target lowerings, proof evidence, and cryptographic signatures. | | **HOLMES / Watson / Moriarty** | The Wesley platform's assurance machinery. **HOLMES** manages evidence and attestations; **Watson** verifies bundle compliance; **Moriarty** runs fuzzing and relapse tests to discover gaps. | | **A-Normal Form (ANF)** | A restricted syntactic structure where every intermediate effectful computation must be bound to a distinct variable. This makes the sequencing of side effects explicit and verifiable. | -| **SHA-Lock / Digest-Lock** | Pinned references to external dependencies (lawpacks, targets) using their SHA-256 digests. This guarantees that dependencies cannot change silently without modifying the intent's identity. | +| **SHA-Lock / Digest-Lock** | Pinned references to external dependencies (lawpacks, targets) using their SHA-256 digests. This guarantees that dependencies cannot change silently without modifying the action's identity. | --- @@ -78,9 +78,9 @@ The Edict ecosystem employs specialized terminology derived from capabilities-ba Edict's architecture separates language syntax, target runtime specifics, and contract admission rules to enforce static capability checks. -Edict compiles source modules containing raw intent declarations to a canonical, runtime-neutral intermediate representation (Core IR). This Core IR represents pure calculations and explicit target adapters, stripping away all ambient authority. The static verification uses hash-locked schemas (GraphQL shapes), lawpacks (rule definitions), and target profiles to form a cryptographic contract bundle. +Edict compiles source modules containing raw action declarations to a canonical, runtime-neutral intermediate representation (Core IR). This Core IR represents pure calculations and explicit target adapters, stripping away all ambient authority. The static verification uses hash-locked schemas (GraphQL shapes), lawpacks (rule definitions), and target profiles to form a cryptographic contract bundle. -This bundle is sent to the Continuum platform for admission check. When the platform policy is satisfied, it issues admission receipts, making the intent available for deterministic execution inside sandboxed target runtimes. +This bundle is sent to the Continuum platform for admission check. When the platform policy is satisfied, it issues admission receipts, making the action available for deterministic execution inside sandboxed target runtimes. --- @@ -98,7 +98,7 @@ During bootstrapping, the Edict compiler (`wesley` or the compiler engine) loads ### The Runtime Phase (Sandboxed Evaluation) -Once compiled and admitted as a **Contract Bundle**, the runtime executes the intent within a Deterministic Process Object (DPO) container: +Once compiled and admitted as a **Contract Bundle**, the runtime executes the action within a Deterministic Process Object (DPO) container: 1. **Budget Enforcement**: Evaluates cost dynamically. The verifier checks operations against a strict fuel limit (`budget <= law.tinyBudget`). 2. **Footprint Bounds**: Dynamic read/write effects are checked against the verified aperture. @@ -144,7 +144,7 @@ sequenceDiagram activate Parser Parser->>Parser: package_decl() Parser->>Parser: import() loops - Parser->>Parser: decl() loops (Type/Intent) + Parser->>Parser: decl() loops (Type/Action) Parser->>Parser: precedence climbing for Expressions Parser-->>AST: Construct ast::Module deactivate Parser @@ -228,7 +228,7 @@ fn string(&mut self) -> Result { ## 6. Syntactic Analysis & Precedence Climbing (`parser.rs`) -The parser is implemented as a recursive-descent parser. Top-level files declare a package, list imports, and describe type declarations and intents. +The parser is implemented as a recursive-descent parser. Top-level files declare a package, list imports, and describe type declarations and actions. ### Module AST Structure @@ -255,7 +255,7 @@ classDiagram class Decl { <> Type(TypeDecl) - Intent(IntentDecl) + Action(ActionDecl) } class TypeDecl { +String name @@ -263,11 +263,11 @@ classDiagram +TypeExpr body +Span span } - class IntentDecl { + class ActionDecl { +String name +Vec~Param~ params +TypeRef returns - +Vec~IntentClause~ clauses + +Vec~ActionClause~ clauses +Block body +Span span } @@ -275,7 +275,7 @@ classDiagram Module --> Import Module --> Decl Decl --> TypeDecl - Decl --> IntentDecl + Decl --> ActionDecl ``` ### Highlight: Precedence Climbing @@ -347,7 +347,7 @@ type HelloReading = { message: String, }; -intent sayHello(input: HelloInput) +action sayHello(input: HelloInput) returns HelloReading profile hello.readOnly basis none @@ -422,7 +422,7 @@ During syntactic analysis, the above code is materialized into the following mem } }, { - "Intent": { + "Action": { "name": "sayHello", "params": [ { @@ -536,7 +536,7 @@ Because the parser uses explicit recursive matching, it bails out immediately at Edict is built from the ground up to prevent the ambient authority problem (FIDLAR). It establishes three crisp boundaries: -1. **Aperture Limits**: Compiled intents cannot issue direct syscalls. Filesystem, networking, and environment configurations are excluded from the grammar. Any dynamic read/write is mapped to target-profile adapters that are explicitly defined and sandboxed. +1. **Aperture Limits**: Compiled actions cannot issue direct syscalls. Filesystem, networking, and environment configurations are excluded from the grammar. Any dynamic read/write is mapped to target-profile adapters that are explicitly defined and sandboxed. 2. **Cryptographic SHA-Locks**: The dependency structure is frozen. If a developer attempts to update a lawpack to change behavior without updating the digest lock `digest "sha256:..."` in the import header, compilation fails. Silent code changes are impossible. 3. **Sandbox Borders**: The compiled bundle executes inside a WASM component sandbox. Interaction with external state is mediated through a WebAssembly Interface Type (WIT) interface, ensuring complete control over runtime actions. @@ -544,11 +544,11 @@ Edict is built from the ground up to prevent the ambient authority problem (FIDL ## 10. Concurrency, Parallelism, and Determinism -Traditional programming languages embrace concurrency (multithreading, async queues, lock coordination) to maximize processing throughput. Edict rejects ambient runtime concurrency within intents to preserve **strict determinism**: +Traditional programming languages embrace concurrency (multithreading, async queues, lock coordination) to maximize processing throughput. Edict rejects ambient runtime concurrency within actions to preserve **strict determinism**: - **No Threads**: There are no thread-spawning primitives, promises, or background tasks within Edict code. - **Purely Sequential Processing**: In Edict, the compilation path enforces A-normal form (ANF) for effectful statements. An effect must be evaluated, resolved, and bound before the next instruction executes. This guarantees that all effects have a single, verifiable, reproducible path of causation. -- **Parallel Verification**: While the execution of an intent is single-threaded, the *verification* of contract bundles and signatures by `wesley`'s assurance platform can be run across thousands of parallel threads safely since individual intents are side-effect-free during the check phase. +- **Parallel Verification**: While the execution of an action is single-threaded, the *verification* of contract bundles and signatures by `wesley`'s assurance platform can be run across thousands of parallel threads safely since individual actions are side-effect-free during the check phase. --- @@ -573,7 +573,7 @@ During the creation of Edict minimal-v1, the architecture made explicit design c ### Contextual Keywords vs. Reserved Keywords -- **Trade-off**: The parser matches keywords (`max`, `min`, `canonical`, `type`, `intent`) contextually using identifier string comparison instead of reserving them globally. +- **Trade-off**: The parser matches keywords (`max`, `min`, `canonical`, `type`, `action`) contextually using identifier string comparison instead of reserving them globally. - **Benefit**: Developers can name fields or parameters `max` or `type` (e.g., `input.type`) without compiling errors. - **Cost**: The parsing code is slightly more complex, requiring defensive identifier peeking to determine whether a keyword represents a declaration keyword or a variable name. @@ -599,7 +599,7 @@ When analyzing computer languages in general, systems engineers evaluate them ac Traditional General-Purpose Languages (GPLs) like Rust, Go, or Python are **Turing-complete** by design. They support arbitrary computation, unbounded loops, dynamic memory allocation, and indirect system calls. In contrast, Domain-Specific Languages (DSLs) restrict execution semantics to solve specific systemic problems efficiently. -Edict is a highly specialized DSL. By design, Edict lacks Turing completeness in its source language: it prohibits arbitrary iterative loops and unbounded recursion. This is not a limitation but a deliberate security feature. It ensures that the Execution Halting Problem is mitigated statically. Every Edict intent can have its cost calculated and capped before execution starts. +Edict is a highly specialized DSL. By design, Edict lacks Turing completeness in its source language: it prohibits arbitrary iterative loops and unbounded recursion. This is not a limitation but a deliberate security feature. It ensures that the Execution Halting Problem is mitigated statically. Every Edict action can have its cost calculated and capped before execution starts. ### State Paradigm & Execution Determinism @@ -630,7 +630,7 @@ Edict incorporates several specific syntactic and behavioral designs that differ ### Quirk B: Contextual Keyword Resolution -- **Quirk**: Keywords like `max`, `min`, `canonical`, `type`, and `intent` are not reserved tokens in Edict. The lexer produces a generic `Ident(String)` token for all keywords. The [parser](../crates/edict-syntax/src/parser.rs#L44-L92) determines contextually whether an identifier represents a declaration keyword or a variable name. +- **Quirk**: Keywords like `max`, `min`, `canonical`, `type`, and `action` are not reserved tokens in Edict. The lexer produces a generic `Ident(String)` token for all keywords. The [parser](../crates/edict-syntax/src/parser.rs#L44-L92) determines contextually whether an identifier represents a declaration keyword or a variable name. - **Why We Did It This Way**: In languages with reserved keyword sets, developers cannot use common words like `type` or `max` as field names (e.g., `payload.type` or `object.max` are syntax errors in Rust and Java unless escaped). By matching keywords contextually, Edict allows developers to write natural, standard API interfaces while keeping the underlying compiler lexer simple and zero-dependency. ### Quirk C: Obstruction-Driven Control Flow (No Exceptions) @@ -641,7 +641,7 @@ Edict incorporates several specific syntactic and behavioral designs that differ ### Quirk D: Constrained Scalar Types (`String`) - **Quirk**: Basic types are parameterized directly in type signatures with bounds (e.g., `String` or `Bytes`). -- **Why We Did It This Way**: GPLs allow dynamically sized strings, allocating memory from a heap as needed. In high-assurance sandboxes, dynamic heap allocation can lead to denial-of-service memory exhaustion attacks. Integrating length bounds directly into the type definition allows the Edict compiler to compute the maximum memory footprint of an intent before admission. +- **Why We Did It This Way**: GPLs allow dynamically sized strings, allocating memory from a heap as needed. In high-assurance sandboxes, dynamic heap allocation can lead to denial-of-service memory exhaustion attacks. Integrating length bounds directly into the type definition allows the Edict compiler to compute the maximum memory footprint of an action before admission. --- @@ -673,7 +673,7 @@ Edict contract bundles are identified by the cryptographic hash of their compile 1. Adding a single comment, space, or running a code reformatter (like `rustfmt`) would shift source spans. 2. Shifting source spans would change the compiled Core IR byte representation. -3. The cryptographic hash of the intent would mutate, changing its identity. +3. The cryptographic hash of the action would mutate, changing its identity. As a result, a developer who merely cleaned up whitespace or comments would create a "new" contract in the eyes of the Continuum admission engine. This would invalidate prior HOLMES attestations and audit approvals. diff --git a/docs/abi/edict-common.cddl b/docs/abi/edict-common.cddl index e21b401..3a44414 100644 --- a/docs/abi/edict-common.cddl +++ b/docs/abi/edict-common.cddl @@ -50,7 +50,7 @@ authority-class = "domainMappable" / "participantOwned" / "integrityFault" / core-type-ref = tstr ; canonical Core type coordinate -; An operation profile supplies the optic template a Core intent resolves its +; An operation profile supplies the optic template a Core action resolves its ; opticKind/boundaryKind/supportPolicy/lossDisposition from. Target profiles and ; lawpacks publish these as a map `{ coordinate => operation-profile }`, so the ; coordinate is the KEY, not a value field (EDICT-OPTIC-TEMPLATE-OWNER-001, @@ -67,7 +67,7 @@ optic-template = { lossDisposition: tstr, ; canonical loss-disposition coordinate ? basisTemplate: tstr, ; optional digest-locked basis template coord ; the aperture requirement this template supplies. Required when the template - ; is the source of a Core optic's apertureRequirement (i.e. the intent has no + ; is the source of a Core optic's apertureRequirement (i.e. the action has no ; source `footprint <= ...`), since apertureRequirement is mandatory in Core ; (EDICT-OPTIC-APERTURE-REF-001). ? apertureRequirement: aperture-requirement, diff --git a/docs/abi/edict-core.cddl b/docs/abi/edict-core.cddl index 5603a22..0ed4d5d 100644 --- a/docs/abi/edict-core.cddl +++ b/docs/abi/edict-core.cddl @@ -10,7 +10,7 @@ core-module = { coordinate: tstr, imports: [* core-import], types: { * tstr => core-type }, - intents: { + tstr => core-intent }, + actions: { + tstr => core-action }, requiredCoreCapabilities: [* tstr], } @@ -204,9 +204,9 @@ input-constraint = { predicate: core-predicate, } -; --- intents, blocks, and nodes ----------------------------------------- +; --- actions, blocks, and nodes ----------------------------------------- -core-intent = { +core-action = { input: core-type-ref, output: core-type-ref, requiredOperationProfile: tstr, diff --git a/docs/abi/edict-target-ir.cddl b/docs/abi/edict-target-ir.cddl index 6d0ca78..d364b05 100644 --- a/docs/abi/edict-target-ir.cddl +++ b/docs/abi/edict-target-ir.cddl @@ -23,10 +23,10 @@ target-ir-artifact = { domain: tstr, targetProfile: target-ir-resource-ref, sourceCoreCoordinate: tstr, - intents: { * tstr => target-ir-intent }, + actions: { * tstr => target-ir-action }, } -target-ir-intent = { +target-ir-action = { operationProfile: tstr, inputConstraints: [* input-constraint], coreEvaluationBudget: core-budget, diff --git a/docs/audit/2026-06-28_code-quality.md b/docs/audit/2026-06-28_code-quality.md index 143cbf4..d233503 100644 --- a/docs/audit/2026-06-28_code-quality.md +++ b/docs/audit/2026-06-28_code-quality.md @@ -84,7 +84,7 @@ CLI diagnostics are strong: structured, staged (`parse`/`semantic`/`cli`), stabl ### 2.1 Documentation Gap -The friction point past basic usage is the absence of an **API/CLI quickstart**. The 584-line `README.md` is an excellent conceptual + spec narrative ("Edict in 10 Seconds", "What An Intent Looks Like") but has **no Installation, no CLI Usage, and no library Quickstart** heading, and the crates carry **zero runnable rustdoc examples** (0 fenced examples in either `lib.rs`). A developer who buys the pitch has nowhere to learn `edict check`. (Full treatment in the companion Documentation audit.) +The friction point past basic usage is the absence of an **API/CLI quickstart**. The 584-line `README.md` is an excellent conceptual + spec narrative ("Edict in 10 Seconds", "What An Action Looks Like") but has **no Installation, no CLI Usage, and no library Quickstart** heading, and the crates carry **zero runnable rustdoc examples** (0 fenced examples in either `lib.rs`). A developer who buys the pitch has nowhere to learn `edict check`. (Full treatment in the companion Documentation audit.) - **Action Prompt (Documentation Creation):** `Add a "Using the CLI" section to README.md with a copy-pasteable edict check example (a JSONL settings + source request piped to the binary, the expected JSONL stdout, and the exit-code table), and add a crate-level rustdoc example to crates/edict-syntax/src/lib.rs showing parse_module + validate_surface. Ensure doc examples compile under cargo test --doc.` diff --git a/docs/audit/2026-06-28_documentation-quality.md b/docs/audit/2026-06-28_documentation-quality.md index 656b4fa..79b713b 100644 --- a/docs/audit/2026-06-28_documentation-quality.md +++ b/docs/audit/2026-06-28_documentation-quality.md @@ -66,7 +66,7 @@ marker. Two distinct audiences are served unevenly: - **Conceptual reader / evaluator** (why does Edict exist?) — **served very well.** - The narrative, the intent example, the "failure is a typed outcome" framing, + The narrative, the action example, the "failure is a typed outcome" framing, and the AION pointer answer "what is it / why / how does it fit". - **Hands-on developer** (how do I run it?) — **not served.** Their top-3 questions — *How do I build/install it? How do I run `edict check`? What does a @@ -91,7 +91,7 @@ is never surfaced as a copy-pasteable example. 1. **Mark the hero diagram as a vision/roadmap view** (or add a parallel "Shipping today" line): label which stages exist at the current alpha - (Write Intent → Compile & Validate via `edict check`) versus envisioned + (Write Action → Compile & Validate via `edict check`) versus envisioned (Seal/Admission/Sandbox), so the first impression matches `Current Status`. 2. **Add a "Build & Run" / "Using the CLI" section**: how to build the binary (`cargo build -p edict-cli` → `target/debug/edict`), a copy-pasteable @@ -164,7 +164,7 @@ not a rewrite. A rewrite would destroy genuinely good conceptual content. ### 3.2 Deliverable (Incremental Update Prompt) -- **Action Prompt:** `Apply incremental documentation fixes to the edict repo without rewriting existing conceptual content: (1) In README.md, annotate the "Edict in 10 Seconds" diagram to distinguish shipping-today stages (Write Intent, Compile & Validate via 'edict check') from envisioned stages (Seal, Admission, WASM Sandbox), keeping it consistent with the Current Status section. (2) Add a "Build & Run" section with cargo build -p edict-cli, a copy-pasteable edict check JSONL example, expected stdout/stderr, and the 0/1/2 exit-code table, linking docs/topics/cli/ and docs/schemas/. (3) Add a "Using the library" rustdoc-backed snippet. (4) Create SECURITY.md (vulnerability reporting), NOTICE (Apache-2.0 attribution), and CODE_OF_CONDUCT.md at repo root. (5) Add a runnable crate-level example to crates/edict-syntax/src/lib.rs and ensure cargo test --doc passes. Run markdownlint-cli2 and cargo xtask verify before finishing.` +- **Action Prompt:** `Apply incremental documentation fixes to the edict repo without rewriting existing conceptual content: (1) In README.md, annotate the "Edict in 10 Seconds" diagram to distinguish shipping-today stages (Write Action, Compile & Validate via 'edict check') from envisioned stages (Seal, Admission, WASM Sandbox), keeping it consistent with the Current Status section. (2) Add a "Build & Run" section with cargo build -p edict-cli, a copy-pasteable edict check JSONL example, expected stdout/stderr, and the 0/1/2 exit-code table, linking docs/topics/cli/ and docs/schemas/. (3) Add a "Using the library" rustdoc-backed snippet. (4) Create SECURITY.md (vulnerability reporting), NOTICE (Apache-2.0 attribution), and CODE_OF_CONDUCT.md at repo root. (5) Add a runnable crate-level example to crates/edict-syntax/src/lib.rs and ensure cargo test --doc passes. Run markdownlint-cli2 and cargo xtask verify before finishing.` ### 3.3 Mitigation Prompt (ready to execute next) diff --git a/docs/design/authority-fact-governance.md b/docs/design/authority-fact-governance.md index 8531061..e978abf 100644 --- a/docs/design/authority-fact-governance.md +++ b/docs/design/authority-fact-governance.md @@ -138,7 +138,7 @@ facts into a bundle. It must prove the route through trusted file-backed facts. The v1 target becomes: ```text -one small intent goes source -> trusted file-backed facts -> Core -> target IR +one small action goes source -> trusted file-backed facts -> Core -> target IR -> bundle -> admission evidence, with lawpack and target-profile provenance visible and digest-bound ``` diff --git a/docs/design/canonical-target-ir-v0.11.md b/docs/design/canonical-target-ir-v0.11.md index 8ce53cd..afa1023 100644 --- a/docs/design/canonical-target-ir-v0.11.md +++ b/docs/design/canonical-target-ir-v0.11.md @@ -51,8 +51,8 @@ serialization: "domain": , "targetProfile": { "id": , "digest": ["sha256", <32 bytes>] }, "sourceCoreCoordinate": , - "intents": { - : { + "actions": { + : { "operationProfile": , "inputConstraints": [], "coreEvaluationBudget": , @@ -81,7 +81,7 @@ hexadecimal characters. ## Ordering -- Intent maps are sorted by intent name. +- Action maps are sorted by action name. - Obstruction arm maps are sorted by failure key. - Input constraints are sorted by canonical value because they are semantic constraints, not an execution order. diff --git a/docs/design/obstruction-strands-v0.md b/docs/design/obstruction-strands-v0.md index 81f70fd..5d5fae7 100644 --- a/docs/design/obstruction-strands-v0.md +++ b/docs/design/obstruction-strands-v0.md @@ -185,10 +185,10 @@ native consequence is blocked, repairable, diagnostic completion, or something else. The Target IR v0 shape models source/Core `require` guards as explicit -requirements on the intent, separate from target effect steps: +requirements on the action, separate from target effect steps: ```text -TargetIrIntent { +TargetIrAction { requirements: [ { id, @@ -207,7 +207,7 @@ precondition or guard obligations. This keeps hard rejection and preserved obstruction digest-distinct without pretending that an obstructed strand is a success-path write. -Intent-level Target IR requirements are pre-step guards. A source/Core `require` +Action-level Target IR requirements are pre-step guards. A source/Core `require` after an emitted target step, including a guard that depends on a prior effect result, needs an ordered or step-attached Target IR shape before it can be represented honestly. diff --git a/docs/releases/v0.11.0-alpha.1.md b/docs/releases/v0.11.0-alpha.1.md index 705a282..d79ad4f 100644 --- a/docs/releases/v0.11.0-alpha.1.md +++ b/docs/releases/v0.11.0-alpha.1.md @@ -77,7 +77,7 @@ general target plugin dispatch, or canonical bytes for the full bundle manifest. `cargo xtask target-ir-goldens --write` and checked by `cargo xtask target-ir-goldens --check`. - Target IR determinism and mutation coverage for map construction order, step - order, target-profile digest, source Core coordinate, intent name, effect + order, target-profile digest, source Core coordinate, action name, effect coordinate, selected intrinsic, input expression, obstruction failures and arms, input constraints, budget, and result expression. - `assemble_contract_bundle_from_target_ir(...)`, which computes diff --git a/docs/releases/v0.2.0-alpha.1.md b/docs/releases/v0.2.0-alpha.1.md index e378a24..03e3935 100644 --- a/docs/releases/v0.2.0-alpha.1.md +++ b/docs/releases/v0.2.0-alpha.1.md @@ -25,7 +25,7 @@ This is not a compiler-spine, target-lowering, or admission-stack release. - Normative `edict.core/v1` CDDL schema in `docs/abi/edict-core.cddl`. - Core semantic model coverage for modules, imports, types, values, expressions, - predicates, input constraints, intents, blocks, nodes, local references, and + predicates, input constraints, actions, blocks, nodes, local references, and pure Core helper bodies. - `docs/topics/core-ir/` current-truth chapter and verification matrix. - Accepted/rejected Core schema-shape fixtures under `fixtures/core/schema/`. diff --git a/docs/releases/v0.8.0-alpha.1.md b/docs/releases/v0.8.0-alpha.1.md index a8fd272..45e59be 100644 --- a/docs/releases/v0.8.0-alpha.1.md +++ b/docs/releases/v0.8.0-alpha.1.md @@ -8,7 +8,7 @@ post-publication evidence recorded on `main`. ## Release Type `v0.8.0-alpha.1` is the minimal effectful compiler-spine alpha. It proves the -first narrow source-to-Core path for an effectful intent while keeping target IR, +first narrow source-to-Core path for an effectful action while keeping target IR, admission execution, broad language lowering, and crates.io publication out of scope. diff --git a/docs/releases/v0.9.0-alpha.1.md b/docs/releases/v0.9.0-alpha.1.md index d762a30..9cc75a3 100644 --- a/docs/releases/v0.9.0-alpha.1.md +++ b/docs/releases/v0.9.0-alpha.1.md @@ -48,7 +48,7 @@ crates.io publication. - Public `edict_syntax::target_ir` API surface for lowering typed Core into selected Target IR review artifacts. - `lower_to_target_ir`, `TargetIrLoweringFacts`, `TargetIrArtifact`, - `TargetIrIntent`, `TargetIrStep`, `TargetLoweringReport`, stable + `TargetIrAction`, `TargetIrStep`, `TargetLoweringReport`, stable `TargetLoweringStatus`, and stable `TargetLoweringFailureKind` values. - Echo Target IR support for `echo.dpo@1` lowering to `echo.span-ir/v1`. - Git-warp Target IR support for `gitwarp.ref_crdt@1` lowering to @@ -57,13 +57,13 @@ crates.io publication. native lowerability support for the explicitly supported target profiles. - Deterministic in-memory review artifacts that preserve Core effect coordinates, effect result bindings, structured inputs, obstruction failure - keys, obstruction arm values, intent input constraints, evaluation budgets, + keys, obstruction arm values, action input constraints, evaluation budgets, and result expressions. - Stable rejections before artifact emission for unsupported target profiles, unsupported Target IR domains, unsupported Core ABI versions, unsupported Core capability flags, floating Core imports, unsupported Core nodes, missing operation-profile support, missing or ambiguous effect lowerings, unsupported - target intrinsics, missing obstruction facts, empty Core modules, and intents + target intrinsics, missing obstruction facts, empty Core modules, and actions with no target-owned steps. - Target IR topic-shelf coverage for the Echo and git-warp slices. - Post-`v0.8.0-alpha.1` release-process evidence updates that mark the diff --git a/docs/topics/compiler-spine/README.md b/docs/topics/compiler-spine/README.md index 35dd6ec..0abb4e6 100644 --- a/docs/topics/compiler-spine/README.md +++ b/docs/topics/compiler-spine/README.md @@ -38,14 +38,14 @@ enter the same `compiler_context_from_authority_facts` path. [CSPINE-REQ-010] ## Current Contract - The lowerable subset is deliberately narrow: local record type declarations, - one-parameter intents, `profile`, `basis none`, `budget <=`, `where` + one-parameter actions, `profile`, `basis none`, `budget <=`, `where` predicates, pure `let` bindings, one annotated effectful `let ... else` shape, lowerable `require ... else` obstruction arms, `return`, strings, booleans, integers, field access, record literals, equality predicates, and string concatenation. [CSPINE-REQ-006] [CSPINE-REQ-011] [CSPINE-REQ-017] - Core lowering produces structured in-memory `CoreModule` values with module - coordinate, imports, types, intents, input constraints, budgets, locals, + coordinate, imports, types, actions, input constraints, budgets, locals, ordered nodes, and result expressions. [CSPINE-REQ-003] - Resolver/type-checker failures use stable `CompilerErrorKind` and `CompilerStage` values. Tests assert those structured values rather than diff --git a/docs/topics/compiler-spine/test-plan.md b/docs/topics/compiler-spine/test-plan.md index 4d18c9b..bd870e7 100644 --- a/docs/topics/compiler-spine/test-plan.md +++ b/docs/topics/compiler-spine/test-plan.md @@ -33,7 +33,7 @@ Out of scope: | CSPINE-REQ-003 | implemented | `lower_core` lowers the typed initial subset into structured in-memory Core IR. | issue #20, docs/abi/edict-core.cddl | | CSPINE-REQ-004 | implemented | `compile_to_core` executes `validate_surface -> resolve_module -> type_check -> lower_core` in order. | issue #20 | | CSPINE-REQ-005 | implemented | Profile and budget source coordinates require explicit deterministic context facts; missing facts reject instead of producing placeholder Core. | issue #20 | -| CSPINE-REQ-006 | implemented | The first lowerable subset covers `bounded-hello` style pure local-record intents and rejects out-of-subset constructs structurally. | fixtures/lang/bounds/bounded-hello.edict, issue #20 | +| CSPINE-REQ-006 | implemented | The first lowerable subset covers `bounded-hello` style pure local-record actions and rejects out-of-subset constructs structurally. | fixtures/lang/bounds/bounded-hello.edict, issue #20 | | CSPINE-REQ-007 | implemented | Compiler-spine errors expose stable stage and kind identities. | crates/edict-syntax/src/compiler.rs | | CSPINE-REQ-008 | implemented | The compiler-spine lowerer embeds no canonical bytes, exact digest, target lowering, or admission artifacts in Core modules. | ROADMAP.md | | CSPINE-REQ-009 | implemented | The compiler spine rejects source effect bodies whose effect write class is not allowed by the resolved operation profile. | issue #54 | @@ -66,7 +66,7 @@ Out of scope: | CSPINE-TP-007 | implemented | Boundary guard | CSPINE-REQ-007, CSPINE-REQ-009 | A write-class effect body under a read-only operation profile rejects in `CompilerStage::TypeCheck` with `ProfileEffectMismatch`. | read_only_profile_rejects_write_effect_body | - | Uses caller-built in-memory context facts. | | CSPINE-TP-008 | implemented | Boundary guard | CSPINE-REQ-007, CSPINE-REQ-009 | A write-class effect in a `let` initializer without an obstruction handler rejects in `CompilerStage::TypeCheck` with `ProfileEffectMismatch`. | read_only_profile_rejects_write_effect_let_without_else | - | The compatibility check is independent of source obstruction syntax. | | CSPINE-TP-009 | implemented | Golden path | CSPINE-REQ-005, CSPINE-REQ-009, CSPINE-REQ-010 | File-backed authority facts produce a compiler context that compiles `bounded-hello` and rejects a read-only profile/write-effect mismatch. | file_backed_authority_facts_compile_bounded_hello, file_backed_authority_facts_reject_write_effect_profile_mismatch | crates/edict-syntax/tests/authority_facts.rs | Proves file-loaded facts enter the same compiler path as in-memory facts. | -| CSPINE-TP-010 | implemented | Golden path | CSPINE-REQ-003, CSPINE-REQ-004, CSPINE-REQ-010, CSPINE-REQ-011 | A minimal annotated effectful `let ... else` source shape compiles through typed Core with a semantic effect node and deterministic obstruction mapping. | effectful_write_intent_lowers_to_typed_core_from_file_backed_facts | - | Uses explicit authority-facts files for profile, budget, and effect write-class facts. | +| CSPINE-TP-010 | implemented | Golden path | CSPINE-REQ-003, CSPINE-REQ-004, CSPINE-REQ-010, CSPINE-REQ-011 | A minimal annotated effectful `let ... else` source shape compiles through typed Core with a semantic effect node and deterministic obstruction mapping. | effectful_write_action_lowers_to_typed_core_from_file_backed_facts | - | Uses explicit authority-facts files for profile, budget, and effect write-class facts. | | CSPINE-TP-011 | implemented | Boundary guard | CSPINE-REQ-007, CSPINE-REQ-012 | An unsupported effectful branch-yield source shape rejects in `CompilerStage::TypeCheck` with `UnsupportedSourceShape` before Core lowering. | unsupported_effectful_branch_yield_rejects_before_core_lowering | - | Keeps v0.8 to one lowerable effectful shape. | | CSPINE-TP-012 | implemented | Error handling | CSPINE-REQ-007, CSPINE-REQ-013 | Duplicate failure keys in a supported obstruction map reject in `CompilerStage::TypeCheck` with `DuplicateObstructionFailure`. | duplicate_obstruction_failures_reject_before_core_lowering | - | Prevents silent effect-node omission when map keys collide. | | CSPINE-TP-013 | implemented | Boundary guard | CSPINE-REQ-007, CSPINE-REQ-012, CSPINE-REQ-014 | A chained effect-call RHS rejects in `CompilerStage::TypeCheck` with `UnsupportedSourceShape`. | chained_effect_calls_reject_before_core_lowering | - | Prevents the lowerer from discarding the inner call shape. | diff --git a/docs/topics/core-ir/README.md b/docs/topics/core-ir/README.md index aa1cddb..6d4cff4 100644 --- a/docs/topics/core-ir/README.md +++ b/docs/topics/core-ir/README.md @@ -34,7 +34,7 @@ outside the schema under `fixtures/core/canonical/`. [COREIR-REQ-007] ## Current Contract -- Core modules carry imports, type definitions, intents, and required Core +- Core modules carry imports, type definitions, actions, and required Core 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] @@ -58,13 +58,13 @@ outside the schema under `fixtures/core/canonical/`. [COREIR-REQ-007] - Input constraints are explicit `input-constraint` records containing a source coordinate, origin class, and predicate tree. They are not validator coordinate strings. [COREIR-REQ-006] [EDICT-CORE-WHERE-HASH-001] -- Intents state the required operation profile as `requiredOperationProfile`. +- Actions state the required operation profile as `requiredOperationProfile`. Verifier reports and target/admission decisions are external to Core. [COREIR-REQ-009] [EDICT-CORE-VERIFIED-EXTERNAL-001] - Edict-authored lawpack pure helper bodies use `core-fn-body`, a pure function body shape. They do not reuse the effect-capable `core-block` node algebra. [COREIR-REQ-011] -- Schema-shape fixtures prove minimal accepted Core module/intent shapes and +- Schema-shape fixtures prove minimal accepted Core module/action shapes and rejected missing or external-evidence fields against the CDDL declarations. [COREIR-REQ-010] - Core modules can be encoded as deterministic `edict.canonical-cbor/v1` bytes diff --git a/docs/topics/core-ir/canonical-encoding.md b/docs/topics/core-ir/canonical-encoding.md index 4489efb..431f815 100644 --- a/docs/topics/core-ir/canonical-encoding.md +++ b/docs/topics/core-ir/canonical-encoding.md @@ -47,7 +47,7 @@ For Core, `core_module_value` projects a `CoreModule` into a map containing: - `coordinate`; - `imports`; - `types`; -- `intents`; +- `actions`; - `requiredCoreCapabilities`. Nested Core values follow the same rule: each semantic variant becomes an diff --git a/docs/topics/core-ir/test-plan.md b/docs/topics/core-ir/test-plan.md index 68298d0..8699360 100644 --- a/docs/topics/core-ir/test-plan.md +++ b/docs/topics/core-ir/test-plan.md @@ -33,7 +33,7 @@ Out of scope: | ID | Status | Requirement | Source | | --- | --- | --- | --- | -| COREIR-REQ-001 | implemented | Core modules are semantic artifacts with `apiVersion: "edict.core/v1"`, imports, types, intents, and required Core capabilities. | docs/abi/edict-core.cddl, issue #3 | +| COREIR-REQ-001 | implemented | Core modules are semantic artifacts with `apiVersion: "edict.core/v1"`, imports, types, actions, and required Core capabilities. | docs/abi/edict-core.cddl, issue #3 | | COREIR-REQ-002 | implemented | Core type schema covers bounded scalars, records, variants, options, lists, maps, and capability references. | docs/abi/edict-core.cddl, issue #3 | | COREIR-REQ-003 | implemented | Core expressions and predicates are separate schema families. | docs/abi/edict-core.cddl, issue #3 | | COREIR-REQ-004 | implemented | Core blocks/nodes represent ordered semantic execution with locals, effects, guards, branches, bounded loops, match blocks, proof obligations, and a result expression. | docs/abi/edict-core.cddl, issue #3 | @@ -58,11 +58,11 @@ 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/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-action-minimal.fields | Accepted Core action field-shape fixture. | Required `core-action` 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. | -| fixtures/core/schema/rejected/core-module-missing-intents.fields | Rejected Core module field-shape fixture. | Missing required `intents` rejects. | +| fixtures/core/schema/rejected/core-module-missing-actions.fields | Rejected Core module field-shape fixture. | Missing required `actions` rejects. | | fixtures/core/schema/rejected/local-ref-missing-alpha-name.fields | Rejected local reference field-shape fixture. | Missing required `alphaName` rejects. | -| fixtures/core/schema/rejected/core-intent-unknown-verified-mode.fields | Rejected Core intent field-shape fixture. | External verifier evidence field rejects as non-Core. | +| fixtures/core/schema/rejected/core-action-unknown-verified-mode.fields | Rejected Core action field-shape fixture. | External verifier evidence field rejects as non-Core. | | 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. | @@ -71,10 +71,10 @@ Out of scope: | ID | Status | Category | Requirement | Oracle | Evidence | Fixtures | Notes | | --- | --- | --- | --- | --- | --- | --- | --- | -| COREIR-TP-001 | implemented | Golden path | COREIR-REQ-001, COREIR-REQ-002, COREIR-REQ-003, COREIR-REQ-004, COREIR-REQ-005, COREIR-REQ-006, COREIR-REQ-008, COREIR-REQ-009, COREIR-REQ-011 | `edict-core.cddl` contains the required module, type, intent, block, node, expression, predicate, function-body, input-constraint, local-ref, alpha-name, and operation-profile declarations. | core_cddl_declares_v1_semantic_model | docs/abi/edict-core.cddl | Static schema contract regression. | +| COREIR-TP-001 | implemented | Golden path | COREIR-REQ-001, COREIR-REQ-002, COREIR-REQ-003, COREIR-REQ-004, COREIR-REQ-005, COREIR-REQ-006, COREIR-REQ-008, COREIR-REQ-009, COREIR-REQ-011 | `edict-core.cddl` contains the required module, type, action, block, node, expression, predicate, function-body, input-constraint, local-ref, alpha-name, and operation-profile declarations. | core_cddl_declares_v1_semantic_model | docs/abi/edict-core.cddl | Static schema contract regression. | | COREIR-TP-002 | implemented | Boundary guard | COREIR-REQ-007 | `edict-core.cddl` contains no fields that freeze self-hash, byte, digest, target, or admission artifacts. | core_cddl_has_no_digest_freeze_fields | docs/abi/edict-core.cddl | Golden byte and digest fixtures live outside the Core schema. | | COREIR-TP-003 | implemented | Contract graph | COREIR-REQ-001, COREIR-REQ-002, COREIR-REQ-003, COREIR-REQ-004, COREIR-REQ-005, COREIR-REQ-006, COREIR-REQ-007, COREIR-REQ-008, COREIR-REQ-009, COREIR-REQ-010, COREIR-REQ-011 | The topic shelf resolves requirement IDs, case IDs, sources, evidence test names, fixtures, and local links. | contract_graph_is_valid | docs/abi/edict-core.cddl | Executed by `cargo xtask contract-check` and `cargo xtask verify`. | -| COREIR-TP-004 | implemented | Schema validation | COREIR-REQ-008, COREIR-REQ-010, COREIR-REQ-011 | Accepted and rejected Core field-shape fixtures validate against the required and allowed fields extracted from `edict-core.cddl`. | core_schema_shape_fixtures_match_cddl | fixtures/core/schema/accepted/core-module-minimal.fields, fixtures/core/schema/accepted/core-intent-minimal.fields, fixtures/core/schema/accepted/core-fn-body-minimal.fields, fixtures/core/schema/rejected/core-module-missing-intents.fields, fixtures/core/schema/rejected/local-ref-missing-alpha-name.fields, fixtures/core/schema/rejected/core-intent-unknown-verified-mode.fields, fixtures/core/schema/rejected/core-fn-body-effect-node-field.fields | Lightweight schema-shape validation, not full CDDL instance validation. | +| COREIR-TP-004 | implemented | Schema validation | COREIR-REQ-008, COREIR-REQ-010, COREIR-REQ-011 | Accepted and rejected Core field-shape fixtures validate against the required and allowed fields extracted from `edict-core.cddl`. | core_schema_shape_fixtures_match_cddl | fixtures/core/schema/accepted/core-module-minimal.fields, fixtures/core/schema/accepted/core-action-minimal.fields, fixtures/core/schema/accepted/core-fn-body-minimal.fields, fixtures/core/schema/rejected/core-module-missing-actions.fields, fixtures/core/schema/rejected/local-ref-missing-alpha-name.fields, fixtures/core/schema/rejected/core-action-unknown-verified-mode.fields, fixtures/core/schema/rejected/core-fn-body-effect-node-field.fields | Lightweight schema-shape validation, not full CDDL instance validation. | | COREIR-TP-005 | planned | Schema validation | COREIR-REQ-008 | Accepted and rejected Core instance fixtures validate against the CDDL through a complete CDDL validator. | - | - | Deferred until the validator harness exists. | | COREIR-TP-006 | implemented | Golden artifact | COREIR-REQ-014 | Reviewed golden bytes and exact digest fixtures are produced from the executable encoder. | reviewed_core_golden_bytes_match_executable_encoder, reviewed_core_digest_matches_exact_fixture | fixtures/core/canonical/bounded-hello.core.cbor, fixtures/core/canonical/bounded-hello.core.sha256 | Initial reviewed Core artifact check. | | COREIR-TP-007 | implemented | Canonical encoding | COREIR-REQ-012 | Equivalent Core modules with maps constructed in different orders encode to the same bytes. | canonical_core_bytes_are_independent_of_map_construction_order | fixtures/lang/bounds/bounded-hello.edict | Tests map-order independence without freezing reviewed golden bytes. | diff --git a/docs/topics/developer-tooling/test-plan.md b/docs/topics/developer-tooling/test-plan.md index a587f68..454a94c 100644 --- a/docs/topics/developer-tooling/test-plan.md +++ b/docs/topics/developer-tooling/test-plan.md @@ -49,11 +49,11 @@ 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-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-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, action 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. | | DEVTOOLS-TP-005 | implemented | Corpus alignment | DEVTOOLS-REQ-003 | Tree-sitter accepts formatted type-call suffixes that the reference parser accepts. | tree_sitter_corpus_examples_match_reference_parser | grammars/tree-sitter-edict/test/corpus/current-subset.txt | A typed call with whitespace before `<...>` parses as a call expression with type arguments and remains accepted by `parse_module`; `tree-sitter test` verifies the checked-in syntax tree. | -| 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-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, action 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. | diff --git a/docs/topics/obstruction-strands/README.md b/docs/topics/obstruction-strands/README.md index a293064..6e98c97 100644 --- a/docs/topics/obstruction-strands/README.md +++ b/docs/topics/obstruction-strands/README.md @@ -64,7 +64,7 @@ CoreNode::Require { Current lowerable Echo Target IR shape: ```text -TargetIrIntent { +TargetIrAction { requirements: [ TargetIrRequirement { predicate, diff --git a/docs/topics/semantic-validation/README.md b/docs/topics/semantic-validation/README.md index 51cd976..6bc0f5c 100644 --- a/docs/topics/semantic-validation/README.md +++ b/docs/topics/semantic-validation/README.md @@ -43,21 +43,21 @@ error ordering is not part of the Phase 2 contract. Tests assert structured - Runtime `String` and `Bytes` type references must carry explicit bounds. The pass checks nested type references recursively, including `Option`, `List`, - `Map`, `CapabilityRef`, variant payloads, intent parameters, intent returns, + `Map`, `CapabilityRef`, variant payloads, action parameters, action returns, typed `let` declarations, and expression type arguments. [SEMVAL-REQ-002] -- Every intent must declare at least one operation mode: `profile` or - `implements`. An intent may declare both. [SEMVAL-REQ-003] -- Every intent must declare a `budget` clause. [SEMVAL-REQ-004] -- Every intent must declare a `basis` clause. This pass cannot yet resolve +- Every action must declare at least one operation mode: `profile` or + `implements`. An action may declare both. [SEMVAL-REQ-003] +- Every action must declare a `budget` clause. [SEMVAL-REQ-004] +- Every action must declare a `basis` clause. This pass cannot yet resolve profile- or lawpack-supplied basis templates, so the current source-level contract requires an explicit source clause. [SEMVAL-REQ-005] -- Singleton intent clauses reject duplicates for `profile`, `implements`, +- Singleton action clauses reject duplicates for `profile`, `implements`, `basis`, `footprint`, and `budget`. [SEMVAL-REQ-006] - Module-scope import aliases, `type` declarations, `enum` declarations, and - `intent` declarations share a source-AST namespace and reject duplicate names. + `action` declarations share a source-AST namespace and reject duplicate names. [SEMVAL-REQ-007] - Source binders reject shadowing of visible module/prelude names, parameters, - and earlier local binders. The check covers intent parameters, `let` binders, + and earlier local binders. The check covers action parameters, `let` binders, bounded-`for` binders, match-arm binders, obstruction-map binders, ordinary blocks, and branch-yield blocks. [SEMVAL-REQ-007] - Branch, loop, match-arm, obstruction-map, and branch-yield scopes are @@ -69,7 +69,7 @@ error ordering is not part of the Phase 2 contract. Tests assert structured compiler-spine stages. [SEMVAL-REQ-009] [SEMVAL-REQ-010] Semantic errors carry source spans. Clause-level duplicate diagnostics currently -report the enclosing intent span because the parser's `IntentClause` AST does +report the enclosing action span because the parser's `ActionClause` AST does not yet retain per-clause spans. [SEMVAL-REQ-001] ## Deferred diff --git a/docs/topics/semantic-validation/test-plan.md b/docs/topics/semantic-validation/test-plan.md index b43969d..3d192ca 100644 --- a/docs/topics/semantic-validation/test-plan.md +++ b/docs/topics/semantic-validation/test-plan.md @@ -26,10 +26,10 @@ Out of scope for this first slice: | --- | --- | --- | --- | | SEMVAL-REQ-001 | implemented | Validation returns structured semantic errors with stable kinds and source span payloads. | crates/edict-syntax/src/semantic.rs | | SEMVAL-REQ-002 | implemented | Runtime `String` and `Bytes` type references must be explicitly bounded. | EDICT-LANG-BOUNDS-001 | -| SEMVAL-REQ-003 | implemented | Each intent must declare at least one operation mode: `profile` or `implements`. | EDICT-LANG-INTENT-CLAUSES-001 | -| SEMVAL-REQ-004 | implemented | Each intent must declare a `budget` clause. | EDICT-LANG-INTENT-CLAUSES-001 | -| SEMVAL-REQ-005 | implemented | Each intent must declare a `basis` clause until template resolution exists. | EDICT-LANG-INTENT-CLAUSES-001 | -| SEMVAL-REQ-006 | implemented | Singleton intent clauses reject duplicates. | docs/SPEC_edict-language-v1.md | +| SEMVAL-REQ-003 | implemented | Each action must declare at least one operation mode: `profile` or `implements`. | EDICT-LANG-ACTION-CLAUSES-001 | +| SEMVAL-REQ-004 | implemented | Each action must declare a `budget` clause. | EDICT-LANG-ACTION-CLAUSES-001 | +| SEMVAL-REQ-005 | implemented | Each action must declare a `basis` clause until template resolution exists. | EDICT-LANG-ACTION-CLAUSES-001 | +| SEMVAL-REQ-006 | implemented | Singleton action clauses reject duplicates. | docs/SPEC_edict-language-v1.md | | SEMVAL-REQ-007 | implemented | Source binders must not shadow visible module/prelude names, parameters, or earlier locals; module-scope import/declaration names cannot collide. | EDICT-LANG-NOSHADOW-001 | | SEMVAL-REQ-008 | implemented | The source/surface validator is an explicit `validate_surface` compiler stage; `validate_module` remains a compatibility alias. | issue #10 | | SEMVAL-REQ-009 | implemented | Surface validation does not require import resolution, named type resolution, callee resolution, or target/lawpack facts. | issue #10 | @@ -48,12 +48,12 @@ Out of scope for this first slice: | ID | Status | Category | Requirement | Oracle | Evidence | Fixtures | Notes | | --- | --- | --- | --- | --- | --- | --- | --- | | SEMVAL-TP-001 | implemented | Golden path | SEMVAL-REQ-001 | Valid Phase 1 fixtures return `Ok(())`. | phase1_fixtures_validate_semantically | fixtures/lang/bounds/bounded-hello.edict, fixtures/lang/effects/read-greeting.edict, fixtures/lang/effects/conditional-blob.edict | Source-AST validator only. | -| SEMVAL-TP-002 | implemented | Error handling | SEMVAL-REQ-002 | Nested unbounded scalars produce `UnboundedScalar` for each occurrence. | unbounded_runtime_scalars_are_rejected_recursively, unbounded_runtime_scalars_are_rejected_in_declaration_type_surfaces, unbounded_runtime_scalars_are_rejected_in_intent_and_expression_surfaces | - | Covers `Option`, `List`, `Map`, `CapabilityRef`, variant payloads, intent params/returns, typed `let`, and expression type args. | -| SEMVAL-TP-003 | implemented | Error handling | SEMVAL-REQ-003, SEMVAL-REQ-004, SEMVAL-REQ-005 | Missing required intent clauses produce `MissingOperationMode`, `MissingBudget`, and `MissingBasis`. | intent_required_clauses_are_validated | - | Does not require import resolution. | +| SEMVAL-TP-002 | implemented | Error handling | SEMVAL-REQ-002 | Nested unbounded scalars produce `UnboundedScalar` for each occurrence. | unbounded_runtime_scalars_are_rejected_recursively, unbounded_runtime_scalars_are_rejected_in_declaration_type_surfaces, unbounded_runtime_scalars_are_rejected_in_action_and_expression_surfaces | - | Covers `Option`, `List`, `Map`, `CapabilityRef`, variant payloads, action params/returns, typed `let`, and expression type args. | +| SEMVAL-TP-003 | implemented | Error handling | SEMVAL-REQ-003, SEMVAL-REQ-004, SEMVAL-REQ-005 | Missing required action clauses produce `MissingOperationMode`, `MissingBudget`, and `MissingBasis`. | action_required_clauses_are_validated | - | Does not require import resolution. | | SEMVAL-TP-004 | implemented | Golden path | SEMVAL-REQ-003 | Either `profile` or `implements` satisfies operation mode. | profile_or_implements_satisfies_operation_mode | - | Both remain legal. | -| SEMVAL-TP-005 | implemented | Error handling | SEMVAL-REQ-006 | Duplicate singleton clauses produce `DuplicateIntentClause`. | duplicate_singleton_intent_clauses_are_rejected, duplicate_implements_and_footprint_clauses_are_rejected | - | Covers `profile`, `implements`, `basis`, `footprint`, and `budget` duplicates. | +| SEMVAL-TP-005 | implemented | Error handling | SEMVAL-REQ-006 | Duplicate singleton clauses produce `DuplicateActionClause`. | duplicate_singleton_action_clauses_are_rejected, duplicate_implements_and_footprint_clauses_are_rejected | - | Covers `profile`, `implements`, `basis`, `footprint`, and `budget` duplicates. | | SEMVAL-TP-006 | implemented | Error handling | SEMVAL-REQ-007 | Module-scope declaration and import aliases cannot collide in the same namespace. | module_namespace_collisions_are_rejected | - | Source-AST environment only. | -| SEMVAL-TP-007 | implemented | Error handling | SEMVAL-REQ-007 | Intent parameters and local binders cannot shadow module names, parameters, or earlier locals. | local_binders_cannot_shadow_visible_names | - | Source-AST environment only. | +| SEMVAL-TP-007 | implemented | Error handling | SEMVAL-REQ-007 | Action parameters and local binders cannot shadow module names, parameters, or earlier locals. | local_binders_cannot_shadow_visible_names | - | Source-AST environment only. | | SEMVAL-TP-008 | implemented | Edge/scope | SEMVAL-REQ-007 | Branch, loop, match, obstruction-map, and branch-yield binders are scoped deterministically and do not leak into sibling or outer scopes. | branch_and_loop_binders_are_scoped, branch_yield_binders_are_scoped, clause_expression_binders_see_parameters, expression_binders_cannot_shadow_visible_names, obstruction_map_binders_cannot_shadow_visible_names | - | Source-AST environment only. | | SEMVAL-TP-009 | implemented | Stage boundary | SEMVAL-REQ-008 | `validate_surface` accepts known-good fixtures and `validate_module` remains equivalent for compatibility. | validate_module_remains_surface_stage_compatibility_alias | fixtures/lang/bounds/bounded-hello.edict | Keeps the stage name explicit without breaking existing callers. | | SEMVAL-TP-010 | implemented | Boundary guard | SEMVAL-REQ-009 | Unresolved imports, named types, callees, and field paths do not fail surface validation when no source/surface rule is violated. | surface_validation_defers_import_and_name_resolution | - | Resolution belongs to the resolver stage. | @@ -79,4 +79,4 @@ Out of scope for this first slice: - Core/assurance relapse-zoo fixtures belong to downstream Core/admission topic shelves after full Core lowering and canonical artifacts exist. - Clause-level diagnostic spans; duplicate singleton diagnostics currently report - at the enclosing intent span because intent clauses do not retain spans. + at the enclosing action span because action clauses do not retain spans. diff --git a/docs/topics/syntax/README.md b/docs/topics/syntax/README.md index 82363dc..c6bb445 100644 --- a/docs/topics/syntax/README.md +++ b/docs/topics/syntax/README.md @@ -44,7 +44,7 @@ programs, prove bounds, or itself lower to Core IR. [SYNTAX-REQ-001] and payload-carrying `variant` types. Empty enums reject. [SYNTAX-REQ-004] - Integer literal suffixes are preserved where syntax carries them, including static bounds. [SYNTAX-REQ-005] -- Intent declarations parse parameters, return type, clause surface, statement +- Action 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 diff --git a/docs/topics/syntax/test-plan.md b/docs/topics/syntax/test-plan.md index a654311..af0444a 100644 --- a/docs/topics/syntax/test-plan.md +++ b/docs/topics/syntax/test-plan.md @@ -32,7 +32,7 @@ Out of scope: | 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-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-006 | implemented | Action 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-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 | @@ -60,7 +60,7 @@ Out of scope: | 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. | -| SYNTAX-TP-007 | implemented | Golden path | SYNTAX-REQ-006 | Intent clauses and bodies parse into expected AST. | bounded_hello_parses, read_greeting_parses, require_statement_parses_terminal_obstruction_source_shape | fixtures/lang/bounds/bounded-hello.edict, fixtures/lang/effects/read-greeting.edict | Requiredness and `require` lowering semantics are deferred. | +| SYNTAX-TP-007 | implemented | Golden path | SYNTAX-REQ-006 | Action clauses and bodies parse into expected AST. | bounded_hello_parses, read_greeting_parses, require_statement_parses_terminal_obstruction_source_shape | fixtures/lang/bounds/bounded-hello.edict, fixtures/lang/effects/read-greeting.edict | Requiredness and `require` lowering semantics are deferred. | | SYNTAX-TP-008 | implemented | Golden path | SYNTAX-REQ-007 | Bounded loops parse literal and coordinate bounds. | for_with_integer_bound_parses, for_with_coordinate_bound_parses | - | Loop semantic proof deferred. | | SYNTAX-TP-009 | implemented | Known failure | SYNTAX-REQ-007 | Missing `bounded` rejects. | for_without_bounded_is_rejected | - | Syntactic mandatory bound. | | SYNTAX-TP-010 | implemented | Error handling | SYNTAX-REQ-007 | Effect positions reject non-call expressions. | effect_positions_must_be_calls | - | Stable `NonCallEffect` kind. | diff --git a/docs/topics/target-ir/test-plan.md b/docs/topics/target-ir/test-plan.md index ca08458..77773a1 100644 --- a/docs/topics/target-ir/test-plan.md +++ b/docs/topics/target-ir/test-plan.md @@ -68,13 +68,13 @@ Out of scope: | ID | Status | Category | Requirement | Oracle | Evidence | Fixtures | Notes | | --- | --- | --- | --- | --- | --- | --- | --- | -| TIR-TP-001 | implemented | Golden path | TIR-REQ-001, TIR-REQ-002 | The supported effectful Core shape lowers to an `echo.span-ir/v1` artifact whose selected profile is `echo.dpo@1` and whose steps preserve effect coordinates, effect result bindings, inputs, obstruction arms, intent input constraints, evaluation budgets, intent results, and deterministic order. | supported_effectful_core_lowers_to_echo_span_ir, obstruction_arm_values_are_preserved_in_echo_span_ir, intent_constraints_and_budget_are_preserved_in_echo_span_ir, intent_result_is_preserved_in_echo_span_ir, effect_result_bindings_are_preserved_in_echo_span_ir | crates/edict-syntax/tests/target_ir.rs | Echo remains the first supported target slice. | +| TIR-TP-001 | implemented | Golden path | TIR-REQ-001, TIR-REQ-002 | The supported effectful Core shape lowers to an `echo.span-ir/v1` artifact whose selected profile is `echo.dpo@1` and whose steps preserve effect coordinates, effect result bindings, inputs, obstruction arms, intent input constraints, evaluation budgets, intent results, and deterministic order. | supported_effectful_core_lowers_to_echo_span_ir, obstruction_arm_values_are_preserved_in_echo_span_ir, action_constraints_and_budget_are_preserved_in_echo_span_ir, action_result_is_preserved_in_echo_span_ir, effect_result_bindings_are_preserved_in_echo_span_ir | crates/edict-syntax/tests/target_ir.rs | Echo remains the first supported target slice. | | TIR-TP-002 | implemented | Golden path | TIR-REQ-001, TIR-REQ-005 | Native lowerability support for `echo.dpo@1` feeds Target IR lowering facts and produces the same Echo intrinsic selection in the emitted artifact; rejected lowerability reports cannot build Target IR lowering facts; derived facts keep the lowerability report's target-profile coordinate, operation profile, and obstruction coordinates; prevalidated target-profile references must match the lowerability report; repeated identical native selections are coalesced. | lowerability_native_support_feeds_echo_target_lowering, lowerability_bridge_carries_only_selected_native_effect, lowerability_bridge_deduplicates_identical_native_effect_selection, unsupported_lowerability_report_does_not_build_target_ir_facts, lowerability_bridge_uses_report_target_profile_identity, lowerability_bridge_uses_report_operation_profile_identity, lowerability_bridge_requires_matching_target_profile_reference | crates/edict-syntax/tests/target_ir.rs | Bridges lowerability evidence to Target IR generation without adapter search or carrying unselected native supports. | | TIR-TP-003 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-003 | Selecting an unsupported target profile returns `TargetLoweringFailureKind::UnsupportedTargetProfile` with no artifact; selecting an unsupported Target IR domain returns `TargetLoweringFailureKind::UnsupportedTargetIrDomain` with no artifact. | unsupported_target_profile_rejects_without_artifact, unsupported_target_ir_domain_rejects_without_artifact | crates/edict-syntax/tests/target_ir.rs | Keeps supported targets explicit. | | TIR-TP-004 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-004 | A Core module with an unsupported node returns `TargetLoweringFailureKind::UnsupportedCoreNode` with no artifact. | unsupported_core_nodes_reject_without_artifact | crates/edict-syntax/tests/target_ir.rs | No silent fallback or partial artifact. | | TIR-TP-005 | policy | Release boundary | TIR-REQ-006 | Roadmap and release scope keep runtime execution, admission, and general plugin dispatch outside the first target IR slices. | - | - | Non-goal boundary; not a substitute for behavior tests. | | TIR-TP-006 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-004 | A Core intent whose required operation profile is absent from the selected target-lowering facts returns `TargetLoweringFailureKind::MissingOperationProfile` with no artifact; missing or ambiguous selected effect lowerings return `MissingEffectLowering` or `AmbiguousEffectLowering` with no artifact. | unsupported_operation_profile_rejects_without_artifact, missing_effect_lowering_rejects_without_artifact, ambiguous_effect_lowering_rejects_without_artifact | crates/edict-syntax/tests/target_ir.rs | Prevents effect-only support from bypassing lowerability profile selection and keeps effect matching deterministic. | -| TIR-TP-007 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-004 | A Core intent with no target-owned steps, or a Core module with no intents, returns `TargetLoweringFailureKind::NoTargetSteps` with no artifact. | empty_target_step_intents_reject_without_artifact, empty_core_modules_reject_without_artifact | crates/edict-syntax/tests/target_ir.rs | Prevents empty Echo artifacts from standing in for unsupported work. | +| TIR-TP-007 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-004 | A Core intent with no target-owned steps, or a Core module with no intents, returns `TargetLoweringFailureKind::NoTargetSteps` with no artifact. | empty_target_step_actions_reject_without_artifact, empty_core_modules_reject_without_artifact | crates/edict-syntax/tests/target_ir.rs | Prevents empty Echo artifacts from standing in for unsupported work. | | TIR-TP-008 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-003 | A matched effect lowering whose target intrinsic belongs to a non-Echo profile returns `TargetLoweringFailureKind::UnsupportedTargetIntrinsic` with no artifact. | foreign_target_intrinsic_rejects_without_artifact | crates/edict-syntax/tests/target_ir.rs | Keeps nominal Echo artifacts from carrying another target's intrinsic. | | TIR-TP-009 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-004 | A Core module whose ABI is not `edict.core/v1` returns `TargetLoweringFailureKind::UnsupportedCoreAbi` with no artifact. | unsupported_core_abi_rejects_without_artifact | crates/edict-syntax/tests/target_ir.rs | Prevents Target IR from silently reinterpreting future or stale Core shapes. | | TIR-TP-010 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-004 | A Core module with unsupported required Core capability flags returns `TargetLoweringFailureKind::UnsupportedCoreCapability` with no artifact. | unsupported_core_capability_rejects_without_artifact | crates/edict-syntax/tests/target_ir.rs | Prevents Target IR from omitting hash-significant Core obligations. | diff --git a/editors/vscode/syntaxes/edict.tmLanguage.json b/editors/vscode/syntaxes/edict.tmLanguage.json index 41c0db4..b566f78 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|action|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" }, "types": { "name": "entity.name.type.edict", diff --git a/fixtures/bundle/assembly/bounded-hello.bundle-digests.txt b/fixtures/bundle/assembly/bounded-hello.bundle-digests.txt index 9427262..fdecf06 100644 --- a/fixtures/bundle/assembly/bounded-hello.bundle-digests.txt +++ b/fixtures/bundle/assembly/bounded-hello.bundle-digests.txt @@ -22,5 +22,5 @@ conformance_fixture_corpus = echo.dpo.fixtures/v1 sha256:99999999999999999999999 verifier_report = echo.dpo.verifier-report/v1 sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa compile_explanation = watson.compile-explanation/v1 sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -semantic_bundle_digest = sha256:dac7812052165b6623c874f3e2252c57c1a9680feff92cdbb734474b7d5b72e0 -release_bundle_digest = sha256:6701f5c1b50b0d639aac935b70a309463952dac16ad37a0f563a3cfc4c1d8ccd +semantic_bundle_digest = sha256:764e87a1856b2fbf7f8a4d767e02391ac42e2ba499ed63d6741fca981aea3ed4 +release_bundle_digest = sha256:5935ab2e9afab573d4020300dc66050a9a0acaf085180fc47f35ed5bb2decbb2 diff --git a/fixtures/cli/01-source-ok/request.jsonl b/fixtures/cli/01-source-ok/request.jsonl index 002fea5..2e4b640 100644 --- a/fixtures/cli/01-source-ok/request.jsonl +++ b/fixtures/cli/01-source-ok/request.jsonl @@ -1,2 +1,2 @@ {"schema":"edict.compiler.settings/v1","type":"compilerSettings","operation":"check"} -{"schema":"edict.compiler.input/v1","type":"compilerInput","kind":"source","name":"inline.edict","source":"package examples.hello@1;\n\nuse lawpack hello.optics@1 digest \"sha256:0000000000000000000000000000000000000000000000000000000000000000\" as hello;\n\ntype HelloInput = {\n name: String,\n};\n\nintent sayHello(input: HelloInput)\n returns HelloInput\n profile hello.readOnly\n basis none\n budget <= hello.tinyBudget\n{\n return { name: input.name };\n}\n"} +{"schema":"edict.compiler.input/v1","type":"compilerInput","kind":"source","name":"inline.edict","source":"package examples.hello@1;\n\nuse lawpack hello.optics@1 digest \"sha256:0000000000000000000000000000000000000000000000000000000000000000\" as hello;\n\ntype HelloInput = {\n name: String,\n};\n\naction sayHello(input: HelloInput)\n returns HelloInput\n profile hello.readOnly\n basis none\n budget <= hello.tinyBudget\n{\n return { name: input.name };\n}\n"} diff --git a/fixtures/cli/05-path-input-ok/inputs/hello.edict b/fixtures/cli/05-path-input-ok/inputs/hello.edict index 39c53bb..dc09617 100644 --- a/fixtures/cli/05-path-input-ok/inputs/hello.edict +++ b/fixtures/cli/05-path-input-ok/inputs/hello.edict @@ -6,7 +6,7 @@ type HelloInput = { name: String, }; -intent sayHello(input: HelloInput) +action sayHello(input: HelloInput) returns HelloInput profile hello.readOnly basis none diff --git a/fixtures/cli/06-directory-expansion-ok/inputs/a.edict b/fixtures/cli/06-directory-expansion-ok/inputs/a.edict index 39c53bb..dc09617 100644 --- a/fixtures/cli/06-directory-expansion-ok/inputs/a.edict +++ b/fixtures/cli/06-directory-expansion-ok/inputs/a.edict @@ -6,7 +6,7 @@ type HelloInput = { name: String, }; -intent sayHello(input: HelloInput) +action sayHello(input: HelloInput) returns HelloInput profile hello.readOnly basis none diff --git a/fixtures/cli/06-directory-expansion-ok/inputs/nested/b.edict b/fixtures/cli/06-directory-expansion-ok/inputs/nested/b.edict index 39c53bb..dc09617 100644 --- a/fixtures/cli/06-directory-expansion-ok/inputs/nested/b.edict +++ b/fixtures/cli/06-directory-expansion-ok/inputs/nested/b.edict @@ -6,7 +6,7 @@ type HelloInput = { name: String, }; -intent sayHello(input: HelloInput) +action sayHello(input: HelloInput) returns HelloInput profile hello.readOnly basis none diff --git a/fixtures/cli/07-path-list-ok/inputs/first.edict b/fixtures/cli/07-path-list-ok/inputs/first.edict index 39c53bb..dc09617 100644 --- a/fixtures/cli/07-path-list-ok/inputs/first.edict +++ b/fixtures/cli/07-path-list-ok/inputs/first.edict @@ -6,7 +6,7 @@ type HelloInput = { name: String, }; -intent sayHello(input: HelloInput) +action sayHello(input: HelloInput) returns HelloInput profile hello.readOnly basis none diff --git a/fixtures/cli/07-path-list-ok/inputs/second.edict b/fixtures/cli/07-path-list-ok/inputs/second.edict index 39c53bb..dc09617 100644 --- a/fixtures/cli/07-path-list-ok/inputs/second.edict +++ b/fixtures/cli/07-path-list-ok/inputs/second.edict @@ -6,7 +6,7 @@ type HelloInput = { name: String, }; -intent sayHello(input: HelloInput) +action sayHello(input: HelloInput) returns HelloInput profile hello.readOnly basis none diff --git a/fixtures/cli/08-glob-expansion-ok/inputs/x.edict b/fixtures/cli/08-glob-expansion-ok/inputs/x.edict index 39c53bb..dc09617 100644 --- a/fixtures/cli/08-glob-expansion-ok/inputs/x.edict +++ b/fixtures/cli/08-glob-expansion-ok/inputs/x.edict @@ -6,7 +6,7 @@ type HelloInput = { name: String, }; -intent sayHello(input: HelloInput) +action sayHello(input: HelloInput) returns HelloInput profile hello.readOnly basis none diff --git a/fixtures/cli/08-glob-expansion-ok/inputs/y.edict b/fixtures/cli/08-glob-expansion-ok/inputs/y.edict index 39c53bb..dc09617 100644 --- a/fixtures/cli/08-glob-expansion-ok/inputs/y.edict +++ b/fixtures/cli/08-glob-expansion-ok/inputs/y.edict @@ -6,7 +6,7 @@ type HelloInput = { name: String, }; -intent sayHello(input: HelloInput) +action sayHello(input: HelloInput) returns HelloInput profile hello.readOnly basis none diff --git a/fixtures/cli/09-shape-source-ok/request.jsonl b/fixtures/cli/09-shape-source-ok/request.jsonl index 64c75d9..a406289 100644 --- a/fixtures/cli/09-shape-source-ok/request.jsonl +++ b/fixtures/cli/09-shape-source-ok/request.jsonl @@ -1,2 +1,2 @@ {"schema":"edict.compiler.settings/v1","type":"compilerSettings","operation":"check"} -{"schema":"edict.compiler.input/v1","type":"compilerInput","kind":"source","name":"greeting.edict","source":"package examples.greeting@1;\n\nuse shape \"schemas/greeting.graphql\" as shape;\nuse lawpack greeting.optics@1 digest \"sha256:0000000000000000000000000000000000000000000000000000000000000000\" as greetingLaw;\nuse target echo.dpo@1 digest \"sha256:1111111111111111111111111111111111111111111111111111111111111111\" as echo;\n\nintent readGreeting(input: shape.ReadGreetingInput)\n returns shape.GreetingReading\n profile echo.readOnly\n basis input.greetingId\n budget <= greetingLaw.readGreetingBudget\n{\n let greetingRef = echo.ref(input.greetingId);\n let greeting = greetingRef.read()\n else greetingLaw.GreetingMissing;\n\n return {\n greetingId: input.greetingId,\n message: greeting.message,\n };\n}\n"} +{"schema":"edict.compiler.input/v1","type":"compilerInput","kind":"source","name":"greeting.edict","source":"package examples.greeting@1;\n\nuse shape \"schemas/greeting.graphql\" as shape;\nuse lawpack greeting.optics@1 digest \"sha256:0000000000000000000000000000000000000000000000000000000000000000\" as greetingLaw;\nuse target echo.dpo@1 digest \"sha256:1111111111111111111111111111111111111111111111111111111111111111\" as echo;\n\naction readGreeting(input: shape.ReadGreetingInput)\n returns shape.GreetingReading\n profile echo.readOnly\n basis input.greetingId\n budget <= greetingLaw.readGreetingBudget\n{\n let greetingRef = echo.ref(input.greetingId);\n let greeting = greetingRef.read()\n else greetingLaw.GreetingMissing;\n\n return {\n greetingId: input.greetingId,\n message: greeting.message,\n };\n}\n"} diff --git a/fixtures/core/canonical/bounded-hello.core.cbor b/fixtures/core/canonical/bounded-hello.core.cbor index bfaa5f96fa0097503dcb02718ed106c5c05bd896..62b1f8ffb147199e513ae28a44de29ea42b746cf 100644 GIT binary patch delta 27 jcmX@ceT;j92qRl!a!F=>-sVcizs!?`8RI9Hu?7GDiBSoC delta 28 kcmX@ceT;j92;*c4);RX`%)FA+ypqjQjDMLYm$3!_0E>eOq5uE@ diff --git a/fixtures/core/canonical/bounded-hello.core.sha256 b/fixtures/core/canonical/bounded-hello.core.sha256 index 5f8784f..4a4a9cd 100644 --- a/fixtures/core/canonical/bounded-hello.core.sha256 +++ b/fixtures/core/canonical/bounded-hello.core.sha256 @@ -1 +1 @@ -sha256:f8243875a77aa3cc8f02529e2c8b18e183ed6b02ad5f6fd6d0795c6ad532ce85 +sha256:3cc1bc25df7c776d6eeec04d62842ff33379a911aef9b6fdad45960c45cd30db diff --git a/fixtures/core/schema/accepted/core-intent-minimal.fields b/fixtures/core/schema/accepted/core-action-minimal.fields similarity index 91% rename from fixtures/core/schema/accepted/core-intent-minimal.fields rename to fixtures/core/schema/accepted/core-action-minimal.fields index 268b28c..3858363 100644 --- a/fixtures/core/schema/accepted/core-intent-minimal.fields +++ b/fixtures/core/schema/accepted/core-action-minimal.fields @@ -1,6 +1,6 @@ # Required fields for a minimal Core intent shape. expect accept -shape core-intent +shape core-action field body field coreEvaluationBudget field input diff --git a/fixtures/core/schema/accepted/core-module-minimal.fields b/fixtures/core/schema/accepted/core-module-minimal.fields index 249ff76..61f3f39 100644 --- a/fixtures/core/schema/accepted/core-module-minimal.fields +++ b/fixtures/core/schema/accepted/core-module-minimal.fields @@ -4,6 +4,6 @@ shape core-module field apiVersion field coordinate field imports -field intents +field actions field requiredCoreCapabilities field types diff --git a/fixtures/core/schema/rejected/core-intent-unknown-verified-mode.fields b/fixtures/core/schema/rejected/core-action-unknown-verified-mode.fields similarity index 92% rename from fixtures/core/schema/rejected/core-intent-unknown-verified-mode.fields rename to fixtures/core/schema/rejected/core-action-unknown-verified-mode.fields index 667ae03..0538820 100644 --- a/fixtures/core/schema/rejected/core-intent-unknown-verified-mode.fields +++ b/fixtures/core/schema/rejected/core-action-unknown-verified-mode.fields @@ -1,6 +1,6 @@ # `verifiedOperationMode` is verifier/admission evidence, not a Core field. expect reject -shape core-intent +shape core-action field body field coreEvaluationBudget field input diff --git a/fixtures/core/schema/rejected/core-module-missing-intents.fields b/fixtures/core/schema/rejected/core-module-missing-actions.fields similarity index 100% rename from fixtures/core/schema/rejected/core-module-missing-intents.fields rename to fixtures/core/schema/rejected/core-module-missing-actions.fields diff --git a/fixtures/lang/bounds/bounded-hello.edict b/fixtures/lang/bounds/bounded-hello.edict index e3443a1..5e41fc9 100644 --- a/fixtures/lang/bounds/bounded-hello.edict +++ b/fixtures/lang/bounds/bounded-hello.edict @@ -10,7 +10,7 @@ type HelloReading = { message: String, }; -intent sayHello(input: HelloInput) +action sayHello(input: HelloInput) returns HelloReading profile hello.readOnly basis none diff --git a/fixtures/lang/effects/conditional-blob.edict b/fixtures/lang/effects/conditional-blob.edict index 1e4f2ca..bd953d2 100644 --- a/fixtures/lang/effects/conditional-blob.edict +++ b/fixtures/lang/effects/conditional-blob.edict @@ -4,7 +4,7 @@ use shape "schemas/blob.graphql" as shape; use lawpack rope.optics@1 digest "sha256:0000000000000000000000000000000000000000000000000000000000000000" as rope; use target echo.dpo@1 digest "sha256:1111111111111111111111111111111111111111111111111111111111111111" as echo; -intent storeBlob(input: shape.StoreBlobInput) +action storeBlob(input: shape.StoreBlobInput) returns shape.MaybeBlob profile echo.readWrite basis input.initialBytes diff --git a/fixtures/lang/effects/read-greeting.edict b/fixtures/lang/effects/read-greeting.edict index 5e3838d..5c9842e 100644 --- a/fixtures/lang/effects/read-greeting.edict +++ b/fixtures/lang/effects/read-greeting.edict @@ -4,7 +4,7 @@ use shape "schemas/greeting.graphql" as shape; use lawpack greeting.optics@1 digest "sha256:0000000000000000000000000000000000000000000000000000000000000000" as greetingLaw; use target echo.dpo@1 digest "sha256:1111111111111111111111111111111111111111111111111111111111111111" as echo; -intent readGreeting(input: shape.ReadGreetingInput) +action readGreeting(input: shape.ReadGreetingInput) returns shape.GreetingReading profile echo.readOnly basis input.greetingId diff --git a/fixtures/lang/tooling/highlight-smoke.edict b/fixtures/lang/tooling/highlight-smoke.edict index 8c2d910..c93440b 100644 --- a/fixtures/lang/tooling/highlight-smoke.edict +++ b/fixtures/lang/tooling/highlight-smoke.edict @@ -12,7 +12,7 @@ type HelloReading = { message: String, }; -intent sayHello(input: HelloInput) +action sayHello(input: HelloInput) returns HelloReading profile hello.readOnly basis none diff --git a/fixtures/lang/types/color-match.edict b/fixtures/lang/types/color-match.edict index dd98d6d..231ad94 100644 --- a/fixtures/lang/types/color-match.edict +++ b/fixtures/lang/types/color-match.edict @@ -12,7 +12,7 @@ type Paint = variant { Transparent, }; -intent describe(input: shape.DescribeInput) +action describe(input: shape.DescribeInput) returns shape.Description profile palette.readOnly basis input.paint diff --git a/fixtures/obstruction-strands/v0/stale-basis/README.md b/fixtures/obstruction-strands/v0/stale-basis/README.md index 25efbfa..c039ef9 100644 --- a/fixtures/obstruction-strands/v0/stale-basis/README.md +++ b/fixtures/obstruction-strands/v0/stale-basis/README.md @@ -15,7 +15,7 @@ claim that every eligible artifact already exists. PR #129 adds Core lowering for the currently lowerable `require` subset. PR #131 adds Echo Target IR requirements for synthetic Core-compatible obstruction fixtures. This stale-basis source remains source-only until the compiler spine -supports non-`basis none` intent bases and the `jim.basisFresh(...)` predicate +supports non-`basis none` action bases and the `jim.basisFresh(...)` predicate form, so this directory still intentionally has no `core.review` or `echo-target-ir.review` artifact. diff --git a/fixtures/obstruction-strands/v0/stale-basis/source.edict b/fixtures/obstruction-strands/v0/stale-basis/source.edict index e487b46..2bdbbfc 100644 --- a/fixtures/obstruction-strands/v0/stale-basis/source.edict +++ b/fixtures/obstruction-strands/v0/stale-basis/source.edict @@ -1,6 +1,6 @@ package examples.obstruction@1; -intent edit(input: jim.EditInput) returns jim.EditOutput +action edit(input: jim.EditInput) returns jim.EditOutput profile jim.edit basis input.basis budget <= jim.tiny { diff --git a/fixtures/provider-contracts/v1/edict-provider-contracts.cddl b/fixtures/provider-contracts/v1/edict-provider-contracts.cddl index 7544122..3751a3b 100644 --- a/fixtures/provider-contracts/v1/edict-provider-contracts.cddl +++ b/fixtures/provider-contracts/v1/edict-provider-contracts.cddl @@ -55,7 +55,7 @@ authority-class = "domainMappable" / "participantOwned" / "integrityFault" / core-type-ref = tstr ; canonical Core type coordinate -; An operation profile supplies the optic template a Core intent resolves its +; An operation profile supplies the optic template a Core action resolves its ; opticKind/boundaryKind/supportPolicy/lossDisposition from. Target profiles and ; lawpacks publish these as a map `{ coordinate => operation-profile }`, so the ; coordinate is the KEY, not a value field (EDICT-OPTIC-TEMPLATE-OWNER-001, @@ -72,7 +72,7 @@ optic-template = { lossDisposition: tstr, ; canonical loss-disposition coordinate ? basisTemplate: tstr, ; optional digest-locked basis template coord ; the aperture requirement this template supplies. Required when the template - ; is the source of a Core optic's apertureRequirement (i.e. the intent has no + ; is the source of a Core optic's apertureRequirement (i.e. the action has no ; source `footprint <= ...`), since apertureRequirement is mandatory in Core ; (EDICT-OPTIC-APERTURE-REF-001). ? apertureRequirement: aperture-requirement, @@ -97,7 +97,7 @@ core-module = { coordinate: tstr, imports: [* core-import], types: { * tstr => core-type }, - intents: { + tstr => core-intent }, + actions: { + tstr => core-action }, requiredCoreCapabilities: [* tstr], } @@ -291,9 +291,9 @@ input-constraint = { predicate: core-predicate, } -; --- intents, blocks, and nodes ----------------------------------------- +; --- actions, blocks, and nodes ----------------------------------------- -core-intent = { +core-action = { input: core-type-ref, output: core-type-ref, requiredOperationProfile: tstr, @@ -764,10 +764,10 @@ target-ir-artifact = { domain: tstr, targetProfile: target-ir-resource-ref, sourceCoreCoordinate: tstr, - intents: { * tstr => target-ir-intent }, + actions: { * tstr => target-ir-action }, } -target-ir-intent = { +target-ir-action = { operationProfile: tstr, inputConstraints: [* input-constraint], coreEvaluationBudget: core-budget, diff --git a/fixtures/provider-contracts/v1/manifest.json b/fixtures/provider-contracts/v1/manifest.json index fe91f62..cfe8d7f 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": "3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d70726f76696465722d636f6e7472616374732e6364646c0a3b2047656e6572617465642066726f6d2045646963742d6f776e65642041424920667261676d656e74732e20444f204e4f5420454449542e0a0a3b202d2d2d2065646963742d636f6d6d6f6e2e6364646c202d2d2d0a3b2065646963742d636f6d6d6f6e2e6364646c0a3b20536861726564204344444c20747970657320666f722074686520456469637420414249732c20646566696e6564204f4e4345206865726520736f20746865792063616e6e6f742064726966740a3b202845444943542d4142492d4e4f4455502d303031292e2054776f2067726f7570733a0a3b2020202d207265736f757263652d7265662c207368613235362d6469676573742c206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c0a3b2020202020636f72652d747970652d7265663a20617373656d626c656420776974682065646963742d7461726765742d70726f66696c652e6364646c20616e640a3b202020202065646963742d6c61777061636b2e6364646c20627920746865206275696c643b2074686f736520736368656d617320646f206e6f74207265646566696e65207468656d2e0a3b2020202d206f7065726174696f6e2d70726f66696c652c206f707469632d74656d706c6174652c2061706572747572652d726571756972656d656e7420616e6420746865697220726566733a0a3b2020202020636f6e73756d65642062792074686520436f72652f6f70746963206c61796572202865646963742d636f72652e6364646c2920616e64207265666572656e636564206279207468650a3b20202020206c616e67756167652f7461726765742d70726f66696c652073706563732e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a0a3b2041206e6f726d617469766520737562636f6d706f6e656e74207265666572656e636564206279206964656e7469747920706c7573206469676573742e204d616e696665737473206e657665720a3b20656d626564207468656972206f776e2073656c662d64696765737420696e20746865697220707265696d616765202845444943542d434f52452d53454c46484153482d303031292e0a7265736f757263652d726566203d207b2069643a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20446967657374732061726520617574686f726974617469766520617320747970656420627974652076616c7565732c206e657665722068657820737472696e67732e20526576696577204a534f4e0a3b2072656e64657273207468697320617320227368613235363a3c3634206c6f77657263617365206865783e22202845444943542d4449474553542d574952452d303031292e0a7368613235362d646967657374203d205b20616c676f726974686d3a2022736861323536222c2062797465733a2062737472202e73697a65203332205d0a0a3b2041206e616d6564206c6f772d6c6576656c206661696c75726520616e206566666563742063616e2072616973652e2054686520736f75726365206f62737472756374696f6e206d61700a3b2062696e64732069742028627920636f6f7264696e6174652920616e6420636f6e73747275637473206120747970656420646f6d61696e206f62737472756374696f6e2066726f6d206974730a3b207061796c6f6164202845444943542d4142492d4641494c5552452d4e414d45442d303031292e0a3b20416e20656666656374277320606566666563744661696c7572657360206c697374204d555354206861766520756e697175652060636f6f7264696e61746560733a2073696e6365207468650a3b206f62737472756374696f6e206d6170206973206b6579656420627920636f6f7264696e6174652c2074776f206661696c757265732073686172696e67206120636f6f7264696e61746520286576656e0a3b207769746820646966666572656e7420617574686f72697479436c6173732f7061796c6f61645479706529206d616b652065786861757374697665206d617070696e6720616e642062696e6465720a3b20747970696e6720616d626967756f757320616e64206172652072656a6563746564202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a3b0a3b2045666665637473206361727279207468656972206661696c757265732061732061206d617020607b206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d600a3b202873656520746865207461726765742f6c61777061636b2065666665637420736368656d6173292e20546865206661696c75726520636f6f7264696e61746520697320746865206d61700a3b204b45592c20736f206974206973206e6f7420726570656174656420696e2074686520626f647920616e642063616e6e6f74206469736167726565207769746820746865206b65792e0a6566666563742d6661696c7572652d626f6479203d207b0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164547970653a20636f72652d747970652d7265662c202020202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b2041206661696c75726520636f6f7264696e617465206d7573742062652061206261726520456469637420606964656e746020286c65747465722f756e64657273636f7265207468656e0a3b206c6574746572732f6469676974732f756e64657273636f7265732920414e44206d757374206e6f742062652061207265736572766564206b6579776f72642028652e672e2060656c7365602c0a3b20606261736973602c20607768657265602c206072657175697265602c2060666f72602c2060696660292e2054686520736f75726365206f62737472756374696f6e2d6d6170204c4853206f6e6c790a3b20616363657074732061206e6f6e2d6b6579776f726420606964656e74602c20736f20612068797068656e2f646f742f6b6579776f726420636f6f7264696e61746520776f756c642062650a3b204142492d76616c69642079657420696d706f737369626c6520746f206d617020657868617573746976656c7920696e20736f757263652e20546865207265676578206361707475726573207468650a3b206c65786963616c2073686170653b206b6579776f7264206578636c7573696f6e20697320616e206164646974696f6e616c2076616c69646174696f6e2072756c650a3b202845444943542d4142492d4641494c5552452d4944454e542d303031292e0a6661696c7572652d6964656e74203d2074737472202e72656765787020225b412d5a612d7a5f5d5b412d5a612d7a302d395f5d2a220a0a6566666563742d6b696e64203d20227265616422202f202263726561746522202f2022656e7375726522202f20227265706c61636522202f202264656c65746522202f0a202020202020202020202020202022617070656e6422202f202272656475636522202f202273656d616e7469632e656d697422202f2022637573746f6d220a0a617574686f726974792d636c617373203d2022646f6d61696e4d61707061626c6522202f20227061727469636970616e744f776e656422202f2022696e746567726974794661756c7422202f0a202020202020202020202020202020202020227265736f757263654661756c7422202f2022696e7465726e616c4661756c74220a0a636f72652d747970652d726566203d20747374722020203b2063616e6f6e6963616c20436f7265207479706520636f6f7264696e6174650a0a3b20416e206f7065726174696f6e2070726f66696c6520737570706c69657320746865206f707469632074656d706c617465206120436f726520696e74656e74207265736f6c766573206974730a3b206f707469634b696e642f626f756e646172794b696e642f737570706f7274506f6c6963792f6c6f7373446973706f736974696f6e2066726f6d2e205461726765742070726f66696c657320616e640a3b206c61777061636b73207075626c6973682074686573652061732061206d617020607b20636f6f7264696e617465203d3e206f7065726174696f6e2d70726f66696c65207d602c20736f207468650a3b20636f6f7264696e61746520697320746865204b45592c206e6f7420612076616c7565206669656c64202845444943542d4f505449432d54454d504c4154452d4f574e45522d3030312c0a3b2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65203d207b0a20206f7074696354656d706c6174653a206f707469632d74656d706c6174652c0a20206566666563745072656469636174653a20747374722c20202020202020202020203b20636f6f7264696e617465206f6620746865206f7065726174696f6e2d6d6f6465207072656469636174650a7d0a0a6f707469632d74656d706c617465203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a2020737570706f7274506f6c6963793a20747374722c202020202020202020202020203b2063616e6f6e6963616c20737570706f72742d706f6c69637920636f6f7264696e6174650a20206c6f7373446973706f736974696f6e3a20747374722c20202020202020202020203b2063616e6f6e6963616c206c6f73732d646973706f736974696f6e20636f6f7264696e6174650a20203f20626173697354656d706c6174653a20747374722c20202020202020202020203b206f7074696f6e616c206469676573742d6c6f636b65642062617369732074656d706c61746520636f6f72640a20203b2074686520617065727475726520726571756972656d656e7420746869732074656d706c61746520737570706c6965732e205265717569726564207768656e207468652074656d706c6174650a20203b2069732074686520736f75726365206f66206120436f7265206f707469632773206170657274757265526571756972656d656e742028692e652e2074686520696e74656e7420686173206e6f0a20203b20736f757263652060666f6f747072696e74203c3d202e2e2e60292c2073696e6365206170657274757265526571756972656d656e74206973206d616e6461746f727920696e20436f72650a20203b202845444943542d4f505449432d41504552545552452d5245462d303031292e0a20203f206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a7d0a0a3b206170657274757265526571756972656d656e742069732061207479706564207265666572656e63652c206e65766572206120667265652d666f726d20737472696e672e2041207265766965770a3b2072656e646572696e67206d61792073686f772069747320636f6f7264696e617465202845444943542d4f505449432d41504552545552452d5245462d303031292e0a61706572747572652d726571756972656d656e74203d20666f6f747072696e742d6365696c696e672d726566202f2061627374726163742d666f6f747072696e742d6f626c69676174696f6e2d7265660a666f6f747072696e742d6365696c696e672d726566203d207b206b696e643a2022666f6f747072696e744365696c696e67222c207265663a2074737472207d0a61627374726163742d666f6f747072696e742d6f626c69676174696f6e2d726566203d207b206b696e643a20226162737472616374466f6f747072696e744f626c69676174696f6e222c207265663a2074737472207d0a0a3b202d2d2d2065646963742d636f72652e6364646c202d2d2d0a3b2065646963742d636f72652e6364646c0a3b204e6f726d617469766520736368656d6120666f722074686520456469637420436f72652076312073656d616e746963206d6f64656c2e0a3b0a3b2053636f706520626f756e646172793a20746869732066696c6520646566696e657320436f7265206d65616e696e6720616e6420736368656d61207368617065206f6e6c792e20497420646f65730a3b206e6f7420646566696e6520612063616e6f6e6963616c20656e636f6465722c20436f7265206d6f64756c652068617368206669656c64732c20686173682066697874757265732c207461726765740a3b206c6f776572696e672c2061646d697373696f6e2062756e646c65732c206f72207461726765742d6f776e65642049522e0a0a636f72652d6d6f64756c65203d207b0a202061706956657273696f6e3a202265646963742e636f72652f7631222c0a2020636f6f7264696e6174653a20747374722c0a2020696d706f7274733a205b2a20636f72652d696d706f72745d2c0a202074797065733a207b202a2074737472203d3e20636f72652d74797065207d2c0a2020696e74656e74733a207b202b2074737472203d3e20636f72652d696e74656e74207d2c0a20207265717569726564436f72654361706162696c69746965733a205b2a20747374725d2c0a7d0a0a636f72652d696d706f7274203d207b0a20206b696e643a20226c61777061636b22202f202274617267657422202f2022636f7265222c0a20207265663a207265736f757263652d7265662c0a20203f20616c6961733a20747374722c0a7d0a0a3b202d2d2d207479706573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d74797065203d20636f72652d7363616c61722d74797065202f20636f72652d7265636f72642d74797065202f20636f72652d76617269616e742d74797065202f0a202020202020202020202020636f72652d6f7074696f6e2d74797065202f20636f72652d6c6973742d74797065202f20636f72652d6d61702d74797065202f0a202020202020202020202020636f72652d6361706162696c6974792d7265662d747970650a0a636f72652d7363616c61722d74797065203d20636f72652d626f6f6c2d74797065202f20636f72652d696e742d74797065202f20636f72652d737472696e672d74797065202f0a20202020202020202020202020202020202020636f72652d62797465732d74797065202f20636f72652d756e69742d747970650a0a636f72652d626f6f6c2d74797065203d207b206b696e643a2022426f6f6c22207d0a636f72652d756e69742d74797065203d207b206b696e643a2022556e697422207d0a636f72652d696e742d74797065203d207b0a20206b696e643a202249363422202f202255363422202f202249333222202f202255333222202f202249313622202f202255313622202f2022493822202f20225538222c0a7d0a636f72652d737472696e672d74797065203d207b0a20206b696e643a2022537472696e67222c0a20206d61783a2075696e742c0a202063616e6f6e6963616c3a2022756e69636f64652d7363616c61722d6e666322202f20227261772d75746638222c0a7d0a636f72652d62797465732d74797065203d207b0a20206b696e643a20224279746573222c0a20206d61783a2075696e742c0a7d0a636f72652d7265636f72642d74797065203d207b0a20206b696e643a20225265636f7264222c0a20206669656c64733a207b202a2074737472203d3e20636f72652d747970652d726566207d2c0a7d0a636f72652d76617269616e742d74797065203d207b0a20206b696e643a202256617269616e74222c0a202063617365733a207b202b2074737472203d3e2076617269616e742d636173652d626f6479207d2c0a7d0a76617269616e742d636173652d626f6479203d207b0a20203f207061796c6f61643a20636f72652d747970652d7265662c0a7d0a636f72652d6f7074696f6e2d74797065203d207b0a20206b696e643a20224f7074696f6e222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a636f72652d6c6973742d74797065203d207b0a20206b696e643a20224c697374222c0a20206974656d3a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6d61702d74797065203d207b0a20206b696e643a20224d6170222c0a20206b65793a20636f72652d747970652d7265662c0a202076616c75653a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6361706162696c6974792d7265662d74797065203d207b0a20206b696e643a20224361706162696c697479526566222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a0a3b20636f72652d747970652d72656620697320646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d612e0a0a3b202d2d2d207265666572656e63657320616e642076616c756573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a3b204c6f63616c206964656e7469747920697320616c7068612d737461626c652e20606964602069732074686520636f6d70696c65722d6f776e6564206c6f63616c20636f6f7264696e6174653b0a3b2060616c7068614e616d656020697320746865206e6f726d616c697a65642068756d616e2f6465627567206e616d652e20536f757263652062696e646572207370656c6c696e67206973206e6f740a3b206964656e746974792e0a6c6f63616c2d726566203d207b0a202069643a20747374722c0a2020616c7068614e616d653a20747374722c0a2020747970653a20636f72652d747970652d7265662c0a7d0a0a636f72652d76616c7565203d20636f72652d6e756c6c2d76616c7565202f20636f72652d626f6f6c2d76616c7565202f20636f72652d696e742d76616c7565202f0a20202020202020202020202020636f72652d737472696e672d76616c7565202f20636f72652d62797465732d76616c7565202f20636f72652d7265636f72642d76616c7565202f0a20202020202020202020202020636f72652d76617269616e742d76616c7565202f20636f72652d6c6973742d76616c7565202f20636f72652d6d61702d76616c7565202f0a20202020202020202020202020636f72652d6361706162696c6974792d76616c75650a0a636f72652d6e756c6c2d76616c7565203d207b206b696e643a20226e756c6c22207d0a636f72652d626f6f6c2d76616c7565203d207b206b696e643a2022626f6f6c222c2076616c75653a20626f6f6c207d0a636f72652d696e742d76616c7565203d207b206b696e643a2022696e74222c2077696474683a20747374722c2076616c75653a20696e74207d0a636f72652d737472696e672d76616c7565203d207b206b696e643a2022737472696e67222c2076616c75653a2074737472207d0a636f72652d62797465732d76616c7565203d207b206b696e643a20226279746573222c2076616c75653a2062737472207d0a636f72652d7265636f72642d76616c7565203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d76616c7565207d207d0a636f72652d76617269616e742d76616c7565203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d76616c75652c0a7d0a636f72652d6c6973742d76616c7565203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d76616c75655d207d0a636f72652d6d61702d76616c7565203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d76616c75652c2076616c75653a20636f72652d76616c75655d5d207d0a636f72652d6361706162696c6974792d76616c7565203d207b0a20206b696e643a20226361706162696c697479222c0a2020726563656970743a207368613235362d6469676573742c0a7d0a0a3b2045646963742d617574686f72656420707572652068656c70657273207573652061207075726520436f72652066756e6374696f6e20626f64792e2054686520626f64792063616e2062696e640a3b20707572652065787072657373696f6e7320616e642072657475726e20616e2065787072657373696f6e2c206275742069742063616e6e6f7420636f6e7461696e20436f7265206566666563742c0a3b2067756172642c206272616e63682c206c6f6f702c206d617463682d6e6f64652c206f722070726f6f662d6f626c69676174696f6e206e6f6465732e0a636f72652d666e2d626f6479203d207b0a2020706172616d733a205b2a206c6f63616c2d7265665d2c0a2020626f64793a20636f72652d707572652d626c6f636b2c0a7d0a0a636f72652d707572652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a202062696e64696e67733a205b2a20707572652d6c65742d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a707572652d6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a0a3b202d2d2d2065787072657373696f6e7320616e642070726564696361746573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d65787072203d206c6f63616c2d65787072202f20636f6e73742d65787072202f207265636f72642d65787072202f206669656c642d65787072202f0a20202020202020202020202076617269616e742d65787072202f206d617463682d65787072202f2063616c6c2d65787072202f206c6973742d65787072202f206d61702d65787072202f0a20202020202020202020202069662d657870720a0a6c6f63616c2d65787072203d207b206b696e643a20226c6f63616c222c207265663a206c6f63616c2d726566207d0a636f6e73742d65787072203d207b206b696e643a2022636f6e7374222c2076616c75653a20636f72652d76616c7565207d0a7265636f72642d65787072203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d65787072207d207d0a6669656c642d65787072203d207b206b696e643a20226669656c64222c20626173653a20636f72652d657870722c206669656c643a2074737472207d0a76617269616e742d65787072203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d657870722c0a7d0a6d617463682d65787072203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d61726d5d2c0a7d0a6d617463682d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d657870722c0a7d0a63616c6c2d65787072203d207b0a20206b696e643a202263616c6c222c0a202063616c6c65653a20747374722c0a202074797065417267733a205b2a20636f72652d747970652d7265665d2c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6c6973742d65787072203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d657870725d207d0a6d61702d65787072203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d657870722c2076616c75653a20636f72652d657870725d5d207d0a69662d65787072203d207b0a20206b696e643a20226966222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d657870722c0a2020656c73653a20636f72652d657870722c0a7d0a0a636f72652d707265646963617465203d20747275652d707265646963617465202f2066616c73652d707265646963617465202f206e6f742d707265646963617465202f0a2020202020202020202020202020202020616c6c2d707265646963617465202f20616e792d707265646963617465202f20636f6d706172652d707265646963617465202f0a202020202020202020202020202020202063616c6c2d707265646963617465202f206f62737472756374696f6e2d7072656469636174650a0a747275652d707265646963617465203d207b206b696e643a20227472756522207d0a66616c73652d707265646963617465203d207b206b696e643a202266616c736522207d0a6e6f742d707265646963617465203d207b206b696e643a20226e6f74222c2076616c75653a20636f72652d707265646963617465207d0a616c6c2d707265646963617465203d207b206b696e643a2022616c6c222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a616e792d707265646963617465203d207b206b696e643a2022616e79222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a636f6d706172652d707265646963617465203d207b0a20206b696e643a2022636f6d70617265222c0a20206f703a20223d3d22202f2022213d22202f20223c22202f20223c3d22202f20223e22202f20223e3d222c0a20206c6566743a20636f72652d657870722c0a202072696768743a20636f72652d657870722c0a7d0a63616c6c2d707265646963617465203d207b0a20206b696e643a202263616c6c222c0a20207072656469636174653a20747374722c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6f62737472756374696f6e2d707265646963617465203d207b0a20206b696e643a20226f62737472756374696f6e222c0a2020636f6f7264696e6174653a206661696c7572652d6964656e742c0a20207061796c6f61643a20636f72652d657870722c0a7d0a0a696e7075742d636f6e73747261696e74203d207b0a2020636f6f7264696e6174653a20747374722c0a2020736f757263653a2022776865726522202f2022636f6d70696c6572222c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a3b202d2d2d20696e74656e74732c20626c6f636b732c20616e64206e6f646573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d696e74656e74203d207b0a2020696e7075743a20636f72652d747970652d7265662c0a20206f75747075743a20636f72652d747970652d7265662c0a202072657175697265644f7065726174696f6e50726f66696c653a20747374722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020626f64793a20636f72652d626c6f636b2c0a20203f206f707469633a20636f72652d6f707469632c0a7d0a0a636f72652d627564676574203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a636f72652d6f70746963203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a20206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a2020737570706f7274506f6c6963793a20747374722c0a20206c6f7373446973706f736974696f6e3a20747374722c0a7d0a0a636f72652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a20206e6f6465733a205b2a20636f72652d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a636f72652d6e6f6465203d206c65742d6e6f6465202f20726571756972652d6e6f6465202f206566666563742d6e6f6465202f2067756172642d6e6f6465202f206272616e63682d6e6f6465202f0a202020202020202020202020666f722d6e6f6465202f206d617463682d6e6f6465202f2070726f6f662d6f626c69676174696f6e2d6e6f64650a0a6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a726571756972652d6e6f6465203d207b0a20206b696e643a202272657175697265222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a726571756972652d6661696c7572652d61726d203d207465726d696e616c2d726571756972652d6661696c757265202f0a20202020202020202020202020202020202020202020636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c7572650a7465726d696e616c2d726571756972652d6661696c757265203d207b0a20206b696e643a20227465726d696e616c222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c757265203d207b0a20206b696e643a2022636f6e74696e75654f627374727563746564222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a6f62737472756374696f6e2d726561736f6e203d207b0a2020726561736f6e4b696e643a20747374722c0a20207061796c6f61643a207b202a2074737472203d3e20636f72652d65787072207d2c0a7d0a6566666563742d6e6f6465203d207b0a20206b696e643a2022656666656374222c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4d61703a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a6f62737472756374696f6e2d61726d203d207b0a202062696e6465723a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a67756172642d6e6f6465203d207b0a20206b696e643a20226775617264222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f62737472756374696f6e3a20636f72652d657870722c0a7d0a6272616e63682d6e6f6465203d207b0a20206b696e643a20226272616e6368222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d626c6f636b2c0a2020656c73653a20636f72652d626c6f636b2c0a7d0a666f722d6e6f6465203d207b0a20206b696e643a2022666f72222c0a202062696e6465723a206c6f63616c2d7265662c0a2020697465723a20636f72652d657870722c0a2020626f756e643a20636f72652d626f756e642c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a6d617463682d6e6f6465203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d626c6f636b2d61726d5d2c0a7d0a6d617463682d626c6f636b2d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a70726f6f662d6f626c69676174696f6e2d6e6f6465203d207b0a20206b696e643a202270726f6f66222c0a2020636f6f7264696e6174653a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a636f72652d626f756e64203d206c69746572616c2d626f756e64202f20636f6f7264696e6174652d626f756e640a6c69746572616c2d626f756e64203d207b206b696e643a20226c69746572616c222c2076616c75653a2075696e74207d0a636f6f7264696e6174652d626f756e64203d207b206b696e643a2022636f6f7264696e617465222c207265663a2074737472207d0a0a3b20536861726564207265736f757263652d7265662c207368613235362d6469676573742c206661696c7572652d6964656e742c2061706572747572652d726571756972656d656e742c20616e640a3b20636f72652d747970652d7265662061726520646566696e6564206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c2e0a0a3b202d2d2d2065646963742d6c61777061636b2e6364646c202d2d2d0a3b2065646963742d6c61777061636b2e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374206c61777061636b206d616e696665737420616e64206578706f727420737572666163652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e204a534f4e20696e207468652070726f73652073706563730a3b2069732061207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a6c61777061636b2d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e6c61777061636b2f7631222c0a202069643a20747374722c0a202076657273696f6e3a20747374722c0a20206163636570746564436f72654162693a205b2b20747374725d2c0a2020646570656e64656e636965733a205b2a206c61777061636b2d6465705d2c202020202020202020203b20616379636c69632c206469676573742d6c6f636b6564202845444943542d4c41575041434b2d4441472d303031290a20206578706f7274733a207265736f757263652d7265662c0a20203f2074617267657441646170746572733a205b2b207461726765742d616461707465725d2c2020203b207265717569726564206f6e6c7920696620616e792072756e74696d6520656666656374206578697374730a20203f2068656c706572436f6d706f6e656e743a2065786563757461626c652d636f6d706f6e656e742c203b2065786563757461626c652068656c70657273206361727279207468656972206f776e2073616e64626f782b6675656c0a202076657269666965723a2076657269666965722c202020202020202020202020202020202020202020203b20636c61737369666965643a206465636c61726174697665206f722065786563757461626c650a2020636f6d7061746962696c6974793a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b2041207665726966696572206973206569746865722061206465636c617261746976652072756c6573657420286e6f2072756e74696d6529206f7220616e2065786563757461626c650a3b20636f6d706f6e656e742e20416e2065786563757461626c65207665726966696572204d55535420636172727920697473206f776e2073616e64626f7820616e64206675656c206d6f64656c2c0a3b20736f2074686520736368656d6120656e666f726365732074686174206e6f2065786563757461626c6520636f6d706f6e656e74206973206c65667420756e626f756e6465640a3b202845444943542d4142492d56455249464945522d424f554e442d303031292e0a7665726966696572203d206465636c617261746976652d7665726966696572202f2065786563757461626c652d76657269666965720a6465636c617261746976652d7665726966696572203d207b20636c6173733a20226465636c61726174697665222c2072756c657365743a207265736f757263652d726566207d0a65786563757461626c652d7665726966696572203d207b0a2020636c6173733a202265786563757461626c65222c0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a3b20416e792065786563757461626c6520636f6d706f6e656e7420697320626f756e64656420627920697473206f776e2073616e64626f78202b206675656c206d6f64656c2e0a65786563757461626c652d636f6d706f6e656e74203d207b0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a6c61777061636b2d646570203d207b2069643a20747374722c2076657273696f6e3a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20416461707465722073656c656374696f6e206b65797320534f4c454c59206f666620746865206469676573742d6c6f636b65642060616363657074656454617267657450726f66696c65600a3b20286974732060696460206973207468652070726f66696c652069643b206974732060646967657374602070696e73207468652065786163742070726f66696c652f76657273696f6e292e2054686572650a3b20617265206e6f20696e646570656e64656e7420646973706c617920737472696e6773207468617420636f756c64206469736167726565207769746820746865206c6f636b2c20736f20610a3b207265736f6c7665722063616e6e6f742062696e6420616e206164617074657220746f206f6e6520746172676574207768696c6520746865206c6f636b2070726f76657320616e6f746865720a3b202845444943542d4c41575041434b2d414441505445522d54415247455449522d303031292e0a7461726765742d61646170746572203d207b0a2020616363657074656454617267657450726f66696c653a207265736f757263652d7265662c202020203b206469676573742d6c6f636b65642c20617574686f72697461746976652073656c6563746f720a2020616363657074656454617267657449723a207265736f757263652d7265662c2020202020202020203b206469676573742d6c6f636b65640a2020616461707465723a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d206578706f72742073757266616365202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a6c61777061636b2d6578706f727473203d207b0a202074797065733a205b2a206578706f727465642d747970655d2c0a2020636f6e7374616e74733a205b2a206578706f727465642d636f6e7374616e745d2c0a20207075726546756e6374696f6e733a205b2a20707572652d66756e6374696f6e5d2c0a2020656666656374733a205b2a2073656d616e7469632d6566666563745d2c0a20206f62737472756374696f6e733a205b2a206f62737472756374696f6e2d6465665d2c0a20203b206b65796564206279206f7065726174696f6e2d70726f66696c6520636f6f7264696e61746520e2869220756e697175656e65737320656e666f726365640a20203b202845444943542d4142492d4f5050524f46494c452d554e495155452d303031290a20203b206f7065726174696f6e2d70726f66696c65207265636f7264732074686973206c61777061636b206578706f72747320286f707469632074656d706c6174657320746861740a20203b2060696d706c656d656e7473602f6070726f66696c656020636c6175736573207265736f6c766520616761696e7374292e206f7065726174696f6e2d70726f66696c652069730a20203b20646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c20203b206b6579656420627920636f6f7264696e6174650a7d0a0a6578706f727465642d7479706520202020203d207b20636f6f7264696e6174653a20747374722c20646566696e6974696f6e3a20636f72652d747970652d726566207d0a6578706f727465642d636f6e7374616e74203d207b20636f6f7264696e6174653a20747374722c20747970653a20636f72652d747970652d7265662c2076616c75653a20616e79207d0a0a3b204120707572652068656c7065722069732061206469736372696d696e6174656420756e696f6e2062792060736f75726365602c20736f2074686520736368656d6120697473656c660a3b2067756172616e7465657320616e20696d706c656d656e746174696f6e20657869737473202845444943542d4c41575041434b2d505552452d494d504c2d303031293a0a3b2020202d20226564696374223a20617574686f72656420696e2045646963742f436f72653b2074686520436f726520626f6479206973206361727269656420696e6c696e6520286861736865640a3b20202020207769746820746865206578706f72742073757266616365292e2054686520736368656d61207265717569726573207468652060626f647960206669656c642e0a3b2020202d2022636f6d706f6e656e74223a20696d706c656d656e746564206f7574736964652045646963743b2063617272696573206e6f20696e6c696e6520626f647920616e6420696e73746561640a3b20202020206361727269657320697473206f776e206469676573742d6c6f636b65642060696d706c656d656e746174696f6e60202873616e64626f78202b206675656c292e20497420646f65730a3b20202020206e6f7420646570656e64206f6e20746865206f7074696f6e616c206d616e69666573742d6c6576656c2068656c706572436f6d706f6e656e742e0a707572652d66756e6374696f6e203d2065646963742d707572652d66756e6374696f6e202f20636f6d706f6e656e742d707572652d66756e6374696f6e0a0a707572652d66756e6374696f6e2d636f6d6d6f6e203d20280a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020706172616d6574657254797065733a205b2a20636f72652d747970652d7265665d2c2020202020203b20616c6c20626f756e6465640a202072657475726e547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020636f737454656d706c6174653a20747374722c0a202064657465726d696e69736d436c6173733a2022746f74616c22202f2022746f74616c2d776974682d74797065642d646961676e6f73746963222c0a290a0a65646963742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a20226564696374222c0a2020626f64793a20636f72652d666e2d626f64792c2020202020202020202020202020202020202020203b20696e6c696e652c20686173682d7369676e69666963616e740a7d0a0a636f6d706f6e656e742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a2022636f6d706f6e656e74222c0a20203b20746865206469676573742d6c6f636b656420636f6d706f6e656e7420696d706c656d656e74696e6720746869732068656c7065722e2052657175697265642061742074686520736368656d610a20203b206c6576656c20736f206120636f6d706f6e656e742068656c7065722063616e206e657665722076616c696461746520776974686f7574206120686173682d626f756e642c0a20203b2073616e64626f782b6675656c2d64657363726962656420696d706c656d656e746174696f6e202845444943542d4c41575041434b2d505552452d494d504c2d303031292e0a2020696d706c656d656e746174696f6e3a2065786563757461626c652d636f6d706f6e656e742c0a7d0a0a3b20636f72652d666e2d626f647920697320646566696e65642062792065646963742d636f72652e6364646c20616e6420617373656d626c656420776974682074686973206c61777061636b0a3b20736368656d612e2049742069732061207075726520436f72652066756e6374696f6e20626f64792c206e6f7420616e206566666563742d63617061626c6520636f72652d626c6f636b2e0a0a73656d616e7469632d656666656374203d207b0a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020696e707574547970653a20636f72652d747970652d7265662c2020202020202020202020202020203b20626f756e6465640a20206f7574707574547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020657865637574696f6e436c6173733a202270726f6f664f6e6c7922202f202272756e74696d65222c2020203b206f7274686f676f6e616c20746f207772697465436c6173730a20206566666563744b696e6448696e743a206566666563742d6b696e642c0a2020666f6f747072696e744f626c69676174696f6e3a20747374722c0a2020636f73744f626c69676174696f6e3a20747374722c0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c20203b206b6579656420627920636f6f7264696e6174653b20756e697175650a20206775617264537570706f72743a20626f6f6c2c0a7d0a0a6f62737472756374696f6e2d646566203d207b0a2020636f6f7264696e6174653a20747374722c0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164536368656d613a20636f72652d747970652d7265662c20202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d7461726765742d70726f66696c652e6364646c202d2d2d0a3b2065646963742d7461726765742d70726f66696c652e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374207461726765742070726f66696c65206d616e69666573742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76310a3b202873656520535045435f636f6e74696e75756d2d636f6e74726163742d62756e646c652d76312e6d64292e204a534f4e20696e207468652070726f736520737065637320697320610a3b207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d613b2074686973204344444c206973207468652073696e676c6520736f757263650a3b206f66207472757468202845444943542d4142492d4e4f4455502d303031292e0a0a7461726765742d70726f66696c652d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652f7631222c0a202069643a20747374722c202020202020202020202020202020202020202020202020203b20652e672e20226563686f2e64706f220a202076657273696f6e3a20747374722c20202020202020202020202020202020202020203b20652e672e202231220a20206163636570746564436f72654162693a205b2b20747374725d2c20202020202020203b20652e672e205b2265646963742e636f72652f7631225d0a0a2020696e7472696e736963733a207265736f757263652d7265662c0a2020696e7472696e7369634e616d6573706163653a20747374722c0a20203b207075626c697368657320746869732070726f66696c652773206f7065726174696f6e2d70726f66696c65207265636f72647320286f707469632074656d706c6174657320746861740a20203b206070726f66696c65602f60696d706c656d656e74736020636c6175736573207265736f6c766520616761696e7374292e205265666572656e63657320616e0a20203b206f7065726174696f6e2d70726f66696c65732d646f63756d656e74202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207265736f757263652d7265662c0a2020666f6f747072696e74416c67656272613a207265736f757263652d7265662c0a2020636f7374416c67656272613a207265736f757263652d7265662c0a202074617267657449723a207265736f757263652d7265662c0a20206f62737472756374696f6e5461786f6e6f6d793a207265736f757263652d7265662c0a202076657269666965723a207265736f757263652d7265662c0a20206c6f77657265723a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a0a20203b206669656c647320746865206c616e67756167652073706563207265717569726573206f662065766572792070726f66696c650a202062756e646c6550726f66696c653a207265736f757263652d7265662c0a202067656e657261746564417274696661637450726f66696c65733a205b2a207265736f757263652d7265665d2c0a202063616e6f6e6963616c456e636f64696e6752756c65733a207265736f757263652d7265662c0a20203b2052657365727665642f64656665727265643a2077696c6c206c697374206163636570746564206c61777061636b2d616461707465722041424920696473206f6e636520746861740a20203b20627974652d6c6576656c20414249202865646963742e6c61777061636b2d616461707465722f763129206973207370656369666965642e2054686520736368656d6120656e666f726365730a20203b20656d7074696e657373206e6f7720e280942074686520747970652069732074686520656d7074792061727261792c20736f206e6f6e2d656d7074792076616c756573206172650a20203b2072656a65637465642c206e6f74206d6572656c7920646973636f757261676564202845444943542d4142492d4c41575041434b2d414441505445522d44454645522d303031292e0a20203f2061636365707465644c61777061636b416461707465724162693a205b5d2c0a2020646961676e6f737469634162693a207265736f757263652d7265662c0a0a20203b206170706c69636174696f6e20646f637472696e650a20206170706c69636174696f6e4d6f64656c3a202261746f6d6963222c0a202072656164436f6e73697374656e63793a20226170706c69636174696f6e2d736e617073686f7422202f20747374722c0a202067756172644576616c756174696f6e3a2022707265636f6d6d69742d61746f6d696322202f20747374722c0a20206f62737472756374696f6e526f6c6c6261636b3a20226e6f2d76697369626c652d6566666563747322202f20747374722c0a20206d756c74695461726765743a20626f6f6c2c0a20203b207768657468657220746865207461726765742063616e206576616c7561746520707265636f6d6d697420706f7374636f6e646974696f6e20286067756172616e746565602920636865636b730a20203b20696e73696465207468652061746f6d6963206170706c69636174696f6e20756e6974202845444943542d5441524745542d504f5354434f4e442d303031290a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a0a202064657465726d696e6973746963457865637574696f6e3a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d6120627920746865206275696c640a3b202845444943542d4142492d4e4f4455502d303031292e205468657920617265206e6f74207265646566696e656420686572652e0a0a3b202d2d2d20696e7472696e736963207369676e6174757265202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e696665737427732060696e7472696e7369637360207265736f757263652d726566206973207468650a3b20696e7472696e7369632d7369676e617475726520636f7270757320646f63756d656e742062656c6f772e20497473206c61796f757420697320666978656420736f2074776f0a3b20696e646570656e64656e742070726f66696c65732076616c69646174652f686173682074686520636f72707573206964656e746963616c6c790a3b202845444943542d4142492d494e5452494e534943532d444f432d303031292e0a0a3b20696e7472696e736963732069732061204d4150206b6579656420627920636f6f7264696e6174652c20736f2074686520736368656d6120697473656c6620656e666f726365730a3b20636f6f7264696e61746520756e697175656e6573732e20412070726f766964657220726563656976657320746865207265736f6c76656420636f7270757320617320610a3b206469676573742d626f756e642073656d616e74696320696e70757420616e64207265736f6c76657320636f6f7264696e617465732077697468696e20746861742061727469666163742e0a3b2045616368206d6170206b6579204d55535420657175616c20697473207265636f726427732060636f6f7264696e61746560206669656c640a3b202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e696e7472696e736963732f7631222c0a2020696e7472696e736963733a207b202a2074737472203d3e20696e7472696e736963207d2c0a7d0a0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e6966657374277320606f7065726174696f6e50726f66696c657360207265736f757263652d7265662e0a3b206f7065726174696f6e2d70726f66696c65202f206f707469632d74656d706c6174652061726520646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c2e204b657965642062790a3b20636f6f7264696e61746520736f207265736f6c7574696f6e2063616e2774207069636b206265747765656e2074776f2073616d652d636f6f7264696e6174652070726f66696c65730a3b202845444943542d4142492d4f5050524f46494c452d534c4f542d3030312c2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e6f7065726174696f6e2d70726f66696c65732f7631222c0a202070726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c0a7d0a0a3b2041207479706564207072652d6c6f776572696e67207175657374696f6e20746861742063616e2062652070726f706f73656420627920576174736f6e206f7220616e206167656e7420616e640a3b20636865636b65642062792074686520636f6d70696c65722e2049742069732063616e6f6e6963616c2d43424f5220656e636f64656420756e6465720a3b206065646963742e6c6f776572696e672d726571756972656d656e74732f7631603b2074686520636f6d70696c657220636865636b7320746869732061727469666163742c206e6f74207468650a3b2070726f736520746861742070726f64756365642069742e0a6c6f776572696e672d726571756972656d656e7473203d207b0a202061706956657273696f6e3a202265646963742e6c6f776572696e672d726571756972656d656e74732f7631222c0a20206f7065726174696f6e50726f66696c653a20747374722c0a202073656d616e746963456666656374733a205b2a2073656d616e7469632d6566666563742d726571756972656d656e745d2c0a202072657175697265645772697465436c61737365733a205b2a2077726974652d636c6173735d2c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a202061746f6d69636974793a2061746f6d69636974792d726571756972656d656e742c0a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a20206f70746963436f6e74726163743a20747374722c0a7d0a0a73656d616e7469632d6566666563742d726571756972656d656e74203d207b0a2020636f6f7264696e6174653a20747374722c0a20207772697465436c6173733a2077726974652d636c6173732c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a7d0a0a77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a2020202020202020202020202020227265706c61636522202f202264656c65746522202f20747374720a67756172642d6b696e64203d2022707265636f6d6d69742d61746f6d696322202f20747374720a61746f6d69636974792d726571756972656d656e74203d202261746f6d696322202f20747374720a0a3b20412067656e75696e6520756e696f6e3a207075726520636f6e7374727563746f7273206361727279206e6f20656666656374206b696e64206f72206661696c757265733b206566666563740a3b20696e7472696e73696373206d757374202845444943542d5441524745542d494e5452494e5349432d434c4153532d303031292e2054686520736368656d6120656e666f7263657320746869732c0a3b206e6f74206120636f6d6d656e742e0a0a3b2054686520696e7472696e736963277320636f6f7264696e6174652069732074686520696e7472696e73696373206d6170204b45592c206e6f7420612076616c7565206669656c642c20736f207468650a3b206b657920616e6420636f6f7264696e6174652063616e206e65766572206469736167726565202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963203d20707572652d696e7472696e736963202f206566666563742d696e7472696e7369630a0a707572652d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a202270757265222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206775617264537570706f72743a2066616c73652c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20226e6f6e65222c0a7d0a0a6566666563742d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a2022656666656374222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206566666563744b696e643a206566666563742d6b696e642c0a20203b206d6170206b65796564206279206661696c75726520636f6f7264696e61746520286661696c7572652d6964656e74293b20746865206661696c75726520636f6f7264696e6174652069730a20203b20746865206b65792c206e6f7420612076616c7565206669656c642c20736f20756e697175656e657373206973207374727563747572616c0a20203b202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c0a20206775617264537570706f72743a20626f6f6c2c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f20227265706c61636522202f0a20202020202020202020202020202264656c65746522202f2022637573746f6d222c0a202063616e5061727469636970617465496e41746f6d696347756172643a20626f6f6c2c0a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d617574686f726974792d66616374732e6364646c202d2d2d0a3b2065646963742d617574686f726974792d66616374732e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f722074686520666972737420636f6d70696c65722d636f6e7465787420617574686f726974792d666163747320646f63756d656e742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20736f20736f757263652e6469676573742075736573207468650a3b20736861726564207368613235362d6469676573742074797065642076616c75652e204a534f4e2069732061207265766965772f696e7075742072656e646572696e673a206974730a3b20607368613235363a3c3634206865783e6020736f75726365206469676573742069732070726f6a656374656420746f205b60736861323536602c203332207261772062797465735d206f6e0a3b2074686520776972652c20616e64206974732066616374206172726179732070726f6a65637420746f2074686520636f6f7264696e6174652d6b65796564206d6170732062656c6f772e0a0a617574686f726974792d6661637473203d207b0a202061706956657273696f6e3a202265646963742e617574686f726974792d66616374732f7631222c0a2020736f757263653a20617574686f726974792d666163742d736f757263652c0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e20617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374207d2c0a20206566666563745772697465436c61737365733a207b202a2074737472203d3e20617574686f726974792d77726974652d636c617373207d2c0a2020627564676574733a207b202a2074737472203d3e20617574686f726974792d6275646765742d66616374207d2c0a7d0a0a617574686f726974792d666163742d736f75726365203d207b0a20206b696e643a20226c61777061636b22202f202274617267657450726f66696c65222c0a2020636f6f7264696e6174653a20747374722c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a3b20546865206d6170206b65792069732074686520736f75726365206f7065726174696f6e2d70726f66696c6520636f6f7264696e6174652e204974206973206e6f7420726570656174656420696e0a3b207468652076616c75652c20736f2061206b657920616e6420656d62656464656420636f6f7264696e6174652063616e6e6f742064697361677265652e20416c6c6f7765642077726974650a3b20636c61737365732061726520612063616e6f6e6963616c206d61702d7365743a2074686520636c6173732069732074686520756e69717565206b657920616e64206e756c6c206973207468650a3b20756e6974206d61726b65722e2043616e6f6e6963616c2043424f52206669786573206b6579206f7264657220776974686f75742061207365636f6e64206f72646572696e672072756c652e0a617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374203d207b0a2020636f72653a20747374722c0a2020616c6c6f7765645772697465436c61737365733a207b202a20617574686f726974792d77726974652d636c617373203d3e206e756c6c207d2c0a7d0a0a3b20546865206566666563745772697465436c6173736573206d6170206b6579206973207468652073656d616e7469632065666665637420636f6f7264696e6174652e2054686520627564676574730a3b206d6170206b65792069732074686520736f757263652062756467657420636f6f7264696e6174652e2043616e6f6e6963616c2043424f52206d61702d6b657920756e697175656e6573730a3b206d616b6573206475706c6963617465206661637420636f6f7264696e61746573207374727563747572616c6c7920756e726570726573656e7461626c652e0a617574686f726974792d6275646765742d66616374203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a3b20417574686f726974794661637473446f63756d656e7420763120696e74656e74696f6e616c6c792061636365707473206f6e6c792074686520777269746520636c6173736573207468650a3b2063757272656e7420636f6d70696c6572206d6f64656c2063616e20636f6e73756d652e2060637573746f6d602069732074686520736f6c6520763120637573746f6d207370656c6c696e673b0a3b20617262697472617279207461726765742d70726f66696c6520657874656e73696f6e20737472696e677320646f206e6f7420656e746572207468697320636f6d70696c657220706174682e0a617574686f726974792d77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a202020202020202020202020202020202020202020202020227265706c61636522202f202264656c65746522202f2022637573746f6d220a0a3b202d2d2d2065646963742d7461726765742d69722e6364646c202d2d2d0a3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d7461726765742d69722e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f72207468652045646963742d6f776e65642054617267657420495220617274696661637420656e76656c6f70652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20616e642065646963742d636f72652e6364646c2e2049740a3b2064656c696265726174656c792072657573657320436f72652065787072657373696f6e732c20707265646963617465732c20627564676574732c206c6f63616c207265666572656e6365732c0a3b206f62737472756374696f6e20726561736f6e732c20616e64206f62737472756374696f6e2061726d7320736f2074686520736368656d61206d617463686573207468652076616c75650a3b20656d6974746564206279207468652063616e6f6e6963616c2054617267657420495220656e636f64657220726174686572207468616e20726573746174696e672074686f73652074797065732e0a3b2049742064657363726962657320746865207374727563747572616c207368617065206f662076616c6964206c6f776572696e672d70726f6475636564206172746966616374732e205468650a3b206c6f776572696e6720616e6420656e636f64657220636f6e7472616374732073657061726174656c7920656e666f7263652073656d616e746963206964656e7469666965722072756c65730a3b20616e642063616e6f6e6963616c206f72646572696e672f64656475706c69636174696f6e20666f72207365742d6c696b652076616c7565732e0a0a3b2054617267657420495220656e636f64696e672072656a6563747320616e20656d707479207461726765742d70726f66696c6520636f6f7264696e617465206265666f72652062797465730a3b2065786973742c20736f207468697320726f6f74207469676874656e732074686520736861726564207374727563747572616c207265736f757263652d726566206163636f7264696e676c792e0a7461726765742d69722d7265736f757263652d726566203d207b0a202069643a2074737472202e7265676578702022283f73292e2b222c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a7461726765742d69722d6172746966616374203d207b0a20206b696e643a202274617267657449724172746966616374222c0a2020646f6d61696e3a20747374722c0a202074617267657450726f66696c653a207461726765742d69722d7265736f757263652d7265662c0a2020736f75726365436f7265436f6f7264696e6174653a20747374722c0a2020696e74656e74733a207b202a2074737472203d3e207461726765742d69722d696e74656e74207d2c0a7d0a0a7461726765742d69722d696e74656e74203d207b0a20206f7065726174696f6e50726f66696c653a20747374722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020726571756972656d656e74733a205b2a207461726765742d69722d726571756972656d656e745d2c0a202073746570733a205b2a207461726765742d69722d737465705d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a7461726765742d69722d726571756972656d656e74203d207b0a202069643a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a0a7461726765742d69722d73746570203d207b0a202069643a20747374722c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020746172676574496e7472696e7369633a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4661696c757265733a205b2a206661696c7572652d6964656e745d2c0a20206f62737472756374696f6e41726d733a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a", - "rawSha256": "92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74" + "bytesHex": "3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d70726f76696465722d636f6e7472616374732e6364646c0a3b2047656e6572617465642066726f6d2045646963742d6f776e65642041424920667261676d656e74732e20444f204e4f5420454449542e0a0a3b202d2d2d2065646963742d636f6d6d6f6e2e6364646c202d2d2d0a3b2065646963742d636f6d6d6f6e2e6364646c0a3b20536861726564204344444c20747970657320666f722074686520456469637420414249732c20646566696e6564204f4e4345206865726520736f20746865792063616e6e6f742064726966740a3b202845444943542d4142492d4e4f4455502d303031292e2054776f2067726f7570733a0a3b2020202d207265736f757263652d7265662c207368613235362d6469676573742c206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c0a3b2020202020636f72652d747970652d7265663a20617373656d626c656420776974682065646963742d7461726765742d70726f66696c652e6364646c20616e640a3b202020202065646963742d6c61777061636b2e6364646c20627920746865206275696c643b2074686f736520736368656d617320646f206e6f74207265646566696e65207468656d2e0a3b2020202d206f7065726174696f6e2d70726f66696c652c206f707469632d74656d706c6174652c2061706572747572652d726571756972656d656e7420616e6420746865697220726566733a0a3b2020202020636f6e73756d65642062792074686520436f72652f6f70746963206c61796572202865646963742d636f72652e6364646c2920616e64207265666572656e636564206279207468650a3b20202020206c616e67756167652f7461726765742d70726f66696c652073706563732e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a0a3b2041206e6f726d617469766520737562636f6d706f6e656e74207265666572656e636564206279206964656e7469747920706c7573206469676573742e204d616e696665737473206e657665720a3b20656d626564207468656972206f776e2073656c662d64696765737420696e20746865697220707265696d616765202845444943542d434f52452d53454c46484153482d303031292e0a7265736f757263652d726566203d207b2069643a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20446967657374732061726520617574686f726974617469766520617320747970656420627974652076616c7565732c206e657665722068657820737472696e67732e20526576696577204a534f4e0a3b2072656e64657273207468697320617320227368613235363a3c3634206c6f77657263617365206865783e22202845444943542d4449474553542d574952452d303031292e0a7368613235362d646967657374203d205b20616c676f726974686d3a2022736861323536222c2062797465733a2062737472202e73697a65203332205d0a0a3b2041206e616d6564206c6f772d6c6576656c206661696c75726520616e206566666563742063616e2072616973652e2054686520736f75726365206f62737472756374696f6e206d61700a3b2062696e64732069742028627920636f6f7264696e6174652920616e6420636f6e73747275637473206120747970656420646f6d61696e206f62737472756374696f6e2066726f6d206974730a3b207061796c6f6164202845444943542d4142492d4641494c5552452d4e414d45442d303031292e0a3b20416e20656666656374277320606566666563744661696c7572657360206c697374204d555354206861766520756e697175652060636f6f7264696e61746560733a2073696e6365207468650a3b206f62737472756374696f6e206d6170206973206b6579656420627920636f6f7264696e6174652c2074776f206661696c757265732073686172696e67206120636f6f7264696e61746520286576656e0a3b207769746820646966666572656e7420617574686f72697479436c6173732f7061796c6f61645479706529206d616b652065786861757374697665206d617070696e6720616e642062696e6465720a3b20747970696e6720616d626967756f757320616e64206172652072656a6563746564202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a3b0a3b2045666665637473206361727279207468656972206661696c757265732061732061206d617020607b206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d600a3b202873656520746865207461726765742f6c61777061636b2065666665637420736368656d6173292e20546865206661696c75726520636f6f7264696e61746520697320746865206d61700a3b204b45592c20736f206974206973206e6f7420726570656174656420696e2074686520626f647920616e642063616e6e6f74206469736167726565207769746820746865206b65792e0a6566666563742d6661696c7572652d626f6479203d207b0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164547970653a20636f72652d747970652d7265662c202020202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b2041206661696c75726520636f6f7264696e617465206d7573742062652061206261726520456469637420606964656e746020286c65747465722f756e64657273636f7265207468656e0a3b206c6574746572732f6469676974732f756e64657273636f7265732920414e44206d757374206e6f742062652061207265736572766564206b6579776f72642028652e672e2060656c7365602c0a3b20606261736973602c20607768657265602c206072657175697265602c2060666f72602c2060696660292e2054686520736f75726365206f62737472756374696f6e2d6d6170204c4853206f6e6c790a3b20616363657074732061206e6f6e2d6b6579776f726420606964656e74602c20736f20612068797068656e2f646f742f6b6579776f726420636f6f7264696e61746520776f756c642062650a3b204142492d76616c69642079657420696d706f737369626c6520746f206d617020657868617573746976656c7920696e20736f757263652e20546865207265676578206361707475726573207468650a3b206c65786963616c2073686170653b206b6579776f7264206578636c7573696f6e20697320616e206164646974696f6e616c2076616c69646174696f6e2072756c650a3b202845444943542d4142492d4641494c5552452d4944454e542d303031292e0a6661696c7572652d6964656e74203d2074737472202e72656765787020225b412d5a612d7a5f5d5b412d5a612d7a302d395f5d2a220a0a6566666563742d6b696e64203d20227265616422202f202263726561746522202f2022656e7375726522202f20227265706c61636522202f202264656c65746522202f0a202020202020202020202020202022617070656e6422202f202272656475636522202f202273656d616e7469632e656d697422202f2022637573746f6d220a0a617574686f726974792d636c617373203d2022646f6d61696e4d61707061626c6522202f20227061727469636970616e744f776e656422202f2022696e746567726974794661756c7422202f0a202020202020202020202020202020202020227265736f757263654661756c7422202f2022696e7465726e616c4661756c74220a0a636f72652d747970652d726566203d20747374722020203b2063616e6f6e6963616c20436f7265207479706520636f6f7264696e6174650a0a3b20416e206f7065726174696f6e2070726f66696c6520737570706c69657320746865206f707469632074656d706c617465206120436f726520616374696f6e207265736f6c766573206974730a3b206f707469634b696e642f626f756e646172794b696e642f737570706f7274506f6c6963792f6c6f7373446973706f736974696f6e2066726f6d2e205461726765742070726f66696c657320616e640a3b206c61777061636b73207075626c6973682074686573652061732061206d617020607b20636f6f7264696e617465203d3e206f7065726174696f6e2d70726f66696c65207d602c20736f207468650a3b20636f6f7264696e61746520697320746865204b45592c206e6f7420612076616c7565206669656c64202845444943542d4f505449432d54454d504c4154452d4f574e45522d3030312c0a3b2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65203d207b0a20206f7074696354656d706c6174653a206f707469632d74656d706c6174652c0a20206566666563745072656469636174653a20747374722c20202020202020202020203b20636f6f7264696e617465206f6620746865206f7065726174696f6e2d6d6f6465207072656469636174650a7d0a0a6f707469632d74656d706c617465203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a2020737570706f7274506f6c6963793a20747374722c202020202020202020202020203b2063616e6f6e6963616c20737570706f72742d706f6c69637920636f6f7264696e6174650a20206c6f7373446973706f736974696f6e3a20747374722c20202020202020202020203b2063616e6f6e6963616c206c6f73732d646973706f736974696f6e20636f6f7264696e6174650a20203f20626173697354656d706c6174653a20747374722c20202020202020202020203b206f7074696f6e616c206469676573742d6c6f636b65642062617369732074656d706c61746520636f6f72640a20203b2074686520617065727475726520726571756972656d656e7420746869732074656d706c61746520737570706c6965732e205265717569726564207768656e207468652074656d706c6174650a20203b2069732074686520736f75726365206f66206120436f7265206f707469632773206170657274757265526571756972656d656e742028692e652e2074686520616374696f6e20686173206e6f0a20203b20736f757263652060666f6f747072696e74203c3d202e2e2e60292c2073696e6365206170657274757265526571756972656d656e74206973206d616e6461746f727920696e20436f72650a20203b202845444943542d4f505449432d41504552545552452d5245462d303031292e0a20203f206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a7d0a0a3b206170657274757265526571756972656d656e742069732061207479706564207265666572656e63652c206e65766572206120667265652d666f726d20737472696e672e2041207265766965770a3b2072656e646572696e67206d61792073686f772069747320636f6f7264696e617465202845444943542d4f505449432d41504552545552452d5245462d303031292e0a61706572747572652d726571756972656d656e74203d20666f6f747072696e742d6365696c696e672d726566202f2061627374726163742d666f6f747072696e742d6f626c69676174696f6e2d7265660a666f6f747072696e742d6365696c696e672d726566203d207b206b696e643a2022666f6f747072696e744365696c696e67222c207265663a2074737472207d0a61627374726163742d666f6f747072696e742d6f626c69676174696f6e2d726566203d207b206b696e643a20226162737472616374466f6f747072696e744f626c69676174696f6e222c207265663a2074737472207d0a0a3b202d2d2d2065646963742d636f72652e6364646c202d2d2d0a3b2065646963742d636f72652e6364646c0a3b204e6f726d617469766520736368656d6120666f722074686520456469637420436f72652076312073656d616e746963206d6f64656c2e0a3b0a3b2053636f706520626f756e646172793a20746869732066696c6520646566696e657320436f7265206d65616e696e6720616e6420736368656d61207368617065206f6e6c792e20497420646f65730a3b206e6f7420646566696e6520612063616e6f6e6963616c20656e636f6465722c20436f7265206d6f64756c652068617368206669656c64732c20686173682066697874757265732c207461726765740a3b206c6f776572696e672c2061646d697373696f6e2062756e646c65732c206f72207461726765742d6f776e65642049522e0a0a636f72652d6d6f64756c65203d207b0a202061706956657273696f6e3a202265646963742e636f72652f7631222c0a2020636f6f7264696e6174653a20747374722c0a2020696d706f7274733a205b2a20636f72652d696d706f72745d2c0a202074797065733a207b202a2074737472203d3e20636f72652d74797065207d2c0a2020616374696f6e733a207b202b2074737472203d3e20636f72652d616374696f6e207d2c0a20207265717569726564436f72654361706162696c69746965733a205b2a20747374725d2c0a7d0a0a636f72652d696d706f7274203d207b0a20206b696e643a20226c61777061636b22202f202274617267657422202f2022636f7265222c0a20207265663a207265736f757263652d7265662c0a20203f20616c6961733a20747374722c0a7d0a0a3b202d2d2d207479706573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d74797065203d20636f72652d7363616c61722d74797065202f20636f72652d7265636f72642d74797065202f20636f72652d76617269616e742d74797065202f0a202020202020202020202020636f72652d6f7074696f6e2d74797065202f20636f72652d6c6973742d74797065202f20636f72652d6d61702d74797065202f0a202020202020202020202020636f72652d6361706162696c6974792d7265662d747970650a0a636f72652d7363616c61722d74797065203d20636f72652d626f6f6c2d74797065202f20636f72652d696e742d74797065202f20636f72652d737472696e672d74797065202f0a20202020202020202020202020202020202020636f72652d62797465732d74797065202f20636f72652d756e69742d747970650a0a636f72652d626f6f6c2d74797065203d207b206b696e643a2022426f6f6c22207d0a636f72652d756e69742d74797065203d207b206b696e643a2022556e697422207d0a636f72652d696e742d74797065203d207b0a20206b696e643a202249363422202f202255363422202f202249333222202f202255333222202f202249313622202f202255313622202f2022493822202f20225538222c0a7d0a636f72652d737472696e672d74797065203d207b0a20206b696e643a2022537472696e67222c0a20206d61783a2075696e742c0a202063616e6f6e6963616c3a2022756e69636f64652d7363616c61722d6e666322202f20227261772d75746638222c0a7d0a636f72652d62797465732d74797065203d207b0a20206b696e643a20224279746573222c0a20206d61783a2075696e742c0a7d0a636f72652d7265636f72642d74797065203d207b0a20206b696e643a20225265636f7264222c0a20206669656c64733a207b202a2074737472203d3e20636f72652d747970652d726566207d2c0a7d0a636f72652d76617269616e742d74797065203d207b0a20206b696e643a202256617269616e74222c0a202063617365733a207b202b2074737472203d3e2076617269616e742d636173652d626f6479207d2c0a7d0a76617269616e742d636173652d626f6479203d207b0a20203f207061796c6f61643a20636f72652d747970652d7265662c0a7d0a636f72652d6f7074696f6e2d74797065203d207b0a20206b696e643a20224f7074696f6e222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a636f72652d6c6973742d74797065203d207b0a20206b696e643a20224c697374222c0a20206974656d3a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6d61702d74797065203d207b0a20206b696e643a20224d6170222c0a20206b65793a20636f72652d747970652d7265662c0a202076616c75653a20636f72652d747970652d7265662c0a20206d61783a2075696e742c0a7d0a636f72652d6361706162696c6974792d7265662d74797065203d207b0a20206b696e643a20224361706162696c697479526566222c0a20206974656d3a20636f72652d747970652d7265662c0a7d0a0a3b20636f72652d747970652d72656620697320646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d612e0a0a3b202d2d2d207265666572656e63657320616e642076616c756573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a3b204c6f63616c206964656e7469747920697320616c7068612d737461626c652e20606964602069732074686520636f6d70696c65722d6f776e6564206c6f63616c20636f6f7264696e6174653b0a3b2060616c7068614e616d656020697320746865206e6f726d616c697a65642068756d616e2f6465627567206e616d652e20536f757263652062696e646572207370656c6c696e67206973206e6f740a3b206964656e746974792e0a6c6f63616c2d726566203d207b0a202069643a20747374722c0a2020616c7068614e616d653a20747374722c0a2020747970653a20636f72652d747970652d7265662c0a7d0a0a636f72652d76616c7565203d20636f72652d6e756c6c2d76616c7565202f20636f72652d626f6f6c2d76616c7565202f20636f72652d696e742d76616c7565202f0a20202020202020202020202020636f72652d737472696e672d76616c7565202f20636f72652d62797465732d76616c7565202f20636f72652d7265636f72642d76616c7565202f0a20202020202020202020202020636f72652d76617269616e742d76616c7565202f20636f72652d6c6973742d76616c7565202f20636f72652d6d61702d76616c7565202f0a20202020202020202020202020636f72652d6361706162696c6974792d76616c75650a0a636f72652d6e756c6c2d76616c7565203d207b206b696e643a20226e756c6c22207d0a636f72652d626f6f6c2d76616c7565203d207b206b696e643a2022626f6f6c222c2076616c75653a20626f6f6c207d0a636f72652d696e742d76616c7565203d207b206b696e643a2022696e74222c2077696474683a20747374722c2076616c75653a20696e74207d0a636f72652d737472696e672d76616c7565203d207b206b696e643a2022737472696e67222c2076616c75653a2074737472207d0a636f72652d62797465732d76616c7565203d207b206b696e643a20226279746573222c2076616c75653a2062737472207d0a636f72652d7265636f72642d76616c7565203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d76616c7565207d207d0a636f72652d76617269616e742d76616c7565203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d76616c75652c0a7d0a636f72652d6c6973742d76616c7565203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d76616c75655d207d0a636f72652d6d61702d76616c7565203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d76616c75652c2076616c75653a20636f72652d76616c75655d5d207d0a636f72652d6361706162696c6974792d76616c7565203d207b0a20206b696e643a20226361706162696c697479222c0a2020726563656970743a207368613235362d6469676573742c0a7d0a0a3b2045646963742d617574686f72656420707572652068656c70657273207573652061207075726520436f72652066756e6374696f6e20626f64792e2054686520626f64792063616e2062696e640a3b20707572652065787072657373696f6e7320616e642072657475726e20616e2065787072657373696f6e2c206275742069742063616e6e6f7420636f6e7461696e20436f7265206566666563742c0a3b2067756172642c206272616e63682c206c6f6f702c206d617463682d6e6f64652c206f722070726f6f662d6f626c69676174696f6e206e6f6465732e0a636f72652d666e2d626f6479203d207b0a2020706172616d733a205b2a206c6f63616c2d7265665d2c0a2020626f64793a20636f72652d707572652d626c6f636b2c0a7d0a0a636f72652d707572652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a202062696e64696e67733a205b2a20707572652d6c65742d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a707572652d6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a0a3b202d2d2d2065787072657373696f6e7320616e642070726564696361746573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d65787072203d206c6f63616c2d65787072202f20636f6e73742d65787072202f207265636f72642d65787072202f206669656c642d65787072202f0a20202020202020202020202076617269616e742d65787072202f206d617463682d65787072202f2063616c6c2d65787072202f206c6973742d65787072202f206d61702d65787072202f0a20202020202020202020202069662d657870720a0a6c6f63616c2d65787072203d207b206b696e643a20226c6f63616c222c207265663a206c6f63616c2d726566207d0a636f6e73742d65787072203d207b206b696e643a2022636f6e7374222c2076616c75653a20636f72652d76616c7565207d0a7265636f72642d65787072203d207b206b696e643a20227265636f7264222c206669656c64733a207b202a2074737472203d3e20636f72652d65787072207d207d0a6669656c642d65787072203d207b206b696e643a20226669656c64222c20626173653a20636f72652d657870722c206669656c643a2074737472207d0a76617269616e742d65787072203d207b0a20206b696e643a202276617269616e74222c0a2020747970653a20636f72652d747970652d7265662c0a2020636173653a20747374722c0a20203f207061796c6f61643a20636f72652d657870722c0a7d0a6d617463682d65787072203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d61726d5d2c0a7d0a6d617463682d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d657870722c0a7d0a63616c6c2d65787072203d207b0a20206b696e643a202263616c6c222c0a202063616c6c65653a20747374722c0a202074797065417267733a205b2a20636f72652d747970652d7265665d2c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6c6973742d65787072203d207b206b696e643a20226c697374222c2076616c7565733a205b2a20636f72652d657870725d207d0a6d61702d65787072203d207b206b696e643a20226d6170222c20656e74726965733a205b2a205b6b65793a20636f72652d657870722c2076616c75653a20636f72652d657870725d5d207d0a69662d65787072203d207b0a20206b696e643a20226966222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d657870722c0a2020656c73653a20636f72652d657870722c0a7d0a0a636f72652d707265646963617465203d20747275652d707265646963617465202f2066616c73652d707265646963617465202f206e6f742d707265646963617465202f0a2020202020202020202020202020202020616c6c2d707265646963617465202f20616e792d707265646963617465202f20636f6d706172652d707265646963617465202f0a202020202020202020202020202020202063616c6c2d707265646963617465202f206f62737472756374696f6e2d7072656469636174650a0a747275652d707265646963617465203d207b206b696e643a20227472756522207d0a66616c73652d707265646963617465203d207b206b696e643a202266616c736522207d0a6e6f742d707265646963617465203d207b206b696e643a20226e6f74222c2076616c75653a20636f72652d707265646963617465207d0a616c6c2d707265646963617465203d207b206b696e643a2022616c6c222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a616e792d707265646963617465203d207b206b696e643a2022616e79222c2076616c7565733a205b2b20636f72652d7072656469636174655d207d0a636f6d706172652d707265646963617465203d207b0a20206b696e643a2022636f6d70617265222c0a20206f703a20223d3d22202f2022213d22202f20223c22202f20223c3d22202f20223e22202f20223e3d222c0a20206c6566743a20636f72652d657870722c0a202072696768743a20636f72652d657870722c0a7d0a63616c6c2d707265646963617465203d207b0a20206b696e643a202263616c6c222c0a20207072656469636174653a20747374722c0a2020617267733a205b2a20636f72652d657870725d2c0a7d0a6f62737472756374696f6e2d707265646963617465203d207b0a20206b696e643a20226f62737472756374696f6e222c0a2020636f6f7264696e6174653a206661696c7572652d6964656e742c0a20207061796c6f61643a20636f72652d657870722c0a7d0a0a696e7075742d636f6e73747261696e74203d207b0a2020636f6f7264696e6174653a20747374722c0a2020736f757263653a2022776865726522202f2022636f6d70696c6572222c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a3b202d2d2d20616374696f6e732c20626c6f636b732c20616e64206e6f646573202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a636f72652d616374696f6e203d207b0a2020696e7075743a20636f72652d747970652d7265662c0a20206f75747075743a20636f72652d747970652d7265662c0a202072657175697265644f7065726174696f6e50726f66696c653a20747374722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020626f64793a20636f72652d626c6f636b2c0a20203f206f707469633a20636f72652d6f707469632c0a7d0a0a636f72652d627564676574203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a636f72652d6f70746963203d207b0a20206f707469634b696e643a2022726576656c6174696f6e22202f20226166666563745265696e746567726174696f6e222c0a2020626f756e646172794b696e643a202270726f6a656374696f6e22202f2022616666656374222c0a20206170657274757265526571756972656d656e743a2061706572747572652d726571756972656d656e742c0a2020737570706f7274506f6c6963793a20747374722c0a20206c6f7373446973706f736974696f6e3a20747374722c0a7d0a0a636f72652d626c6f636b203d207b0a20206c6f63616c733a205b2a206c6f63616c2d7265665d2c0a20206e6f6465733a205b2a20636f72652d6e6f64655d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a636f72652d6e6f6465203d206c65742d6e6f6465202f20726571756972652d6e6f6465202f206566666563742d6e6f6465202f2067756172642d6e6f6465202f206272616e63682d6e6f6465202f0a202020202020202020202020666f722d6e6f6465202f206d617463682d6e6f6465202f2070726f6f662d6f626c69676174696f6e2d6e6f64650a0a6c65742d6e6f6465203d207b0a20206b696e643a20226c6574222c0a202062696e64696e673a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a726571756972652d6e6f6465203d207b0a20206b696e643a202272657175697265222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a726571756972652d6661696c7572652d61726d203d207465726d696e616c2d726571756972652d6661696c757265202f0a20202020202020202020202020202020202020202020636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c7572650a7465726d696e616c2d726571756972652d6661696c757265203d207b0a20206b696e643a20227465726d696e616c222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a636f6e74696e75652d6f6273747275637465642d726571756972652d6661696c757265203d207b0a20206b696e643a2022636f6e74696e75654f627374727563746564222c0a2020726561736f6e3a206f62737472756374696f6e2d726561736f6e2c0a7d0a6f62737472756374696f6e2d726561736f6e203d207b0a2020726561736f6e4b696e643a20747374722c0a20207061796c6f61643a207b202a2074737472203d3e20636f72652d65787072207d2c0a7d0a6566666563742d6e6f6465203d207b0a20206b696e643a2022656666656374222c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4d61703a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a6f62737472756374696f6e2d61726d203d207b0a202062696e6465723a206c6f63616c2d7265662c0a202076616c75653a20636f72652d657870722c0a7d0a67756172642d6e6f6465203d207b0a20206b696e643a20226775617264222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f62737472756374696f6e3a20636f72652d657870722c0a7d0a6272616e63682d6e6f6465203d207b0a20206b696e643a20226272616e6368222c0a20207072656469636174653a20636f72652d7072656469636174652c0a20207468656e3a20636f72652d626c6f636b2c0a2020656c73653a20636f72652d626c6f636b2c0a7d0a666f722d6e6f6465203d207b0a20206b696e643a2022666f72222c0a202062696e6465723a206c6f63616c2d7265662c0a2020697465723a20636f72652d657870722c0a2020626f756e643a20636f72652d626f756e642c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a6d617463682d6e6f6465203d207b0a20206b696e643a20226d61746368222c0a20207363727574696e65653a20636f72652d657870722c0a202061726d733a205b2b206d617463682d626c6f636b2d61726d5d2c0a7d0a6d617463682d626c6f636b2d61726d203d207b0a2020636173653a20747374722c0a20203f2062696e6465723a206c6f63616c2d7265662c0a2020626f64793a20636f72652d626c6f636b2c0a7d0a70726f6f662d6f626c69676174696f6e2d6e6f6465203d207b0a20206b696e643a202270726f6f66222c0a2020636f6f7264696e6174653a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a7d0a0a636f72652d626f756e64203d206c69746572616c2d626f756e64202f20636f6f7264696e6174652d626f756e640a6c69746572616c2d626f756e64203d207b206b696e643a20226c69746572616c222c2076616c75653a2075696e74207d0a636f6f7264696e6174652d626f756e64203d207b206b696e643a2022636f6f7264696e617465222c207265663a2074737472207d0a0a3b20536861726564207265736f757263652d7265662c207368613235362d6469676573742c206661696c7572652d6964656e742c2061706572747572652d726571756972656d656e742c20616e640a3b20636f72652d747970652d7265662061726520646566696e6564206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c2e0a0a3b202d2d2d2065646963742d6c61777061636b2e6364646c202d2d2d0a3b2065646963742d6c61777061636b2e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374206c61777061636b206d616e696665737420616e64206578706f727420737572666163652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e204a534f4e20696e207468652070726f73652073706563730a3b2069732061207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a6c61777061636b2d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e6c61777061636b2f7631222c0a202069643a20747374722c0a202076657273696f6e3a20747374722c0a20206163636570746564436f72654162693a205b2b20747374725d2c0a2020646570656e64656e636965733a205b2a206c61777061636b2d6465705d2c202020202020202020203b20616379636c69632c206469676573742d6c6f636b6564202845444943542d4c41575041434b2d4441472d303031290a20206578706f7274733a207265736f757263652d7265662c0a20203f2074617267657441646170746572733a205b2b207461726765742d616461707465725d2c2020203b207265717569726564206f6e6c7920696620616e792072756e74696d6520656666656374206578697374730a20203f2068656c706572436f6d706f6e656e743a2065786563757461626c652d636f6d706f6e656e742c203b2065786563757461626c652068656c70657273206361727279207468656972206f776e2073616e64626f782b6675656c0a202076657269666965723a2076657269666965722c202020202020202020202020202020202020202020203b20636c61737369666965643a206465636c61726174697665206f722065786563757461626c650a2020636f6d7061746962696c6974793a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b2041207665726966696572206973206569746865722061206465636c617261746976652072756c6573657420286e6f2072756e74696d6529206f7220616e2065786563757461626c650a3b20636f6d706f6e656e742e20416e2065786563757461626c65207665726966696572204d55535420636172727920697473206f776e2073616e64626f7820616e64206675656c206d6f64656c2c0a3b20736f2074686520736368656d6120656e666f726365732074686174206e6f2065786563757461626c6520636f6d706f6e656e74206973206c65667420756e626f756e6465640a3b202845444943542d4142492d56455249464945522d424f554e442d303031292e0a7665726966696572203d206465636c617261746976652d7665726966696572202f2065786563757461626c652d76657269666965720a6465636c617261746976652d7665726966696572203d207b20636c6173733a20226465636c61726174697665222c2072756c657365743a207265736f757263652d726566207d0a65786563757461626c652d7665726966696572203d207b0a2020636c6173733a202265786563757461626c65222c0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a3b20416e792065786563757461626c6520636f6d706f6e656e7420697320626f756e64656420627920697473206f776e2073616e64626f78202b206675656c206d6f64656c2e0a65786563757461626c652d636f6d706f6e656e74203d207b0a2020636f6d706f6e656e743a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a7d0a0a6c61777061636b2d646570203d207b2069643a20747374722c2076657273696f6e3a20747374722c206469676573743a207368613235362d646967657374207d0a0a3b20416461707465722073656c656374696f6e206b65797320534f4c454c59206f666620746865206469676573742d6c6f636b65642060616363657074656454617267657450726f66696c65600a3b20286974732060696460206973207468652070726f66696c652069643b206974732060646967657374602070696e73207468652065786163742070726f66696c652f76657273696f6e292e2054686572650a3b20617265206e6f20696e646570656e64656e7420646973706c617920737472696e6773207468617420636f756c64206469736167726565207769746820746865206c6f636b2c20736f20610a3b207265736f6c7665722063616e6e6f742062696e6420616e206164617074657220746f206f6e6520746172676574207768696c6520746865206c6f636b2070726f76657320616e6f746865720a3b202845444943542d4c41575041434b2d414441505445522d54415247455449522d303031292e0a7461726765742d61646170746572203d207b0a2020616363657074656454617267657450726f66696c653a207265736f757263652d7265662c202020203b206469676573742d6c6f636b65642c20617574686f72697461746976652073656c6563746f720a2020616363657074656454617267657449723a207265736f757263652d7265662c2020202020202020203b206469676573742d6c6f636b65640a2020616461707465723a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d206578706f72742073757266616365202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a6c61777061636b2d6578706f727473203d207b0a202074797065733a205b2a206578706f727465642d747970655d2c0a2020636f6e7374616e74733a205b2a206578706f727465642d636f6e7374616e745d2c0a20207075726546756e6374696f6e733a205b2a20707572652d66756e6374696f6e5d2c0a2020656666656374733a205b2a2073656d616e7469632d6566666563745d2c0a20206f62737472756374696f6e733a205b2a206f62737472756374696f6e2d6465665d2c0a20203b206b65796564206279206f7065726174696f6e2d70726f66696c6520636f6f7264696e61746520e2869220756e697175656e65737320656e666f726365640a20203b202845444943542d4142492d4f5050524f46494c452d554e495155452d303031290a20203b206f7065726174696f6e2d70726f66696c65207265636f7264732074686973206c61777061636b206578706f72747320286f707469632074656d706c6174657320746861740a20203b2060696d706c656d656e7473602f6070726f66696c656020636c6175736573207265736f6c766520616761696e7374292e206f7065726174696f6e2d70726f66696c652069730a20203b20646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c20203b206b6579656420627920636f6f7264696e6174650a7d0a0a6578706f727465642d7479706520202020203d207b20636f6f7264696e6174653a20747374722c20646566696e6974696f6e3a20636f72652d747970652d726566207d0a6578706f727465642d636f6e7374616e74203d207b20636f6f7264696e6174653a20747374722c20747970653a20636f72652d747970652d7265662c2076616c75653a20616e79207d0a0a3b204120707572652068656c7065722069732061206469736372696d696e6174656420756e696f6e2062792060736f75726365602c20736f2074686520736368656d6120697473656c660a3b2067756172616e7465657320616e20696d706c656d656e746174696f6e20657869737473202845444943542d4c41575041434b2d505552452d494d504c2d303031293a0a3b2020202d20226564696374223a20617574686f72656420696e2045646963742f436f72653b2074686520436f726520626f6479206973206361727269656420696e6c696e6520286861736865640a3b20202020207769746820746865206578706f72742073757266616365292e2054686520736368656d61207265717569726573207468652060626f647960206669656c642e0a3b2020202d2022636f6d706f6e656e74223a20696d706c656d656e746564206f7574736964652045646963743b2063617272696573206e6f20696e6c696e6520626f647920616e6420696e73746561640a3b20202020206361727269657320697473206f776e206469676573742d6c6f636b65642060696d706c656d656e746174696f6e60202873616e64626f78202b206675656c292e20497420646f65730a3b20202020206e6f7420646570656e64206f6e20746865206f7074696f6e616c206d616e69666573742d6c6576656c2068656c706572436f6d706f6e656e742e0a707572652d66756e6374696f6e203d2065646963742d707572652d66756e6374696f6e202f20636f6d706f6e656e742d707572652d66756e6374696f6e0a0a707572652d66756e6374696f6e2d636f6d6d6f6e203d20280a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020706172616d6574657254797065733a205b2a20636f72652d747970652d7265665d2c2020202020203b20616c6c20626f756e6465640a202072657475726e547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020636f737454656d706c6174653a20747374722c0a202064657465726d696e69736d436c6173733a2022746f74616c22202f2022746f74616c2d776974682d74797065642d646961676e6f73746963222c0a290a0a65646963742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a20226564696374222c0a2020626f64793a20636f72652d666e2d626f64792c2020202020202020202020202020202020202020203b20696e6c696e652c20686173682d7369676e69666963616e740a7d0a0a636f6d706f6e656e742d707572652d66756e6374696f6e203d207b0a2020707572652d66756e6374696f6e2d636f6d6d6f6e2c0a2020736f757263653a2022636f6d706f6e656e74222c0a20203b20746865206469676573742d6c6f636b656420636f6d706f6e656e7420696d706c656d656e74696e6720746869732068656c7065722e2052657175697265642061742074686520736368656d610a20203b206c6576656c20736f206120636f6d706f6e656e742068656c7065722063616e206e657665722076616c696461746520776974686f7574206120686173682d626f756e642c0a20203b2073616e64626f782b6675656c2d64657363726962656420696d706c656d656e746174696f6e202845444943542d4c41575041434b2d505552452d494d504c2d303031292e0a2020696d706c656d656e746174696f6e3a2065786563757461626c652d636f6d706f6e656e742c0a7d0a0a3b20636f72652d666e2d626f647920697320646566696e65642062792065646963742d636f72652e6364646c20616e6420617373656d626c656420776974682074686973206c61777061636b0a3b20736368656d612e2049742069732061207075726520436f72652066756e6374696f6e20626f64792c206e6f7420616e206566666563742d63617061626c6520636f72652d626c6f636b2e0a0a73656d616e7469632d656666656374203d207b0a2020636f6f7264696e6174653a20747374722c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020696e707574547970653a20636f72652d747970652d7265662c2020202020202020202020202020203b20626f756e6465640a20206f7574707574547970653a20636f72652d747970652d7265662c20202020202020202020202020203b20626f756e6465640a2020657865637574696f6e436c6173733a202270726f6f664f6e6c7922202f202272756e74696d65222c2020203b206f7274686f676f6e616c20746f207772697465436c6173730a20206566666563744b696e6448696e743a206566666563742d6b696e642c0a2020666f6f747072696e744f626c69676174696f6e3a20747374722c0a2020636f73744f626c69676174696f6e3a20747374722c0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c20203b206b6579656420627920636f6f7264696e6174653b20756e697175650a20206775617264537570706f72743a20626f6f6c2c0a7d0a0a6f62737472756374696f6e2d646566203d207b0a2020636f6f7264696e6174653a20747374722c0a2020617574686f72697479436c6173733a20617574686f726974792d636c6173732c0a20207061796c6f6164536368656d613a20636f72652d747970652d7265662c20202020202020202020203b2074797065642c20626f756e64656420286d617920626520656d707479207265636f7264290a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d7461726765742d70726f66696c652e6364646c202d2d2d0a3b2065646963742d7461726765742d70726f66696c652e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f7220746865204564696374207461726765742070726f66696c65206d616e69666573742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76310a3b202873656520535045435f636f6e74696e75756d2d636f6e74726163742d62756e646c652d76312e6d64292e204a534f4e20696e207468652070726f736520737065637320697320610a3b207265766965772072656e646572696e672067656e6572617465642066726f6d207468697320736368656d613b2074686973204344444c206973207468652073696e676c6520736f757263650a3b206f66207472757468202845444943542d4142492d4e4f4455502d303031292e0a0a7461726765742d70726f66696c652d6d616e6966657374203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652f7631222c0a202069643a20747374722c202020202020202020202020202020202020202020202020203b20652e672e20226563686f2e64706f220a202076657273696f6e3a20747374722c20202020202020202020202020202020202020203b20652e672e202231220a20206163636570746564436f72654162693a205b2b20747374725d2c20202020202020203b20652e672e205b2265646963742e636f72652f7631225d0a0a2020696e7472696e736963733a207265736f757263652d7265662c0a2020696e7472696e7369634e616d6573706163653a20747374722c0a20203b207075626c697368657320746869732070726f66696c652773206f7065726174696f6e2d70726f66696c65207265636f72647320286f707469632074656d706c6174657320746861740a20203b206070726f66696c65602f60696d706c656d656e74736020636c6175736573207265736f6c766520616761696e7374292e205265666572656e63657320616e0a20203b206f7065726174696f6e2d70726f66696c65732d646f63756d656e74202845444943542d4142492d4f5050524f46494c452d534c4f542d303031292e0a20206f7065726174696f6e50726f66696c65733a207265736f757263652d7265662c0a2020666f6f747072696e74416c67656272613a207265736f757263652d7265662c0a2020636f7374416c67656272613a207265736f757263652d7265662c0a202074617267657449723a207265736f757263652d7265662c0a20206f62737472756374696f6e5461786f6e6f6d793a207265736f757263652d7265662c0a202076657269666965723a207265736f757263652d7265662c0a20206c6f77657265723a207265736f757263652d7265662c0a202073616e64626f783a207265736f757263652d7265662c0a20206675656c4d6f64656c3a207265736f757263652d7265662c0a0a20203b206669656c647320746865206c616e67756167652073706563207265717569726573206f662065766572792070726f66696c650a202062756e646c6550726f66696c653a207265736f757263652d7265662c0a202067656e657261746564417274696661637450726f66696c65733a205b2a207265736f757263652d7265665d2c0a202063616e6f6e6963616c456e636f64696e6752756c65733a207265736f757263652d7265662c0a20203b2052657365727665642f64656665727265643a2077696c6c206c697374206163636570746564206c61777061636b2d616461707465722041424920696473206f6e636520746861740a20203b20627974652d6c6576656c20414249202865646963742e6c61777061636b2d616461707465722f763129206973207370656369666965642e2054686520736368656d6120656e666f726365730a20203b20656d7074696e657373206e6f7720e280942074686520747970652069732074686520656d7074792061727261792c20736f206e6f6e2d656d7074792076616c756573206172650a20203b2072656a65637465642c206e6f74206d6572656c7920646973636f757261676564202845444943542d4142492d4c41575041434b2d414441505445522d44454645522d303031292e0a20203f2061636365707465644c61777061636b416461707465724162693a205b5d2c0a2020646961676e6f737469634162693a207265736f757263652d7265662c0a0a20203b206170706c69636174696f6e20646f637472696e650a20206170706c69636174696f6e4d6f64656c3a202261746f6d6963222c0a202072656164436f6e73697374656e63793a20226170706c69636174696f6e2d736e617073686f7422202f20747374722c0a202067756172644576616c756174696f6e3a2022707265636f6d6d69742d61746f6d696322202f20747374722c0a20206f62737472756374696f6e526f6c6c6261636b3a20226e6f2d76697369626c652d6566666563747322202f20747374722c0a20206d756c74695461726765743a20626f6f6c2c0a20203b207768657468657220746865207461726765742063616e206576616c7561746520707265636f6d6d697420706f7374636f6e646974696f6e20286067756172616e746565602920636865636b730a20203b20696e73696465207468652061746f6d6963206170706c69636174696f6e20756e6974202845444943542d5441524745542d504f5354434f4e442d303031290a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a0a202064657465726d696e6973746963457865637574696f6e3a207265736f757263652d7265662c0a2020636f6e666f726d616e636546697874757265436f727075733a207265736f757263652d7265662c0a7d0a0a3b20536861726564207479706573207265736f757263652d72656620616e64207368613235362d6469676573742061726520646566696e6564206f6e636520696e0a3b2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d6120627920746865206275696c640a3b202845444943542d4142492d4e4f4455502d303031292e205468657920617265206e6f74207265646566696e656420686572652e0a0a3b202d2d2d20696e7472696e736963207369676e6174757265202d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e696665737427732060696e7472696e7369637360207265736f757263652d726566206973207468650a3b20696e7472696e7369632d7369676e617475726520636f7270757320646f63756d656e742062656c6f772e20497473206c61796f757420697320666978656420736f2074776f0a3b20696e646570656e64656e742070726f66696c65732076616c69646174652f686173682074686520636f72707573206964656e746963616c6c790a3b202845444943542d4142492d494e5452494e534943532d444f432d303031292e0a0a3b20696e7472696e736963732069732061204d4150206b6579656420627920636f6f7264696e6174652c20736f2074686520736368656d6120697473656c6620656e666f726365730a3b20636f6f7264696e61746520756e697175656e6573732e20412070726f766964657220726563656976657320746865207265736f6c76656420636f7270757320617320610a3b206469676573742d626f756e642073656d616e74696320696e70757420616e64207265736f6c76657320636f6f7264696e617465732077697468696e20746861742061727469666163742e0a3b2045616368206d6170206b6579204d55535420657175616c20697473207265636f726427732060636f6f7264696e61746560206669656c640a3b202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e696e7472696e736963732f7631222c0a2020696e7472696e736963733a207b202a2074737472203d3e20696e7472696e736963207d2c0a7d0a0a3b20546865206172746966616374207265666572656e63656420627920746865206d616e6966657374277320606f7065726174696f6e50726f66696c657360207265736f757263652d7265662e0a3b206f7065726174696f6e2d70726f66696c65202f206f707469632d74656d706c6174652061726520646566696e656420696e2065646963742d636f6d6d6f6e2e6364646c2e204b657965642062790a3b20636f6f7264696e61746520736f207265736f6c7574696f6e2063616e2774207069636b206265747765656e2074776f2073616d652d636f6f7264696e6174652070726f66696c65730a3b202845444943542d4142492d4f5050524f46494c452d534c4f542d3030312c2045444943542d4142492d4f5050524f46494c452d554e495155452d303031292e0a6f7065726174696f6e2d70726f66696c65732d646f63756d656e74203d207b0a202061706956657273696f6e3a202265646963742e7461726765742d70726f66696c652e6f7065726174696f6e2d70726f66696c65732f7631222c0a202070726f66696c65733a207b202a2074737472203d3e206f7065726174696f6e2d70726f66696c65207d2c0a7d0a0a3b2041207479706564207072652d6c6f776572696e67207175657374696f6e20746861742063616e2062652070726f706f73656420627920576174736f6e206f7220616e206167656e7420616e640a3b20636865636b65642062792074686520636f6d70696c65722e2049742069732063616e6f6e6963616c2d43424f5220656e636f64656420756e6465720a3b206065646963742e6c6f776572696e672d726571756972656d656e74732f7631603b2074686520636f6d70696c657220636865636b7320746869732061727469666163742c206e6f74207468650a3b2070726f736520746861742070726f64756365642069742e0a6c6f776572696e672d726571756972656d656e7473203d207b0a202061706956657273696f6e3a202265646963742e6c6f776572696e672d726571756972656d656e74732f7631222c0a20206f7065726174696f6e50726f66696c653a20747374722c0a202073656d616e746963456666656374733a205b2a2073656d616e7469632d6566666563742d726571756972656d656e745d2c0a202072657175697265645772697465436c61737365733a205b2a2077726974652d636c6173735d2c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a202061746f6d69636974793a2061746f6d69636974792d726571756972656d656e742c0a2020706f7374636f6e646974696f6e537570706f72743a20626f6f6c2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a20206f70746963436f6e74726163743a20747374722c0a7d0a0a73656d616e7469632d6566666563742d726571756972656d656e74203d207b0a2020636f6f7264696e6174653a20747374722c0a20207772697465436c6173733a2077726974652d636c6173732c0a202067756172644b696e64733a205b2a2067756172642d6b696e645d2c0a20206f62737472756374696f6e436f6f7264696e617465733a205b2a20747374725d2c0a2020666f6f747072696e744f626c69676174696f6e733a205b2a20747374725d2c0a2020636f73744f626c69676174696f6e733a205b2a20747374725d2c0a7d0a0a77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a2020202020202020202020202020227265706c61636522202f202264656c65746522202f20747374720a67756172642d6b696e64203d2022707265636f6d6d69742d61746f6d696322202f20747374720a61746f6d69636974792d726571756972656d656e74203d202261746f6d696322202f20747374720a0a3b20412067656e75696e6520756e696f6e3a207075726520636f6e7374727563746f7273206361727279206e6f20656666656374206b696e64206f72206661696c757265733b206566666563740a3b20696e7472696e73696373206d757374202845444943542d5441524745542d494e5452494e5349432d434c4153532d303031292e2054686520736368656d6120656e666f7263657320746869732c0a3b206e6f74206120636f6d6d656e742e0a0a3b2054686520696e7472696e736963277320636f6f7264696e6174652069732074686520696e7472696e73696373206d6170204b45592c206e6f7420612076616c7565206669656c642c20736f207468650a3b206b657920616e6420636f6f7264696e6174652063616e206e65766572206469736167726565202845444943542d4142492d494e5452494e5349432d554e495155452d303031292e0a696e7472696e736963203d20707572652d696e7472696e736963202f206566666563742d696e7472696e7369630a0a707572652d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a202270757265222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206775617264537570706f72743a2066616c73652c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20226e6f6e65222c0a7d0a0a6566666563742d696e7472696e736963203d207b0a2020696e7472696e736963436c6173733a2022656666656374222c0a202074797065506172616d65746572733a205b2a20747374725d2c0a2020617267756d656e7454797065733a205b2a20636f72652d747970652d7265665d2c0a202072657475726e547970653a20636f72652d747970652d7265662c0a20206566666563744b696e643a206566666563742d6b696e642c0a20203b206d6170206b65796564206279206661696c75726520636f6f7264696e61746520286661696c7572652d6964656e74293b20746865206661696c75726520636f6f7264696e6174652069730a20203b20746865206b65792c206e6f7420612076616c7565206669656c642c20736f20756e697175656e657373206973207374727563747572616c0a20203b202845444943542d4142492d4641494c5552452d554e495155452d303031292e0a20206566666563744661696c757265733a207b202a206661696c7572652d6964656e74203d3e206566666563742d6661696c7572652d626f6479207d2c0a20206775617264537570706f72743a20626f6f6c2c0a2020666f6f747072696e7454656d706c6174653a20747374722c0a2020636f737454656d706c6174653a20747374722c0a20207772697465436c6173733a20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f20227265706c61636522202f0a20202020202020202020202020202264656c65746522202f2022637573746f6d222c0a202063616e5061727469636970617465496e41746f6d696347756172643a20626f6f6c2c0a7d0a0a3b206566666563742d6661696c7572652d626f64792c206566666563742d6b696e642c20617574686f726974792d636c6173732c20616e6420636f72652d747970652d7265662061726520646566696e65640a3b206f6e636520696e2065646963742d636f6d6d6f6e2e6364646c20616e6420617373656d626c65642077697468207468697320736368656d61202845444943542d4142492d4e4f4455502d303031292e0a0a3b202d2d2d2065646963742d617574686f726974792d66616374732e6364646c202d2d2d0a3b2065646963742d617574686f726974792d66616374732e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f722074686520666972737420636f6d70696c65722d636f6e7465787420617574686f726974792d666163747320646f63756d656e742e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20736f20736f757263652e6469676573742075736573207468650a3b20736861726564207368613235362d6469676573742074797065642076616c75652e204a534f4e2069732061207265766965772f696e7075742072656e646572696e673a206974730a3b20607368613235363a3c3634206865783e6020736f75726365206469676573742069732070726f6a656374656420746f205b60736861323536602c203332207261772062797465735d206f6e0a3b2074686520776972652c20616e64206974732066616374206172726179732070726f6a65637420746f2074686520636f6f7264696e6174652d6b65796564206d6170732062656c6f772e0a0a617574686f726974792d6661637473203d207b0a202061706956657273696f6e3a202265646963742e617574686f726974792d66616374732f7631222c0a2020736f757263653a20617574686f726974792d666163742d736f757263652c0a20206f7065726174696f6e50726f66696c65733a207b202a2074737472203d3e20617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374207d2c0a20206566666563745772697465436c61737365733a207b202a2074737472203d3e20617574686f726974792d77726974652d636c617373207d2c0a2020627564676574733a207b202a2074737472203d3e20617574686f726974792d6275646765742d66616374207d2c0a7d0a0a617574686f726974792d666163742d736f75726365203d207b0a20206b696e643a20226c61777061636b22202f202274617267657450726f66696c65222c0a2020636f6f7264696e6174653a20747374722c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a3b20546865206d6170206b65792069732074686520736f75726365206f7065726174696f6e2d70726f66696c6520636f6f7264696e6174652e204974206973206e6f7420726570656174656420696e0a3b207468652076616c75652c20736f2061206b657920616e6420656d62656464656420636f6f7264696e6174652063616e6e6f742064697361677265652e20416c6c6f7765642077726974650a3b20636c61737365732061726520612063616e6f6e6963616c206d61702d7365743a2074686520636c6173732069732074686520756e69717565206b657920616e64206e756c6c206973207468650a3b20756e6974206d61726b65722e2043616e6f6e6963616c2043424f52206669786573206b6579206f7264657220776974686f75742061207365636f6e64206f72646572696e672072756c652e0a617574686f726974792d6f7065726174696f6e2d70726f66696c652d66616374203d207b0a2020636f72653a20747374722c0a2020616c6c6f7765645772697465436c61737365733a207b202a20617574686f726974792d77726974652d636c617373203d3e206e756c6c207d2c0a7d0a0a3b20546865206566666563745772697465436c6173736573206d6170206b6579206973207468652073656d616e7469632065666665637420636f6f7264696e6174652e2054686520627564676574730a3b206d6170206b65792069732074686520736f757263652062756467657420636f6f7264696e6174652e2043616e6f6e6963616c2043424f52206d61702d6b657920756e697175656e6573730a3b206d616b6573206475706c6963617465206661637420636f6f7264696e61746573207374727563747572616c6c7920756e726570726573656e7461626c652e0a617574686f726974792d6275646765742d66616374203d207b0a20206d617853746570733a2075696e742c0a20206d6178416c6c6f636174656442797465733a2075696e742c0a20206d61784f757470757442797465733a2075696e742c0a7d0a0a3b20417574686f726974794661637473446f63756d656e7420763120696e74656e74696f6e616c6c792061636365707473206f6e6c792074686520777269746520636c6173736573207468650a3b2063757272656e7420636f6d70696c6572206d6f64656c2063616e20636f6e73756d652e2060637573746f6d602069732074686520736f6c6520763120637573746f6d207370656c6c696e673b0a3b20617262697472617279207461726765742d70726f66696c6520657874656e73696f6e20737472696e677320646f206e6f7420656e746572207468697320636f6d70696c657220706174682e0a617574686f726974792d77726974652d636c617373203d20226e6f6e6522202f20227265616422202f202263726561746522202f2022656e7375726522202f2022617070656e6422202f0a202020202020202020202020202020202020202020202020227265706c61636522202f202264656c65746522202f2022637573746f6d220a0a3b202d2d2d2065646963742d7461726765742d69722e6364646c202d2d2d0a3b20535044582d4c6963656e73652d4964656e7469666965723a204170616368652d322e300a3b2065646963742d7461726765742d69722e6364646c0a3b2043616e6f6e6963616c20736368656d6120666f72207468652045646963742d6f776e65642054617267657420495220617274696661637420656e76656c6f70652e0a3b20417574686f7269746174697665206279746520656e636f64696e673a2065646963742e63616e6f6e6963616c2d63626f722f76312e0a3b0a3b205468697320736368656d6120697320617373656d626c656420776974682065646963742d636f6d6d6f6e2e6364646c20616e642065646963742d636f72652e6364646c2e2049740a3b2064656c696265726174656c792072657573657320436f72652065787072657373696f6e732c20707265646963617465732c20627564676574732c206c6f63616c207265666572656e6365732c0a3b206f62737472756374696f6e20726561736f6e732c20616e64206f62737472756374696f6e2061726d7320736f2074686520736368656d61206d617463686573207468652076616c75650a3b20656d6974746564206279207468652063616e6f6e6963616c2054617267657420495220656e636f64657220726174686572207468616e20726573746174696e672074686f73652074797065732e0a3b2049742064657363726962657320746865207374727563747572616c207368617065206f662076616c6964206c6f776572696e672d70726f6475636564206172746966616374732e205468650a3b206c6f776572696e6720616e6420656e636f64657220636f6e7472616374732073657061726174656c7920656e666f7263652073656d616e746963206964656e7469666965722072756c65730a3b20616e642063616e6f6e6963616c206f72646572696e672f64656475706c69636174696f6e20666f72207365742d6c696b652076616c7565732e0a0a3b2054617267657420495220656e636f64696e672072656a6563747320616e20656d707479207461726765742d70726f66696c6520636f6f7264696e617465206265666f72652062797465730a3b2065786973742c20736f207468697320726f6f74207469676874656e732074686520736861726564207374727563747572616c207265736f757263652d726566206163636f7264696e676c792e0a7461726765742d69722d7265736f757263652d726566203d207b0a202069643a2074737472202e7265676578702022283f73292e2b222c0a20206469676573743a207368613235362d6469676573742c0a7d0a0a7461726765742d69722d6172746966616374203d207b0a20206b696e643a202274617267657449724172746966616374222c0a2020646f6d61696e3a20747374722c0a202074617267657450726f66696c653a207461726765742d69722d7265736f757263652d7265662c0a2020736f75726365436f7265436f6f7264696e6174653a20747374722c0a2020616374696f6e733a207b202a2074737472203d3e207461726765742d69722d616374696f6e207d2c0a7d0a0a7461726765742d69722d616374696f6e203d207b0a20206f7065726174696f6e50726f66696c653a20747374722c0a2020696e707574436f6e73747261696e74733a205b2a20696e7075742d636f6e73747261696e745d2c0a2020636f72654576616c756174696f6e4275646765743a20636f72652d6275646765742c0a2020726571756972656d656e74733a205b2a207461726765742d69722d726571756972656d656e745d2c0a202073746570733a205b2a207461726765742d69722d737465705d2c0a2020726573756c743a20636f72652d657870722c0a7d0a0a7461726765742d69722d726571756972656d656e74203d207b0a202069643a20747374722c0a20207072656469636174653a20636f72652d7072656469636174652c0a20206f6e4661696c7572653a20726571756972652d6661696c7572652d61726d2c0a7d0a0a7461726765742d69722d73746570203d207b0a202069643a20747374722c0a202062696e64696e673a206c6f63616c2d7265662c0a20206566666563743a20747374722c0a2020746172676574496e7472696e7369633a20747374722c0a2020696e7075743a20636f72652d657870722c0a20206f62737472756374696f6e4661696c757265733a205b2a206661696c7572652d6964656e745d2c0a20206f62737472756374696f6e41726d733a207b202a206661696c7572652d6964656e74203d3e206f62737472756374696f6e2d61726d207d2c0a7d0a", + "rawSha256": "eae1444f70144740bc60a253821c5757bc4496717ef710d3f878d9ad9596558e" }, "contracts": [ {"contract": "authority-facts", "rootRule": "authority-facts"}, diff --git a/fixtures/providers/components/inventory.json b/fixtures/providers/components/inventory.json index 5446659..531cbf4 100644 --- a/fixtures/providers/components/inventory.json +++ b/fixtures/providers/components/inventory.json @@ -3,9 +3,9 @@ "components": { "instantiation-failure-lowerer": "sha256:4ca29c3c16f1c0c3c3d80d5fa877cd853d41b9a84d5f30dbb11e2387f81b6e7c", "instantiation-fuel-lowerer": "sha256:6511898df2801170b40323038ef642e32d57c9a9e3f0fd97b616cd2873031bdd", - "lowerer": "sha256:bec69c4fa02aa2dfb4f492d4a1c6849ae4ebe81c1725477efb6b0f2e885676aa", + "lowerer": "sha256:f4bbfab31275d939321ef8727f795670f3161b6c3e87cc00dfe636737c547d5c", "malformed-lowerer": "sha256:dfcd171918373d18b9dff16778e98b7618eeb4ac85976dd7134b9e201562f41b", "verifier": "sha256:9fa8e16ed7735075d559e3094685ce846d06425b4bb479be31f7498417bf87e4" }, - "sourceDigest": "sha256:247da4c15b0b174cca5fef9720d55914345d4b674fdd9eb6febe097425ac047a" + "sourceDigest": "sha256:cfcd319fd26c9b295d7ab3a8aa439a1ac20ab2ef6ff4da44dc4c1c9b1e46591b" } diff --git a/fixtures/providers/components/lowerer.component.wasm b/fixtures/providers/components/lowerer.component.wasm index d9af894b2bbdf7c2a2e02718a51e02c70d47d7d6..f456a83cbbb47917a0c79a564356a5931d3e7961 100644 GIT binary patch delta 21 dcmaEUi}Cp_#tnCZ*bc7bhzK3igPNoIcDMpJ2K06MD$^Z)<= delta 17 Ycmcb>c7bhzK3is9NoronMpJ2K06SU+2mk;8 diff --git a/fixtures/target-ir/canonical/echo-effectful.target-ir.sha256 b/fixtures/target-ir/canonical/echo-effectful.target-ir.sha256 index eac28e2..f0d2996 100644 --- a/fixtures/target-ir/canonical/echo-effectful.target-ir.sha256 +++ b/fixtures/target-ir/canonical/echo-effectful.target-ir.sha256 @@ -1 +1 @@ -sha256:440b22de32f8ad683c70499a3290fe46ee9e7b0f174a4c7a97a9f7109be24524 +sha256:8ea1e1ccf06d54d151d16fc6fd199719b5bf9be81eb38a3e43b23fb76a582797 diff --git a/fixtures/target-ir/canonical/gitwarp-append.target-ir.cbor b/fixtures/target-ir/canonical/gitwarp-append.target-ir.cbor index 25e5beae542a0c928c9769e4593d94679e8f7543..645afeef0f731e14eb07c6549a03e041995a6d31 100644 GIT binary patch delta 17 Ycmcb@afM@oEn8x8NoIcDMrT1506ZoJ6951J delta 17 Ycmcb@afM@oEn8+@NoronMrT1506f(OCjbBd diff --git a/fixtures/target-ir/canonical/gitwarp-append.target-ir.sha256 b/fixtures/target-ir/canonical/gitwarp-append.target-ir.sha256 index 8751b46..d9f5b71 100644 --- a/fixtures/target-ir/canonical/gitwarp-append.target-ir.sha256 +++ b/fixtures/target-ir/canonical/gitwarp-append.target-ir.sha256 @@ -1 +1 @@ -sha256:287e36eafa59bf3c3825428b1c98e3c5253c74e835000eb077d8a7d849a860ef +sha256:fe84ffc9176a17828f25225b764b024389b1041cb3bb9be4385fbe8a5ab11ad8 diff --git a/grammars/textmate/edict.tmLanguage.json b/grammars/textmate/edict.tmLanguage.json index 41c0db4..b566f78 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|action|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" }, "types": { "name": "entity.name.type.edict", diff --git a/grammars/tree-sitter-edict/grammar.js b/grammars/tree-sitter-edict/grammar.js index a82f5b9..9c3e6e6 100644 --- a/grammars/tree-sitter-edict/grammar.js +++ b/grammars/tree-sitter-edict/grammar.js @@ -66,7 +66,7 @@ module.exports = grammar({ _declaration: $ => choice( $.type_declaration, $.enum_declaration, - $.intent_declaration, + $.action_declaration, ), enum_declaration: $ => seq( @@ -140,13 +140,13 @@ module.exports = grammar({ choice($.number, $.string, $.qualified_identifier), ), - intent_declaration: $ => seq( - 'intent', + action_declaration: $ => seq( + 'action', field('name', $.identifier), $.parameter_list, 'returns', field('returns', $.type_reference), - repeat($.intent_clause), + repeat($.action_clause), field('body', $.block), ), @@ -162,7 +162,7 @@ module.exports = grammar({ field('type', $.type_reference), ), - intent_clause: $ => choice( + action_clause: $ => choice( seq('profile', $.qualified_identifier), seq('implements', $.qualified_identifier), seq('basis', choice('none', $.expression)), diff --git a/grammars/tree-sitter-edict/src/grammar.json b/grammars/tree-sitter-edict/src/grammar.json index 55df730..8e4b5c3 100644 --- a/grammars/tree-sitter-edict/src/grammar.json +++ b/grammars/tree-sitter-edict/src/grammar.json @@ -185,7 +185,7 @@ }, { "type": "SYMBOL", - "name": "intent_declaration" + "name": "action_declaration" } ] }, @@ -736,12 +736,12 @@ } ] }, - "intent_declaration": { + "action_declaration": { "type": "SEQ", "members": [ { "type": "STRING", - "value": "intent" + "value": "action" }, { "type": "FIELD", @@ -771,7 +771,7 @@ "type": "REPEAT", "content": { "type": "SYMBOL", - "name": "intent_clause" + "name": "action_clause" } }, { @@ -867,7 +867,7 @@ } ] }, - "intent_clause": { + "action_clause": { "type": "CHOICE", "members": [ { diff --git a/grammars/tree-sitter-edict/src/node-types.json b/grammars/tree-sitter-edict/src/node-types.json index 520f6c4..37a51bc 100644 --- a/grammars/tree-sitter-edict/src/node-types.json +++ b/grammars/tree-sitter-edict/src/node-types.json @@ -691,7 +691,7 @@ "fields": {} }, { - "type": "intent_clause", + "type": "action_clause", "named": true, "fields": {}, "children": { @@ -710,7 +710,7 @@ } }, { - "type": "intent_declaration", + "type": "action_declaration", "named": true, "fields": { "body": { @@ -749,7 +749,7 @@ "required": true, "types": [ { - "type": "intent_clause", + "type": "action_clause", "named": true }, { @@ -1179,7 +1179,7 @@ "named": true }, { - "type": "intent_declaration", + "type": "action_declaration", "named": true }, { @@ -1716,7 +1716,7 @@ "named": false }, { - "type": "intent", + "type": "action", "named": false }, { diff --git a/grammars/tree-sitter-edict/test/corpus/current-subset.txt b/grammars/tree-sitter-edict/test/corpus/current-subset.txt index 732c5ed..6937209 100644 --- a/grammars/tree-sitter-edict/test/corpus/current-subset.txt +++ b/grammars/tree-sitter-edict/test/corpus/current-subset.txt @@ -14,7 +14,7 @@ type HelloReading = { message: String, }; -intent sayHello(input: HelloInput) +action sayHello(input: HelloInput) returns HelloReading profile hello.readOnly basis none @@ -66,7 +66,7 @@ intent sayHello(input: HelloInput) (type_arguments (type_constraint_argument (number))))))) - (intent_declaration + (action_declaration (identifier) (parameter_list (parameter @@ -77,16 +77,16 @@ intent sayHello(input: HelloInput) (type_reference (qualified_identifier (identifier))) - (intent_clause + (action_clause (qualified_identifier (identifier) (identifier))) - (intent_clause) - (intent_clause + (action_clause) + (action_clause (qualified_identifier (identifier) (identifier))) - (intent_clause + (action_clause (expression (binary_expression (expression @@ -122,7 +122,7 @@ use shape "schemas/blob.graphql" as shape; use lawpack rope.optics@1 digest "sha256:0000000000000000000000000000000000000000000000000000000000000000" as rope; use target echo.dpo@1 digest "sha256:1111111111111111111111111111111111111111111111111111111111111111" as echo; -intent storeBlob(input: shape.StoreBlobInput) +action storeBlob(input: shape.StoreBlobInput) returns shape.MaybeBlob profile echo.readWrite basis input.initialBytes @@ -170,7 +170,7 @@ intent storeBlob(input: shape.StoreBlobInput) (digest_literal (string)) (identifier)) - (intent_declaration + (action_declaration (identifier) (parameter_list (parameter @@ -183,16 +183,16 @@ intent storeBlob(input: shape.StoreBlobInput) (qualified_identifier (identifier) (identifier))) - (intent_clause + (action_clause (qualified_identifier (identifier) (identifier))) - (intent_clause + (action_clause (expression (qualified_identifier (identifier) (identifier)))) - (intent_clause + (action_clause (qualified_identifier (identifier) (identifier))) @@ -269,7 +269,7 @@ package examples.spacing@1; use shape "schemas/blob.graphql" as shape; -intent check(input: shape.In) +action check(input: shape.In) returns shape.Out profile shape.readOnly basis none @@ -292,7 +292,7 @@ intent check(input: shape.In) (import_kind) (string) (identifier)) - (intent_declaration + (action_declaration (identifier) (parameter_list (parameter @@ -305,12 +305,12 @@ intent check(input: shape.In) (qualified_identifier (identifier) (identifier))) - (intent_clause + (action_clause (qualified_identifier (identifier) (identifier))) - (intent_clause) - (intent_clause + (action_clause) + (action_clause (qualified_identifier (identifier) (identifier))) @@ -343,7 +343,7 @@ use shape "schemas/greeting.graphql" as shape; use lawpack greeting.optics@1 digest "sha256:0000000000000000000000000000000000000000000000000000000000000000" as greetingLaw; use target echo.dpo@1 digest "sha256:1111111111111111111111111111111111111111111111111111111111111111" as echo; -intent readGreeting(input: shape.ReadGreetingInput) +action readGreeting(input: shape.ReadGreetingInput) returns shape.GreetingReading profile echo.readOnly basis input.greetingId @@ -388,7 +388,7 @@ intent readGreeting(input: shape.ReadGreetingInput) (digest_literal (string)) (identifier)) - (intent_declaration + (action_declaration (identifier) (parameter_list (parameter @@ -401,16 +401,16 @@ intent readGreeting(input: shape.ReadGreetingInput) (qualified_identifier (identifier) (identifier))) - (intent_clause + (action_clause (qualified_identifier (identifier) (identifier))) - (intent_clause + (action_clause (expression (qualified_identifier (identifier) (identifier)))) - (intent_clause + (action_clause (qualified_identifier (identifier) (identifier))) @@ -464,7 +464,7 @@ package examples.uppercase@1; use shape "schemas/upper.graphql" as Shape; -intent Check(Input: Shape.In) +action Check(Input: Shape.In) returns Shape.Out profile Shape.readOnly basis none @@ -487,7 +487,7 @@ intent Check(Input: Shape.In) (import_kind) (string) (identifier)) - (intent_declaration + (action_declaration (identifier) (parameter_list (parameter @@ -500,12 +500,12 @@ intent Check(Input: Shape.In) (qualified_identifier (identifier) (identifier))) - (intent_clause + (action_clause (qualified_identifier (identifier) (identifier))) - (intent_clause) - (intent_clause + (action_clause) + (action_clause (qualified_identifier (identifier) (identifier))) @@ -539,7 +539,7 @@ type Paint = variant { Transparent, }; -intent describe(input: shape.DescribeInput) +action describe(input: shape.DescribeInput) returns shape.Description profile palette.readOnly basis input.paint @@ -599,7 +599,7 @@ intent describe(input: shape.DescribeInput) (identifier)))) (variant_case (type_identifier)))) - (intent_declaration + (action_declaration (identifier) (parameter_list (parameter @@ -612,16 +612,16 @@ intent describe(input: shape.DescribeInput) (qualified_identifier (identifier) (identifier))) - (intent_clause + (action_clause (qualified_identifier (identifier) (identifier))) - (intent_clause + (action_clause (expression (qualified_identifier (identifier) (identifier)))) - (intent_clause + (action_clause (qualified_identifier (identifier) (identifier))) diff --git a/xtask/src/goldens.rs b/xtask/src/goldens.rs index b9c0644..fa88182 100644 --- a/xtask/src/goldens.rs +++ b/xtask/src/goldens.rs @@ -227,7 +227,7 @@ const TARGET_IR_ECHO_SOURCE: &str = "package a.b@1;\n\ type Input = { id: String, };\n\ type Receipt = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.effectful\n\ basis none\n\ budget <= p.tiny {\n\ @@ -240,7 +240,7 @@ const TARGET_IR_GITWARP_SOURCE: &str = "package a.git@1;\n\ type Input = { id: String, };\n\ type Receipt = { id: String, };\n\ type Output = { id: String, };\n\ - intent t(input: Input) returns Output\n\ + action t(input: Input) returns Output\n\ profile p.gitwarp\n\ basis none\n\ budget <= p.tiny\n\ diff --git a/xtask/src/tests.rs b/xtask/src/tests.rs index 858648c..77c1507 100644 --- a/xtask/src/tests.rs +++ b/xtask/src/tests.rs @@ -807,8 +807,8 @@ fn tree_sitter_grammar_declares_current_editor_contract() { "type_declaration", "enum_declaration", "variant_type", - "intent_declaration", - "intent_clause", + "action_declaration", + "action_clause", "block", "let_statement", "return_statement", @@ -840,7 +840,7 @@ fn tree_sitter_grammar_declares_current_editor_contract() { "type", "enum", "variant", - "intent", + "action", "returns", "profile", "implements", @@ -1015,7 +1015,7 @@ fn textmate_grammar_covers_public_highlight_roles() { let punctuation_regex = textmate_repository_match(&grammar, "punctuation"); 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 \ + let source = "package use type enum variant action 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 HelloInput input = == != < <= > >= + - * / % ! && || => -> :: ... ; : , . @ \ @@ -4018,7 +4018,7 @@ fn core_cddl_declares_v1_semantic_model() { "core-module =", "apiVersion: \"edict.core/v1\"", "core-type =", - "core-intent =", + "core-action =", "core-block =", "core-node =", "core-expr =", From 5449fc0ef3e5c470317932028e44c712cdf5ef54 Mon Sep 17 00:00:00 2001 From: James Ross Date: Tue, 4 Aug 2026 02:06:15 -0700 Subject: [PATCH 2/4] docs: complete Intent to Action rename and log the break The workspace-wide Intent -> Action rename in 9f1a11e0 missed docs/topics/target-ir/README.md entirely and left stale prose in docs/topics/target-ir/test-plan.md, where sentences still said "intent" next to already-renamed evidence names such as action_constraints_and_budget_are_preserved_in_echo_span_ir. The contract-check gate could not catch this: link, evidence-name, and fixture-path checks all still resolved, which is exactly the deterministic-vs-human split DOCS-REQ-006 describes. Also records the rename in the Unreleased CHANGELOG, which had rewritten historical entries in place but never logged the change itself. It is breaking twice over: the `action` declaration keyword replaces `intent` in surface syntax, and the canonical CBOR map key is now `actions`, which moved the reviewed Core and Target IR golden digests. Refreshes the documentation coverage matrix cell that was gated on "once #21 lands"; #21 and #20 both closed on 2026-06-24, so the first-success path is now an admitted gap rather than pending work. That cell was the only one in the matrix keyed to an issue number instead of a capability, per DOCS-REQ-005. docs-impact: documentation only; no behavior, schema, or fixture change. Verified with markdownlint (0 errors), cargo xtask contract-check (23 shelves), and cargo xtask target-ir-goldens --check (2 cases). BREAKING CHANGE: the `action` keyword replaces `intent` in Edict surface syntax, and the canonical CBOR action-map key is now `actions` in edict.core/v1 and edict.target-ir.artifact/v1. Previously published digests do not carry over. --- CHANGELOG.md | 12 ++++++++++++ docs/topics/documentation/README.md | 2 +- docs/topics/target-ir/README.md | 12 ++++++------ docs/topics/target-ir/test-plan.md | 10 +++++----- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c57bc0d..e239df6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -140,6 +140,18 @@ versions still track specification maturity rather than a released product. ### Changed +- **BREAKING (language surface):** the action declaration keyword is now + `action` instead of `intent`. Source files that declare `intent name { ... }` + no longer parse. The rename is workspace-wide and also covers the public Rust + surface (`ActionDecl`, `ActionClause`, `CoreAction`, `TargetIrAction`, + `CoreModule::actions`, `ResolvedAction`, `TypedAction`). +- **BREAKING (canonical ABI):** the canonical CBOR map key for the action map is + now `actions` instead of `intents` in both `edict.core/v1` and + `edict.target-ir.artifact/v1`. Canonical bytes and every derived digest move + accordingly; the reviewed Core and Target IR goldens under + `fixtures/core/canonical/` and `fixtures/target-ir/canonical/` were + regenerated. Previously published digests do not carry over. Fixtures, specs, + CDDL, and topic shelves use the new term throughout. - The local `cargo xtask verify` gate now schedules one default workspace test pass, which already includes doctests, instead of repeating every workspace doctest in a second Cargo invocation. diff --git a/docs/topics/documentation/README.md b/docs/topics/documentation/README.md index 2e26c90..b9896db 100644 --- a/docs/topics/documentation/README.md +++ b/docs/topics/documentation/README.md @@ -59,7 +59,7 @@ require an empty placeholder page. [DOCS-REQ-003] | --- | --- | --- | --- | --- | --- | --- | --- | | Edict language | user, integrator, maintainer | planned when a runnable compiler surface exists | planned around compile/canonicalize tasks | current specs and requirement registry | current README and assurance guide | gap until user-facing failures exist | current topic shelves | | Syntax and surface validation | maintainer, integrator | not needed: library surface is narrow and test-driven | optional parser/validator recipes | current syntax and semantic-validation shelves | current topic READMEs | gap for diagnostics beyond stable error kinds | current test plans and fixtures | -| Compiler spine and Core IR | maintainer, integrator | planned for source-to-Core/canonicalization once #21 lands | planned for canonicalize and inspect workflows | current compiler-spine and Core IR shelves | current topic READMEs and specs | gap for compiler-stage diagnostics | current test plans and CDDL fixtures | +| Compiler spine and Core IR | maintainer, integrator | gap for a source-to-Core/canonicalization walkthrough now that the spine and reference encoder have landed | planned for canonicalize and inspect workflows | current compiler-spine and Core IR shelves | current topic READMEs and specs | gap for compiler-stage diagnostics | current test plans and CDDL fixtures | | CLI | user, integrator, maintainer | current JSONL `check` smoke path | current CLI topic | current CLI topic and CLI stream-contract JSON Schemas | current CLI topic | current structured stderr diagnostics | current CLI test plan | | Fixtures and golden artifacts | maintainer, reviewer | not needed: fixture use is contributor workflow | current fixture topic and Core golden regeneration notes | current fixtures topic and `fixtures/README.md` | current topic README | gap for fixture-generation failures beyond Core goldens | current fixture and Core IR test plans | | Lawpacks and assurance | integrator, maintainer | planned when executable lawpack or assurance tools exist | planned around lawpack validation and assurance explainers | current lawpacks and assurance shelves plus specs | current topic READMEs and assurance guide | gap until executable tools exist | current lawpacks and assurance test plans | diff --git a/docs/topics/target-ir/README.md b/docs/topics/target-ir/README.md index ffcabba..82daa80 100644 --- a/docs/topics/target-ir/README.md +++ b/docs/topics/target-ir/README.md @@ -54,7 +54,7 @@ profiles and does not perform adapter-chain search or general target plugin dispatch. Target-lowering facts also carry the operation profiles selected by -lowerability. A Core intent whose `required_operation_profile` is absent from +lowerability. A Core action whose `required_operation_profile` is absent from that explicit set rejects before Target IR is emitted. For the supported Echo and git-warp slices, each supported Core effect node @@ -78,7 +78,7 @@ git-warp does not currently claim Target IR requirement support. A Core module with `require` nodes selected for git-warp rejects before artifact emission with `TargetLoweringFailureKind::UnsupportedTargetFeature`. -Intent-level Target IR requirements are pre-step guards. A Core `require` after +Action-level Target IR requirements are pre-step guards. A Core `require` after an emitted target step rejects with `TargetLoweringFailureKind::UnsupportedTargetFeature` before artifact emission. If the requirement predicate or reason payload references a local produced by an @@ -86,7 +86,7 @@ earlier target step, lowering uses the same stable failure kind with a more specific step-output-dependency detail. Ordered or step-attached guards remain a future artifact-model change. -Each Target IR intent also preserves the Core input constraints, Core evaluation +Each Target IR action also preserves the Core input constraints, Core evaluation budget, source-ordered requirements, source-ordered effect steps, and structured Core result expression for the supported slice. This records preconditions, evaluation limits, guard dispositions, and success-output semantics without @@ -101,7 +101,7 @@ CBOR for: ``` The canonical value includes the artifact's own domain, digest-locked target -profile resource, source Core coordinate, sorted intent map, input constraints, +profile resource, source Core coordinate, sorted action map, 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 digests are @@ -147,8 +147,8 @@ 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 action with no target-owned requirements +or steps, or a Core module with no actions, 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. diff --git a/docs/topics/target-ir/test-plan.md b/docs/topics/target-ir/test-plan.md index 77773a1..e53ba6e 100644 --- a/docs/topics/target-ir/test-plan.md +++ b/docs/topics/target-ir/test-plan.md @@ -68,13 +68,13 @@ Out of scope: | ID | Status | Category | Requirement | Oracle | Evidence | Fixtures | Notes | | --- | --- | --- | --- | --- | --- | --- | --- | -| TIR-TP-001 | implemented | Golden path | TIR-REQ-001, TIR-REQ-002 | The supported effectful Core shape lowers to an `echo.span-ir/v1` artifact whose selected profile is `echo.dpo@1` and whose steps preserve effect coordinates, effect result bindings, inputs, obstruction arms, intent input constraints, evaluation budgets, intent results, and deterministic order. | supported_effectful_core_lowers_to_echo_span_ir, obstruction_arm_values_are_preserved_in_echo_span_ir, action_constraints_and_budget_are_preserved_in_echo_span_ir, action_result_is_preserved_in_echo_span_ir, effect_result_bindings_are_preserved_in_echo_span_ir | crates/edict-syntax/tests/target_ir.rs | Echo remains the first supported target slice. | +| TIR-TP-001 | implemented | Golden path | TIR-REQ-001, TIR-REQ-002 | The supported effectful Core shape lowers to an `echo.span-ir/v1` artifact whose selected profile is `echo.dpo@1` and whose steps preserve effect coordinates, effect result bindings, inputs, obstruction arms, action input constraints, evaluation budgets, action results, and deterministic order. | supported_effectful_core_lowers_to_echo_span_ir, obstruction_arm_values_are_preserved_in_echo_span_ir, action_constraints_and_budget_are_preserved_in_echo_span_ir, action_result_is_preserved_in_echo_span_ir, effect_result_bindings_are_preserved_in_echo_span_ir | crates/edict-syntax/tests/target_ir.rs | Echo remains the first supported target slice. | | TIR-TP-002 | implemented | Golden path | TIR-REQ-001, TIR-REQ-005 | Native lowerability support for `echo.dpo@1` feeds Target IR lowering facts and produces the same Echo intrinsic selection in the emitted artifact; rejected lowerability reports cannot build Target IR lowering facts; derived facts keep the lowerability report's target-profile coordinate, operation profile, and obstruction coordinates; prevalidated target-profile references must match the lowerability report; repeated identical native selections are coalesced. | lowerability_native_support_feeds_echo_target_lowering, lowerability_bridge_carries_only_selected_native_effect, lowerability_bridge_deduplicates_identical_native_effect_selection, unsupported_lowerability_report_does_not_build_target_ir_facts, lowerability_bridge_uses_report_target_profile_identity, lowerability_bridge_uses_report_operation_profile_identity, lowerability_bridge_requires_matching_target_profile_reference | crates/edict-syntax/tests/target_ir.rs | Bridges lowerability evidence to Target IR generation without adapter search or carrying unselected native supports. | | TIR-TP-003 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-003 | Selecting an unsupported target profile returns `TargetLoweringFailureKind::UnsupportedTargetProfile` with no artifact; selecting an unsupported Target IR domain returns `TargetLoweringFailureKind::UnsupportedTargetIrDomain` with no artifact. | unsupported_target_profile_rejects_without_artifact, unsupported_target_ir_domain_rejects_without_artifact | crates/edict-syntax/tests/target_ir.rs | Keeps supported targets explicit. | | TIR-TP-004 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-004 | A Core module with an unsupported node returns `TargetLoweringFailureKind::UnsupportedCoreNode` with no artifact. | unsupported_core_nodes_reject_without_artifact | crates/edict-syntax/tests/target_ir.rs | No silent fallback or partial artifact. | | TIR-TP-005 | policy | Release boundary | TIR-REQ-006 | Roadmap and release scope keep runtime execution, admission, and general plugin dispatch outside the first target IR slices. | - | - | Non-goal boundary; not a substitute for behavior tests. | -| TIR-TP-006 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-004 | A Core intent whose required operation profile is absent from the selected target-lowering facts returns `TargetLoweringFailureKind::MissingOperationProfile` with no artifact; missing or ambiguous selected effect lowerings return `MissingEffectLowering` or `AmbiguousEffectLowering` with no artifact. | unsupported_operation_profile_rejects_without_artifact, missing_effect_lowering_rejects_without_artifact, ambiguous_effect_lowering_rejects_without_artifact | crates/edict-syntax/tests/target_ir.rs | Prevents effect-only support from bypassing lowerability profile selection and keeps effect matching deterministic. | -| TIR-TP-007 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-004 | A Core intent with no target-owned steps, or a Core module with no intents, returns `TargetLoweringFailureKind::NoTargetSteps` with no artifact. | empty_target_step_actions_reject_without_artifact, empty_core_modules_reject_without_artifact | crates/edict-syntax/tests/target_ir.rs | Prevents empty Echo artifacts from standing in for unsupported work. | +| TIR-TP-006 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-004 | A Core action whose required operation profile is absent from the selected target-lowering facts returns `TargetLoweringFailureKind::MissingOperationProfile` with no artifact; missing or ambiguous selected effect lowerings return `MissingEffectLowering` or `AmbiguousEffectLowering` with no artifact. | unsupported_operation_profile_rejects_without_artifact, missing_effect_lowering_rejects_without_artifact, ambiguous_effect_lowering_rejects_without_artifact | crates/edict-syntax/tests/target_ir.rs | Prevents effect-only support from bypassing lowerability profile selection and keeps effect matching deterministic. | +| TIR-TP-007 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-004 | A Core action with no target-owned steps, or a Core module with no actions, returns `TargetLoweringFailureKind::NoTargetSteps` with no artifact. | empty_target_step_actions_reject_without_artifact, empty_core_modules_reject_without_artifact | crates/edict-syntax/tests/target_ir.rs | Prevents empty Echo artifacts from standing in for unsupported work. | | TIR-TP-008 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-003 | A matched effect lowering whose target intrinsic belongs to a non-Echo profile returns `TargetLoweringFailureKind::UnsupportedTargetIntrinsic` with no artifact. | foreign_target_intrinsic_rejects_without_artifact | crates/edict-syntax/tests/target_ir.rs | Keeps nominal Echo artifacts from carrying another target's intrinsic. | | TIR-TP-009 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-004 | A Core module whose ABI is not `edict.core/v1` returns `TargetLoweringFailureKind::UnsupportedCoreAbi` with no artifact. | unsupported_core_abi_rejects_without_artifact | crates/edict-syntax/tests/target_ir.rs | Prevents Target IR from silently reinterpreting future or stale Core shapes. | | TIR-TP-010 | implemented | Boundary guard | TIR-REQ-001, TIR-REQ-004 | A Core module with unsupported required Core capability flags returns `TargetLoweringFailureKind::UnsupportedCoreCapability` with no artifact. | unsupported_core_capability_rejects_without_artifact | crates/edict-syntax/tests/target_ir.rs | Prevents Target IR from omitting hash-significant Core obligations. | @@ -86,14 +86,14 @@ Out of scope: | TIR-TP-016 | implemented | Golden path | TIR-REQ-001, TIR-REQ-005, TIR-REQ-007 | Native lowerability support for `gitwarp.ref_crdt@1` feeds Target IR lowering facts and produces the same git-warp intrinsic selection in the emitted artifact. | lowerability_native_support_feeds_gitwarp_target_lowering | crates/edict-syntax/tests/target_ir.rs | Bridge remains native-only and explicit. | | TIR-TP-017 | implemented | Golden path | TIR-REQ-008 | Echo and git-warp Target IR artifacts encode to deterministic canonical CBOR, decode as canonical CBOR, and produce stable `edict.target-ir.artifact/v1` digest review strings without colliding with each other. | target_ir_artifact_bytes_and_digests_are_deterministic | crates/edict-syntax/tests/target_ir.rs | The digest domain is the Edict envelope; the artifact domain remains inside the value. | | 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-019 | implemented | Mutation sensitivity | TIR-REQ-008 | Target profile digest, source Core coordinate, action 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-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. | | TIR-TP-025 | implemented | Boundary guard | TIR-REQ-003, TIR-REQ-011 | Targets without obstruction-strand requirement support reject Core `require` nodes with a stable target-feature failure kind and no artifact. | targets_without_obstruction_requirement_support_reject_with_stable_feature_kind | crates/edict-syntax/tests/target_ir.rs | Unsupported feature is not a generic lowering crash. | -| 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-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 | Action-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. | From 7aae09afca111cc49d6b4251f0e950243b583601 Mon Sep 17 00:00:00 2001 From: James Ross Date: Tue, 4 Aug 2026 02:51:32 -0700 Subject: [PATCH 3/4] fix(docs): realign release dates to actual git tag dates CHANGELOG release headings recorded a planned biweekly schedule running 2026-06-24 through 2026-11-04, while every tag was actually cut between 2026-06-21 and 2026-06-30. The dates were never a typo: release-prep writes "## [{tag}] - {target_date}" at scaffold time from next_release_target_date(), and nothing back-filled the real date after tagging. Keep a Changelog, which this file claims to follow, defines that heading as the release date. Realigns all four surfaces that carry the dates: - CHANGELOG.md: 11 release headings - docs/topics/release-process/policy.toml: 10 target_date fields - docs/releases/*.md: 11 "Target date" lines - xtask/src/tests.rs: the alpha_changelog_dates_match_release_policy table and the nine release_policy_tracks_v0_N_boundary guards The synthetic temp-repo fixtures in xtask/src/tests.rs keep their original dates; their values are load-bearing for the +14 scaffolding arithmetic and describe no real release. target_date keeps its field name but now holds the actual publication date, so RELEASE-REQ-008, its fixture oracle, and RELEASE-TP-004 are restated to say so rather than describing a planned date. Known gap, recorded in the release-process test plan: next_release_target_date still adds 14 days to the last entry, which now seeds 2026-07-14, already past. release-prep needs an explicit or clock-derived date before the next release. docs-impact: documentation and release-guard fixtures only; no library, schema, or golden artifact change. Verified with cargo xtask verify (full gate, 52s), 76 xtask tests, contract-check 23 shelves, and markdownlint 0 errors. --- CHANGELOG.md | 29 ++++++++++------- docs/releases/v0.1.0-alpha.1.md | 2 +- docs/releases/v0.10.0-alpha.1.md | 2 +- docs/releases/v0.11.0-alpha.1.md | 2 +- docs/releases/v0.2.0-alpha.1.md | 2 +- docs/releases/v0.3.0-alpha.1.md | 2 +- docs/releases/v0.4.0-alpha.1.md | 2 +- docs/releases/v0.5.0-alpha.1.md | 2 +- docs/releases/v0.6.0-alpha.1.md | 2 +- docs/releases/v0.7.0-alpha.1.md | 2 +- docs/releases/v0.8.0-alpha.1.md | 2 +- docs/releases/v0.9.0-alpha.1.md | 2 +- docs/topics/release-process/policy.toml | 20 ++++++------ docs/topics/release-process/test-plan.md | 11 +++++-- xtask/src/tests.rs | 40 ++++++++++++------------ 15 files changed, 67 insertions(+), 55 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e239df6..002112a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -152,6 +152,13 @@ versions still track specification maturity rather than a released product. `fixtures/core/canonical/` and `fixtures/target-ir/canonical/` were regenerated. Previously published digests do not carry over. Fixtures, specs, CDDL, and topic shelves use the new term throughout. +- Release dates across `CHANGELOG.md`, `docs/topics/release-process/policy.toml`, + `docs/releases/*.md`, and the `xtask` release guards now record the actual git + tag dates (2026-06-21 through 2026-06-30) instead of the planned biweekly + schedule that ran to 2026-11-04. The `target_date` field keeps its name but now + holds the real publication date, so `RELEASE-REQ-008` is restated accordingly. + `next_release_target_date` still increments the last entry by 14 days and now + seeds a past date; see the release-process test plan for that open gap. - The local `cargo xtask verify` gate now schedules one default workspace test pass, which already includes doctests, instead of repeating every workspace doctest in a second Cargo invocation. @@ -264,7 +271,7 @@ versions still track specification maturity rather than a released product. release notes, recording the immutable tag, workflow evidence, milestone closure, release URL, and no-crates publication evidence. -## [v0.11.0-alpha.1] - 2026-11-04 +## [v0.11.0-alpha.1] - 2026-06-30 ### Added @@ -318,7 +325,7 @@ versions still track specification maturity rather than a released product. recorded durable release evidence for its tag, workflow runs, milestone closure, and no-crates publication boundary. -## [v0.10.0-alpha.1] - 2026-10-21 +## [v0.10.0-alpha.1] - 2026-06-29 ### Added @@ -358,7 +365,7 @@ versions still track specification maturity rather than a released product. recorded durable release evidence for its tag, workflow runs, milestone closure, and no-crates publication boundary. -## [v0.9.0-alpha.1] - 2026-10-07 +## [v0.9.0-alpha.1] - 2026-06-28 ### Added @@ -389,7 +396,7 @@ versions still track specification maturity rather than a released product. recorded durable release evidence for its tag, workflow runs, milestone closure, and no-crates publication boundary. -## [v0.8.0-alpha.1] - 2026-09-23 +## [v0.8.0-alpha.1] - 2026-06-28 ### Added @@ -407,7 +414,7 @@ versions still track specification maturity rather than a released product. recorded durable release evidence for its tag, workflow runs, milestone closure, and no-crates publication boundary. -## [v0.7.0-alpha.1] - 2026-09-09 +## [v0.7.0-alpha.1] - 2026-06-27 ### Added @@ -430,7 +437,7 @@ versions still track specification maturity rather than a released product. - Added the review-process topic shelf and structured CodeRabbit-to-Codex fallback policy for review-bot outages, rate limits, and credit exhaustion. -## [v0.6.0-alpha.1] - 2026-08-26 +## [v0.6.0-alpha.1] - 2026-06-26 ### Added @@ -454,7 +461,7 @@ versions still track specification maturity rather than a released product. - Added a release-prep topic-shelf audit gate requiring `docs/topics/` coverage and accuracy to both meet at least 90% before release. -## [v0.5.0-alpha.1] - 2026-08-12 +## [v0.5.0-alpha.1] - 2026-06-25 ### Added @@ -471,7 +478,7 @@ versions still track specification maturity rather than a released product. - Added the admission topic shelf for the Edict/Continuum admission-boundary contract and verification matrix. -## [v0.4.0-alpha.1] - 2026-07-29 +## [v0.4.0-alpha.1] - 2026-06-24 ### Added @@ -508,7 +515,7 @@ versions still track specification maturity rather than a released product. native support, exactly one direct adapter, or unsupported. General composite adapter-chain search remains future v2 design work. -## [v0.3.0-alpha.1] - 2026-07-15 +## [v0.3.0-alpha.1] - 2026-06-24 ### Added @@ -557,7 +564,7 @@ versions still track specification maturity rather than a released product. parameter renaming stays hash-invariant while Core local identity mutations still change canonical bytes and digests. -## [v0.2.0-alpha.1] - 2026-07-01 +## [v0.2.0-alpha.1] - 2026-06-21 ### Added @@ -576,7 +583,7 @@ versions still track specification maturity rather than a released product. - Relaxed Markdown heading duplication checks to allow changelog section headings to repeat across different release versions. -## [v0.1.0-alpha.1] - 2026-06-24 +## [v0.1.0-alpha.1] - 2026-06-21 ### Added diff --git a/docs/releases/v0.1.0-alpha.1.md b/docs/releases/v0.1.0-alpha.1.md index a1519f7..5dd9605 100644 --- a/docs/releases/v0.1.0-alpha.1.md +++ b/docs/releases/v0.1.0-alpha.1.md @@ -1,6 +1,6 @@ # v0.1.0-alpha.1 Release Notes -Target date: 2026-06-24 +Target date: 2026-06-21 Status: published as a GitHub prerelease from the signed `v0.1.0-alpha.1` tag on 2026-06-21. diff --git a/docs/releases/v0.10.0-alpha.1.md b/docs/releases/v0.10.0-alpha.1.md index c3f453c..363c4e9 100644 --- a/docs/releases/v0.10.0-alpha.1.md +++ b/docs/releases/v0.10.0-alpha.1.md @@ -1,6 +1,6 @@ # v0.10.0-alpha.1 Release Notes -Target date: 2026-10-21 +Target date: 2026-06-29 Status: published GitHub prerelease notes for `v0.10.0-alpha.1`, with post-publication evidence recorded on `main`. diff --git a/docs/releases/v0.11.0-alpha.1.md b/docs/releases/v0.11.0-alpha.1.md index d79ad4f..98bac14 100644 --- a/docs/releases/v0.11.0-alpha.1.md +++ b/docs/releases/v0.11.0-alpha.1.md @@ -1,6 +1,6 @@ # v0.11.0-alpha.1 Release Notes -Target date: 2026-11-04 +Target date: 2026-06-30 Status: published GitHub prerelease notes for `v0.11.0-alpha.1`, with post-publication evidence recorded on `main`. diff --git a/docs/releases/v0.2.0-alpha.1.md b/docs/releases/v0.2.0-alpha.1.md index 03e3935..b2984bc 100644 --- a/docs/releases/v0.2.0-alpha.1.md +++ b/docs/releases/v0.2.0-alpha.1.md @@ -1,6 +1,6 @@ # v0.2.0-alpha.1 Release Notes -Target date: 2026-07-01 +Target date: 2026-06-21 Status: published as a GitHub prerelease from the signed `v0.2.0-alpha.1` tag on 2026-06-21. diff --git a/docs/releases/v0.3.0-alpha.1.md b/docs/releases/v0.3.0-alpha.1.md index 90dd52d..bf023ba 100644 --- a/docs/releases/v0.3.0-alpha.1.md +++ b/docs/releases/v0.3.0-alpha.1.md @@ -1,6 +1,6 @@ # v0.3.0-alpha.1 Release Notes -Target date: 2026-07-15 +Target date: 2026-06-24 Status: published as a GitHub prerelease from the signed `v0.3.0-alpha.1` tag targeting `4ea3d993f74490b495fe6e6a9ec2d52f889ccceb`. diff --git a/docs/releases/v0.4.0-alpha.1.md b/docs/releases/v0.4.0-alpha.1.md index 3b71ab3..60df854 100644 --- a/docs/releases/v0.4.0-alpha.1.md +++ b/docs/releases/v0.4.0-alpha.1.md @@ -1,6 +1,6 @@ # v0.4.0-alpha.1 Release Notes -Target date: 2026-07-29 +Target date: 2026-06-24 Status: published as a GitHub prerelease from the signed `v0.4.0-alpha.1` tag targeting `65c80ce4660b384ebf9fd482c59fff402f34d47b`. diff --git a/docs/releases/v0.5.0-alpha.1.md b/docs/releases/v0.5.0-alpha.1.md index 148659a..7c7354e 100644 --- a/docs/releases/v0.5.0-alpha.1.md +++ b/docs/releases/v0.5.0-alpha.1.md @@ -1,6 +1,6 @@ # v0.5.0-alpha.1 Release Notes -Target date: 2026-08-12 +Target date: 2026-06-25 Status: published as a GitHub prerelease from the `v0.5.0-alpha.1` tag targeting `e680adf0a3ccc72559d1c4710e81010738918532`. diff --git a/docs/releases/v0.6.0-alpha.1.md b/docs/releases/v0.6.0-alpha.1.md index d05ac7b..3f2a0ed 100644 --- a/docs/releases/v0.6.0-alpha.1.md +++ b/docs/releases/v0.6.0-alpha.1.md @@ -1,6 +1,6 @@ # v0.6.0-alpha.1 Release Notes -Target date: 2026-08-26 +Target date: 2026-06-26 Status: published as a GitHub prerelease from the `v0.6.0-alpha.1` tag targeting `ec1ce8a5186801caa3c627c6714cedff7fdf43bc`. diff --git a/docs/releases/v0.7.0-alpha.1.md b/docs/releases/v0.7.0-alpha.1.md index 4a52d46..57671d5 100644 --- a/docs/releases/v0.7.0-alpha.1.md +++ b/docs/releases/v0.7.0-alpha.1.md @@ -1,6 +1,6 @@ # v0.7.0-alpha.1 Release Notes -Target date: 2026-09-09 +Target date: 2026-06-27 Status: published GitHub prerelease notes for `v0.7.0-alpha.1`, with post-publication evidence recorded on `main`. diff --git a/docs/releases/v0.8.0-alpha.1.md b/docs/releases/v0.8.0-alpha.1.md index 45e59be..2b83433 100644 --- a/docs/releases/v0.8.0-alpha.1.md +++ b/docs/releases/v0.8.0-alpha.1.md @@ -1,6 +1,6 @@ # v0.8.0-alpha.1 Release Notes -Target date: 2026-09-23 +Target date: 2026-06-28 Status: published GitHub prerelease notes for `v0.8.0-alpha.1`, with post-publication evidence recorded on `main`. diff --git a/docs/releases/v0.9.0-alpha.1.md b/docs/releases/v0.9.0-alpha.1.md index 9cc75a3..ab17653 100644 --- a/docs/releases/v0.9.0-alpha.1.md +++ b/docs/releases/v0.9.0-alpha.1.md @@ -1,6 +1,6 @@ # v0.9.0-alpha.1 Release Notes -Target date: 2026-10-07 +Target date: 2026-06-28 Status: published GitHub prerelease notes for `v0.9.0-alpha.1`, with post-publication evidence recorded on `main`. diff --git a/docs/topics/release-process/policy.toml b/docs/topics/release-process/policy.toml index b03dd22..3c77d1c 100644 --- a/docs/topics/release-process/policy.toml +++ b/docs/topics/release-process/policy.toml @@ -146,7 +146,7 @@ required_evidence = [ [release_notes.v0_2_0_alpha_1] tag = "v0.2.0-alpha.1" -target_date = "2026-07-01" +target_date = "2026-06-21" status = "published" release_issue = 28 scope = [ @@ -166,7 +166,7 @@ non_goals = [ [release_notes.v0_3_0_alpha_1] tag = "v0.3.0-alpha.1" -target_date = "2026-07-15" +target_date = "2026-06-24" status = "published" release_issue = 35 scope = [ @@ -191,7 +191,7 @@ non_goals = [ [release_notes.v0_4_0_alpha_1] tag = "v0.4.0-alpha.1" -target_date = "2026-07-29" +target_date = "2026-06-24" status = "published" release_issue = 39 scope = [ @@ -213,7 +213,7 @@ non_goals = [ [release_notes.v0_5_0_alpha_1] tag = "v0.5.0-alpha.1" -target_date = "2026-08-12" +target_date = "2026-06-25" status = "published" release_issue = 42 scope = [ @@ -240,7 +240,7 @@ non_goals = [ [release_notes.v0_6_0_alpha_1] tag = "v0.6.0-alpha.1" -target_date = "2026-08-26" +target_date = "2026-06-26" status = "published" release_issue = 50 scope = [ @@ -271,7 +271,7 @@ non_goals = [ [release_notes.v0_7_0_alpha_1] tag = "v0.7.0-alpha.1" -target_date = "2026-09-09" +target_date = "2026-06-27" status = "published" release_issue = 59 published_at = "2026-06-27T22:31:49Z" @@ -316,7 +316,7 @@ non_goals = [ [release_notes.v0_8_0_alpha_1] tag = "v0.8.0-alpha.1" -target_date = "2026-09-23" +target_date = "2026-06-28" status = "published" release_issue = 62 published_at = "2026-06-28T01:41:16Z" @@ -365,7 +365,7 @@ non_goals = [ [release_notes.v0_9_0_alpha_1] tag = "v0.9.0-alpha.1" -target_date = "2026-10-07" +target_date = "2026-06-28" status = "published" release_issue = 70 published_at = "2026-06-28T07:04:06Z" @@ -407,7 +407,7 @@ non_goals = [ [release_notes.v0_10_0_alpha_1] tag = "v0.10.0-alpha.1" -target_date = "2026-10-21" +target_date = "2026-06-29" status = "published" release_issue = 76 published_at = "2026-06-29T04:21:11Z" @@ -446,7 +446,7 @@ non_goals = [ [release_notes.v0_11_0_alpha_1] tag = "v0.11.0-alpha.1" -target_date = "2026-11-04" +target_date = "2026-06-30" status = "published" release_issue = 109 published_at = "2026-06-30T07:58:29Z" diff --git a/docs/topics/release-process/test-plan.md b/docs/topics/release-process/test-plan.md index 79e64c9..0887a0b 100644 --- a/docs/topics/release-process/test-plan.md +++ b/docs/topics/release-process/test-plan.md @@ -39,7 +39,7 @@ Out of scope: | RELEASE-REQ-005 | implemented | The current release workflow does not publish crates or other package artifacts. | .github/workflows/release.yml | | RELEASE-REQ-006 | implemented | Pushed release tags are durable; recovery must not move, delete, or recreate release tags. | docs/topics/release-process/policy.toml | | RELEASE-REQ-007 | implemented | Structured release policy captures the `v0.2.0-alpha.1` Core schema scope and explicit non-goals. | docs/topics/release-process/policy.toml | -| RELEASE-REQ-008 | implemented | Scheduled alpha release changelog dates match their structured release policy target dates. | CHANGELOG.md, docs/topics/release-process/policy.toml | +| RELEASE-REQ-008 | implemented | Published alpha release changelog dates match the actual tag dates recorded in the structured release policy `target_date` fields. | CHANGELOG.md, docs/topics/release-process/policy.toml | | RELEASE-REQ-009 | implemented | Release preparation follows a documented runbook with branch prep, local verification, PR merge gate, tag publication, workflow watch, evidence capture, and non-mutating recovery phases. | docs/topics/release-process/runbook.md, docs/topics/release-process/policy.toml | | RELEASE-REQ-010 | implemented | Structured release policy captures the `v0.3.0-alpha.1` compiler-spine, canonical encoder, reviewed golden fixture, exact digest, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | | RELEASE-REQ-011 | implemented | Structured release policy captures the `v0.4.0-alpha.1` target-profile, lowerability, contract-bundle validation, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | @@ -73,7 +73,7 @@ Out of scope: | docs/releases/v0.9.0-alpha.1.md | Published release notes for the first Target IR alpha. | The release workflow looked up this file by full tag name after the release-prep PR merged. | | docs/releases/v0.11.0-alpha.1.md | Published notes for the contract-bundle assembly and canonical Target IR artifact freeze alpha. | The release workflow looked up this file by full tag name after the release-prep PR merged. | | .github/workflows/auto-release-tag.yml | Successful main-CI release-prep merges create immutable release tags and dispatch publication. | The workflow derives tags only from merged `release/*-prep` branches and refuses tag mutation. | -| CHANGELOG.md | Release history for published and release-prep alpha releases. | Scheduled alpha release sections use the matching release target date. | +| CHANGELOG.md | Release history for published and release-prep alpha releases. | Published alpha release sections use the actual tag date recorded in release policy. | | docs/topics/release-process/policy.toml | Structured release-tag, runbook, and alpha boundary policy. | Tag mutation is forbidden, runbook phases are named, and release scope/non-goals are structured. | | docs/topics/release-process/runbook.md | Operator steps for preparing, tagging, publishing, and recovering releases. | The structured policy names the phases and checks the runbook must cover. | | `cargo xtask release-prep ` | Mechanical scaffold for the next release-prep branch. | The xtask regression exercises the writer against a temp repo skeleton and checks every generated file surface. | @@ -85,7 +85,7 @@ Out of scope: | RELEASE-TP-001 | implemented | Golden path | RELEASE-REQ-001, RELEASE-REQ-002, RELEASE-REQ-003, RELEASE-REQ-004, RELEASE-REQ-005 | The workflow contains the tag trigger, main reachability guard, full-tag release-notes path, verified GitHub Release creation, prerelease flag, and no package publish command. | release_workflow_publishes_only_main_reachable_tags | docs/releases/v0.1.0-alpha.1.md | Static workflow contract regression. | | RELEASE-TP-002 | implemented | Policy guard | RELEASE-REQ-006 | Structured policy forbids tag mutation and names existing-valid-tag publication as recovery. | release_tag_recovery_policy_is_structured | docs/topics/release-process/policy.toml | Policy evidence is structured, not prose. | | RELEASE-TP-003 | implemented | Boundary guard | RELEASE-REQ-007 | Structured policy captures the v0.2 Core schema scope and non-goals for lowering, encoder, bytes, digests, targets, and admission. | release_policy_tracks_v0_2_boundary | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the Core milestone. | -| RELEASE-TP-004 | implemented | Consistency guard | RELEASE-REQ-008 | Scheduled alpha changelog section dates equal their target dates in structured release policy. | alpha_changelog_dates_match_release_policy | CHANGELOG.md, docs/topics/release-process/policy.toml | Prevents release chronology drift across release prep and publication. | +| RELEASE-TP-004 | implemented | Consistency guard | RELEASE-REQ-008 | Published alpha changelog section dates equal the actual tag dates recorded in structured release policy. | alpha_changelog_dates_match_release_policy | CHANGELOG.md, docs/topics/release-process/policy.toml | Prevents release chronology drift across release prep and publication. | | RELEASE-TP-005 | implemented | Runbook guard | RELEASE-REQ-009 | Structured policy names the release-prep phases and required checks for local verification, PR checks, and release existence. | release_runbook_policy_is_structured | docs/topics/release-process/policy.toml, docs/topics/release-process/runbook.md | Keeps the human runbook tied to a stable release contract. | | RELEASE-TP-006 | implemented | Boundary guard | RELEASE-REQ-010 | Structured policy captures the v0.3 compiler-spine, canonical encoder, reviewed golden fixture, exact digest, target-lowering, and admission boundaries. | release_policy_tracks_v0_3_boundary | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the compiler-spine milestone. | | RELEASE-TP-007 | implemented | Boundary guard | RELEASE-REQ-011 | Structured policy captures the v0.4 target-profile, lowerability, contract-bundle validation, target-lowering, admission, and publication boundaries. | release_policy_tracks_v0_4_boundary | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the target-profile and lowerability milestone. | @@ -125,3 +125,8 @@ Out of scope: - The current checker proves workflow contract structure, not GitHub API availability. - No crates.io policy exists; package publication remains intentionally absent. +- `target_date` now records the actual tag date rather than a planned date, but + `next_release_target_date` still seeds the next value by adding 14 days to the + last entry. Seeded from the realigned history it computes 2026-07-14, which is + already past, so `release-prep` needs an explicit or clock-derived date before + the next release rather than the biweekly increment. diff --git a/xtask/src/tests.rs b/xtask/src/tests.rs index 77c1507..43a2205 100644 --- a/xtask/src/tests.rs +++ b/xtask/src/tests.rs @@ -2730,7 +2730,7 @@ fn release_policy_tracks_v0_3_boundary() { for required in [ "[release_notes.v0_3_0_alpha_1]", "tag = \"v0.3.0-alpha.1\"", - "target_date = \"2026-07-15\"", + "target_date = \"2026-06-24\"", "status = \"published\"", "compiler_spine", "surface_validation_split", @@ -2755,7 +2755,7 @@ fn release_policy_tracks_v0_4_boundary() { for required in [ "[release_notes.v0_4_0_alpha_1]", "tag = \"v0.4.0-alpha.1\"", - "target_date = \"2026-07-29\"", + "target_date = \"2026-06-24\"", "status = \"published\"", "target_profile_conformance", "lowerability_direct_adapter", @@ -2779,7 +2779,7 @@ fn release_policy_tracks_v0_5_boundary() { for required in [ "[release_notes.v0_5_0_alpha_1]", "tag = \"v0.5.0-alpha.1\"", - "target_date = \"2026-08-12\"", + "target_date = \"2026-06-25\"", "status = \"published\"", "edict_owned_continuum_participation_boundary", "gate_c_admission_request_validation", @@ -2812,7 +2812,7 @@ fn release_policy_tracks_v0_6_boundary() { for required in [ "[release_notes.v0_6_0_alpha_1]", "tag = \"v0.6.0-alpha.1\"", - "target_date = \"2026-08-26\"", + "target_date = \"2026-06-26\"", "status = \"published\"", "editor_highlight_roles", "tree_sitter_grammar_source", @@ -2842,7 +2842,7 @@ fn release_policy_tracks_v0_7_boundary() { for required in [ "[release_notes.v0_7_0_alpha_1]", "tag = \"v0.7.0-alpha.1\"", - "target_date = \"2026-09-09\"", + "target_date = \"2026-06-27\"", "status = \"published\"", "release_issue = 59", "published_at = \"2026-06-27T22:31:49Z\"", @@ -2897,7 +2897,7 @@ fn release_policy_tracks_v0_8_boundary() { for required in [ "[release_notes.v0_8_0_alpha_1]", "tag = \"v0.8.0-alpha.1\"", - "target_date = \"2026-09-23\"", + "target_date = \"2026-06-28\"", "status = \"published\"", "release_issue = 62", "published_at = \"2026-06-28T01:41:16Z\"", @@ -2950,7 +2950,7 @@ fn release_policy_tracks_v0_9_boundary() { for required in [ "[release_notes.v0_9_0_alpha_1]", "tag = \"v0.9.0-alpha.1\"", - "target_date = \"2026-10-07\"", + "target_date = \"2026-06-28\"", "status = \"published\"", "release_issue = 70", "published_at = \"2026-06-28T07:04:06Z\"", @@ -3001,7 +3001,7 @@ fn release_policy_tracks_v0_10_boundary() { for required in [ "[release_notes.v0_10_0_alpha_1]", "tag = \"v0.10.0-alpha.1\"", - "target_date = \"2026-10-21\"", + "target_date = \"2026-06-29\"", "status = \"published\"", "release_issue = 76", "published_at = \"2026-06-29T04:21:11Z\"", @@ -3050,7 +3050,7 @@ fn release_policy_tracks_v0_11_boundary() { for required in [ "[release_notes.v0_11_0_alpha_1]", "tag = \"v0.11.0-alpha.1\"", - "target_date = \"2026-11-04\"", + "target_date = \"2026-06-30\"", "status = \"published\"", "release_issue = 109", "published_at = \"2026-06-30T07:58:29Z\"", @@ -3103,16 +3103,16 @@ fn alpha_changelog_dates_match_release_policy() { let policy = fs::read_to_string(root.join("docs/topics/release-process/policy.toml")) .expect("release policy"); for (tag, target) in [ - ("v0.2.0-alpha.1", "2026-07-01"), - ("v0.3.0-alpha.1", "2026-07-15"), - ("v0.4.0-alpha.1", "2026-07-29"), - ("v0.5.0-alpha.1", "2026-08-12"), - ("v0.6.0-alpha.1", "2026-08-26"), - ("v0.7.0-alpha.1", "2026-09-09"), - ("v0.8.0-alpha.1", "2026-09-23"), - ("v0.9.0-alpha.1", "2026-10-07"), - ("v0.10.0-alpha.1", "2026-10-21"), - ("v0.11.0-alpha.1", "2026-11-04"), + ("v0.2.0-alpha.1", "2026-06-21"), + ("v0.3.0-alpha.1", "2026-06-24"), + ("v0.4.0-alpha.1", "2026-06-24"), + ("v0.5.0-alpha.1", "2026-06-25"), + ("v0.6.0-alpha.1", "2026-06-26"), + ("v0.7.0-alpha.1", "2026-06-27"), + ("v0.8.0-alpha.1", "2026-06-28"), + ("v0.9.0-alpha.1", "2026-06-28"), + ("v0.10.0-alpha.1", "2026-06-29"), + ("v0.11.0-alpha.1", "2026-06-30"), ] { assert!( policy.contains(&format!("tag = \"{tag}\"")), @@ -3992,7 +3992,7 @@ fn release_policy_tracks_v0_2_boundary() { for required in [ "[release_notes.v0_2_0_alpha_1]", "tag = \"v0.2.0-alpha.1\"", - "target_date = \"2026-07-01\"", + "target_date = \"2026-06-21\"", "core_semantic_model", "normative_core_schema", "no_source_to_core_lowering", From 66188d56ceb8bc164be8cb64d6b3a6714697f2a6 Mon Sep 17 00:00:00 2001 From: James Ross Date: Tue, 4 Aug 2026 03:15:20 -0700 Subject: [PATCH 4/4] refactor(xtask): reconcile release dates against tags, not against copies The release date guards were fragile and largely tautological. alpha_changelog_dates_match_release_policy asserted that CHANGELOG.md dates equalled policy.toml target_date values, but release-prep generates both from that one field. Two copies written from a single source always agree, including when the source is wrong, so the guard stayed green through four months of drift across all eleven releases and only failed once a human corrected one side by hand. It detected intervention, not incorrectness. The nine release_policy_tracks_v0_N_boundary tests (plus v0_2) matched substrings against the whole policy file rather than the release's own block. Verified: setting v0.3's target_date to 1999-01-01 left release_policy_tracks_v0_3_boundary passing, because v0.4 carried the same date string. Releases tagged on the same day share dates, so that is the normal case. They were also ~250 lines of frozen-history boilerplate, auto-written by a generator, asserting that a static file still contained strings someone typed. Replaces them with: - `cargo xtask release-dates`, which reconciles policy.toml, CHANGELOG.md, and docs/releases/*.md against `git for-each-ref refs/tags` -- the independent authority for when a release happened. Wired into `xtask verify`. Date contradictions fail; absent surfaces are reported as uncovered rather than failing an otherwise-correct history, since the earliest releases predate these surfaces. A clone without tags says it skipped instead of passing vacuously. - release_policy_blocks_are_structurally_complete, one data-driven test over parsed blocks, replacing eleven near-duplicates. - release_policy_block_parsing_scopes_fields_to_their_own_release, a regression guard pinning the 1999-01-01 false-pass. - reconcile_release_dates, a pure function over already-read inputs, with hermetic tests. cargo test needs no git tags, so CI's shallow checkout is unaffected. release-prep no longer scaffolds Rust test stubs or changelog date guard entries. Per-release scope and non-goal content is now reviewed rather than string-tested, matching the repo's own rule in docs/topics/documentation/test-plan.md that policy detail is not encoded as a Rust test; the ten affected requirement and test-case rows move to `policy` status accordingly. docs-impact: release-process README, test plan, and policy.toml updated with the new design and two recorded open gaps. Verified with cargo xtask verify (full gate, 9s), 69 xtask tests, and markdownlint 0 errors. --- docs/topics/release-process/README.md | 17 +- docs/topics/release-process/policy.toml | 2 - docs/topics/release-process/test-plan.md | 59 ++- xtask/src/main.rs | 17 +- xtask/src/release_dates.rs | 244 ++++++++++ xtask/src/release_prep.rs | 104 +--- xtask/src/tests.rs | 593 +++++++---------------- 7 files changed, 481 insertions(+), 555 deletions(-) create mode 100644 xtask/src/release_dates.rs diff --git a/docs/topics/release-process/README.md b/docs/topics/release-process/README.md index f5bb357..fd26460 100644 --- a/docs/topics/release-process/README.md +++ b/docs/topics/release-process/README.md @@ -55,10 +55,19 @@ captured in [`policy.toml`](./policy.toml). [RELEASE-REQ-009] `cargo xtask release-prep ` scaffolds the mechanical release-prep surfaces that must move together: workspace package versions, lockfile package versions, a dated changelog section, release policy boundary block, release -notes stub, release boundary test stub, changelog date guard entry, and paired -release-process test-plan rows. The command does not write the release thesis, -replace topic-shelf audits, or decide scope/non-goals; reviewers must replace -the scaffold placeholders before release. [RELEASE-REQ-024] +notes stub, and paired release-process test-plan rows. The command does not +write the release thesis, replace topic-shelf audits, decide scope/non-goals, or +generate Rust test stubs; reviewers must replace the scaffold placeholders +before release. [RELEASE-REQ-024] + +`cargo xtask release-dates` reconciles the dates recorded in the release policy, +`CHANGELOG.md`, and `docs/releases/*.md` against the git tags that published +them. Those three surfaces are written from one field at scaffold time, so +comparing them against each other only proves the copy succeeded; the tags are +the independent authority for when a release happened. The check runs inside +`cargo xtask verify` and reports uncovered surfaces separately from date drift. +A clone without tags reports that it skipped rather than passing silently. +[RELEASE-REQ-008] Every release-prep branch must audit `docs/topics/` coverage and accuracy before the release-prep pull request opens. Coverage is audited topic shelves divided diff --git a/docs/topics/release-process/policy.toml b/docs/topics/release-process/policy.toml index 3c77d1c..0b1b269 100644 --- a/docs/topics/release-process/policy.toml +++ b/docs/topics/release-process/policy.toml @@ -44,8 +44,6 @@ scaffold_outputs = [ "dated_changelog_section", "release_policy_boundary_block", "release_notes_stub", - "release_boundary_test_stub", - "alpha_changelog_date_guard_entry", "release_process_test_plan_rows", ] steps = [ diff --git a/docs/topics/release-process/test-plan.md b/docs/topics/release-process/test-plan.md index 0887a0b..3750eba 100644 --- a/docs/topics/release-process/test-plan.md +++ b/docs/topics/release-process/test-plan.md @@ -38,24 +38,25 @@ Out of scope: | RELEASE-REQ-004 | implemented | SemVer prerelease tags publish as GitHub prereleases. | .github/workflows/release.yml | | RELEASE-REQ-005 | implemented | The current release workflow does not publish crates or other package artifacts. | .github/workflows/release.yml | | RELEASE-REQ-006 | implemented | Pushed release tags are durable; recovery must not move, delete, or recreate release tags. | docs/topics/release-process/policy.toml | -| RELEASE-REQ-007 | implemented | Structured release policy captures the `v0.2.0-alpha.1` Core schema scope and explicit non-goals. | docs/topics/release-process/policy.toml | -| RELEASE-REQ-008 | implemented | Published alpha release changelog dates match the actual tag dates recorded in the structured release policy `target_date` fields. | CHANGELOG.md, docs/topics/release-process/policy.toml | +| RELEASE-REQ-007 | policy | Structured release policy captures the `v0.2.0-alpha.1` Core schema scope and explicit non-goals. | docs/topics/release-process/policy.toml | +| RELEASE-REQ-008 | implemented | Release dates recorded in the structured release policy, `CHANGELOG.md`, and release notes are reconciled against the git tags that published them, rather than against each other. | CHANGELOG.md, docs/topics/release-process/policy.toml, xtask/src/release_dates.rs | | RELEASE-REQ-009 | implemented | Release preparation follows a documented runbook with branch prep, local verification, PR merge gate, tag publication, workflow watch, evidence capture, and non-mutating recovery phases. | docs/topics/release-process/runbook.md, docs/topics/release-process/policy.toml | -| RELEASE-REQ-010 | implemented | Structured release policy captures the `v0.3.0-alpha.1` compiler-spine, canonical encoder, reviewed golden fixture, exact digest, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | -| RELEASE-REQ-011 | implemented | Structured release policy captures the `v0.4.0-alpha.1` target-profile, lowerability, contract-bundle validation, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | +| RELEASE-REQ-010 | policy | Structured release policy captures the `v0.3.0-alpha.1` compiler-spine, canonical encoder, reviewed golden fixture, exact digest, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | +| RELEASE-REQ-011 | policy | Structured release policy captures the `v0.4.0-alpha.1` target-profile, lowerability, contract-bundle validation, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | | RELEASE-REQ-012 | implemented | Successful `main` CI on a merged `release/vX.Y.Z-alpha.N-prep` pull request creates an immutable `vX.Y.Z-alpha.N` tag and dispatches release publication. | .github/workflows/auto-release-tag.yml, docs/topics/release-process/policy.toml | | RELEASE-REQ-013 | implemented | Release publication closes the matching GitHub milestone only after the release exists and the milestone has zero open issues. | .github/workflows/release.yml, docs/topics/release-process/policy.toml | -| RELEASE-REQ-014 | implemented | Structured release policy captures the `v0.5.0-alpha.1` Gate C admission-boundary scope, release automation, and explicit Continuum-owned non-goal boundaries. | docs/topics/release-process/policy.toml | +| RELEASE-REQ-014 | policy | Structured release policy captures the `v0.5.0-alpha.1` Gate C admission-boundary scope, release automation, and explicit Continuum-owned non-goal boundaries. | docs/topics/release-process/policy.toml | | RELEASE-REQ-015 | implemented | Manual auto-release recovery must only tag a requested `v*` release when the provided SHA is reachable from `origin/main`, has successful `main` CI, came from exactly one merged `release/*-prep` pull request, and derives the requested tag. | .github/workflows/auto-release-tag.yml, docs/topics/release-process/policy.toml | | RELEASE-REQ-016 | implemented | Release preparation must audit `docs/topics/` coverage and accuracy, and releases are blocked unless both metrics are at least 90%. | docs/topics/release-process/policy.toml, docs/topics/release-process/runbook.md | -| RELEASE-REQ-017 | implemented | Structured release policy captures the `v0.6.0-alpha.1` developer-tooling scope, supported editor integration boundary, topic-shelf audit, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | +| RELEASE-REQ-017 | policy | Structured release policy captures the `v0.6.0-alpha.1` developer-tooling scope, supported editor integration boundary, topic-shelf audit, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | | RELEASE-REQ-018 | implemented | Release preparation must record a release thesis, previous-tag diff reconciliation, zero-open milestone evidence before tag creation, no-crates publication evidence, and a release report with plan-versus-actual, fallout, and next-thesis sections. | docs/topics/release-process/policy.toml, docs/topics/release-process/runbook.md | -| RELEASE-REQ-019 | implemented | Structured release policy captures the `v0.7.0-alpha.1` file-backed authority-facts scope, governance-design boundary, policy hardening, review fallback, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | -| RELEASE-REQ-020 | implemented | Structured release policy captures the `v0.8.0-alpha.1` minimal effectful compiler-spine scope, Core effect-node boundary, unsupported-form rejection boundary, pure-golden stability boundary, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | -| RELEASE-REQ-021 | implemented | Structured release policy captures the `v0.9.0-alpha.1` first Target IR scope, Echo and git-warp target artifact boundaries, lowerability bridge, stable failure boundary, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | -| RELEASE-REQ-022 | implemented | Structured release policy captures the `v0.10.0-alpha.1` first public CLI scope, JSONL check workflow, stream record schemas, stable diagnostic kind codes, golden fixture corpus, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | -| RELEASE-REQ-023 | implemented | Structured release policy captures the `v0.11.0-alpha.1` contract-bundle assembly and canonical Target IR artifact freeze scope, checked digest/byte goldens, computed bundle integration, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | -| RELEASE-REQ-024 | implemented | `cargo xtask release-prep ` scaffolds the mechanical release-prep files that have drifted before: workspace package versions, lockfile package versions, a dated changelog section, the release policy boundary block, release notes stub, boundary test stub, changelog date guard entry, and paired release-process test-plan rows. | xtask/src/release_prep.rs | +| RELEASE-REQ-019 | policy | Structured release policy captures the `v0.7.0-alpha.1` file-backed authority-facts scope, governance-design boundary, policy hardening, review fallback, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | +| RELEASE-REQ-020 | policy | Structured release policy captures the `v0.8.0-alpha.1` minimal effectful compiler-spine scope, Core effect-node boundary, unsupported-form rejection boundary, pure-golden stability boundary, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | +| RELEASE-REQ-021 | policy | Structured release policy captures the `v0.9.0-alpha.1` first Target IR scope, Echo and git-warp target artifact boundaries, lowerability bridge, stable failure boundary, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | +| RELEASE-REQ-022 | policy | Structured release policy captures the `v0.10.0-alpha.1` first public CLI scope, JSONL check workflow, stream record schemas, stable diagnostic kind codes, golden fixture corpus, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | +| RELEASE-REQ-023 | policy | Structured release policy captures the `v0.11.0-alpha.1` contract-bundle assembly and canonical Target IR artifact freeze scope, checked digest/byte goldens, computed bundle integration, and explicit non-goal boundaries. | docs/topics/release-process/policy.toml | +| RELEASE-REQ-024 | implemented | `cargo xtask release-prep ` scaffolds the mechanical release-prep files that have drifted before: workspace package versions, lockfile package versions, a dated changelog section, the release policy boundary block, release notes stub, and paired release-process test-plan rows. It does not scaffold Rust test stubs. | xtask/src/release_prep.rs | +| RELEASE-REQ-025 | implemented | Every `[release_notes.*]` policy block is structurally complete and uniquely keyed, with per-block field lookup rather than whole-file substring matching. | docs/topics/release-process/policy.toml, xtask/src/release_dates.rs | ## Fixtures @@ -84,23 +85,25 @@ Out of scope: | --- | --- | --- | --- | --- | --- | --- | --- | | RELEASE-TP-001 | implemented | Golden path | RELEASE-REQ-001, RELEASE-REQ-002, RELEASE-REQ-003, RELEASE-REQ-004, RELEASE-REQ-005 | The workflow contains the tag trigger, main reachability guard, full-tag release-notes path, verified GitHub Release creation, prerelease flag, and no package publish command. | release_workflow_publishes_only_main_reachable_tags | docs/releases/v0.1.0-alpha.1.md | Static workflow contract regression. | | RELEASE-TP-002 | implemented | Policy guard | RELEASE-REQ-006 | Structured policy forbids tag mutation and names existing-valid-tag publication as recovery. | release_tag_recovery_policy_is_structured | docs/topics/release-process/policy.toml | Policy evidence is structured, not prose. | -| RELEASE-TP-003 | implemented | Boundary guard | RELEASE-REQ-007 | Structured policy captures the v0.2 Core schema scope and non-goals for lowering, encoder, bytes, digests, targets, and admission. | release_policy_tracks_v0_2_boundary | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the Core milestone. | -| RELEASE-TP-004 | implemented | Consistency guard | RELEASE-REQ-008 | Published alpha changelog section dates equal the actual tag dates recorded in structured release policy. | alpha_changelog_dates_match_release_policy | CHANGELOG.md, docs/topics/release-process/policy.toml | Prevents release chronology drift across release prep and publication. | +| RELEASE-TP-003 | policy | Boundary guard | RELEASE-REQ-007 | Review confirms structured policy captures the v0.2 Core schema scope and non-goals for lowering, encoder, bytes, digests, targets, and admission. | - | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the Core milestone. Frozen historical record; reviewed, not string-tested. Block structure is covered by `release_policy_blocks_are_structurally_complete`. | +| RELEASE-TP-004 | implemented | Consistency guard | RELEASE-REQ-008 | Reconciliation reports drift when a release's policy, changelog, and release-notes dates agree with each other but contradict the git tag, and reports uncovered surfaces separately from drift. | release_date_reconciliation_reports_drift_and_gaps, release_date_reconciliation_accepts_dates_matching_their_tags | CHANGELOG.md, docs/topics/release-process/policy.toml | The previous guard compared changelog against policy, which release-prep writes from one field, so both drifted together undetected. | | RELEASE-TP-005 | implemented | Runbook guard | RELEASE-REQ-009 | Structured policy names the release-prep phases and required checks for local verification, PR checks, and release existence. | release_runbook_policy_is_structured | docs/topics/release-process/policy.toml, docs/topics/release-process/runbook.md | Keeps the human runbook tied to a stable release contract. | -| RELEASE-TP-006 | implemented | Boundary guard | RELEASE-REQ-010 | Structured policy captures the v0.3 compiler-spine, canonical encoder, reviewed golden fixture, exact digest, target-lowering, and admission boundaries. | release_policy_tracks_v0_3_boundary | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the compiler-spine milestone. | -| RELEASE-TP-007 | implemented | Boundary guard | RELEASE-REQ-011 | Structured policy captures the v0.4 target-profile, lowerability, contract-bundle validation, target-lowering, admission, and publication boundaries. | release_policy_tracks_v0_4_boundary | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the target-profile and lowerability milestone. | +| RELEASE-TP-006 | policy | Boundary guard | RELEASE-REQ-010 | Review confirms structured policy captures the v0.3 compiler-spine, canonical encoder, reviewed golden fixture, exact digest, target-lowering, and admission boundaries. | - | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the compiler-spine milestone. Frozen historical record; reviewed, not string-tested. Block structure is covered by `release_policy_blocks_are_structurally_complete`. | +| RELEASE-TP-007 | policy | Boundary guard | RELEASE-REQ-011 | Review confirms structured policy captures the v0.4 target-profile, lowerability, contract-bundle validation, target-lowering, admission, and publication boundaries. | - | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the target-profile and lowerability milestone. Frozen historical record; reviewed, not string-tested. Block structure is covered by `release_policy_blocks_are_structurally_complete`. | | RELEASE-TP-008 | implemented | Automation guard | RELEASE-REQ-012, RELEASE-REQ-013 | The auto-release workflow watches successful `main` CI, derives tags from merged release-prep PRs, refuses tag moves, dispatches release publication, and the Release workflow closes zero-open milestones after publication. | release_automation_policy_is_structured, auto_release_tag_workflow_is_guarded, release_workflow_supports_dispatch_and_milestone_closure | .github/workflows/auto-release-tag.yml, .github/workflows/release.yml, docs/topics/release-process/policy.toml | Keeps release automation deterministic and non-mutating. | -| RELEASE-TP-009 | implemented | Boundary guard | RELEASE-REQ-014 | Structured policy captures the v0.5 admission-boundary scope and explicit non-goals for Continuum-owned policy, identity, delegation, revocation, ledger persistence, signature verification, target lowering, and crates.io publication. | release_policy_tracks_v0_5_boundary | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the Gate C admission milestone. | +| RELEASE-TP-009 | policy | Boundary guard | RELEASE-REQ-014 | Review confirms structured policy captures the v0.5 admission-boundary scope and explicit non-goals for Continuum-owned policy, identity, delegation, revocation, ledger persistence, signature verification, target lowering, and crates.io publication. | - | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the Gate C admission milestone. Frozen historical record; reviewed, not string-tested. Block structure is covered by `release_policy_blocks_are_structurally_complete`. | | RELEASE-TP-010 | implemented | Recovery guard | RELEASE-REQ-015 | The manual auto-release recovery path requires a successful main-CI release-prep merge, derives the tag from the merged release-prep PR, and rejects mismatched operator tag input before writing release outputs. | auto_release_tag_manual_dispatch_checks_verified_main_sha, auto_release_tag_workflow_is_guarded | .github/workflows/auto-release-tag.yml, docs/topics/release-process/policy.toml | Keeps manual recovery idempotent without allowing arbitrary tag/SHA pairing. | | RELEASE-TP-011 | implemented | Audit guard | RELEASE-REQ-016 | Structured policy defines the `docs/topics/` coverage and accuracy formulas, requires issue-or-PR evidence before merge, records release-blocking evidence fields, requires stale current-truth claims to be corrected or removed before counting as accurate, and sets both floors to at least 90%. | release_topic_audit_policy_sets_minimums | docs/topics/release-process/policy.toml | Keeps release preparation from shipping stale or under-reviewed topic shelves. | -| RELEASE-TP-012 | implemented | Boundary guard | RELEASE-REQ-017 | Structured policy captures the v0.6 developer-tooling scope, supported VS Code/Cursor integration, topic-shelf audit, and explicit non-goals for compiler CLI, language-server diagnostics, marketplace publication, target lowering, and admission tooling. | release_policy_tracks_v0_6_boundary | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the developer-tooling milestone. | +| RELEASE-TP-012 | policy | Boundary guard | RELEASE-REQ-017 | Review confirms structured policy captures the v0.6 developer-tooling scope, supported VS Code/Cursor integration, topic-shelf audit, and explicit non-goals for compiler CLI, language-server diagnostics, marketplace publication, target lowering, and admission tooling. | - | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the developer-tooling milestone. Frozen historical record; reviewed, not string-tested. Block structure is covered by `release_policy_blocks_are_structurally_complete`. | | RELEASE-TP-013 | implemented | Runbook guard | RELEASE-REQ-018 | Structured policy requires release thesis, previous-tag diff reconciliation, milestone-zero evidence at tag time, no-crates verification, release-report sections, and next-release thesis evidence. | release_runbook_policy_is_structured | docs/topics/release-process/policy.toml | Makes release claim integrity durable before and after publication. | -| RELEASE-TP-014 | implemented | Boundary guard | RELEASE-REQ-019 | Structured policy captures the v0.7 file-backed authority-facts scope, first compiler fact classes, governance design note, policy hardening, review fallback, and explicit non-goals for trusted authorship, full manifests, broader fact corpora, target IR, admission execution, and crates.io publication. | release_policy_tracks_v0_7_boundary | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the authority-facts milestone. | -| RELEASE-TP-015 | implemented | Boundary guard | RELEASE-REQ-020 | Structured policy captures the v0.8 minimal effectful compiler-spine scope, Core effect-node model, file-backed fact dependency, unsupported-form rejection boundary, pure Core golden stability, and explicit non-goals for target IR, runtime execution, CLI, admission, governance, and crates.io publication. | release_policy_tracks_v0_8_boundary | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the effectful compiler-spine milestone. | -| RELEASE-TP-016 | implemented | Boundary guard | RELEASE-REQ-021 | Structured policy captures the v0.9 first Target IR scope, Echo and git-warp review-artifact boundary, lowerability bridge, stable target-lowering failure boundary, and explicit non-goals for runtime execution, canonical target bytes, bundles, admission, v2 adapters, CLI, and crates.io publication. | release_policy_tracks_v0_9_boundary | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the first Target IR milestone. | -| RELEASE-TP-017 | implemented | Boundary guard | RELEASE-REQ-022 | Structured policy captures the v0.10 first public CLI scope, JSONL check workflow, deterministic input expansion, stream record schemas, stable diagnostic kind codes, golden fixture corpus, and explicit non-goals for compile/lower/explain/bundle/admission commands, human-pretty output, embedded schema validation, language server, marketplace packaging, participant policy, and crates.io publication. | release_policy_tracks_v0_10_boundary | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the first public CLI milestone. | -| RELEASE-TP-018 | implemented | Boundary guard | RELEASE-REQ-023 | Structured policy captures the v0.11 contract-bundle assembly and canonical Target IR artifact freeze scope, including semantic/release bundle digest goldens, Target IR byte/digest goldens, computed bundle integration, and explicit non-goals for runtime execution, admission execution, participant policy logic, Echo verifier completeness, git-warp commit creation, git-warp CRDT reducer verification, general target plugin dispatch, additional target profiles, extra source-to-target fixtures, canonical bundle-manifest bytes, and crates.io publication. | release_policy_tracks_v0_11_boundary | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the v0.11 cryptographic freeze. | -| RELEASE-TP-019 | implemented | Scaffolding guard | RELEASE-REQ-024 | Given a temp repo skeleton with the current release-process surfaces, `cargo xtask release-prep ` writes the version bump, lockfile package versions, dated changelog section, release policy boundary block, release notes stub, boundary test stub, changelog date guard entry, and paired planned release-process rows deterministically. | release_prep_scaffolds_version_policy_changelog_and_test_stub | xtask/src/release_prep.rs, xtask/src/tests.rs | Keeps release-prep setup mechanical so review focuses on release thesis, scope, non-goals, and evidence rather than missed boilerplate. | +| RELEASE-TP-014 | policy | Boundary guard | RELEASE-REQ-019 | Review confirms structured policy captures the v0.7 file-backed authority-facts scope, first compiler fact classes, governance design note, policy hardening, review fallback, and explicit non-goals for trusted authorship, full manifests, broader fact corpora, target IR, admission execution, and crates.io publication. | - | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the authority-facts milestone. Frozen historical record; reviewed, not string-tested. Block structure is covered by `release_policy_blocks_are_structurally_complete`. | +| RELEASE-TP-015 | policy | Boundary guard | RELEASE-REQ-020 | Review confirms structured policy captures the v0.8 minimal effectful compiler-spine scope, Core effect-node model, file-backed fact dependency, unsupported-form rejection boundary, pure Core golden stability, and explicit non-goals for target IR, runtime execution, CLI, admission, governance, and crates.io publication. | - | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the effectful compiler-spine milestone. Frozen historical record; reviewed, not string-tested. Block structure is covered by `release_policy_blocks_are_structurally_complete`. | +| RELEASE-TP-016 | policy | Boundary guard | RELEASE-REQ-021 | Review confirms structured policy captures the v0.9 first Target IR scope, Echo and git-warp review-artifact boundary, lowerability bridge, stable target-lowering failure boundary, and explicit non-goals for runtime execution, canonical target bytes, bundles, admission, v2 adapters, CLI, and crates.io publication. | - | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the first Target IR milestone. Frozen historical record; reviewed, not string-tested. Block structure is covered by `release_policy_blocks_are_structurally_complete`. | +| RELEASE-TP-017 | policy | Boundary guard | RELEASE-REQ-022 | Review confirms structured policy captures the v0.10 first public CLI scope, JSONL check workflow, deterministic input expansion, stream record schemas, stable diagnostic kind codes, golden fixture corpus, and explicit non-goals for compile/lower/explain/bundle/admission commands, human-pretty output, embedded schema validation, language server, marketplace packaging, participant policy, and crates.io publication. | - | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the first public CLI milestone. Frozen historical record; reviewed, not string-tested. Block structure is covered by `release_policy_blocks_are_structurally_complete`. | +| RELEASE-TP-018 | policy | Boundary guard | RELEASE-REQ-023 | Review confirms structured policy captures the v0.11 contract-bundle assembly and canonical Target IR artifact freeze scope, including semantic/release bundle digest goldens, Target IR byte/digest goldens, computed bundle integration, and explicit non-goals for runtime execution, admission execution, participant policy logic, Echo verifier completeness, git-warp commit creation, git-warp CRDT reducer verification, general target plugin dispatch, additional target profiles, extra source-to-target fixtures, canonical bundle-manifest bytes, and crates.io publication. | - | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the v0.11 cryptographic freeze. Frozen historical record; reviewed, not string-tested. Block structure is covered by `release_policy_blocks_are_structurally_complete`. | +| RELEASE-TP-019 | implemented | Scaffolding guard | RELEASE-REQ-024 | Given a temp repo skeleton with the current release-process surfaces, `cargo xtask release-prep ` writes the version bump, lockfile package versions, dated changelog section, release policy boundary block, release notes stub, and paired planned release-process rows deterministically, and writes no Rust test stub. | release_prep_scaffolds_version_policy_changelog_and_test_stub | xtask/src/release_prep.rs, xtask/src/tests.rs | Keeps release-prep setup mechanical so review focuses on release thesis, scope, non-goals, and evidence rather than missed boilerplate. | +| RELEASE-TP-020 | implemented | Consistency guard | RELEASE-REQ-025 | Every `[release_notes.*]` block parses with a unique section and tag, an ISO `target_date`, a known status, and `scope`/`non_goals` lists, and published blocks retain no scaffold placeholders. | release_policy_blocks_are_structurally_complete | docs/topics/release-process/policy.toml | Replaces eleven near-duplicate per-release guards with one data-driven check. | +| RELEASE-TP-021 | implemented | Boundary guard | RELEASE-REQ-025 | Block-scoped parsing reports a release's own `target_date` even when a different release in the same file carries the expected date string. | release_policy_block_parsing_scopes_fields_to_their_own_release | docs/topics/release-process/policy.toml | Regression guard: whole-file substring matching passed a wrong per-release date whenever any other release shared the expected string, which is normal for releases tagged the same day. | ## Determinism Obligations @@ -129,4 +132,10 @@ Out of scope: `next_release_target_date` still seeds the next value by adding 14 days to the last entry. Seeded from the realigned history it computes 2026-07-14, which is already past, so `release-prep` needs an explicit or clock-derived date before - the next release rather than the biweekly increment. + the next release rather than the biweekly increment. `cargo xtask + release-dates` will report the resulting drift once the tag exists, so this + fails loudly at release time rather than silently. +- Per-release scope and non-goal content is reviewed rather than string-tested. + `release_policy_blocks_are_structurally_complete` proves each block is present + and complete, but nothing mechanically checks that a block's declared scope + matches what the release actually shipped. diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 833eec6..684983c 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -6,6 +6,7 @@ mod goldens; mod provider_components; mod provider_contract_pack; mod provider_dependencies; +mod release_dates; mod release_prep; mod util; @@ -25,6 +26,7 @@ use goldens::{ use provider_components::{provider_component_fixtures, ProviderComponentFixtureMode}; use provider_contract_pack::{provider_contract_pack, ProviderContractPackMode}; use provider_dependencies::provider_runtime_dependencies; +use release_dates::release_dates; use release_prep::release_prep; use util::{diff_check_base, repo_root, run_cmd, run_cmd_slice}; @@ -125,15 +127,27 @@ fn run() -> Result<(), String> { } provider_runtime_dependencies(&repo_root()?) } + Some("release-dates") => run_release_dates(&mut args), Some("verify") => verify(&repo_root()?), Some(cmd) => Err(format!("unknown xtask command `{cmd}`")), None => Err( - "usage: cargo xtask " + "usage: cargo xtask " .into(), ), } } +fn run_release_dates(args: &mut impl Iterator) -> Result<(), String> { + match args.next().as_deref() { + Some("--check") | None => {} + Some(flag) => return Err(format!("unknown release-dates flag `{flag}`")), + } + if let Some(extra) = args.next() { + return Err(format!("unexpected release-dates argument `{extra}`")); + } + release_dates(&repo_root()?) +} + fn run_authority_facts_goldens(args: &mut impl Iterator) -> Result<(), String> { let mode = match args.next().as_deref() { Some("--write") => AuthorityFactsGoldenMode::Write, @@ -194,6 +208,7 @@ fn verify(root: &Path) -> Result<(), String> { provider_contract_pack(root, ProviderContractPackMode::Check)?; provider_runtime_dependencies(root)?; contract_check(root)?; + release_dates(root)?; let base = diff_check_base(root)?; run_cmd(root, "git", ["diff", "--check", &format!("{base}...HEAD")])?; Ok(()) diff --git a/xtask/src/release_dates.rs b/xtask/src/release_dates.rs new file mode 100644 index 0000000..02bcfda --- /dev/null +++ b/xtask/src/release_dates.rs @@ -0,0 +1,244 @@ +//! Reconcile recorded release dates against the git tags that published them. +//! +//! The structured release policy, `CHANGELOG.md`, and `docs/releases/*.md` all +//! record a date per release. Those three surfaces are written by +//! `cargo xtask release-prep` from a single field, so comparing them against +//! each other only proves the copy succeeded. This check compares them against +//! `git for-each-ref refs/tags`, which is the independent authority for when a +//! release actually happened. + +use std::collections::BTreeMap; +use std::path::Path; +use std::process::Command; + +use crate::util::read_to_string; + +/// One `[release_notes.*]` block parsed from the structured release policy. +/// +/// Parsing into blocks keeps every field lookup scoped to the release that owns +/// it. A whole-file substring search cannot distinguish one release's +/// `target_date` from another's, which matters because releases tagged on the +/// same day legitimately share a date string. +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct ReleasePolicyBlock { + pub(crate) section: String, + pub(crate) tag: Option, + pub(crate) target_date: Option, + pub(crate) status: Option, + pub(crate) body: String, +} + +pub(crate) fn parse_release_policy_blocks(policy: &str) -> Vec { + let mut blocks = Vec::new(); + let mut current: Option = None; + for line in policy.lines() { + let trimmed = line.trim(); + if trimmed.starts_with('[') { + if let Some(block) = current.take() { + blocks.push(block); + } + if let Some(section) = trimmed + .strip_prefix("[release_notes.") + .and_then(|rest| rest.strip_suffix(']')) + { + current = Some(ReleasePolicyBlock { + section: section.to_owned(), + tag: None, + target_date: None, + status: None, + body: String::new(), + }); + } + continue; + } + if let Some(block) = current.as_mut() { + block.body.push_str(line); + block.body.push('\n'); + if let Some(value) = quoted_field(trimmed, "tag") { + block.tag = Some(value); + } else if let Some(value) = quoted_field(trimmed, "target_date") { + block.target_date = Some(value); + } else if let Some(value) = quoted_field(trimmed, "status") { + block.status = Some(value); + } + } + } + if let Some(block) = current.take() { + blocks.push(block); + } + blocks +} + +fn quoted_field(line: &str, name: &str) -> Option { + let rest = line.strip_prefix(name)?.trim_start(); + let rest = rest.strip_prefix('=')?.trim_start(); + let rest = rest.strip_prefix('"')?; + let end = rest.find('"')?; + Some(rest[..end].to_owned()) +} + +pub(crate) fn changelog_release_date(changelog: &str, tag: &str) -> Option { + let needle = format!("## [{tag}] - "); + changelog.lines().find_map(|line| { + line.trim_end() + .strip_prefix(&needle) + .map(|date| date.trim().to_owned()) + }) +} + +pub(crate) fn release_notes_date(notes: &str) -> Option { + notes.lines().find_map(|line| { + line.trim_end() + .strip_prefix("Target date: ") + .map(|date| date.trim().to_owned()) + }) +} + +fn git_tag_dates(root: &Path) -> Result, String> { + let output = Command::new("git") + .args([ + "for-each-ref", + "--format=%(refname:short)\t%(creatordate:short)", + "refs/tags", + ]) + .current_dir(root) + .output() + .map_err(|err| format!("failed to run `git for-each-ref`: {err}"))?; + if !output.status.success() { + return Err("`git for-each-ref refs/tags` failed".to_owned()); + } + let text = String::from_utf8(output.stdout) + .map_err(|err| format!("`git for-each-ref` output is not utf-8: {err}"))?; + let mut dates = BTreeMap::new(); + for line in text.lines() { + if let Some((tag, date)) = line.split_once('\t') { + let (tag, date) = (tag.trim(), date.trim()); + if !tag.is_empty() && !date.is_empty() { + dates.insert(tag.to_owned(), date.to_owned()); + } + } + } + Ok(dates) +} + +/// Outcome of comparing recorded dates against tag dates. +/// +/// A recorded date that contradicts its tag is `drift` and fails the gate. A +/// surface that does not exist has no date to contradict anything; the earliest +/// releases predate these surfaces, so absence is reported as a `gap` rather +/// than failing an otherwise-correct history. +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub(crate) struct ReleaseDateReport { + pub(crate) drift: Vec, + pub(crate) gaps: Vec, +} + +/// Pure comparison over already-read inputs. +/// +/// `release_notes` maps a tag to the date parsed from its release notes; an +/// absent key means the notes file itself is missing, and a `None` value means +/// the file exists without a `Target date:` line. +pub(crate) fn reconcile_release_dates( + tags: &BTreeMap, + policy: &str, + changelog: &str, + release_notes: &BTreeMap>, +) -> ReleaseDateReport { + let blocks = parse_release_policy_blocks(policy); + let mut drift = Vec::new(); + let mut gaps = Vec::new(); + + for (tag, tag_date) in tags { + match blocks + .iter() + .find(|block| block.tag.as_deref() == Some(tag.as_str())) + { + Some(block) => { + let section = &block.section; + match block.target_date.as_deref() { + Some(date) if date == tag_date => {} + Some(date) => drift.push(format!( + "policy.toml [release_notes.{section}] target_date is {date}, but tag {tag} was created {tag_date}" + )), + None => gaps.push(format!( + "policy.toml [release_notes.{section}] has no target_date for tag {tag}" + )), + } + if block.status.as_deref() != Some("published") { + let status = block.status.as_deref().unwrap_or(""); + drift.push(format!( + "policy.toml [release_notes.{section}] has status `{status}`, but tag {tag} exists" + )); + } + for field in ["scope = [", "non_goals = ["] { + if !block.body.contains(field) { + gaps.push(format!( + "policy.toml [release_notes.{section}] is missing `{field}` for tag {tag}" + )); + } + } + } + None => gaps.push(format!( + "policy.toml has no [release_notes.*] block for tag {tag}" + )), + } + + match changelog_release_date(changelog, tag) { + Some(date) if date == *tag_date => {} + Some(date) => drift.push(format!( + "CHANGELOG.md `## [{tag}]` is dated {date}, but the tag was created {tag_date}" + )), + None => gaps.push(format!("CHANGELOG.md has no `## [{tag}]` section")), + } + + match release_notes.get(tag) { + Some(Some(date)) if date == tag_date => {} + Some(Some(date)) => drift.push(format!( + "docs/releases/{tag}.md records `Target date: {date}`, but the tag was created {tag_date}" + )), + Some(None) => gaps.push(format!("docs/releases/{tag}.md has no `Target date:` line")), + None => gaps.push(format!("docs/releases/{tag}.md is missing")), + } + } + + ReleaseDateReport { drift, gaps } +} + +pub(crate) fn release_dates(root: &Path) -> Result<(), String> { + let tags = git_tag_dates(root)?; + if tags.is_empty() { + // A shallow clone without tags cannot reconcile anything. Say so out + // loud rather than reporting a vacuous pass. + println!("release-dates: no git tags present; reconciliation skipped"); + return Ok(()); + } + + let policy = read_to_string(&root.join("docs/topics/release-process/policy.toml"))?; + let changelog = read_to_string(&root.join("CHANGELOG.md"))?; + let mut release_notes = BTreeMap::new(); + for tag in tags.keys() { + let notes_path = root.join(format!("docs/releases/{tag}.md")); + if notes_path.is_file() { + release_notes.insert( + tag.clone(), + release_notes_date(&read_to_string(¬es_path)?), + ); + } + } + + let report = reconcile_release_dates(&tags, &policy, &changelog, &release_notes); + for gap in &report.gaps { + println!("release-dates: uncovered - {gap}"); + } + if report.drift.is_empty() { + let (count, gap_count) = (tags.len(), report.gaps.len()); + println!( + "release-dates: {count} tag(s) reconciled against git, {gap_count} uncovered surface(s)" + ); + return Ok(()); + } + Err(format!( + "release dates disagree with git tags:\n {}", + report.drift.join("\n ") + )) +} diff --git a/xtask/src/release_prep.rs b/xtask/src/release_prep.rs index ebcfcea..8d050e1 100644 --- a/xtask/src/release_prep.rs +++ b/xtask/src/release_prep.rs @@ -9,7 +9,6 @@ struct ReleasePrepVersion { tag: String, package_version: String, policy_key: String, - boundary_test_name: String, } impl ReleasePrepVersion { @@ -29,16 +28,10 @@ impl ReleasePrepVersion { } let alpha = parse_decimal(alpha, "alpha")?; let policy_key = format!("v{major}_{minor}_{patch}_alpha_{alpha}"); - let boundary_key = if patch == 0 && alpha == 1 { - format!("v{major}_{minor}") - } else { - policy_key.clone() - }; Ok(Self { tag: input.to_owned(), package_version: package_version.to_owned(), policy_key, - boundary_test_name: format!("release_policy_tracks_{boundary_key}_boundary"), }) } @@ -68,7 +61,6 @@ pub(crate) fn release_prep(root: &Path, input: &str) -> Result<(), String> { let changelog_path = root.join("CHANGELOG.md"); let policy_path = root.join("docs/topics/release-process/policy.toml"); let test_plan_path = root.join("docs/topics/release-process/test-plan.md"); - let xtask_tests_path = root.join("xtask/src/tests.rs"); let release_notes_path = release_notes_path(root, &version); if release_notes_path.exists() { @@ -98,11 +90,6 @@ pub(crate) fn release_prep(root: &Path, input: &str) -> Result<(), String> { let policy = append_release_policy_block(&policy, &version, &target_date)?; let release_notes = render_release_notes_stub(&version, &target_date); let test_plan = insert_release_test_plan_rows(&read_to_string(&test_plan_path)?, &version)?; - let xtask_tests = insert_release_boundary_test_stub( - &read_to_string(&xtask_tests_path)?, - &version, - &target_date, - )?; write_file(&cli_manifest_path, &cli_manifest)?; write_file(&syntax_manifest_path, &syntax_manifest)?; @@ -111,7 +98,6 @@ pub(crate) fn release_prep(root: &Path, input: &str) -> Result<(), String> { write_file(&policy_path, &policy)?; write_file(&release_notes_path, &release_notes)?; write_file(&test_plan_path, &test_plan)?; - write_file(&xtask_tests_path, &xtask_tests)?; println!( "release-prep: scaffolded {} with target date {}", @@ -300,7 +286,7 @@ fn insert_release_test_plan_rows( test_plan: &str, version: &ReleasePrepVersion, ) -> Result { - if test_plan.contains(&version.tag) || test_plan.contains(&version.boundary_test_name) { + if test_plan.contains(&version.tag) { return Err(format!( "release-process test plan already contains {}", version.tag @@ -330,99 +316,13 @@ fn insert_release_test_plan_rows( &with_fixture, "\n## Determinism Obligations\n", &format!( - "| {tp_id} | planned | Boundary guard | {req_id} | Structured policy captures the `{tag}` scope and non-goal boundary after scaffold placeholders are replaced. | {test_name} | docs/topics/release-process/policy.toml, docs/releases/{tag}.md | Scaffolded by `cargo xtask release-prep {tag}`; reviewers must replace TODO scope/non-goal values before release. |\n", + "| {tp_id} | planned | Boundary guard | {req_id} | Review confirms structured policy captures the `{tag}` scope and non-goal boundary after scaffold placeholders are replaced. | - | docs/topics/release-process/policy.toml, docs/releases/{tag}.md | Scaffolded by `cargo xtask release-prep {tag}`; reviewers must replace TODO scope/non-goal values before release. Block structure is checked by `release_policy_blocks_are_structurally_complete`; dates are reconciled against git tags by `cargo xtask release-dates`. |\n", tag = version.tag, - test_name = version.boundary_test_name, ), "release-process test case table", ) } -fn insert_release_boundary_test_stub( - source: &str, - version: &ReleasePrepVersion, - target_date: &str, -) -> Result { - if source.contains(&format!("fn {}()", version.boundary_test_name)) { - return Err(format!( - "xtask source already contains {}", - version.boundary_test_name - )); - } - let date_case = format!(" (\"{}\", \"{target_date}\"),\n", version.tag); - let with_date = insert_in_alpha_changelog_date_cases(source, &date_case)?; - let alpha_marker = - if with_date.contains(" #[test]\n fn alpha_changelog_dates_match_release_policy()") { - " #[test]\n fn alpha_changelog_dates_match_release_policy()" - } else { - "fn alpha_changelog_dates_match_release_policy()" - }; - insert_before_once( - &with_date, - alpha_marker, - &format!( - "\ - #[test] - fn {test_name}() {{ - let root = repo_root().expect(\"repo root\"); - let policy = fs::read_to_string(root.join(\"docs/topics/release-process/policy.toml\")) - .expect(\"release policy\"); - let release_policy = toml_section(&policy, \"{section}\"); - for required in [ - \"{section}\", - \"tag = \\\"{tag}\\\"\", - \"target_date = \\\"{target_date}\\\"\", - \"status = \\\"prep\\\"\", - \"scope = [\", - \"non_goals = [\", - ] {{ - assert!( - release_policy.contains(required), - \"{tag} release policy missing structured field: {{required}}\" - ); - }} - for forbidden in [ - \"TODO_release_scope\", - \"TODO_release_non_goal\", - ] {{ - assert!( - !release_policy.contains(forbidden), - \"{tag} release policy must replace scaffold placeholder: {{forbidden}}\" - ); - }} - }} - -", - test_name = version.boundary_test_name, - section = version.policy_section(), - tag = version.tag, - ), - "xtask alpha changelog date guard", - ) -} - -fn insert_in_alpha_changelog_date_cases(source: &str, date_case: &str) -> Result { - if source.contains(date_case.trim()) { - return Err("alpha changelog date guard already contains release".into()); - } - let function_start = source - .find("fn alpha_changelog_dates_match_release_policy()") - .ok_or_else(|| { - "xtask source missing alpha_changelog_dates_match_release_policy".to_owned() - })?; - let tail = &source[function_start..]; - let list_end = tail - .find("] {") - .ok_or_else(|| "alpha changelog date guard missing tuple list terminator".to_owned())?; - let line_start = tail[..list_end].rfind('\n').map_or(0, |index| index + 1); - let insert_at = function_start + line_start; - let mut output = String::with_capacity(source.len() + date_case.len()); - output.push_str(&source[..insert_at]); - output.push_str(date_case); - output.push_str(&source[insert_at..]); - Ok(output) -} - fn next_numbered_id(text: &str, prefix: &str) -> Result { let mut max_id = None; let mut width = 3usize; diff --git a/xtask/src/tests.rs b/xtask/src/tests.rs index 43a2205..d8ba914 100644 --- a/xtask/src/tests.rs +++ b/xtask/src/tests.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeSet; +use std::collections::{BTreeMap, BTreeSet}; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; @@ -1680,8 +1680,6 @@ fn release_runbook_policy_is_structured() { "dated_changelog_section", "release_policy_boundary_block", "release_notes_stub", - "release_boundary_test_stub", - "alpha_changelog_date_guard_entry", "release_process_test_plan_rows", ] { assert!( @@ -1747,21 +1745,16 @@ fn release_prep_scaffolds_version_policy_changelog_and_test_stub() { let test_plan = fs::read_to_string(root.join("docs/topics/release-process/test-plan.md")) .expect("test plan"); assert!( - test_plan.contains("RELEASE-REQ-025") - && test_plan.contains("RELEASE-TP-020") - && test_plan.contains("release_policy_tracks_v0_12_boundary"), + test_plan.contains("RELEASE-REQ-025") && test_plan.contains("RELEASE-TP-020"), "release prep must add paired requirement and test-plan rows" ); + // Release prep no longer writes Rust test stubs. Per-release policy content + // is reviewed, block structure is covered by one data-driven test, and + // dates are reconciled against git tags by `cargo xtask release-dates`. let xtask_source = fs::read_to_string(root.join("xtask/src/tests.rs")).expect("xtask tests"); assert!( - xtask_source.contains("fn release_policy_tracks_v0_12_boundary()") - && xtask_source.contains("(\"v0.12.0-alpha.1\", \"2026-11-18\")"), - "release prep must add the boundary test stub and changelog date case" - ); - assert!( - xtask_source.contains("!release_policy.contains(forbidden)") - && xtask_source.contains("release policy must replace scaffold placeholder"), - "release boundary test must fail while scaffold placeholders remain" + !xtask_source.contains("release_policy_tracks_v0_12_boundary"), + "release prep must not scaffold per-release boundary test stubs" ); } @@ -2723,410 +2716,193 @@ fn release_automation_policy_is_structured() { } #[test] -fn release_policy_tracks_v0_3_boundary() { +fn release_policy_blocks_are_structurally_complete() { let root = repo_root().expect("repo root"); let policy = fs::read_to_string(root.join("docs/topics/release-process/policy.toml")) .expect("release policy"); - for required in [ - "[release_notes.v0_3_0_alpha_1]", - "tag = \"v0.3.0-alpha.1\"", - "target_date = \"2026-06-24\"", - "status = \"published\"", - "compiler_spine", - "surface_validation_split", - "canonical_core_encoder", - "reviewed_core_golden_fixture", - "exact_core_digest_fixture", - "no_target_lowering", - "no_bundle_admission", - ] { - assert!( - policy.contains(required), - "v0.3 release policy missing structured field: {required}" - ); - } -} + let blocks = crate::release_dates::parse_release_policy_blocks(&policy); -#[test] -fn release_policy_tracks_v0_4_boundary() { - let root = repo_root().expect("repo root"); - let policy = fs::read_to_string(root.join("docs/topics/release-process/policy.toml")) - .expect("release policy"); - for required in [ - "[release_notes.v0_4_0_alpha_1]", - "tag = \"v0.4.0-alpha.1\"", - "target_date = \"2026-06-24\"", - "status = \"published\"", - "target_profile_conformance", - "lowerability_direct_adapter", - "contract_bundle_manifest_validation", - "no_target_lowerer_execution", - "no_admission_policy", - "no_crates_io_publish", - ] { - assert!( - policy.contains(required), - "v0.4 release policy missing structured field: {required}" - ); - } -} + assert!( + blocks.len() >= 10, + "release policy should retain a block per published release, found {}", + blocks.len() + ); -#[test] -fn release_policy_tracks_v0_5_boundary() { - let root = repo_root().expect("repo root"); - let policy = fs::read_to_string(root.join("docs/topics/release-process/policy.toml")) - .expect("release policy"); - for required in [ - "[release_notes.v0_5_0_alpha_1]", - "tag = \"v0.5.0-alpha.1\"", - "target_date = \"2026-06-25\"", - "status = \"published\"", - "edict_owned_continuum_participation_boundary", - "gate_c_admission_request_validation", - "gate_c_admission_receipt_validation", - "admission_request_digest_binding", - "policy_epoch_receipt_binding", - "invocation_capability_evidence_validation", - "release_automation_after_main_ci", - "no_participant_policy_evaluation", - "no_participant_identity_delegation_or_revocation", - "no_admission_ledger_persistence", - "no_signature_verification", - "no_target_lowerer_execution", - "no_bundle_digest_recomputation", - "no_crates_io_publish", - ] { + let mut seen_sections = BTreeSet::new(); + let mut seen_tags = BTreeSet::new(); + for block in &blocks { + let section = &block.section; assert!( - policy.contains(required), - "v0.5 release policy missing structured field: {required}" + seen_sections.insert(section.clone()), + "duplicate release policy section [release_notes.{section}]" ); - } -} - -#[test] -fn release_policy_tracks_v0_6_boundary() { - let root = repo_root().expect("repo root"); - let policy = fs::read_to_string(root.join("docs/topics/release-process/policy.toml")) - .expect("release policy"); - let v0_6_policy = toml_section(&policy, "[release_notes.v0_6_0_alpha_1]"); - for required in [ - "[release_notes.v0_6_0_alpha_1]", - "tag = \"v0.6.0-alpha.1\"", - "target_date = \"2026-06-26\"", - "status = \"published\"", - "editor_highlight_roles", - "tree_sitter_grammar_source", - "textmate_grammar_artifact", - "vscode_cursor_extension_package", - "topic_shelf_coverage_audit", - "no_compiler_cli", - "no_language_server", - "no_marketplace_publication", - "no_target_lowering", - "no_admission_tooling", - "no_crates_io_publish", - ] { + let Some(tag) = block.tag.as_deref() else { + panic!("[release_notes.{section}] has no tag"); + }; assert!( - v0_6_policy.contains(required), - "v0.6 release policy missing structured field: {required}" + seen_tags.insert(tag.to_owned()), + "duplicate release policy tag {tag} in [release_notes.{section}]" ); - } -} - -#[test] -fn release_policy_tracks_v0_7_boundary() { - let root = repo_root().expect("repo root"); - let policy = fs::read_to_string(root.join("docs/topics/release-process/policy.toml")) - .expect("release policy"); - let v0_7_policy = toml_section(&policy, "[release_notes.v0_7_0_alpha_1]"); - for required in [ - "[release_notes.v0_7_0_alpha_1]", - "tag = \"v0.7.0-alpha.1\"", - "target_date = \"2026-06-27\"", - "status = \"published\"", - "release_issue = 59", - "published_at = \"2026-06-27T22:31:49Z\"", - "release_url = \"https://github.com/flyingrobots/edict/releases/tag/v0.7.0-alpha.1\"", - "tag_object = \"f7888160f7f9a0d7b9b82d4f78bb38b886856a1e\"", - "peeled_commit = \"6f9c731b4f36d3283dcb448b14761832ab916b07\"", - "release_notes_source_commit = \"6f9c731b4f36d3283dcb448b14761832ab916b07\"", - "post_publication_evidence_pr = 61", - "main_ci_run = 28303787401", - "auto_release_tag_run = 28303801200", - "release_workflow_run = 28303809157", - "milestone_number = 8", - "milestone_closed_at = \"2026-06-27T22:31:50Z\"", - "milestone_open_issues = 0", - "release_assets = 0", - "crates_io_published = false", - "file_backed_authority_facts", - "operation_profile_facts", - "profile_write_class_allowances", - "effect_write_classes", - "budget_facts", - "lawpack_source_identity", - "target_profile_source_identity", - "deterministic_loaded_fact_harness", - "stable_load_failure_kinds", - "authority_fact_governance_design_note", - "release_policy_and_rust_standards_hardening", - "review_bot_fallback_policy", - "no_trusted_lawpack_or_target_profile_authorship", - "no_full_lawpack_manifest_loading", - "no_full_target_profile_manifest_loading", - "no_obstruction_obligation_adapter_footprint_cost_or_target_capability_corpus_loading", - "no_global_registry_trust_root_identity_system_or_revocation_model", - "no_target_ir_generation", - "no_full_effectful_source_lowering", - "no_admission_execution_workflow", - "no_crates_io_publish", - ] { + let Some(date) = block.target_date.as_deref() else { + panic!("[release_notes.{section}] has no target_date"); + }; assert!( - v0_7_policy.contains(required), - "v0.7 release policy missing structured field: {required}" + is_iso_date(date), + "[release_notes.{section}] target_date `{date}` is not an ISO date" ); - } -} - -#[test] -fn release_policy_tracks_v0_8_boundary() { - let root = repo_root().expect("repo root"); - let policy = fs::read_to_string(root.join("docs/topics/release-process/policy.toml")) - .expect("release policy"); - let v0_8_policy = toml_section(&policy, "[release_notes.v0_8_0_alpha_1]"); - for required in [ - "[release_notes.v0_8_0_alpha_1]", - "tag = \"v0.8.0-alpha.1\"", - "target_date = \"2026-06-28\"", - "status = \"published\"", - "release_issue = 62", - "published_at = \"2026-06-28T01:41:16Z\"", - "release_url = \"https://github.com/flyingrobots/edict/releases/tag/v0.8.0-alpha.1\"", - "tag_object = \"32e843c5e5f7b9252078c2b8a99afa23daeab411\"", - "peeled_commit = \"c6a166ccea0fcb61fff9b8d76bfb5d51d613e2eb\"", - "release_notes_source_commit = \"c6a166ccea0fcb61fff9b8d76bfb5d51d613e2eb\"", - "main_ci_run = 28307840316", - "auto_release_tag_run = 28307856119", - "release_workflow_run = 28307864582", - "milestone_number = 9", - "milestone_closed_at = \"2026-06-28T01:41:16Z\"", - "milestone_open_issues = 0", - "release_assets = 0", - "crates_io_published = false", - "minimal_effectful_compiler_spine", - "core_effect_node_model", - "core_obstruction_arm_model", - "canonical_effect_node_encoding", - "file_backed_effectful_compiler_context", - "annotated_effectful_let_lowering", - "deterministic_obstruction_map_lowering", - "source_order_stable_obstruction_binders", - "unsupported_effectful_branch_yield_rejection", - "chained_effect_call_rejection", - "typed_effect_call_rejection", - "duplicate_obstruction_failure_rejection", - "pure_core_golden_stability", - "no_target_ir_generation", - "no_target_runtime_execution", - "no_adapter_composition", - "no_public_cli", - "no_admission_execution_workflow", - "no_lawpack_governance_implementation", - "no_crates_io_publish", - ] { + let Some(status) = block.status.as_deref() else { + panic!("[release_notes.{section}] has no status"); + }; assert!( - v0_8_policy.contains(required), - "v0.8 release policy missing structured field: {required}" + matches!(status, "published" | "planned" | "prep"), + "[release_notes.{section}] has unexpected status `{status}`" ); + for field in ["scope = [", "non_goals = ["] { + assert!( + block.body.contains(field), + "[release_notes.{section}] is missing `{field}`" + ); + } + if status == "published" { + for placeholder in ["TODO_release_scope", "TODO_release_non_goal"] { + assert!( + !block.body.contains(placeholder), + "published [release_notes.{section}] still has scaffold placeholder `{placeholder}`" + ); + } + } } } -#[test] -fn release_policy_tracks_v0_9_boundary() { - let root = repo_root().expect("repo root"); - let policy = fs::read_to_string(root.join("docs/topics/release-process/policy.toml")) - .expect("release policy"); - let v0_9_policy = toml_section(&policy, "[release_notes.v0_9_0_alpha_1]"); - for required in [ - "[release_notes.v0_9_0_alpha_1]", - "tag = \"v0.9.0-alpha.1\"", - "target_date = \"2026-06-28\"", - "status = \"published\"", - "release_issue = 70", - "published_at = \"2026-06-28T07:04:06Z\"", - "release_url = \"https://github.com/flyingrobots/edict/releases/tag/v0.9.0-alpha.1\"", - "tag_object = \"c6a4ea6b10d438cd407cd7f273fecf1fd012b2d3\"", - "peeled_commit = \"81bacc5a240bd3ea50af934a3611ce6b3f505043\"", - "release_notes_source_commit = \"81bacc5a240bd3ea50af934a3611ce6b3f505043\"", - "main_ci_run = 28314566818", - "auto_release_tag_run = 28314582826", - "release_workflow_run = 28314590143", - "milestone_number = 10", - "milestone_closed_at = \"2026-06-28T07:04:06Z\"", - "milestone_open_issues = 0", - "release_assets = 0", - "crates_io_published = false", - "first_target_ir_alpha", - "echo_span_ir_review_artifact", - "gitwarp_commit_reducer_ir_review_artifact", - "lowerability_to_target_ir_bridge", - "explicit_target_profile_selection", - "stable_target_lowering_failure_kinds", - "core_obligation_preservation", - "target_ir_topic_shelf", - "no_runtime_execution", - "no_echo_verifier", - "no_gitwarp_commit_creation", - "no_gitwarp_crdt_reducer_verification", - "no_general_target_plugin_dispatch", - "no_canonical_target_ir_bytes_or_digests", - "no_bundle_or_admission_generation", - "no_v2_adapter_composition", - "no_public_cli", - "no_crates_io_publish", - ] { - assert!( - v0_9_policy.contains(required), - "v0.9 release policy missing structured field: {required}" - ); - } -} +/// Regression guard for the substring-matching flaw this test file used to +/// have. The previous per-release guards asserted +/// `policy.contains("target_date = \"...\"")` against the whole file, so a +/// release carrying a wrong date still passed whenever any other release +/// happened to carry the expected string. Releases tagged on the same day +/// share a date, so that is the normal case, not a corner case. +#[test] +fn release_policy_block_parsing_scopes_fields_to_their_own_release() { + let policy = concat!( + "[release_notes.v0_3_0_alpha_1]\n", + "tag = \"v0.3.0-alpha.1\"\n", + "target_date = \"1999-01-01\"\n", + "status = \"published\"\n", + "scope = [\n \"first_scope\",\n]\n", + "non_goals = [\n \"first_non_goal\",\n]\n", + "\n", + "[release_notes.v0_4_0_alpha_1]\n", + "tag = \"v0.4.0-alpha.1\"\n", + "target_date = \"2026-06-24\"\n", + "status = \"published\"\n", + "scope = [\n \"second_scope\",\n]\n", + "non_goals = [\n \"second_non_goal\",\n]\n", + ); + let blocks = crate::release_dates::parse_release_policy_blocks(policy); + assert_eq!(blocks.len(), 2, "both release blocks must parse"); + + let first = blocks + .iter() + .find(|block| block.tag.as_deref() == Some("v0.3.0-alpha.1")) + .expect("v0.3 block"); + + // A whole-file substring search finds the correct-looking date here... + assert!(policy.contains("target_date = \"2026-06-24\"")); + // ...but it belongs to v0.4, and block-scoped parsing reports v0.3's own. + assert_eq!(first.target_date.as_deref(), Some("1999-01-01")); + assert_eq!(first.status.as_deref(), Some("published")); + assert!(first.body.contains("first_scope")); + assert!(!first.body.contains("second_scope")); +} + +/// The reconciliation compares recorded dates against git tag dates, which is +/// the check the previous guard could not perform: it compared `CHANGELOG.md` +/// against `policy.toml`, and release-prep writes both from one field, so the +/// two agreed even while both disagreed with the tags. +#[test] +fn release_date_reconciliation_reports_drift_and_gaps() { + let policy = concat!( + "[release_notes.v0_9_0_alpha_1]\n", + "tag = \"v0.9.0-alpha.1\"\n", + "target_date = \"2026-10-07\"\n", + "status = \"published\"\n", + "scope = [\n \"only_scope\",\n]\n", + "non_goals = [\n \"only_non_goal\",\n]\n", + ); + let changelog = + "# Changelog\n\n## [v0.9.0-alpha.1] - 2026-10-07\n\n## [v0.8.0-alpha.1] - 2026-06-28\n"; + let tags = BTreeMap::from([ + ("v0.9.0-alpha.1".to_owned(), "2026-06-28".to_owned()), + ("v0.8.0-alpha.1".to_owned(), "2026-06-28".to_owned()), + ]); + let notes = BTreeMap::from([("v0.9.0-alpha.1".to_owned(), Some("2026-10-07".to_owned()))]); + + let report = crate::release_dates::reconcile_release_dates(&tags, policy, changelog, ¬es); + + // v0.9 is internally consistent across all three surfaces and still wrong. + assert_eq!( + report.drift.len(), + 3, + "policy, changelog, and notes each contradict the tag: {:?}", + report.drift + ); + assert!(report + .drift + .iter() + .any(|entry| entry.contains("target_date is 2026-10-07") + && entry.contains("created 2026-06-28"))); + assert!(report + .drift + .iter() + .any(|entry| entry.contains("CHANGELOG.md `## [v0.9.0-alpha.1]` is dated 2026-10-07"))); -#[test] -fn release_policy_tracks_v0_10_boundary() { - let root = repo_root().expect("repo root"); - let policy = fs::read_to_string(root.join("docs/topics/release-process/policy.toml")) - .expect("release policy"); - let v0_10_policy = toml_section(&policy, "[release_notes.v0_10_0_alpha_1]"); - for required in [ - "[release_notes.v0_10_0_alpha_1]", - "tag = \"v0.10.0-alpha.1\"", - "target_date = \"2026-06-29\"", - "status = \"published\"", - "release_issue = 76", - "published_at = \"2026-06-29T04:21:11Z\"", - "release_url = \"https://github.com/flyingrobots/edict/releases/tag/v0.10.0-alpha.1\"", - "tag_object = \"11e516c8ea8be5fa6739efd545c5b8fb40cbc46d\"", - "peeled_commit = \"622834138af249e70d717d6b7a940e4b01e23f4d\"", - "release_notes_source_commit = \"622834138af249e70d717d6b7a940e4b01e23f4d\"", - "main_ci_run = 28348355987", - "auto_release_tag_run = 28348383070", - "release_workflow_run = 28348397035", - "milestone_number = 11", - "milestone_closed_at = \"2026-06-29T04:21:11Z\"", - "milestone_open_issues = 0", - "release_assets = 0", - "crates_io_published = false", - "first_public_cli_surface", - "jsonl_check_workflow", - "deterministic_input_expansion", - "compiler_settings_json_schema", - "cli_stream_record_schemas", - "structured_cli_diagnostics", - "stable_diagnostic_kind_codes", - "golden_cli_fixture_corpus", - "cli_topic_shelf", - "no_compile_lower_explain_bundle_or_admission_commands", - "no_human_pretty_output", - "no_embedded_json_schema_validation_engine", - "no_language_server", - "no_marketplace_packaging", - "no_participant_policy_execution", - "no_crates_io_publish", - ] { - assert!( - v0_10_policy.contains(required), - "v0.10 release policy missing structured field: {required}" - ); - } + // v0.8 matches its tag, so its only findings are uncovered surfaces. + assert!(report + .gaps + .iter() + .any(|entry| entry.contains("no [release_notes.*] block for tag v0.8.0-alpha.1"))); + assert!(report + .gaps + .iter() + .any(|entry| entry.contains("docs/releases/v0.8.0-alpha.1.md is missing"))); + assert!( + !report.drift.iter().any(|entry| entry.contains("v0.8")), + "a correctly dated release must not be reported as drift: {:?}", + report.drift + ); } #[test] -fn release_policy_tracks_v0_11_boundary() { - let root = repo_root().expect("repo root"); - let policy = fs::read_to_string(root.join("docs/topics/release-process/policy.toml")) - .expect("release policy"); - let v0_11_policy = toml_section(&policy, "[release_notes.v0_11_0_alpha_1]"); - for required in [ - "[release_notes.v0_11_0_alpha_1]", - "tag = \"v0.11.0-alpha.1\"", - "target_date = \"2026-06-30\"", - "status = \"published\"", - "release_issue = 109", - "published_at = \"2026-06-30T07:58:29Z\"", - "release_url = \"https://github.com/flyingrobots/edict/releases/tag/v0.11.0-alpha.1\"", - "tag_object = \"e5d207527d737131e54a8d2614765e63ff7218e6\"", - "peeled_commit = \"3eb71f6127e31b68ea4e0bb766623930ce24ae46\"", - "release_notes_source_commit = \"3eb71f6127e31b68ea4e0bb766623930ce24ae46\"", - "main_ci_run = 28429259737", - "auto_release_tag_run = 28429294814", - "release_workflow_run = 28429313876", - "milestone_number = 12", - "milestone_closed_at = \"2026-06-30T07:58:29Z\"", - "milestone_open_issues = 0", - "release_assets = 0", - "crates_io_published = false", - "contract_bundle_assembly", - "semantic_bundle_digest_preimage", - "release_bundle_digest_preimage", - "bundle_digest_goldens", - "canonical_target_ir_value_model", - "canonical_target_ir_cbor_bytes", - "target_ir_artifact_digest_frame", - "target_ir_byte_digest_goldens", - "computed_target_ir_bundle_assembly", - "xtask_core_target_ir_bundle_golden_checks", - "no_runtime_execution", - "no_admission_execution", - "no_participant_policy_logic", - "no_verifier_completeness", - "no_echo_verifier_completeness", - "no_git_warp_commit_creation", - "no_git_warp_crdt_reducer_verification", - "no_general_target_plugin_dispatch", - "no_additional_target_profiles", - "no_extra_source_to_target_fixtures", - "no_canonical_contract_bundle_manifest_bytes", - "no_crates_io_publish", - ] { - assert!( - v0_11_policy.contains(required), - "v0.11 release policy missing structured field: {required}" - ); - } +fn release_date_reconciliation_accepts_dates_matching_their_tags() { + let policy = concat!( + "[release_notes.v0_8_0_alpha_1]\n", + "tag = \"v0.8.0-alpha.1\"\n", + "target_date = \"2026-06-28\"\n", + "status = \"published\"\n", + "scope = [\n \"only_scope\",\n]\n", + "non_goals = [\n \"only_non_goal\",\n]\n", + ); + let changelog = "# Changelog\n\n## [v0.8.0-alpha.1] - 2026-06-28\n"; + let tags = BTreeMap::from([("v0.8.0-alpha.1".to_owned(), "2026-06-28".to_owned())]); + let notes = BTreeMap::from([("v0.8.0-alpha.1".to_owned(), Some("2026-06-28".to_owned()))]); + + let report = crate::release_dates::reconcile_release_dates(&tags, policy, changelog, ¬es); + assert!( + report.drift.is_empty(), + "unexpected drift: {:?}", + report.drift + ); + assert!(report.gaps.is_empty(), "unexpected gaps: {:?}", report.gaps); } -#[test] -fn alpha_changelog_dates_match_release_policy() { - let root = repo_root().expect("repo root"); - let changelog = fs::read_to_string(root.join("CHANGELOG.md")).expect("changelog"); - let policy = fs::read_to_string(root.join("docs/topics/release-process/policy.toml")) - .expect("release policy"); - for (tag, target) in [ - ("v0.2.0-alpha.1", "2026-06-21"), - ("v0.3.0-alpha.1", "2026-06-24"), - ("v0.4.0-alpha.1", "2026-06-24"), - ("v0.5.0-alpha.1", "2026-06-25"), - ("v0.6.0-alpha.1", "2026-06-26"), - ("v0.7.0-alpha.1", "2026-06-27"), - ("v0.8.0-alpha.1", "2026-06-28"), - ("v0.9.0-alpha.1", "2026-06-28"), - ("v0.10.0-alpha.1", "2026-06-29"), - ("v0.11.0-alpha.1", "2026-06-30"), - ] { - assert!( - policy.contains(&format!("tag = \"{tag}\"")), - "release policy missing tag {tag}" - ); - assert!( - policy.contains(&format!("target_date = \"{target}\"")), - "release policy missing target date {target}" - ); - assert!( - changelog.contains(&format!("## [{tag}] - {target}")), - "{tag} changelog date must match release policy target date {target}" - ); - } +fn is_iso_date(value: &str) -> bool { + let bytes = value.as_bytes(); + bytes.len() == 10 + && bytes[4] == b'-' + && bytes[7] == b'-' + && bytes + .iter() + .enumerate() + .all(|(index, byte)| index == 4 || index == 7 || byte.is_ascii_digit()) } fn wit_named_type(interface: &Interface, name: &str) -> TypeId { @@ -3984,31 +3760,6 @@ fn legacy_target_profile_wit_is_not_provider_envelope() { .contains_key(&WorldKey::Name("verify".to_owned()))); } -#[test] -fn release_policy_tracks_v0_2_boundary() { - let root = repo_root().expect("repo root"); - let policy = fs::read_to_string(root.join("docs/topics/release-process/policy.toml")) - .expect("release policy"); - for required in [ - "[release_notes.v0_2_0_alpha_1]", - "tag = \"v0.2.0-alpha.1\"", - "target_date = \"2026-06-21\"", - "core_semantic_model", - "normative_core_schema", - "no_source_to_core_lowering", - "no_canonical_encoder", - "no_golden_core_bytes", - "no_exact_core_digests", - "no_target_lowering", - "no_bundle_admission", - ] { - assert!( - policy.contains(required), - "v0.2 release policy missing structured field: {required}" - ); - } -} - #[test] fn core_cddl_declares_v1_semantic_model() { let root = repo_root().expect("repo root");