> For the complete documentation index, see [llms.txt](https://docs.gomboc.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.gomboc.ai/orl/agent-skills/gomboc-enterprise-skills/cspm-triage-loop.md).

# CSPM Triage Loop

The CSPM Triage Loop is the end-to-end agentic workflow Gomboc uses to trace a live cloud security alert through to a released ORL rule and a local code fix. It bridges the gap between a Wiz finding and the infrastructure code that causes it: fetch the alert, identify the affected resource in your IaC, build or reuse an ORL rule, apply the fix, and post a resolution note back to the observation — all in a single skill invocation.

You can run the full loop with `cspm-triage`, or fall back to a lighter tier when CSPM service access or local code is unavailable.

For installing the plugin in your agent first, see [Gomboc Enterprise Skills](/orl/agent-skills/gomboc-enterprise-skills.md).

***

### The full loop

```
┌─────────────────────────────────────────────────────────────────────┐
│                           cspm-triage                               │
│                                                                     │
│  Phase 0: collect inputs (observation ID, local source path)        │
│       ↓                                                             │
│  Phase 1: parallel validation (env vars, service health,            │
│           observation fetch, IaC detection, classification lookup)  │
│       ↓                                                             │
│  Phase 2: tier assignment                                           │
│       ↓                                                             │
│  1. Gather context ─────── Body type, control ID, severity,         │
│       ↓                    resource type, remediation spec          │
│  2. Classify ──────────── Create gomboc-ai/wiz/<control-id> YAML    │
│       ↓                   (skipped if already exists)               │
│  3. Diagnose local code ─ IaC match + code confidence gate          │
│       ↓                                                             │
│  4. Build ORL rule ─────── build-rule delegates to orl-planner      │
│       ↓                    → orl-expert → orl-test-planner          │
│  5. Enrich metadata ─────── enrich-rule (risk scores, diff example) │
│       ↓                                                             │
│  6. Review + release ───── review-rule → release-rule               │
│       ↓                                                             │
│  7. Apply, commit, PR ──── orl remediate + gh pr create             │
│       ↓                   + write triage artifact (.gomboc/triage/) │
│  8. Resolution note ─────── add_observation_note.py → Wiz UI        │
└─────────────────────────────────────────────────────────────────────┘
```

The eight numbered steps are organized into two pre-work phases and one output phase:

* **Phase 0** — the skill prompts once for everything it needs: observation ID (or auto-ranks via `--top`), local source root, and whether to auto-select the top candidate.
* **Phase 1** — all environment and connectivity checks run in parallel before any action is taken. Hard stops halt immediately; soft gates ask for acknowledgment.
* **Steps 1–8** — the core triage loop, executed sequentially.

***

### Stage breakdown

| Stage                      | Skills / tools involved                                                              | What it produces                                                                 |
| -------------------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- |
| **0. Input collection**    | `fetch_observation.py --top` or `--id`                                               | Observation selected; source path confirmed                                      |
| **1. Parallel validation** | `curl` health checks, `fetch_observation.py`, `orl detect-language`, `rules-service` | Validation table; tier assigned; hard stops surfaced before any writes           |
| **2. Tier assignment**     | (internal)                                                                           | Tier 1–4 announced to user; Tier 2 triage brief written                          |
| **1. Gather context**      | `fetch_observation.py --summary`                                                     | Body type, control ID, effective severity, resource type, remediation spec       |
| **2. Classify**            | `classification-expert-wiz`, `rules-service`                                         | `gomboc-ai/wiz/<control-id>.yaml` (skipped if classification already exists)     |
| **3. Diagnose local code** | `diagnose`                                                                           | Language detection, violation list, code match confidence (HIGH / MEDIUM / LOW)  |
| **4. Build ORL rule**      | `build-rule`                                                                         | Rule package with `rule.orl`, `test.orl`, workspace files                        |
| **5. Enrich metadata**     | `enrich-rule`                                                                        | Rule annotated with risk/impact scores, classification references, diff example  |
| **6. Review + release**    | `review-rule` → `release-rule`                                                       | Rule published to Gomboc Rules Service; release PR opened in org rule repository |
| **7. Apply, commit, PR**   | `orl remediate`, `git`, `gh pr create`, `write_triage_finding.py`                    | Fixed files committed; GitHub PR opened; triage artifact at `.gomboc/triage/`    |
| **8. Resolution note**     | `add_observation_note.py`                                                            | Timestamped note posted to observation; visible in Wiz UI                        |

For details on what each skill does individually, browse the installed skill catalog in your agent: **Settings → Rules** in Cursor, `/skills` in Codex, `/plugins` in Claude Code, or `gemini extensions list` then `/help` in Gemini CLI.

***

### Tiers

The skill adjusts its behavior based on what data is available at runtime. The assigned tier is announced to the user before work begins.

| Tier                        | Condition                                               | What the loop produces                                                                                  |
| --------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| **1 — Full loop**           | Observation found + local source path + IaC detected    | Full pipeline: classification → diagnose → build → enrich → review → release → apply → note             |
| **2 — Classification only** | Observation found; source path deferred or missing      | Classification YAML + triage brief. Deferred steps noted for resumption when a source path is provided. |
| **3 — Manual CSPM input**   | cspm-service unreachable or observation data incomplete | User pastes fields from Wiz UI; loop continues with a manual-data warning.                              |
| **4 — Identification only** | No observation ID and no CSPM access                    | Maps a plain-text issue description to the closest existing classification; LOW confidence warning.     |

A Tier 2 triage brief is written to the conversation when the source path is deferred, tracking which steps have been completed and providing resume instructions.

***

### Code match confidence gate

After `diagnose` runs in Step 3, the skill scores how confidently the CSPM alert can be traced to the provided source path. This gate is independent of the alert's severity.

| Confidence | Condition                                                                 | Action                                                              |
| ---------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| **HIGH**   | Resource type found AND violation pattern matched                         | Proceed automatically                                               |
| **MEDIUM** | Resource type found but match uncertain (dynamic blocks, indirect config) | Show warning; require user acknowledgment before continuing         |
| **LOW**    | Resource type not found in provided code                                  | Stop. Confirm to fall to Tier 2 or provide a different source path. |

The gate prevents building and releasing rules that would never fire against the actual deployment code.

***

### Caveats

#### `exampleObservationId` may be null or stale in unique-observation results

When using `fetch_observation.py --top`, the `exampleObservationId` field references a specific observation record. If it is null or the referenced observation has been deleted, the script falls back to using the unique-observation item directly — which contains sufficient context (`policyContextMarkdown`, control ID, severity, resource type) to complete Steps 1–5. Step 8 uses the `example_observation_id` captured in the triage artifact during the run.

#### GitHub token and PR creation

`gh pr create` requires `GITHUB_TOKEN` (a PAT with `repo` scope) and an authenticated `gh` CLI. If `gh` is unavailable or not authenticated, the branch is still committed locally and the triage artifact is written — the PR must be opened manually from the printed branch name.

***

### Required environment variables

These must be present in the container environment before `cspm-triage` runs. Do not ask the user for them at runtime — they are pre-injected during deployment.

| Variable                   | Purpose                                                                                         |
| -------------------------- | ----------------------------------------------------------------------------------------------- |
| `CSPM_API_URL`             | Base URL of the cspm-service (e.g. `https://cspm.internal.example.com`)                         |
| `CSPM_API_TOKEN`           | Bearer token for the cspm-service API                                                           |
| `GOMBOC_API_TOKEN`         | Bearer token for the Gomboc Rules Service (used by `rules-service` and scripts)                 |
| `GITHUB_TOKEN`             | GitHub PAT with `repo` scope — used by `gh` for branch push and PR creation                     |
| `GOMBOC_RULES_SERVICE_URL` | *(optional)* Override for the rules service base URL; defaults to `https://rules.app.gomboc.ai` |

***

### How to trigger the loop in each agent

| Agent            | How to trigger                                                                                                                                                                                                      |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Claude Code**  | Type `/gomboc:cspm-fix` to invoke directly, or describe the goal in natural language — Claude activates the skill automatically. Example: *"Triage the CSPM alert POD-005 against the code in `./infrastructure`."* |
| **Gemini CLI**   | `/gomboc:cspm-fix` or `/gomboc:cspm-fix <observation-id>` to skip auto-ranking                                                                                                                                      |
| **OpenAI Codex** | `$cspm-fix` (Codex native skill invocation)                                                                                                                                                                         |
| **Cursor**       | Type `/gomboc:cspm-fix` in Agent chat, or describe the goal in natural language and let Agent Decides pick the skill.                                                                                               |

***

### Output artifacts

The skill writes a structured JSON artifact to `.gomboc/triage/` in the repo root. The file is committed on the fix branch and included in the PR.

```
.gomboc/
└── triage/
    └── <control-id>-<date>.json     # full triage record
```

The artifact captures:

* Observation metadata (control ID, severity, body type, source URL, occurrence count, last seen)
* Triage outcome (tier, confidence, steps completed, steps pending)
* Rule details (classification name, rule name, IaC language, files scanned, violations found)
* Git attribution (repo URL, branch, commit hash, PR URL)
* `alert_comment` — the pre-formatted text posted back to the observation in Step 8; read by the skill via `jq` and passed to `add_observation_note.py`
* `resolution_note` — populated after Step 8 with `observation_id`, `posted_at`, and `status` (`posted` / `skipped` / `failed`); records execution outcome without duplicating the note text

***

### Common starting points (partial loop)

You do not always need the full loop. The right entry point depends on what you already have:

| Starting point                                    | What to run                                                                         |
| ------------------------------------------------- | ----------------------------------------------------------------------------------- |
| A Wiz alert ID and local IaC code                 | `cspm-triage` — full Tier 1 loop handles everything                                 |
| An alert ID but no local code yet                 | `cspm-triage` — completes Tier 2 (classification + brief); resume later with a path |
| Classification already exists; need a rule for it | `build-rule` → `enrich-rule` → `review-rule` → `release-rule`                       |
| Rule exists; need to apply it and track the fix   | `orl remediate` → `write_triage_finding.py` → `add_observation_note.py`             |
| No CSPM access; want to map a plain-text issue    | `cspm-triage` — Tier 4 produces a classification candidate with confidence warning  |

***

### Quick-start examples

**Full triage of a specific alert against local Terraform**

In Claude Code or Cursor, type in Agent chat:

```
Triage CSPM observation POD-005 against the Kubernetes manifests in ./infrastructure/helm.
```

In Gemini CLI:

```bash
/gomboc:cspm-fix POD-005
```

In Codex:

```
$cspm-fix POD-005
```

**Auto-rank top alerts and select interactively**

In Claude Code or Cursor:

```
Triage the highest-severity CSPM alerts against ./infrastructure. Show me the ranked list and let me pick.
```

In Gemini CLI:

```bash
/gomboc:cspm-fix
```

**Dry-run: classification and diagnosis only, no rule creation**

In Claude Code or Cursor:

```
Fetch CSPM observation COMPUTE-013 and diagnose ./infra against it. Do not build or release any rules yet.
```

**Resume a deferred Tier 2 triage**

In Claude Code or Cursor:

```
Resume the CSPM triage for observation COMPUTE-013 — source path is now ./terraform/prod.
```

***

### How cspm-triage fits in the broader Gomboc plugin

`cspm-triage` is the CSPM-alert entry point into the same underlying rule pipeline used by `enforce-policy`. The key distinction:

| Workflow         | Entry point                            | Starts from                         |
| ---------------- | -------------------------------------- | ----------------------------------- |
| `enforce-policy` | A codebase + a policy name             | Code you control; policy is known   |
| `cspm-triage`    | A live cloud alert + (optionally) code | A runtime finding in deployed infra |

Both workflows produce the same outputs — ORL rules released to the Rules Service and code fixes applied locally — but `cspm-triage` adds the CSPM-specific context bridge: it fetches the alert, maps it to a classification, scores the IaC match, and closes the loop by posting a resolution note back to Wiz.

Use `enforce-policy` when you are enforcing a known policy against code you are writing. Use `cspm-triage` when you are acting on a cloud security alert and want to trace it back to infrastructure code.

***

### See also

* [Gomboc Enterprise Skills](/orl/agent-skills/gomboc-enterprise-skills.md) — install the plugin in Claude Code, Gemini CLI, OpenAI Codex, or Cursor.
* [Policy Enforcement Loop](/orl/agent-skills/gomboc-enterprise-skills/policy-enforcement-loop.md) — the code-first enforcement workflow.
* [ORL (Open Remediation Language)](/orl.md) — the language and engine that backs ORL rules.
* [Custom Rules Quickstart](/orl/quickstart.md) — write your first ORL rule.
* [Publish](/orl/publish.md) — publish ORL rules to the Gomboc Rules Service.
* [Rules](/policy-management/rules.md) — Gomboc rule concepts in the platform.
