---
name: validate-repo
description: Audit a dbt project on six dimensions (Gemma SQL style, internal naming consistency, logic hygiene, target-structure doc alignment, CLAUDE.md alignment, Kimball modelling) by dispatching parallel sub-agents and consolidating findings into a single report. Read-only — never modifies the audited repo.
disable-model-invocation: true
argument-hint: "[path-to-dbt-project]"
---

# Validate dbt Repo

Audit an existing dbt project across six dimensions. Each dimension runs in its own sub-agent in parallel; the orchestrator consolidates findings into a single severity-sorted report with concrete suggested fixes. **This skill never edits the audited repo.**

`$ARGUMENTS` is optional:
- One token → treat it as the absolute path to the dbt project root.
- Empty → treat the current working directory as the dbt project root.

If the resolved path does not contain a `dbt_project.yml`, stop and tell the user.

## A note on `${CLAUDE_SKILL_DIR}`

Throughout this file you'll see `${CLAUDE_SKILL_DIR}/references/...` paths in sub-agent prompts. When the skill is invoked via the `Skill` tool, the harness auto-resolves this placeholder to the absolute path of this skill's directory.

If you're testing this skill manually (e.g., from a PR branch before the plugin is merged/installed), the orchestrator must interpolate `${CLAUDE_SKILL_DIR}` itself. Substitute the absolute path of `plugins/gemma-dbt/skills/validate-repo/` in the toolkit checkout you're running from.

Files under `references/`:
- `finding-format.md` — JSON schema sub-agents must conform to
- `gemma-sql-style-checklist.md` — rule list for sub-agent #1
- `kimball-validation-checklist.md` — rule list for sub-agent #6
- `report-template.md` — Phase C report shape
- `issue-families.md` — cross-agent issue-family dedup table used in Phase C2

## Expected cost and runtime

The skill spawns six sub-agents in parallel; cost scales with repo size.

| Repo size | Total tokens | Wall-time | Notes |
|---|---|---|---|
| Small (<40 SQL models) | ~150–250k | 2–4 min | Each agent reads all model files |
| Medium (40–200 SQL models) | ~400–600k | 5–10 min | Sub-agents sample per layer (up to 10 files per layer) |
| Large (>200 SQL models) | ~600k–1M | 10–15 min | Most savings come from manifest-driven analysis |

The orchestrator (the model running this skill) is typically opus-class. Sub-agents are `sonnet` by default. The two smaller-scope sub-agents (`target-structure`, `claudemd`) can be downgraded to `haiku` on small repos for additional savings (~20–30% cost reduction).

`dbt parse` runs once in Phase A and adds 5–10 seconds.

## What this skill does

1. **Phase A — Discover** the repo (orchestrator, sequential): inventory models, parse the dbt manifest if possible, find the project's target-structure doc and CLAUDE.md.
2. **Phase B — Validate** in parallel: spawn six sub-agents (one per dimension) using the Task tool in a single message. Each returns structured JSON.
3. **Phase C — Consolidate** the findings into a markdown report and present it to the user.

---

## Phase A — Discover the repo

Run these steps sequentially. Do NOT spawn sub-agents yet.

### A1. Resolve and verify the project path

Resolve `$ARGUMENTS` to an absolute path (or use the cwd). Confirm `<path>/dbt_project.yml` exists. If not, stop with: "No dbt_project.yml at `<path>` — is this a dbt project?"

### A2. Read the project anchors

Read these files (skip cleanly if absent except `dbt_project.yml`):
- `<path>/dbt_project.yml`
- `<path>/packages.yml`
- `<path>/README.md`
- `<path>/CLAUDE.md`

From `dbt_project.yml`, extract: `name`, `version`, `model-paths`, `seed-paths`, `macro-paths`, `snapshot-paths`, top-level `models:` config (materializations per folder).

### A3. Find the target-structure doc

Search `<path>/docs/` (case-insensitive, recursive) for a markdown file whose name matches any of these patterns, in priority order:

1. `target*structure*` or `*target*structure*`
2. `data*model*`
3. `architecture*`
4. `model*structure*`
5. `structure*` (only if none of the above matched)

**Behaviour:**
- **Exactly one match** → use it.
- **Multiple matches** → list them with paths and ask the user which one to use (or "all of them" — in that case concatenate, separated by `---` headers).
- **Zero matches** → walk the fallback chain in order, stopping at the first hit:
  1. `<path>/CLAUDE.md` — if present and contains a section heading matching `model layers?`, `architecture`, `repo structure`, or `target structure` (case-insensitive), use the file as the target-structure doc.
  2. `<repo-root>/CLAUDE.md` — if different from `<path>/CLAUDE.md` (e.g., dbt is a subfolder of a larger repo), apply the same heading check.
  3. **Ask the user** (only when interaction is available): "I could not find a target-structure document under `docs/` or a recognizable section in `CLAUDE.md`. Paste it inline, give me a path I missed, or say `skip`."
  4. **Non-interactive fallback:** if no user is available and steps 1–2 found nothing, record `target-structure-doc-not-found` in `skipped_dimensions` and skip sub-agent #4. The report's "Skipped checks" section makes this prominent.

Always record which source was used (or that the dimension was skipped) in the report's "What was checked" section so the substitution is transparent.

### A4. Inventory the repo

Build a manifest (in memory) listing:
- All `.sql` files under model-paths, grouped by top-level folder (`base/`, `interim/`, `analytics/`, `reporting/`, `export/`, plus any others)
- All `.yml` files under model-paths
- All seed files (`.csv` paths only — do NOT read seed contents; seeds may contain client data on this connection)
- All snapshot files
- All macro files
- A list of fact/dim model names (anything matching `fact_*` or `dim_*`)

### A5. Run `dbt parse` (best effort)

Run from the project root:

```bash
cd <path> && dbt parse
```

`dbt parse` is allowed on the Anthropic-API connection (it does not return data). If it succeeds, the file `<path>/target/manifest.json` will exist and downstream agents can use it for richer dependency analysis. If it fails (e.g., missing profile, dependency error), capture the error message and continue — agents will fall back to filename-only analysis and the report will note the degradation.

### A6. Build the override summary

Concatenate any explicit conventions from CLAUDE.md and the target-structure doc that affect Kimball or naming rules. This will be passed to sub-agents #2 and #6 so they can suppress / downgrade rules the project has explicitly overridden.

Examples of overrides to capture:
- "We use plural names for dim tables (`dim_customers`, not `dim_customer`)"
- "We do not use surrogate keys in this project — natural keys only"
- "Reporting models may select directly from base"
- "Project-specific layer names like `models/marts/` instead of `models/analytics/`"

If neither file contains override-style guidance, the override summary is empty.

---

## Phase B — Dispatch six sub-agents in parallel

Send a **single message with six Task tool calls** so all sub-agents run concurrently. Each prompt is a self-contained markdown block — every sub-agent receives all the metadata it needs and does NOT re-read project anchors that Phase A already loaded.

**Sub-agent type: `general-purpose`.** Do NOT use `Explore` — its own description warns it is "not for code review, design-doc auditing, cross-file consistency checks, or open-ended analysis," which is exactly what these validators do. `general-purpose` has the right tool set (Read/Glob/Grep/Bash) and read budget.

**Model: `sonnet` is the default recommendation.** Sub-agents do meaningful judgement work but don't need orchestration depth. The smaller-scope agents (`target-structure`, `claudemd`) can run on `haiku` for cost savings on small repos (<40 SQL files).

For each sub-agent, the prompt format is:

> **Mission:** <agent-specific>
>
> **Repo root (read-only):** `<absolute path>`
>
> **Inputs:** <agent-specific manifest, paths, content snippets>
>
> **Reference (read but do not modify):** <absolute path to checklist file, or content>
>
> **Return contract:** Read `${CLAUDE_SKILL_DIR}/references/finding-format.md` for the JSON schema. Return ONE JSON object conforming to it. Do not include any prose outside the JSON.
>
> **Constraints:**
> - You MAY read any file under the repo root.
> - You MUST NOT modify the repo. No Edit, no Write, no `dbt run/build/test/seed/snapshot`, no shell commands that mutate state.
> - You MUST NOT run `dbt show` or any data-returning command.
> - Severity must follow the definitions in `finding-format.md`.
> - **Evidence for absence claims:** any finding asserting something is missing/absent (no test, no description, no key, etc.) MUST quote the exact block you inspected at a cited `file:line` in the `evidence` field. If you did not actually read that block, do NOT assert the absence — skip it (`skipped_checks`) or emit at `confidence: low`. See the "Evidence requirement" section of `finding-format.md`.
> - **Degraded mode:** if `manifest.json` is null (no `dbt parse`), do not use `confidence: high` for findings that depend on parsing nested YAML blocks — cap at `medium`, and still back absence claims with a quoted `evidence` block (skip if it can't be located).

The six sub-agents:

### Sub-agent 1 — Gemma SQL Style validator

> **Mission:** Audit every `.sql` and `.yml` file in the repo against the Gemma SQL Style Guide. Identify violations and produce findings.
>
> **Repo root:** `<absolute path>`
>
> **Inputs:**
> - SQL file list: `<list from A4>`
> - YAML file list: `<list from A4>`
> - dbt_project.yml summary: `<from A2>`
>
> **References:**
> - Distilled checklist (use this as the rule list): `${CLAUDE_SKILL_DIR}/references/gemma-sql-style-checklist.md`
>
> **Approach:**
> 1. Read the distilled checklist. Use the `rule` identifiers it defines.
> 2. Pick a representative sample of files per layer (base, interim, analytics, reporting): up to 10 files per layer if the repo is large. Read all files if the repo has fewer than 40 SQL files total.
> 3. For each rule, scan the sampled files. Record one finding per violation. Aggregate when a single rule fires across many files: produce one finding per file, not one per occurrence within a file.
> 4. For aggregate-feeling rules (e.g., `consistent-pluralization`), produce ONE finding describing the inconsistency across files, with the offending filenames listed in `details`.
>
> **Override hints to apply:** `<override summary from A6>`. If an override contradicts a Gemma style rule, downgrade severity and note the override in `details`.
>
> **Set `agent: "gemma-style"`** in the return JSON.

### Sub-agent 2 — Naming consistency validator

> **Mission:** Check that naming conventions hold *internally* across the repo, regardless of the Gemma style guide. Same business entity → same name everywhere; same column convention applied uniformly.
>
> **Repo root:** `<absolute path>`
>
> **Inputs:**
> - SQL file list: `<list from A4>`
> - YAML file list: `<list from A4>`
> - Override summary: `<from A6>` (project may have its own conventions that supersede generic ones)
>
> **Approach:**
> 1. Extract every column alias and table/CTE name from every model file. Build maps:
>    - business-entity-name → set of names used (e.g., does the user concept appear as `user`, `users`, `customer`, `account`?)
>    - column-suffix-pattern → set of columns matching it (e.g., are date columns suffixed `_date` AND `_on` AND nothing? mixed suggests inconsistency)
>    - boolean-prefix → are booleans prefixed `is_*`/`has_*` consistently?
> 2. Flag any inconsistency where one convention dominates and a small minority deviates. Examples:
>    - 95% of dim tables use `dim_<singular>`, 5% use `dim_<plural>` → flag the 5% as `naming-dim-pluralization-inconsistent` (**major**).
>    - The same FK column appears as both `customer_id` and `cust_id` in different models → flag as `naming-fk-column-name-drift` (**major**).
> 3. Do NOT flag a convention violation that is universal (the whole repo uses `dim_<plural>`) — that's an internal convention, not an inconsistency. Mention it as `info` in `metadata.notes`.
> 4. If `target/manifest.json` exists, prefer the parsed model node names; otherwise rely on filenames + grep.
>
> **Set `agent: "naming"`** in the return JSON.

### Sub-agent 3 — Logic validator

> **Mission:** Find logic / dependency / hygiene issues in the dbt project.
>
> **Repo root:** `<absolute path>`
>
> **Inputs:**
> - dbt manifest: `<path>/target/manifest.json` if it exists, otherwise `null`
> - SQL file list: `<list from A4>`
> - dbt_project.yml summary: `<from A2>`
>
> **Approach:**
> 1. If `manifest.json` exists, build the model dependency graph from `nodes[*].depends_on.nodes`. If not, fall back to grepping `{{ ref('...') }}` and `{{ source('...', '...') }}` calls.
> 2. Check for these issues (each maps to a rule id):
>    - `logic-base-not-from-source` — a `base_*` model that does NOT reference `{{ source(...) }}`. **major**.
>    - `logic-non-base-from-source` — a non-`base_*` model that references `{{ source(...) }}`. **critical**.
>    - `logic-dead-model` — a model that no other model `ref`s and that is not in `reporting/`, `export/`, or marked as exposed. **minor**.
>    - `logic-circular-dependency` — a cycle in the ref graph. **critical**.
>    - `logic-cross-layer-skip` — analytics model that selects from base directly without an interim layer (when interim layer is in use elsewhere). **minor**.
>    - `logic-pk-test-missing` — **`fact_*` or `dim_*` models ONLY** (never base/interim/staging): the model's primary-key column has **neither** a `unique` **nor** a `not_null` test. **critical** for dims, **major** for facts. You MUST open the model's YAML doc block, read the PK column's `data_tests:`/`tests:` list, and quote it in `evidence` — flag only if both tests are genuinely absent. If a column already has `unique` (or `not_null`), do NOT flag it. If you cannot locate/parse the column's test block, do NOT assert absence: add `could-not-verify-pk-tests` to `skipped_checks` instead.
>    - `logic-hardcoded-value` — model contains numeric/string literals that look like business constants (e.g., `WHERE country = 'DE'`). **minor**, low confidence — flag only if the literal is NOT obviously a join condition.
>    - `logic-suspicious-join` — JOIN where one side has no `unique` test on the join column AND the join is not LEFT (Cartesian risk). **major**, low confidence. This is an absence claim — quote the inspected test block (or its absence) in `evidence`.
> 3. If `manifest.json` is null, add `dbt-parse-failed` to `skipped_checks` for any rule that needs it (`logic-dead-model`, `logic-circular-dependency`), and treat YAML-block reads as filename-only: per `finding-format.md`, cap confidence at `medium` for findings that depend on parsing nested YAML, and back every absence claim with a quoted `evidence` block (skip if it can't be located).
>
> **Set `agent: "logic"`** in the return JSON.

### Sub-agent 4 — Target-structure validator

> **Mission:** Check that the actual repo structure matches the project's documented target structure.
>
> **Repo root:** `<absolute path>`
>
> **Inputs:**
> - Target-structure doc content (full text): `<from A3>`. If A3 returned `skip`, do NOT run this agent — set `agent: "target-structure"` and return `{ "findings": [], "skipped_checks": [{"rule": "no-target-structure-doc", "reason": "user opted to skip this dimension"}] }`.
> - Repo manifest from A4
>
> **Approach:**
> 1. Parse the target-structure doc. Extract the prescribed:
>    - folder structure (which layers exist, what they're called)
>    - naming patterns (any explicit `fact_*`, `dim_*`, custom prefixes)
>    - layer responsibilities (what each layer is/isn't allowed to do)
>    - any explicit do-NOT items
> 2. Compare against the actual manifest:
>    - Folders mentioned in the doc but missing from the repo → finding `target-structure-folder-missing` (**major**).
>    - Folders in the repo not mentioned in the doc → finding `target-structure-folder-undocumented` (**minor**).
>    - Models in folders the doc forbids them in → finding `target-structure-model-misplaced` (**major**).
>    - Naming patterns in the doc that the repo violates → finding `target-structure-naming-violation` (**major**).
> 3. If the doc is vague, lower confidence to `medium` or `low` rather than skipping.
>
> **Set `agent: "target-structure"`** in the return JSON.

### Sub-agent 5 — CLAUDE.md validator

> **Mission:** Check that the actual repo behaviour matches the project's `CLAUDE.md` directives.
>
> **Repo root:** `<absolute path>`
>
> **Inputs:**
> - CLAUDE.md content (full text): `<from A2>`. If absent, do NOT run this agent — set `agent: "claudemd"` and return `{ "findings": [], "skipped_checks": [{"rule": "no-claudemd", "reason": "project has no CLAUDE.md"}] }`.
> - Repo manifest from A4
>
> **Approach:**
> 1. Parse CLAUDE.md. Identify directives that are statically checkable. Examples:
>    - "Always use `uv` to run Python" → check whether `python` / `pip` appear in shell scripts or Makefiles in the repo
>    - "All models must have descriptions" → check the YAML files
>    - "Use `dbt build`, not `dbt run`" → check Makefiles, GitHub Actions, scripts/
>    - "Models in folder X are deprecated" → check that nothing new lives there
>    - "Use BigQuery" / "Use Snowflake" → cross-check against `profiles.yml` references and dbt_project.yml
> 2. For each directive, generate a finding when the repo violates it.
> 3. Skip directives that cannot be checked statically (e.g., "Always discuss with stakeholders before X") — record them in `skipped_checks` with reason `claudemd-directive-not-statically-checkable`.
> 4. Severity heuristic: phrasing like "MUST", "always", "never" → **major**; "prefer", "recommend" → **minor**.
>
> **Set `agent: "claudemd"`** in the return JSON.

### Sub-agent 6 — Kimball validator

> **Mission:** Check Kimball dimensional-modelling consistency. **Soft rules**: any rule contradicted by CLAUDE.md or the target-structure doc must be suppressed or downgraded.
>
> **Repo root:** `<absolute path>`
>
> **Inputs:**
> - Repo manifest from A4
> - dbt manifest: `<path>/target/manifest.json` if available
> - Override summary: `<from A6>`
>
> **References:**
> - Validation checklist (your rule list): `${CLAUDE_SKILL_DIR}/references/kimball-validation-checklist.md`
>
> **Approach:**
> 1. Read the checklist. Use the `rule` identifiers it defines (all start with `kimball-`).
> 2. For each `fact_*` and `dim_*` model in the manifest, evaluate the relevant rules. Read the model's YAML doc block to assess grain declarations, additivity annotations, and PK tests. **Any "missing"/absent claim (no grain doc, no PK test, no surrogate key) is an absence claim**: quote the inspected block at a cited `file:line` in `evidence`, and if both a `unique` and `not_null` test are present on a key, do NOT report it as missing. If you cannot read the block, skip rather than assert absence (per `finding-format.md`).
> 3. **Override processing (mandatory):** Before reporting any finding, check the override summary for relevant overrides. If found:
>    - Suppress the rule entirely (add to `skipped_checks` with `reason: "<rule-id> overridden by <CLAUDE.md | target-structure-doc>"`), OR
>    - Downgrade severity to `info` and note the override in `details`.
> 4. Never report `critical` or `major` severity for any rule that has been explicitly overridden.
> 5. Cross-table conformance checks (`kimball-conformed-mismatched-cols`, `kimball-multiple-date-dims`) require comparing across files — do those last, after per-model checks.
>
> **Set `agent: "kimball"`** in the return JSON.

---

## Phase C — Consolidate

After all six sub-agents return:

### C1. Validate each return JSON

For each sub-agent response, confirm:
- It is valid JSON
- `agent` matches the expected value
- `findings` is an array; each entry has all required keys
- `severity` and `confidence` use exact-spelling enums from `finding-format.md`

If a response is malformed, do NOT silently drop it. Add a finding under `agent: "orchestrator"` with `rule: "subagent-malformed-response"`, severity `major`, `details` quoting the problem, and continue with the others.

### C2. De-duplicate

The orchestrator de-duplicates in three passes:

**Pass 1 — exact triple dedup.** If two sub-agents flag the same `(file, line, rule)` triple, keep the higher-severity entry and append `"also reported by: <other-agents>"` to its `details`.

**Pass 2 — issue-family dedup.** Some real-world issues get flagged by multiple agents under different rule IDs. The orchestrator MUST consult the issue-family table at `${CLAUDE_SKILL_DIR}/references/issue-families.md` and collapse all findings in the same family (same `file`, line need not match) into one canonical entry:

- Pick the highest-severity finding as canonical.
- Append `"Also reported as: <rule-id> (<agent>) at <file>:<line>"` to its `details` for each related finding.
- Remove the others from the rendered report (but keep them in the raw findings JSON for traceability — if you offer to persist the JSON alongside the markdown, include them there).

**Pass 3 — defer-rule overlap.** For naming-style overlaps not in the issue-family table: defer to Gemma-style for naming rules, Kimball for grain/additivity rules.

**Never silently drop a finding without recording the overlap.** If the orchestrator is unsure whether two findings are the same issue, keep both with a note in the lower-severity one's `details`.

### C3. Sort

Group by severity (`critical → major → minor → info`). Within each severity group, sort by `(agent, file, line)`.

### C3b. Verify critical & major findings (withdrawal gate)

Sub-agents sometimes assert a problem that isn't real (e.g. "no `unique` test" when the test
exists). Before rendering, the orchestrator independently verifies **every `critical` and
`major` finding** — these are the high-impact claims and the ones that reach "Top three
priorities":

1. Verify each critical/major finding **cheaply — do NOT re-read whole files** (re-reading
   entire models is the single biggest cost driver of this phase). In order:
   a. **Check the finding's `evidence` first.** Sub-agents now attach an `evidence` quote of the
      exact block they inspected. If that quote already proves or disproves the claim, decide
      from it alone — no file read.
   b. **Only if `evidence` is missing or insufficient**, re-read a **tight window around the
      cited `file:line`** — e.g. `grep -n -B2 -A8` for the column/CTE, or a small line-range
      read of just that block. Never load the whole file, and never re-read files for findings
      whose `evidence` already settles them.
      - For **absence** claims (e.g. "no `unique` test"), make sure the window spans the column's
        **entire** `data_tests:`/`tests:` block — widen to `-A15` or more if the block is visibly
        cut off. A window that clips a long test list is the one way this step can re-introduce a
        false "missing test" positive, so err wider here (it's still far cheaper than a full read).
2. A finding that is **contradicted** (the "missing" test/description/key is actually present) or
   that **cannot be confirmed** (no usable `evidence` and the targeted window doesn't support it,
   or file/line absent) is **withdrawn**: remove it from the rendered finding set and record it
   in a "Withdrawn during verification" list with its `rule`, `file:line`, and a one-line reason.
3. Do this verification work yourself in the orchestrator context — do not spawn more sub-agents,
   and do not re-run the dimension audits.
4. `minor` and `info` findings are not gated here; they rely on the per-agent evidence rules in
   `finding-format.md`.

This formalizes (and makes consistent) the ad-hoc self-corrections the orchestrator would
otherwise scatter through the body. Never leave a withdrawn finding numbered in the findings
sections.

### C4. Render the report

Render using the template at `${CLAUDE_SKILL_DIR}/references/report-template.md`. Fill in:
- Executive summary table (counts per dimension × severity). **Compute these counts from the
  FINAL rendered finding set only** — i.e. after C2 de-duplication and after C3b withdrawals.
  Never carry forward raw sub-agent tallies, and never count a withdrawn or issue-family-collapsed
  finding.
- **Reconciliation (mandatory):** the per-severity totals in the table MUST equal the number of
  findings actually rendered under "Findings by severity". Re-derive the counts by tallying the
  rendered findings; if the table and the rendered list disagree, the rendered list wins —
  recount before finalizing. Do not emit a note claiming "counts already reflect removal" unless
  the table literally matches the rendered findings.
- "Top three priorities" — the three findings with the highest combined `(severity, confidence)` weight; for ties, prefer findings that span multiple files
- Findings sections grouped by severity
- "Withdrawn during verification" — the C3b list (omit the section if nothing was withdrawn)
- "What was checked" with explicit dimensions, references, and degradation notes (e.g., "dbt parse failed — logic checks ran in filename-only mode")
- "Skipped checks" — concatenated from all sub-agents
- "Suggested next actions" — a 3–5 item ordered list synthesised from the highest-severity findings, grouped by similar fixes

### C5. Present and offer persistence

Print the report to the conversation. Then ask the user:

> "Would you like me to save this report to `<repo>/audit/repo-validation-<YYYY-MM-DD>.md`? (yes / no / different path)"

If `yes`, create the `audit/` folder if missing and write the report. Confirm the path written. Do not auto-commit.

---

## Boundaries

- **Read-only on the audited repo.** Never run `Edit`, `Write` against the audited path (only against `<repo>/audit/<file>.md` if the user opts in at C5).
- **No data-returning commands.** Never `dbt show`, `dbt seed --show`, raw SQL, or anything that prints rows. `dbt parse` is the only allowed dbt invocation.
- **Never read `.env`, `profiles.yml` content, credentials, or `seeds/*.csv` contents.** You may list them; you may not read them.
- **Never auto-fix.** Always report and suggest. The user (or a separate skill) decides what to apply.

---

## See also

- `examples/` — worked examples of the consolidated Phase C report from past audits. Useful as exemplars for the orchestrator and for reviewers gauging skill output quality.
- `references/` — rule lists, finding-format JSON schema, report template, and issue-families dedup table consumed by sub-agents and the orchestrator.
- `automation/` — how to run this skill **unattended** (headless `claude --print`, containerized, scheduled via cron / CI / Airflow) and optionally turn its findings into a report PR plus an optional conservative-fixes PR. Orchestrator-agnostic — no scheduler lock-in.
