# Guard Injection Semantics This document describes the runtime guard model for View Contracts. Views describe portable structural contracts. Guarded views refine those contracts with executable predicates while keeping ordinary value-level code free of `Maybe`, `Result`, sentinel, or host-language abort handling. ```tri viewGuarded baseView guard ``` A guarded view means: when this guarded view is observed along the reachable checked-execution path, run `guard` against the runtime value. ## Goals - Preserve ordinary value-level program shapes. - Keep guard failure out of user code. - Avoid Haskell-specific checker/runtime semantics. - Represent guard boundaries explicitly in portable tree data. - Make successful guarded execution transparent: guarded values are unwrapped before ordinary code receives them. - Prefer correctness-by-default over avoiding repeated predicate cost. ## Non-goals - Preventing user-written guards from diverging. - Letting guards author their own diagnostics. - Solving IO interaction-tree composition. - Finalizing long-term artifact identity policy. - Deduplicating or hoisting repeated guard checks. ## Plain Views vs Guards Plain Views still provide concrete benefits without guards: - structural flow checking; - portable API metadata; - module/export contract metadata; - content-store view-tree metadata; - cross-frontend agreement on contract structure; - diagnostics for wrong-view flows. Guards are for invariants that require runtime value inspection, such as: - non-empty list; - sorted list; - byte string of exactly 32 bytes; - protocol payload with a valid checksum; - domain-specific runtime predicate. Guards are deliberately more expensive than ordinary Views. Use them when the runtime contract must be enforced. ## Guard Result Protocol Guards return one of two standardized shapes: ```tri guardOk value guardFail ``` Guards do not provide diagnostics. The checked-exec runner owns diagnostics. Malformed guard output is treated as a checked-runtime failure. ## Checked Execution Protocol A successful typed-program check returns a checked-execution artifact, not a raw payload. Current constructors: ```tri checkedPure value checkedFail diagnostic checkedGuard view guard value continuation checkedGuardWithContext context view guard value continuation checkedBind exec continuation ``` `checkedGuard` is the compatibility/default constructor. It lowers to `checkedGuardWithContext` with an unknown context. Checker-injected guard boundaries use `checkedGuardWithContext` so failures can identify where the boundary came from. Runner: ```tri runChecked checkedExec ``` Semantics: ```text runChecked (checkedPure value) = checkedRuntimeOk value runChecked (checkedFail diagnostic) = checkedRuntimeFail diagnostic runChecked (checkedGuardWithContext context view guard value continuation) = case guard value of guardOk checkedValue -> runChecked (continuation checkedValue) guardFail -> checkedRuntimeFail (guardFailed context view) malformed -> checkedRuntimeFail (malformedGuardResult context view malformed) runChecked (checkedGuard view guard value continuation) = runChecked (checkedGuardWithContext unknownContext view guard value continuation) runChecked (checkedBind exec continuation) = case runChecked exec of checkedRuntimeOk value -> runChecked (continuation value) checkedRuntimeFail diag -> checkedRuntimeFail diag ``` Important invariant: > Guard failure is consumed by `runChecked`. It is never passed into ordinary > user code. ## Checker Result Shape `checkTypedProgramWith` returns checked-exec on success: ```tri ok checkedExec env ``` Even unguarded programs return: ```tri checkedPure rootPayload ``` Compatibility helper: ```tri checkedProgramTree result ``` `checkedProgramTree` runs/unwraps checked-exec to preserve older raw-tree helper behavior. The Haskell `tricu check` path now evaluates successful checker output through `runChecked`, so source-level guarded annotations fail through the same portable checked-exec protocol. ## Boundary Semantics Guard insertion follows correctness-first semantics: > Every guarded View observation on the reachable checked-execution path runs > its guard. Important boundary kinds: ### Guarded typed value ```tri typedValue sym (viewGuarded base guard) payload ``` This observes `sym` as a guarded value. It also supplies base-view evidence for flow checking. ### Guarded requirement ```tri typedRequire sym (viewGuarded base guard) payload ``` The symbol must satisfy `base`; the guarded observation is attached to `sym` and is enforced when `sym` is used or exposed along the reachable root path. ### Guarded function argument For: ```tri viewFn [(viewGuarded base guard)] result ``` application checking guards the argument before the callee receives it. ### Guarded function result For: ```tri viewFn [arg] (viewGuarded base guard) ``` application checking guards the application result before exposing it as the result value. ### Guarded callee symbol If a function symbol itself has a guarded observation, that guard runs before the function value is applied. A successful guard may transform the function value; the application uses the guarded value. ## Global Symbol Observations Guarded `typedValue` and `typedRequire` nodes are **global per-symbol observations**, not position-sensitive flow events. All guarded observations for a symbol compose in typed-node order whenever that symbol is used or exposed on the reachable checked-execution path. This means a later requirement still applies to an earlier syntactic use: ```tri typedValue 1 viewString "x" typedApply 2 f 1 "x" typedRequire 1 (viewGuarded viewString guard) "x" ``` The guarded requirement is attached to symbol `1`; compiling the reachable root path that uses symbol `1` runs that guard. Rationale: - typed programs are declarative symbol graphs, not imperative event traces; - global observations are simpler and more correct-by-default; - producers cannot accidentally bypass a guard by ordering a requirement too late; - staged raw/checked phases should use distinct symbols. ## Reachability and Repetition Guards are not run eagerly for every guarded node in a program. Execution is root-reachable: ```tri compileSymbol (typedProgramRoot program) ``` Only guarded observations reachable from the root checked-execution path run. Unreachable guarded symbols do not pay guard cost and do not fail execution. Repeated reachable uses rerun guards. There is currently no deduplication or hoisting. This is intentional: each guarded observation/use is a runtime contract boundary. Future optimization policies may add explicit deduplication or hoisting, but the baseline semantics are repeated, deterministic guard execution. ## Function and Application Compilation Checked execution is built compositionally from typed-node dependencies: 1. compile the callee symbol; 2. compile the argument symbol; 3. run any guarded observations attached to the argument symbol; 4. run the guarded function-argument boundary, if present; 5. apply the callee to the checked argument; 6. run the guarded function-result boundary, if present; 7. run guarded observations attached to the application result symbol. This handles nested and curried application chains because each `typedApply` consumes one function argument and produces a symbol whose inferred view is the function residual/result view. ## Diagnostics Guards do not author diagnostics. The checked-exec runner renders diagnostics from checker-owned boundary context plus the guarded View. Checker-injected guard nodes carry portable structural context. Current context kinds are: - root `typedValue` exposure; - root `typedRequire` exposure; - non-root `typedValue` symbol observation; - non-root `typedRequire` symbol observation; - function argument boundary; - function result boundary; - unknown/default context for manually constructed `checkedGuard` values. Examples: ```text guard failed at root typedValue symbol 0 for Guarded String guard failed at root typedRequire symbol 3 for Guarded String guard failed at typedRequire symbol 6 for Guarded String guard failed at argument 0 of application symbol 2 (callee symbol 0, arg symbol 1) for Guarded String guard failed at result of application symbol 2 (callee symbol 0, arg symbol 1) for Guarded String malformed guard result at argument 0 of application symbol 2 (callee symbol 0, arg symbol 1) for Guarded String ``` Manually constructed `checkedGuard` values use unknown context and therefore render without a boundary suffix: ```text guard failed for String malformed guard result for String ``` The context is diagnostic-only. It does not affect guard execution, View compatibility, success/failure semantics, or continuation values. The context deliberately contains raw portable data such as symbols and application edges. It does not preserve source aliases such as `NonEmptyString`, and it does not rely on Haskell-side post-processing or source-name annotation. Named View rendering is a separate future design topic. ## Why Not Abort in Haskell? A host-level abort primitive would move guard semantics into Haskell. The design instead encodes guard failure in portable checked-exec artifacts and interprets it with portable `tricu` code. Haskell may evaluate the runner, but Haskell is not the semantic source of guard validity or failure behavior. ## Why Not Maybe / Result Everywhere? Returning `Maybe` or `Result` from every guarded boundary would infect ordinary APIs. A function expecting a `List Byte` would have to accept `Maybe (List Byte)` or `Result Error (List Byte)`, and every downstream caller would need defensive handling. The checked-exec runner avoids this. It unwraps successful guard results before continuing and stops checked execution on failure. ## Known Sharp Edges ### Guard divergence A user-written guard may diverge. This design handles intentional failure via `guardFail`; it does not solve arbitrary nontermination. Fuel or timeouts are separate runtime concerns. ### Payload trust Typed nodes carry executable payloads. Guard injection must not expose an unchecked precomputed payload at a guarded boundary. Boundaries are mediated by checked-exec nodes. This does not make malicious producer forgery impossible; it gives honest frontends a portable, checkable protocol that avoids accidental bypasses. ### Cyclic typed-apply graphs The current symbol compiler assumes typed programs are well-founded dependency graphs as emitted by the frontend/lowering path. Cyclic typed-apply graphs are a malformed-program validation concern, not a guard-specific semantic feature. ## Current Implementation Status Implemented in `lib/view.tri` and exercised by tests: - `guardOk` / `guardFail`; - `checkedPure`, `checkedFail`, `checkedGuard`, `checkedGuardWithContext`, `checkedBind`; - `runChecked`; - success from `checkTypedProgramWith` returns checked-exec; - `checkedProgramTree` compatibility helper; - guarded root exposure; - guarded `typedValue` and `typedRequire`; - guarded function arguments and results; - guarded callee observations; - nested/curried application guard composition; - global per-symbol observations; - root-reachability behavior; - repeated reachable uses rerun guards; - source/Haskell `tricu check` integration; - imported/module `VTGuarded` lowering to portable `viewGuarded`; - portable guard boundary diagnostics with symbol/application context.