Skip to main content

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.

You are a coding agent. Given source legislation + test cases, you’re producing a deterministic rule ruleset. This is the authoring workflow. Three phases — each gated on the previous. Only phase 3 has a TDD loop today; phases 1 and 2 are informal. Requires an Aethis API key with projects:write and rulesets:write. Set AETHIS_API_KEY in your environment. If you’re using MCP, the key goes in the MCP client config (see MCP install).

Phase 1 — Section discovery

Legislation often covers multiple independent criteria. Split each into its own section so they can be tested and regenerated separately.

CLI path

aethis init my-rule                   # scaffolds .aethis/ + aethis.yaml
cd my-rule
aethis sections discover --file path/to/legislation.pdf
# review the output, edit aethis.yaml if needed

MCP path

aethis_discover_sections({
  source_text: "<paste legislation>",
  hint: "Split into independent eligibility gates"
})
For each identified section, create one project.

Phase 2 — Field vocabulary

Define the input fields each section will consume. Shared vocabulary across sections lets rulebooks compose cleanly.

CLI path

aethis fields discover --section my_section
# or
aethis fields set --section my_section \
  --field 'applicant.age:int:"Applicant age in whole years"' \
  --field 'applicant.visa_status:enum:[student,work,settled]:"Current UK visa status"'

MCP path

aethis_discover_fields({
  project_id: "proj_abc",
  source_text: "<section text>"
})

aethis_set_field_spec({
  project_id: "proj_abc",
  field_id: "applicant.age",
  field_type: "int",
  description: "Applicant age in whole years"
})
Phase 2 must be settled before generation starts — fields can’t be renamed freely once rules reference them.

Phase 3 — Rule generation (TDD loop)

The only phase with an automated test gate. Test-first is non-negotiable: write the tests, THEN generate, THEN iterate until green.

Step 3.1 — Write tests

# .aethis/tests.yaml
tests:
  - name: "eligible — meets age and visa"
    inputs:
      applicant.age: 35
      applicant.visa_status: settled
    expected: eligible

  - name: "not_eligible — student visa"
    inputs:
      applicant.age: 35
      applicant.visa_status: student
    expected: not_eligible

Step 3.2 — Generate

aethis generate --poll     # creates a draft ruleset, waits for compilation
Via MCP:
aethis_generate_and_test({
  project_id: "proj_abc",
  max_iterations: 3    // compiler + tester loop up to N times
})

Step 3.3 — Review + refine

aethis test                # runs all test cases against the draft ruleset
If any test fails:
aethis refine --hint "The age cap is 65, not 60 — see Section 7.2"
aethis generate --poll
aethis test
Via MCP, aethis_generate_and_test handles this internally — you just pass guidance hints via aethis_add_guidance.

Step 3.4 — Publish

Only publish when aethis test shows 100% pass. The publish endpoint runs the tests again server-side as a gate.
aethis publish --slug my-team/my-rule
# → ✓ Published ruleset <ruleset_id> — slug: my-team/my-rule
Via MCP:
aethis_publish({
  project_id: "proj_abc",
  slug: "my-team/my-rule"
})
Publishing under the reserved aethis/* namespace requires an internal: true API key. External tenants get HTTP 403 reason_code: "reserved_namespace". See Nomenclature.

Composing sections into a rulebook

Once each section has an active ruleset with a stable slug, create the rulebook. See the worked UK Free School Meals example for a full multi-section composition including the outcome_logic AST.
curl -X POST https://api.aethis.ai/api/v1/public/rulebooks/ \
  -H "x-api-key: $AETHIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Rulebook",
    "domain": "my_domain",
    "slug": "my-team/my-rulebook",
    "ruleset_refs": [
      { "section_id": "section_a", "pin_mode": "latest_active" },
      { "section_id": "section_b", "pin_mode": "latest_active" }
    ],
    "outcome_logic": {
      "type": "op", "operator": "and",
      "args": [
        { "type": "field_ref", "key": "section_a_group" },
        { "type": "field_ref", "key": "section_b_group" }
      ]
    }
  }'
The outcome_logic must be a proper Expr AST. The server rejects the text shorthand { "expr": "A AND B" } with HTTP 422 at ingress since aethis-core 0.7.2. To find the group names your sections emit, inspect each ruleset’s compiled criteria — or use a known pattern like the one in the FSM example.

Verification checklist

Before declaring a rule complete:
  • aethis test passes 100% on the ruleset that’s about to be published
  • /decide with the published ruleset_id returns the expected decision for each test input
  • If the rule is public-facing, visibility is public (auto-set for aethis/* slugs; use PATCH /rulesets/<id>/visibility otherwise)
  • The ruleset shows up in GET /api/v1/public/rulesets (public) or via the authored key (private)

Next steps