state_schema:
  # fixed, system-owned
  artifacts:
    discovery_result: {}
    plan: {}
    execution_result: {}
    critic_decision: {}

  signals:
    tests: { status: "unknown" }      # pass | fail | unknown
    failure: "none"                  # none | schema_fail | test_fail | hallucination | timeout
    complexity: 0.0                  # 0..1 (heuristic)
    retries: 0
    iteration: 0

  pointers:
    current_agent: null
    last_agent: null
    last_artifact: null

limits:
  max_total_steps: 12
  max_retries_per_agent: 3

agents:
  discovery:
    models: [small]
    prompt: prompts/discovery.md
    needs: []
    produces: [discovery_result]

  planner:
    models: [small, medium]
    prompt: prompts/planner.md
    needs: [discovery_result]
    produces: [plan]

  executor:
    models: [medium]
    prompt: prompts/executor.md
    needs: [plan]
    produces: [execution_result]

  critic:
    models: [small]
    prompt: prompts/critic_struct.md
    needs: [execution_result]
    produces: [critic_decision]

  fixer_minimal:
    models: [medium, large]
    prompt: prompts/fix_minimal.md
    needs: [execution_result]
    produces: [execution_result]

selection:
  default: smallest_sufficient

  overrides:
    - when: complexity > 0.6
      pick: medium

    - when: failure == "hallucination"
      pick: large

    - when: retries >= 2
      pick: large

routing:
  start: discovery

  rules:
    # initial flow
    - when: !has(discovery_result)
      next: discovery

    - when: has(discovery_result) && !has(plan)
      next: planner

    - when: has(plan) && !has(execution_result)
      next: executor

    - when: has(execution_result) && !has(critic_decision)
      next: critic

    # success exit
    - when: critic_decision.decision == "pass" || tests.status == "pass"
      next: done

    # retry loops
    - when: critic_decision.decision == "retry" && failure == "test_fail"
      next: executor
      mutate:
        - inject_failure_reason
        - increment_retries

    - when: critic_decision.decision == "retry" && failure == "schema_fail"
      next: executor
      mutate:
        - tighten_constraints
        - increment_retries

    - when: critic_decision.decision == "retry" && failure == "hallucination"
      next: fixer_minimal
      mutate:
        - restrict_to_provided_context
        - increment_retries

    # escalation
    - when: retries >= 2 && tests.status == "fail"
      next: fixer_minimal

    # fallback
    - when: iteration >= 10
      next: done