> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aethis.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Evaluate a case

> Agent recipe: schema → collect fields → decide → explain. End-to-end, copy-pasteable.

**You are a coding agent. You want to answer 'is this person eligible for X?'** This page is the full sequence.

The three-call pattern:

1. **Discover the schema** — what fields does the ruleset need?
2. **Collect values** — either from user input, context, or an upstream tool.
3. **Decide** — send field values, receive a decision envelope.

Optional fourth step: **explain** — when the decision is `not_eligible` and the user asks why.

Uses the public FSM child-eligibility ruleset. No API key needed.

***

## 1. Discover the schema

<Tabs>
  <Tab title="CLI">
    ```bash theme={null}
    aethis fields -b aethis/uk-fsm/child-eligibility
    ```
  </Tab>

  <Tab title="Python SDK">
    ```python theme={null}
    from aethis_sdk import Aethis

    with Aethis() as client:
        schema = client.get_schema("aethis/uk-fsm/child-eligibility")
        for f in schema.fields:
            print(f.field_id, f.field_type, f.description)
    ```
  </Tab>

  <Tab title="curl">
    ```bash theme={null}
    curl https://api.aethis.ai/api/v1/public/rulesets/aethis/uk-fsm/child-eligibility/schema
    ```
  </Tab>

  <Tab title="MCP">
    Ask your coding agent (Claude Code, Cursor, Windsurf, …) in natural language:

    > *"Use Aethis to fetch the field schema for `aethis/uk-fsm/child-eligibility`."*

    Your agent invokes the `aethis_schema` tool for you:

    ```
    aethis_schema({ ruleset_id: "aethis/uk-fsm/child-eligibility" })
    ```
  </Tab>
</Tabs>

The response (same shape across all four interfaces):

```json theme={null}
{
  "ruleset_id": "aethis/uk-fsm/child-eligibility",
  "slug": "aethis/uk-fsm/child-eligibility",
  "name": "UK FSM Child Eligibility",
  "fields": [
    {
      "field_id": "child.age",
      "field_type": "int",
      "description": "Child's age in whole years at the start of the relevant academic year (1 September).",
      "question": "Child's age in whole years at the start of the relevant academic year (1 September).",
      "weight": 1,
      "enum_values": null,
      "notes": [
        {
          "note_text": "WHY: Free school meals eligibility is tied to compulsory-school-age status, which is determined by the child's age at the start of the academic year.",
          "source": "DfE Free School Meals: guidance for local authorities",
          "metadata": { "type": "why", "section": "child_eligibility" }
        },
        {
          "note_text": "LEGAL BACKGROUND:\n\nCompulsory school age runs from 1 September following the child's 5th birthday to the last Friday of June in the academic year of their 16th birthday. Age is taken as at 1 September of the relevant academic year, not the application date.",
          "source": "Education Act 1996, s.8",
          "metadata": { "type": "legal_background", "section": "child_eligibility" }
        }
      ]
    },
    {
      "field_id": "child.school_type",
      "field_type": "enum",
      "description": "Type of school the child attends.",
      "weight": 2,
      "enum_values": ["state_funded", "independent", "home_educated"]
    }
  ]
}
```

**What the agent needs:** a map from `field_id` → value. For `enum` fields, only values listed in `enum_values` are accepted. For `int` / `bool`, standard JSON.

**`notes`** — each field can carry a list of structured `FieldNoteOut` entries. By convention each note has `metadata.type ∈ {"why", "legal_background"}` and a `source` citation. Conversational agents (e.g. an information-gathering paralegal bot) typically surface the `WHY:`-prefixed note verbatim when a user replies "why?" to a question, and draw on the `LEGAL BACKGROUND:`-prefixed note for follow-up questions about evidence and edge cases. Older bundles may return `"notes": []` — your code should treat the array as optional.

**`weight`** — higher = less preferred to ask. The decision engine uses this for optimal-path computation when there are multiple ways to satisfy a ruleset (e.g. ask the cheap nationality question before the expensive English-test one).

***

## 2. Collect field values

Agent-side logic — the API doesn't care how you got the values. In practice: ask the user, read from a session object, or derive from upstream context. If a field is missing, the decision can still return (as `undetermined`) — just be ready to ask for it next.

***

## 3. Decide

<Tabs>
  <Tab title="CLI">
    ```bash theme={null}
    aethis decide \
      -b aethis/uk-fsm/child-eligibility \
      -i '{"child.age": 10, "child.school_type": "state_funded"}' \
      --explain
    ```
  </Tab>

  <Tab title="Python SDK">
    ```python theme={null}
    from aethis_sdk import Aethis

    with Aethis() as client:
        response = client.decide(
            ruleset_id="aethis/uk-fsm/child-eligibility",
            field_values={
                "child.age": 10,
                "child.school_type": "state_funded",
            },
            include_trace=True,
        )
        print(response.decision)
    ```
  </Tab>

  <Tab title="curl">
    ```bash theme={null}
    curl -X POST https://api.aethis.ai/api/v1/public/decide \
      -H "Content-Type: application/json" \
      -d '{
        "ruleset_id": "aethis/uk-fsm/child-eligibility",
        "field_values": {
          "child.age": 10,
          "child.school_type": "state_funded"
        },
        "include_trace": true
      }'
    ```
  </Tab>

  <Tab title="MCP">
    Ask your coding agent in natural language:

    > *"Use Aethis to check whether a 10-year-old at a state-funded school qualifies for free school meals under `aethis/uk-fsm/child-eligibility`. Include the trace."*

    Your agent invokes the `aethis_decide` tool for you:

    ```
    aethis_decide({
      ruleset_id: "aethis/uk-fsm/child-eligibility",
      field_values: { "child.age": 10, "child.school_type": "state_funded" },
      include_trace: true
    })
    ```
  </Tab>
</Tabs>

All four return the same decision envelope:

```json theme={null}
{
  "decision": "eligible",
  "fields_provided": 2,
  "fields_evaluated": 2,
  "slug": "aethis/uk-fsm/child-eligibility",
  "ruleset_version": "unknown",
  "engine_version": "aethis-core@0.36.0",
  "decision_id": "dec_...",
  "inputs_hash": "sha256:...",
  "decision_time": "2026-04-19T22:00:00.000Z"
}
```

Three possible outcomes:

* `eligible` — all criteria satisfied
* `not_eligible` — one or more criteria failed
* `undetermined` — engine couldn't decide (missing field, discretionary clause, or case outside the compiled rules)

When you get `undetermined`, inspect `missing_fields` in the response. Add the missing values and call again. `include_trace` adds per-group outcomes so you can show the user *why*.

***

## 4. (Optional) Explain the decision

For a `not_eligible` where the user asks "why":

<Tabs>
  <Tab title="CLI">
    ```bash theme={null}
    aethis decide \
      -b aethis/uk-fsm/child-eligibility \
      -i '{"child.age": 16, "child.school_type": "state_funded"}' \
      --explain
    ```

    The CLI's `--explain` flag calls `/decide` with trace, then `/explain-failure` when the outcome is `not_eligible`, and renders both in one pass.
  </Tab>

  <Tab title="Python SDK">
    <Note>Requires aethis-sdk >= 0.6.0.</Note>

    Pass the concrete `ruleset_id` from the `/decide` envelope, not the slug —
    `/explain-failure` path resolution doesn't accept slugs.

    ```python theme={null}
    from aethis_sdk import Aethis

    with Aethis() as client:
        response = client.decide(
            ruleset_id="aethis/uk-fsm/child-eligibility",
            field_values={"child.age": 16, "child.school_type": "state_funded"},
            include_trace=True,
        )
        if response.decision == "not_eligible":
            # ruleset_id must be the concrete identifier, not a slug
            explanation = client.explain_failure(
                ruleset_id=response.ruleset_id,
                field_values={"child.age": 16, "child.school_type": "state_funded"},
                expected_outcome="eligible",
            )
            print(explanation)
    ```
  </Tab>

  <Tab title="curl">
    `/explain-failure` currently requires the concrete `ruleset_id` on its path, not a slug. Get it from the `/decide` envelope's `ruleset_id` field (alongside the slug you called with).

    ```bash theme={null}
    curl -X POST https://api.aethis.ai/api/v1/public/rulesets/$RULESET_ID/explain-failure \
      -H "Content-Type: application/json" \
      -d '{
        "field_values": { "child.age": 16, "child.school_type": "state_funded" },
        "expected_outcome": "eligible"
      }'
    ```
  </Tab>

  <Tab title="MCP">
    Ask your coding agent in natural language:

    > *"Use Aethis to explain why a 16-year-old at a state-funded school is not eligible under `aethis/uk-fsm/child-eligibility`."*

    Your agent invokes the `aethis_explain_failure` tool for you (it accepts either the slug or the concrete `ruleset_id` — slugs are resolved client-side):

    ```
    aethis_explain_failure({
      ruleset_id: "aethis/uk-fsm/child-eligibility",
      field_values: { "child.age": 16, "child.school_type": "state_funded" },
      expected_outcome: "eligible"
    })
    ```
  </Tab>
</Tabs>

Returns human-readable reasons for the failure, keyed by criterion, plus a DSL mechanism hint.

***

## Composing multiple sections (rulebook)

If your domain has more than one section and you want one decision over the composition (e.g. FSM: *child eligibility AND (household criteria OR universal infant)*), use the `rulebook_id` field instead of `ruleset_id`. Rulebook evaluation always requires an API key — see [Nomenclature](/concepts/nomenclature) for why.

```bash theme={null}
curl -X POST https://api.aethis.ai/api/v1/public/decide \
  -H "Content-Type: application/json" \
  -H "x-api-key: $AETHIS_API_KEY" \
  -d '{
    "rulebook_id": "aethis/uk-fsm",
    "field_values": { ... }
  }'
```

The response includes a `section_results` array with each section's individual outcome.

***

## Next steps

* [Debug a failing decide](/recipes/debug-a-decide) — when things don't go as expected
* [MCP tools reference](/mcp-server/tools) — every tool + parameters
* [Decision envelope](/concepts/decision-envelope) — replay and audit contract
