# AUTOMATION SCHEMA (from runbook-server/internal/model/workflow.go)

## TOP LEVEL STRUCTURE:
{
  "name": "string (required)",
  "definition": { ... },  // AutomationDefinition (required)
  "tags": {},             // map[string]any (optional)
  "status": "ACTIVE"      // AutomationStatus (optional): "ACTIVE", "INACTIVE", "PAUSED" (no DRAFT). New automations default to PAUSED.
}

## DEFINITION STRUCTURE (inside "definition"):
{
  "version": "v1",                    // string (optional, usually "v1")
  "inputs": [...],                    // []Input (optional)
  "triggers": [...],                  // []Trigger (required, min=1)
  "tasks": [...],                     // []Task (required, min=1)
  "hooks": {...},                     // *Hooks (optional)
  "output": {},                       // map[string]any (optional)
  "set_execution_tags": [],           // []string (optional)
  "retry_policy": {...},              // *AutomationRetryPolicy (optional)
  "timeout": "30m"                    // string duration (optional, e.g., "30m", "1h")
}

## TASK STRUCTURE:
{
  "id": "string (required)",          // Validated as taskid
  "type": "string (required)",        // Task type from available types
  "params": {},                       // map[string]any (optional)
  "tasks": [],                        // []Task (optional, for nested tasks)
  "set_vars": {},                     // map[string]any (optional)
  "set_state": {},                    // map[string]any (optional)
  "depends_on": [],                   // []string (optional)
  "if": "string (optional)",          // Jinja2 condition
  "matrix": {},                       // map[string]any (optional)
  "failure_policy": {...},            // *FailurePolicy (optional)
  "timeout": "5m",                    // string duration (optional)
  "hooks": {...}                      // *Hooks (optional)
}

## TRIGGER STRUCTURE:
{
  "type": "string (required)",        // one of: manual, schedule, webhook, event, optimization
  "params": {},                       // map[string]any — EVERY trigger setting lives in here
  "layout": {"x": 0, "y": 0}          // optional canvas position
}
"type" and "params" are the ONLY keys you may write on a trigger. A trigger setting placed
at the top level instead of inside "params" is DISCARDED when the definition is decoded,
which produces a trigger that can never fire. The "Requires params:" lists below name the
keys that go INSIDE "params" — they are never trigger-level keys.

  WRONG: {"type": "webhook", "integration_name": "my-hook", "filter": "..."}
  RIGHT: {"type": "webhook", "params": {"integration_name": "my-hook", "filter": "..."}}

  WRONG: {"type": "schedule", "cron": "0 9 * * *"}
  RIGHT: {"type": "schedule", "params": {"cron": "0 9 * * *", "overlap_policy": "Skip"}}

  WRONG: {"type": "event", "event_type": "alert"}
  RIGHT: {"type": "event", "params": {"event_type": "alert"}}

"manual" is the ONLY type that takes no params — {"type": "manual"} is complete as written.
Do not generalise from it to the other four types.

## TRIGGER TYPES:
- "manual" - No params required (params must be empty or omitted). User-supplied inputs available as {{ Inputs.<key> }} in tasks.
- "schedule" - Requires params: {"cron": "0 * * * *" (5-field UTC), "overlap_policy": "Skip|BufferOne|BufferAll|AllowAll|CancelOther|TerminateOther" (optional, default "Skip"), "catchup_window": Go time.ParseDuration string using units ns|us|ms|s|m|h ONLY — day/week units ("7d", "1w") are NOT supported; use hours instead ("168h" = 7 days). Compound durations are allowed ("1h30m", "90m15s"). Examples: "60s", "10m", "1h", "1h30m", "168h"; default "60s"}. Auto-injected: {{ Inputs.workflow_scheduled_time }}, {{ Inputs.workflow_execution_time }}.
- "webhook" - Requires params: {"integration_name": "string (a workflow_webhook integration name)", "secret": "string (optional)", "filter": "jinja2 (optional, must render to literal \"true\" or \"1\")"}. Filter sees {{ webhook_payload }} at root. Tasks read request body via {{ Inputs.webhook_payload }}.
- "event" - Requires AT LEAST ONE of: event_type OR filter (both is fine; rejecting both empty). Params: {"event_type": "string or [string,...]", "filter": "jinja2", "on": "lifecycle phase (optional, default event.created)"}.
    - The filter sees the event at ROOT: {{ event.<field> }}. Tasks read the same event via {{ Inputs.event.<field> }}. NEVER use {{ Inputs.event }} inside the filter — the filter context has no "Inputs".
    - AVAILABLE event.<field> (these are the ONLY top-level fields; do NOT invent others — there is no event.reason and no event.message):
        event_type, source, title, description, failure, finding_type, category,
        priority (HIGH|MEDIUM|LOW|INFO|DEBUG — coarse, MOST events are HIGH; a poor severity gate on its own),
        status (FIRING|RESOLVED|CLOSED), nb_status (OPEN|DUPLICATE|SUPPRESSED|RESOLVED|ACTION_REQUIRED|DROPPED),
        computed_priority (P0|P1|P2|P3 — the real triage tier; may be ABSENT for un-scored events),
        computed_score (integer 0-100 — P0>=80, P1 60-79, P2 40-59, P3<40; may be ABSENT),
        subject_type, subject_name, subject_namespace, subject_node, subject_owner, subject_owner_kind,
        service_key, cluster (a cluster NAME like "prod-cluster" — NEVER an account/cluster UUID),
        fingerprint, cloud_resource_id, principal, aggregation_key,
        labels (a free-form map of alert labels — keys are source-specific, e.g. labels.alertname, labels.severity, labels.summary, labels.namespace; call get_event_trigger_schema to see the REAL keys for this account — do NOT guess label keys).
    - SEVERITY: to fire on high-severity incidents use computed_priority/computed_score (e.g. {{ event.computed_priority in ['P0','P1'] }} or {{ (event.computed_score | default(0) | int) >= 80 }}), NOT a guessed label. Add {{ event.nb_status == 'OPEN' }} to skip duplicates/suppressed (most events are DUPLICATE).
    - LLM ANALYSIS / RCA is NOT a field on the event. To include AI analysis, add an "llm.event_investigate" task and reference ITS output — do not read event.labels for a "reason"/"analysis".
    - "on" (lifecycle phase) selects WHEN the workflow fires: event.created (default), event.triaged, event.updated, investigation.completed (use this when the task needs the LLM RCA, which is only ready by then), event.resolved, event.closed.
    - BEFORE building an event trigger, call get_event_trigger_schema to confirm the live fields, the real label keys for this account, and a sample event.
- "optimization" - All params optional (empty = match every recommendation). Params: {"categories": ["PodRightSizing"|"RightSizing"|"K8sInstanceRecommendation"|"K8sSpotRecommendation"|"Configuration"|"Security"|"K8sMissingAttribute"], "rule_names": ["vertical_rightsize"|"horizontal_rightsize"|"pvc_rightsize"|"continuous_rightsize"|"replica_right_sizing"|"Spot instance recommendation"|"Abandoned resource"], "clusters": ["string",...], "filter": "jinja2 (optional)"}. Filter and tasks see the recommendation event — fields: category, rule_name, cluster, resource_id, estimated_savings, severity, recommendation_id. Tasks read it via {{ Inputs.event.<field> }}.

## INPUT STRUCTURE:
{
  "id": "string (required)",
  "description": "string (optional)",
  "type": "string (optional)",        // e.g., "string", "json", "number", "boolean"
  "default": any (optional),
  "required": bool (optional)
}

## FAILURE POLICY:
{
  "retry": {
    "initial_interval": "1s",
    "backoff_coefficient": 2.0,
    "maximum_interval": "1m",
    "maximum_attempts": 3,
    "non_retryable_error_types": []
  },
  "action": "continue|fail"            // "continue" or "fail" (default)
}

## HOOKS:
{
  "success": [{"type": "string", "params": {}}],
  "failure": [{"type": "string", "params": {}}],
  "always": [{"type": "string", "params": {}}]
}

## TASK STATUS VALUES (for {{ Tasks['id'].status }}):
- COMPLETED
- FAILED
- SKIPPED
- STARTED
- SCHEDULED
- TIMED_OUT
- CANCELED

## VALIDATION RULES:
1. "name" is required at top level
2. "definition" is required at top level
3. "definition.triggers" is required and must have at least 1 trigger
4. "definition.tasks" is required and must have at least 1 task
5. Each task must have "id" and "type"
6. "depends_on" task IDs must exist in automation
7. Jinja2 templates in "if", "params", "set_state", "output" are parsed and validated. Templates are Jinja2 ONLY — JMESPath/JSONPath constructs ([*], [?...], .., @) are NOT supported and fail validation with: invalid expression ... near "*". Map list fields in an upstream scripting.run_script task, not in the template
8. Duration fields ("timeout") must be valid durations (e.g., "30s", "5m", "1h")
9. Manual trigger must NOT have params (or empty params)
10. Schedule trigger MUST have "cron" param. Optional "overlap_policy" must be one of Skip|BufferOne|BufferAll|AllowAll|CancelOther|TerminateOther. Optional "catchup_window" MUST use Go time.ParseDuration units (ns|us|ms|s|m|h) — day units like "7d" are NOT supported (use "168h" for 7 days); compound durations like "1h30m" ARE allowed
11. Webhook trigger MUST have "integration_name" param
12. Event trigger MUST have AT LEAST ONE of "event_type" OR "filter". "event_type" may be a string or array of strings
13. Optimization trigger: all params are optional (categories[], rule_names[], clusters[], filter). Empty params means "match every recommendation". Array params must contain strings only
