---
name: rulewalk
description: >
  Use when the user shares a business rule, filter condition, policy, or code
  function that applies sequential logic to a dataset and asks to understand,
  explain, document, trace, or visualize what it does to the data. Triggers on:
  "explain this rule", "what does this filter do", "document this logic",
  "trace this function", "walk me through", "show what happens to the data",
  "which records pass", "what gets rejected", "rastreia essa regra",
  "explica esse filtro", "documenta essa lógica", "o que esse pipeline faz",
  "mostra o que acontece com os registros", "cria um walkthrough".
  Also activates when user pastes code containing .filter(), .map(), conditional
  branches, or SQL WHERE/JOIN and asks what it does. Does NOT activate for
  generic questions about business rules unrelated to tracing data flow.
---

# RuleWalk

Turn a business rule into an interactive HTML walkthrough: step-by-step cards that show exactly which records pass, get rejected, get enriched, grouped, or trigger an action at each stage of the rule.

## When to activate

- User shows a business rule, filter, or automation ("pedidos pendentes há mais de 3 dias")
- User says "walk me through this rule", "explica essa regra", "cria um walkthrough", "mostra o que acontece com os dados"
- User provides a code snippet or document that applies sequential logic to a dataset

## CLI location

The CLI is `bin/rulewalk.bundle.mjs`, a sibling of this SKILL.md file. Its absolute path is
always relative to the **directory containing this SKILL.md**, not relative to the
project being analyzed.

The skill path appears in the system context (e.g.
`C:\Users\<user>\.claude\skills\rulewalk\SKILL.md`). Derive the CLI path from it:

```
<skill-dir>\bin\rulewalk.bundle.mjs
```

Never write `node rulewalk/bin/rulewalk.bundle.mjs` in commands sent to the shell — that
path is only valid if the CWD happens to be the RuleWalk dev repo itself. Always use
the absolute path resolved from the skill directory.

## Setup check

```
node "<skill-dir>\bin\rulewalk.bundle.mjs" doctor
```

Run this first. It checks that `pipeline.schema.json` and `viewer.html.template` are
present and valid. If it fails, stop and report the error.

---

## Output convention (fixed)

Save all output to `./rulewalk-out/<slug>/` relative to the current working directory,
where `<slug>` is a short kebab-case name derived from the rule title.

| File | Path |
|------|------|
| Pipeline IR | `./rulewalk-out/<slug>/pipeline.json` |
| Rendered HTML | `./rulewalk-out/<slug>/rendered.html` |

Use this path unless the user explicitly specifies otherwise. Do not choose a location
ad hoc on each invocation.

---

## Reporting (silent mode — default)

In normal use, do NOT narrate file reads, schema lookups, count recalculations, or
hash verifications in conversation text. These are deterministic CLI operations — run
them, do not re-derive them in prose.

After a successful `deliver`, report exactly this to the user:

```
✓ rulewalk-out/<slug>/rendered.html
  <initial> → <final> records · <N> steps · <K> assumed  (omit confidence line if all confirmed)
```

**Never paste the full pipeline.json in the conversation** unless the user explicitly
asks to review the IR.

---

## Workflow

### 1 — Understand the rule

Identify:

- **The entity** — what is the "record"? (order, lead, invoice, ticket, etc.)
- **The steps** — what happens to each record, in order?
- **The data source** — provided sample, or synthesize?

### 2 — Design the pipeline steps

| step_type   | When to use                                                     |
|-------------|------------------------------------------------------------------|
| `filter`    | Records are kept or discarded based on a condition               |
| `transform` | Fields are added or modified on every record (none removed)      |
| `group`     | Records are aggregated into groups (unit changes: N → M groups)  |
| `lookup`    | Records are enriched by joining with an external reference table |
| `action`    | A side-effect is triggered (email, webhook, write) — no output   |

#### When source_type is "code"

1. **One block = one step by default.** A single `.filter()` call with compound `&&` / `||` conditions maps to one step. Split only when the code itself signals distinct intent through separate named functions, independent blocks, or explicit comments.
2. **rule_text describes only what the condition does — never invent the business reason.** If the code has no comment, describe only the mechanics. If you must invent a why without any source evidence, set `confidence: "approximated"` and note what was invented.
3. **Lookup patterns in code:** `.find()` / `.get()` against a different collection, `await db.query(...)`, `await api.get(...)` → `lookup` step. Chaining `.filter()` / `.map()` on the same input collection is never a lookup.
4. **entity_schema documents only fields observed in the code snippet.** Do not infer fields not referenced. Note in `initial_dataset.note` that the real entity may have additional fields.
5. **data_origin defaults to `"synthetic"` for source_type: "code".** If test fixtures exist, use `data_origin: "provided"` and set `meta.rule_source` to the test file path.
6. **condition_expr preserves the literal source code syntax.** Never translate to pseudo-SQL. The readable business description goes only in `rule_text`.

### 3 — Generate synthetic data (when no sample provided)

Generate **12–20 records** covering all branches. Rules:

- Use realistic-looking values (not "foo", "test1")
- Distribute records deliberately: if a filter removes 30%, ~30% of the sample must fail
- All `entity_schema` fields must appear in every initial record
- Samples must be subsets/transformations of initial records — never invent new records

### 4 — Scaffold then fill the pipeline JSON

Generate the skeleton — one command, correct structure guaranteed:

```
node "<skill-dir>\bin\rulewalk.bundle.mjs" init <step-types> -o ./rulewalk-out/<slug>/pipeline.json
```

`<step-types>` is a comma-separated list in pipeline order, e.g. `filter,transform,action`.

Then open the file and replace every `FILL:` / `FILL_*` placeholder with real values:

| Placeholder location | What to put |
|----------------------|-------------|
| `meta.title` | Rule title |
| `meta.source_type` | `"doc"` or `"code"` |
| `meta.data_origin` | `"synthetic"` or `"provided"` |
| `meta.entity_schema` | Actual fields and types |
| `initial_dataset.count` / `.sample` | Real or synthetic records |
| Each step: `name`, `rule_text`, `condition_expr` | Actual rule content |
| Each step: `input_count`, `output_count`, samples | Correct counts and records |
| `final_result.count` / `.sample` | Final output |

Count chain — write correct counts on the first fill (the validator enforces them):

1. `steps[0].input_count === initial_dataset.count`
2. `steps[i].input_count === steps[i-1].output_count`
3. `transform` and `lookup`: `input_count === output_count`
4. `action.trigger_count` (if present) must equal the previous step's `output_count`
5. `final_result.count === last data-producing step's output_count`

### 5 — Validate

```
node "<skill-dir>\bin\rulewalk.bundle.mjs" validate ./rulewalk-out/<slug>/pipeline.json
```

The validator is the authority. Do not manually verify counts or schema rules in conversation text.

### 6 — Deliver

```
node "<skill-dir>\bin\rulewalk.bundle.mjs" deliver ./rulewalk-out/<slug>/pipeline.json ./rulewalk-out/<slug>/rendered.html
```

Validates first; writes HTML only if the pipeline is clean.

### 7 — Report

Reply with the compact summary format defined in **Reporting** above. Nothing else unless asked.

---

## Cheat sheet — step shapes

Use this instead of reading `pipeline.schema.json` from disk.

### filter
```json
{
  "step_type": "filter",   "id": "step-N",
  "name": "...",           "rule_text": "...",
  "condition_expr": "field op value",
  "input_count": N,        "output_count": M,
  "passed_sample":   [ ...records... ],
  "rejected_sample": [ { "record": {...}, "reason": "..." } ]
}
```
`passed_sample` + `rejected_sample` required. `output_sample` **forbidden**.

### transform
```json
{
  "step_type": "transform", "id": "step-N",
  "name": "...",            "rule_text": "...",
  "condition_expr": "new_field = formula",
  "input_count": N,         "output_count": N,
  "output_sample": [ ...enriched records... ],
  "changes": ["field_name added"]
}
```
`input_count` must equal `output_count`. `passed_sample` / `rejected_sample` **forbidden**.

### group
```json
{
  "step_type": "group",    "id": "step-N",
  "name": "...",           "rule_text": "...",
  "group_by": "field",     "output_unit": "groups by field",
  "input_count": N,        "output_count": G,
  "output_sample": [ { "field": "...", "count": K } ]
}
```
`output_count` = number of groups. `rejected_sample` **forbidden**.

### lookup
```json
{
  "step_type": "lookup",   "id": "step-N",
  "name": "...",           "rule_text": "...",
  "lookup_source": "table — description",
  "lookup_key": "join_field",
  "input_count": N,        "output_count": N,
  "output_sample": [ ...enriched records... ],
  "changes": ["field_added"]
}
```
`input_count` must equal `output_count`. `rejected_sample` **forbidden**.

### action
```json
{
  "step_type": "action",   "id": "step-N",
  "name": "...",           "rule_text": "...",
  "trigger_count": N,
  "effects": [ { "type": "email|webhook|notification|database-write", "description": "...", "count": N } ]
}
```
No `input_count`, `output_count`, or sample fields. When last step is action, `final_result` requires `result_from`.

---

## confidence values

| Value | When to use |
|-------|-------------|
| `"confirmed"` | Rule is unambiguous and directly traceable to the source. Default when field is absent. |
| `"assumed"` | Source states the rule but with uncertainty (magic numbers, "uns 60 pontos", undocumented thresholds). |
| `"approximated"` | Rule was not in the source — RuleWalk inferred or constructed it. Always include `confidence_note`. |

Add `"confidence"` + `"confidence_note"` to any step where the rule is not fully explicit.

---

## Quick reference: required fields per step type

| Field            | filter | transform | group | lookup | action |
|------------------|:------:|:---------:|:-----:|:------:|:------:|
| `step_type`      | ✓      | ✓         | ✓     | ✓      | ✓      |
| `id`             | ✓      | ✓         | ✓     | ✓      | ✓      |
| `name`           | ✓      | ✓         | ✓     | ✓      | ✓      |
| `rule_text`      | ✓      | ✓         | ✓     | ✓      | ✓      |
| `input_count`    | ✓      | ✓         | ✓     | ✓      | —      |
| `output_count`   | ✓      | ✓         | ✓     | ✓      | —      |
| `passed_sample`  | ✓      | ✗         | ✗     | ✗      | ✗      |
| `rejected_sample`| ✓      | ✗         | ✗     | ✗      | ✗      |
| `output_sample`  | ✗      | ✓         | ✓     | ✓      | ✗      |
| `changes`        | —      | opt       | —     | opt    | —      |
| `group_by`       | —      | —         | ✓     | —      | —      |
| `output_unit`    | —      | —         | ✓     | —      | —      |
| `lookup_source`  | —      | —         | —     | ✓      | —      |
| `lookup_key`     | —      | —         | —     | opt    | —      |
| `trigger_count`  | —      | —         | —     | —      | opt    |
| `effects`        | —      | —         | —     | —      | ✓      |
| `confidence`     | opt    | opt       | opt   | opt    | opt    |
| `confidence_note`| opt    | opt       | opt   | opt    | opt    |

`✓` required · `✗` forbidden · `opt` optional · `—` not applicable
