# `JevDSL`
[🔗](https://github.com/boxxxie/jev_dsl/blob/v0.1.0/lib/jev_dsl.ex#L1)

Compile-time Elixir macro DSL, two-tier caching layer, and execution runner for
TypeSafe Jev (`SystemOneSDK`) semantic contracts.

`JevDSL` compiles `%SystemOneSDK.Prepared{}` contracts at build time (`defnoul`,
`defchoice`, `defscore`, `defjev`, `defalias`), caches rehydrated `Jason.Fragment`
wire payloads in `:persistent_term` in O(1), provides deterministic SHA-256
content-addressed L1/L2 caching (`JevDSL.Cache`), and generates `evaluate/3`,
`evaluate!/3`, `evaluate_many/3`, `available?/2`, `cache_key/2`, `contract/1`,
`prepared/1`, and `__export_cue__/0` directly on your contract module.

Both atoms and strings are supported interchangeably for contract keys, question
keys, stab-clause option keys, and answer lookups (`fetch_answer/2`, `choice?/2`,
`probability/2`).

## Quick Start

    defmodule MyApp.TriageContracts do
      use JevDSL,
        otp_app: :my_app,
        cache: JevDSL.Cache.ETS

      defnoul :is_spam do
        "Does this message contain unsolicited promotional spam?"
      end

      defchoice :sentiment,
        ask: "Classify the overall tone of the message." do
        positive -> "Constructive, appreciative, or favorable tone."
        neutral -> "Factual or informational without strong emotion."
        negative -> "Critical, frustrated, or dissatisfied tone."
      end

      defscore :completeness,
        ask: "Rate how complete the submission is." do
        incomplete -> "Missing required identity or contact fields."
        partial -> "Core fields are present but optional context is missing."
        complete -> "All identity, contact, and biographical fields are populated."
      end

      defjev :ticket_triage do
        noul :requires_escalation do
          "Does this issue require immediate engineering escalation?"
        end

        choice :priority,
          ask: "Select the appropriate response priority." do
          urgent -> "Production outage or security incident."
          normal -> "Standard bug report or feature question."
        end

        score :clarity,
          ask: "Rate the clarity of the reproduction steps." do
          vague -> "No steps or environment details provided."
          clear -> "Includes clear steps and expected vs actual behavior."
        end
      end
    end

## Evaluating Contracts

    # Single evaluation (automatically cached by state + contract fingerprint)
    {:ok, response} = MyApp.TriageContracts.evaluate(:sentiment, %{text: "Great work!"})

    # Concurrent batch evaluation (resolves L1/L2 cache hits first, batches only misses)
    results = MyApp.TriageContracts.evaluate_many(:is_spam, [%{body: "a"}, %{body: "b"}])

    # Deterministic unit tests via 1-arity `:eval_fn` (or `:cache_only` / `SystemOneSDK.Test`)
    {:ok, mocked} =
      MyApp.TriageContracts.evaluate(:is_spam, %{body: "a"},
        eval_fn: fn _state -> {:ok, %{answers: %{is_spam: %{noul: 0.05}}}} end
      )

# `choice?`

```elixir
@spec choice?(
  SystemOneSDK.ChoiceAnswer.t() | map() | atom() | String.t(),
  atom() | String.t()
) ::
  boolean()
```

Returns `true` if a `ChoiceAnswer` (or choice value) selected `expected_option`,
comparing atoms and strings equivalently.

## Examples

    iex> ans = %SystemOneSDK.ChoiceAnswer{choice: "positive", confidence: 0.9, probabilities: %{"positive" => 0.9}}
    iex> JevDSL.choice?(ans, :positive)
    true
    iex> JevDSL.choice?(ans, "positive")
    true
    iex> JevDSL.choice?(ans, :negative)
    false

# `defalias`
*macro* 

Aliases a secondary or legacy contract name (atom or string) to a canonical contract name.

# `defchoice`
*macro* 

Defines a single-question Choice contract (`SystemOneSDK.Question.Choice`) using `key -> "description"` stab clauses.
Preserves the exact declaration order of options and compiles option keys as atoms.

# `defjev`
*macro* 

Defines a multi-question contract combining `choice`, `noul`, and/or `score` sub-questions.

# `defnoul`
*macro* 

Defines a single-question Noul (boolean probability) contract (`SystemOneSDK.Question.Noul`).

Supports a string block, `ask: "..."` option, or `true -> "..."` / `false -> "..."` stab clauses.

# `defscore`
*macro* 

Defines a single-question ordered Score contract (`SystemOneSDK.Question.Score`, 2..10 rubric levels)
using `label -> "description"` stab clauses.

# `export_cue`

```elixir
@spec export_cue(%{optional(atom() | String.t()) =&gt; JevDSL.Contract.t() | map()}) ::
  String.t()
```

Exports compiled Jev contracts into a CUE schema document.

# `fetch_answer`

```elixir
@spec fetch_answer(SystemOneSDK.SystemOneResponse.t() | map(), atom() | String.t()) ::
  {:ok, term()} | :error
```

Fetches an answer from a `%SystemOneSDK.SystemOneResponse{}` (or map with `:answers`),
matching `key` whether passed as an atom or a string.

## Examples

    iex> resp = %{answers: %{tone: %{choice: "positive"}}}
    iex> JevDSL.fetch_answer(resp, :tone)
    {:ok, %{choice: "positive"}}
    iex> JevDSL.fetch_answer(resp, "tone")
    {:ok, %{choice: "positive"}}
    iex> JevDSL.fetch_answer(resp, :missing)
    :error

# `fetch_answer!`

```elixir
@spec fetch_answer!(SystemOneSDK.SystemOneResponse.t() | map(), atom() | String.t()) ::
  term()
```

Fetches an answer by atom or string `key`, raising `KeyError` if not present.

# `hydrate_prepared`

```elixir
@spec hydrate_prepared(SystemOneSDK.Prepared.t()) :: SystemOneSDK.Prepared.t()
```

Rehydrates and caches the `Jason.Fragment` wire encoder on a compile-time `%SystemOneSDK.Prepared{}` struct.

# `probability`

```elixir
@spec probability(SystemOneSDK.ChoiceAnswer.t() | map(), atom() | String.t()) ::
  {:ok, number()} | :error
```

Fetches the probability for `option_key` (atom or string) from a `ChoiceAnswer` or probability map.

## Examples

    iex> ans = %SystemOneSDK.ChoiceAnswer{choice: "positive", confidence: 0.9, probabilities: %{"positive" => 0.9, "neutral" => 0.1}}
    iex> JevDSL.probability(ans, :positive)
    {:ok, 0.9}
    iex> JevDSL.probability(ans, "neutral")
    {:ok, 0.1}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
