diff --git a/.claude/skills/verify/SKILL.md b/.claude/skills/verify/SKILL.md new file mode 100644 index 0000000..7d399f2 --- /dev/null +++ b/.claude/skills/verify/SKILL.md @@ -0,0 +1,46 @@ +--- +name: verify +description: Launch and drive parsedmarc's CLI to verify parser/output changes end-to-end against the bundled sample reports. +--- + +# Verifying parsedmarc changes + +The runtime surface is the `parsedmarc` CLI; the library's stream APIs are +drivable via `python -c` through the public `parsedmarc` package. + +## Launch + +```bash +# No config file → results print as JSON to stdout. --offline skips DNS/downloads. +GITHUB_ACTIONS=true .venv/bin/python -m parsedmarc.cli --offline +``` + +- Do **not** use `-c ci.ini` locally: it points at `http://localhost:9200` + Elasticsearch (a CI service container) and retries for ~75s before failing. +- `GITHUB_ACTIONS=true` skips live DNS lookups. +- `--debug` surfaces per-file parse warnings/errors (invalid reports are + otherwise dropped silently from the JSON). + +## Good sample inputs (all under `samples/`) + +- `aggregate/rfc9990-sample.xml` — RFC 9990 aggregate report +- `aggregate/*.xml.zip`, `aggregate/*.xml.gz` — archive extraction paths +- `failure/dmarc_ruf_report_linkedin.eml` — failure (RUF) report with an + embedded rfc822 sample (exercises `utils.parse_email`) +- `aggregate/invalid_xml.xml` — recovered via lxml, parses with `errors` set + +## Stream API (stdin is genuinely non-seekable when piped) + +```bash +cat samples/aggregate/*.xml.gz | .venv/bin/python -c \ + "import sys, parsedmarc; print(parsedmarc.extract_report(sys.stdin.buffer)[:80])" +# Text-mode stdin must raise ParserError ("binary (rb) mode"): +cat samples/extract_report/nice-input.xml | .venv/bin/python -c \ + "import sys, parsedmarc; parsedmarc.extract_report(sys.stdin)" +``` + +## Gotchas + +- Parse failures are WARNING-level log lines, not stderr errors — grep the + `--debug` output; the JSON just omits the report. +- To compare against unfixed code: `git stash push -- `, run, `git stash pop`. diff --git a/CLAUDE.md b/CLAUDE.md index 078c29c..749642c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,4 +2,14 @@ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +## Model roles for feature work + +Any new feature or modification to an existing feature must follow this model split: + +1. **Plan with Fable** (fall back to Opus only if Fable is unavailable). Enter plan mode, design the implementation, and present the plan to the user for approval or modification. Do not start implementing until the user approves the plan. +2. **Implement with Sonnet.** Once the plan is approved, carry out the implementation using Sonnet (e.g. by delegating the implementation steps to Sonnet subagents via the Agent tool with `model: "sonnet"`). +3. **Review with Fable** (fall back to Opus only if Fable is unavailable). After implementation, all work must be reviewed by Fable before it is considered done. + +**PR reviews** must also use Fable, with Opus as the fallback if Fable is unavailable. + @AGENTS.md