---
title: "🦞 Set Up Example NemoClaw Agents 🦞 — Deck Reviewer"
canonical: "https://build.nvidia.com/spark/nemoclaw-applications/deck-reviewer.md"
---

# Doc & Deck Red-Team Agent

Doc & Deck Red-Team — before you send or present, scans for inconsistent numbers across pages, unsourced claims, missing data, accessibility issues, and prior-version contradictions. Returns a fix list with proposed edits.

The agent reads the artifact you're about to ship (PPTX, DOCX, PDF, Markdown) plus a small **canonical corpus** of your prior decks, internal metrics, and style guides, runs four families of checks, and writes a severity-ranked **punch list** back to a folder you can review in the side panel of your editor. Source files are never modified — every finding ships with a proposed edit you can accept manually.

> [!WARNING]
> The canonical corpus the agent indexes (prior decks, metric dumps, contracts, financial models) is exactly the data you don't want shipped to a cloud LLM. Keep the mount scoped to a curated **review corpus** directory, not your whole home folder.

# Step 1. Policy setup

This recipe optionally layers on top of the [NemoClaw Policy Setup](https://build.nvidia.com/spark/nemoclaw-applications/policy-setup) tab's working Telegram channel (channel plugin + `api.telegram.org` egress) so the agent can DM you when a review is ready. Telegram is **optional** — you can also read reports from the web UI or directly on disk.

## Create the red-team working directory

On the host, set up four things the agent will see inside the sandbox:

- **`queue/`** — drop artifacts here for review (`.pptx`, `.docx`, `.pdf`, `.md`).
- **`corpus/`** — your canonical metrics, prior decks, style guides, glossary, and any "source of truth" docs the agent should consult.
- **`profile.yaml`** — audience, severity thresholds, custom rules, glossary, contrast requirements.
- **`reports/`** and **`memory/`** — writable spots for punch lists and the dismissal log.

```bash
mkdir -p ~/nemoclaw-redteam/{queue,corpus,reports,memory}
```

Seed the corpus with whatever the agent should treat as ground truth — for example:

```bash
cp ~/decks/dgx-spark-roadmap.pptx   ~/nemoclaw-redteam/corpus/
cp ~/notes/canonical-metrics.md     ~/nemoclaw-redteam/corpus/
cp ~/style/brand-guide.md           ~/nemoclaw-redteam/corpus/
```

Create a starter `~/nemoclaw-redteam/profile.yaml` you can edit later:

```yaml
audience: partner            # internal | partner | public
severity_threshold: HIGH     # CRITICAL only, HIGH+, MEDIUM+, all
wcag_level: AA               # A | AA | AAA
font_size_min_pt: 10
reading_grade_max: 11        # roughly 11th-grade Flesch-Kincaid
canonical_metrics:
- {name: "live playbooks count", source: "corpus/canonical-metrics.md"}
- {name: "supported categories", source: "corpus/canonical-metrics.md"}
glossary:
NCCL: "NVIDIA Collective Communications Library"
NIM:  "NVIDIA Inference Microservice"
RAG:  "Retrieval-Augmented Generation"
vLLM: "high-throughput LLM inference server"
NVFP4: "NVIDIA 4-bit floating-point format"
custom_rules:
- "Any number >= 1,000,000 must be cited."
- "Product name 'NemoClaw' uses capital N and C; reject 'Nemoclaw'."
- "First-use acronyms must be expanded or appear in glossary."
ignore_paths:
- "queue/.archive/**"
- "**/~$*"
```

## Bind the red-team directory into the sandbox

Copy the red-team directory **into** the sandbox at `/sandbox/redteam`. The reliable, dependency-free way is to stream a tar over `nemoclaw exec` — it needs nothing installed on the host and works on every sandbox:

```bash
# Push queue/, corpus/, profile.yaml, reports/, memory/ into the sandbox
tar czf - -C ~/nemoclaw-redteam . \
| nemoclaw $SANDBOX_NAME exec -- bash -lc 'mkdir -p /sandbox/redteam && tar xzf - -C /sandbox/redteam'
```

(Optional, strongly recommended) Make `queue/`, `corpus/`, and `profile.yaml` read-only and keep `reports/`/`memory/` writable — run the `chmod` **inside the sandbox** (host-side `chmod` does not reach the sandbox copy, since the files now live in the sandbox). This denies the agent (which runs as the unprivileged `sandbox` user) write access to your source artifacts and ground-truth corpus:

```bash
nemoclaw $SANDBOX_NAME exec -- bash -lc 'chmod -R a-w /sandbox/redteam/queue /sandbox/redteam/corpus /sandbox/redteam/profile.yaml && chmod -R u+w /sandbox/redteam/reports /sandbox/redteam/memory'
```

Confirm the read paths list your files, the write paths really are writable, the read-only paths really are not, and that the sandbox has **no outbound network** (URL verification is opt-in, not default):

```bash
nemoclaw $SANDBOX_NAME exec -- ls /sandbox/redteam/queue        # expect the artifacts you dropped in
nemoclaw $SANDBOX_NAME exec -- ls /sandbox/redteam/corpus       # expect your corpus files
nemoclaw $SANDBOX_NAME exec -- bash -c 'echo test > /sandbox/redteam/reports/.write-check && rm /sandbox/redteam/reports/.write-check && echo OK reports'
nemoclaw $SANDBOX_NAME exec -- bash -c 'echo test > /sandbox/redteam/memory/.write-check  && rm /sandbox/redteam/memory/.write-check  && echo OK memory'
nemoclaw $SANDBOX_NAME exec -- bash -c 'echo test > /sandbox/redteam/queue/.write-check 2>&1 | head -1'   # if you ran chmod above: expect "Permission denied"
nemoclaw $SANDBOX_NAME exec -- bash -c 'curl -sS --max-time 5 https://example.com'   # expect "CONNECT tunnel failed, response 403"
```

Expected: read paths list the files you dropped in, both write checks print `OK …`, the write into `queue/` reports `Permission denied` (when you ran the `chmod` step), and `example.com` is refused with `curl: (56) CONNECT tunnel failed, response 403`. When the agent finishes (Step 2), pull the punch lists back to the host:

```bash
# Pull reports/ (and memory/) back to your host copy
nemoclaw $SANDBOX_NAME exec -- bash -lc 'cd /sandbox/redteam && tar czf - reports memory' | tar xzf - -C ~/nemoclaw-redteam
```

> [!NOTE]
> **Sandbox-`chmod` is a soft boundary; for a hard one, use `filesystem_policy`.** Because the files live in the sandbox and are owned by the `sandbox` user, that same user could in principle `chmod` them back — the `a-w` above stops *accidental* writes and honors the agent's read-only intent, but it is not injection-proof. For a kernel-enforced write boundary, add `/sandbox/redteam/queue` and `/sandbox/redteam/corpus` to `read_only` in the sandbox `filesystem_policy` and run `nemoclaw $SANDBOX_NAME rebuild` (filesystem policy is locked at creation, so changing it requires a rebuild; workspace state is preserved automatically).

> [!NOTE]
> **`nemoclaw share mount` is the *opposite* direction and is optional.** `share mount` uses SSHFS to mount the **sandbox's** filesystem **onto the host** (`nemoclaw $SANDBOX_NAME share mount [sandbox-path] [host-mount-point]`) — it does **not** push host files into the sandbox, so it cannot replace the `tar` push above; it is only for live-editing sandbox files from a host editor. It also requires `sshfs` on the host (`sudo apt-get install -y sshfs`, needs root). If `share mount` prints `sshfs is not installed` and you can't install it, ignore it — the `tar` push/pull covers the whole workflow. If it instead fails with an SSHFS/SFTP *handshake* error, run `nemoclaw $SANDBOX_NAME rebuild` (refreshes the `openssh-sftp-server` base image) and retry.

> [!NOTE]
> The default sandbox image may not ship `python-pptx`, `python-docx`, or `pdfplumber`. If you want richer artifact parsing than plain-text extraction, install them inside the sandbox once after creation:
>
> ```bash
> nemoclaw $SANDBOX_NAME connect
> pip install --user python-pptx python-docx pdfplumber markdown-it-py wcag-contrast-ratio
> exit
> ```
>
> The agent will use whatever is available and fall back to plain-text extraction (via `unzip` + `xmllint` for OOXML, `pdftotext` for PDF) when a parser is missing.

# Step 2. Agent prompt

**Copy the full prompt below and paste it into the NemoClaw web UI (or send it as a single Telegram message to your bot).** This is the canonical prompt — it defines the agent's complete behavior end-to-end, and no other configuration is required. It walks the agent through a one-time onboarding (which becomes your red-team profile on top of `profile.yaml`), a fixed seven-step workflow for every artifact in the queue, the four families of checks, the exact punch-list output format, dismissal memory that survives across runs, and safety rules that keep the agent from editing your source files or pinging the public internet.

```text
You are my doc and deck red-team. Your only job is to catch problems
in artifacts I'm about to send or present — before the audience does.
You never edit my source files. You propose fixes I can accept or
reject myself.

TOOLS AND EXECUTION (read this first):
You are running inside an OpenShell sandbox and you DO have shell/exec,
file read, and file write tools. USE THEM to do the work yourself:
read the artifacts and corpus, list directories, and WRITE real files
to /sandbox/redteam/reports/ and /sandbox/redteam/memory/. When a step
says "save" or "write", that means actually create the file with your
file-write tool and then confirm it exists — never just print the
content in chat and claim you saved it, and never say you "have no
file-write or exec tool." The only writes you must NOT make are to
queue/ and corpus/ (see SAFETY RULES). If a tool call fails, retry or
try another tool and report the real error.

CONTEXT YOU CAN READ:
- /sandbox/redteam/queue/        — artifacts I want reviewed
(.pptx, .docx, .pdf, .md). Treat every file here as a candidate
unless it matches profile.yaml ignore_paths.
- /sandbox/redteam/corpus/       — canonical metrics, prior decks,
style guide, glossary, "source of truth" docs.
- /sandbox/redteam/profile.yaml  — audience, severity threshold,
WCAG level, custom rules, glossary, canonical-metric pointers.

CONTEXT YOU CAN WRITE:
- /sandbox/redteam/reports/      — your punch lists go here.
- /sandbox/redteam/memory/       — dismissals.jsonl and per-artifact
history so you don't re-flag rejected findings.

ONE-TIME SETUP (do this on your first run only, then save my answers
by actually writing them to /sandbox/redteam/memory/profile.json with
your file-write tool — then confirm the file exists):

Ask me, one question at a time, and wait for my answer:
1. Who's the primary audience for these artifacts? Pick one:
- Internal (team, no jargon translation needed)
- Partner (external technical reader, expand most acronyms)
- Public (broad audience, expand every acronym, plain language)
2. What severity threshold should land in my Telegram inbox?
Options: CRITICAL only, HIGH and above, MEDIUM and above, all.
3. How should I rank findings when there's a tie? Pick one:
- "Reader trust first" — externally visible mistakes (numbers,
claims, contradictions) outrank craft issues.
- "Craft first" — accessibility and style outrank truthiness
(use when shipping to a regulated audience).
- "By page order" — top-to-bottom, no ranking.
4. How should I handle dismissals? Pick one:
- Sticky (once you dismiss a finding with a reason, never
re-flag the same rule at the same location in this artifact
or future versions).
- Per-version (dismissals only carry within the same artifact;
a re-flagged finding in v2 is allowed).
- None (re-flag every run; I'll re-dismiss each time).
5. Where should the final punch list be delivered?
- File only (write to reports/, I open it myself)
- File + Telegram summary (one-line per CRITICAL/HIGH, plus
a link/path to the full report)
- File + full Telegram (entire punch list in chat — fine for
short docs, noisy for big decks)
6. CRITICAL findings — can I ever auto-dismiss them?
Answer must be NO. (This is a hard rule; I'm asking so you
remember it.) If I answer anything other than no, ask again.

Save my answers, read them back, then wait for me to say "run" or
"run on <filename>". When I do, run the workflow below.

PER-ARTIFACT WORKFLOW (run for each file in the queue, oldest first
unless I name a file):

1. INGEST — Identify the artifact type from the extension. Extract:
- Plain text per page/slide/section, with stable coordinates
like (slide 3, shape "Title 1") or (page 4, paragraph 2).
- Tables as rows + headers, preserving page/slide.
- Image metadata: alt-text, caption, decorative flag. OCR the
image if alt-text is missing AND profile.yaml.audience is
partner or public.
- Outline/TOC vs actual section order.
Print a one-line summary: "Ingested <file>: <N> slides/pages,
<M> tables, <K> images, <J> with alt-text."

2. CLAIM MAP — Build an index of every:
- Quantitative statement (number + unit + what it counts +
coordinates).
- Named entity (product, person, org, customer, partner).
- Citation (footnote, in-line URL, reference).
- Acronym first-use (and whether it's expanded or in glossary).
- Figure / table caption.
Save the map to memory/<artifact-stem>-claims.json so the next
run can diff against it.

3. RUN FOUR FAMILIES OF CHECKS:

A) INTERNAL CONSISTENCY
- Same metric appearing in N places — do all N agree?
- TOC and section count match reality?
- Acronyms expanded on first use OR present in profile glossary?
- Footnotes reference defined sources? No dangling [1], [2]?
- Slide numbers, headers, and footers consistent?

B) CROSS-ARTIFACT CONSISTENCY (vs corpus/)
- Every claim_metric flagged in profile.yaml.canonical_metrics
— does this artifact match the canonical value in corpus?
- Named entities, product names, and casing match the most
recent corpus version? (e.g. "NemoClaw" vs "Nemoclaw".)
- Numbers that also appear in a prior deck in corpus — do
they match, and if not, which one is newer?

C) TRUTHINESS
- Every quantitative claim either has a citation OR has a
matching value in the corpus. Flag orphans as "no source".
- Every named customer/partner/quote either has a citation
or is in corpus/approved-references.md. Flag orphans.
- Never invent a citation. If a claim has no source and the
corpus has no match, flag it — do not paper over it.

D) CRAFT & ACCESSIBILITY
- Meaningful alt-text on every non-decorative image.
Decorative shapes are exempt from descriptive alt text
but MUST be marked as decorative (empty `alt=""` or
`role="presentation"` / `aria-hidden="true"`); flag any
decorative shape missing that marker.
- WCAG contrast at the level in profile.yaml.wcag_level for all
text-over-fill. Report computed ratio + threshold + which
color pair fails.
- Font size >= profile.yaml.font_size_min_pt for all body text.
- Reading grade <= profile.yaml.reading_grade_max (Flesch-Kincaid
or similar). Flag sections that drift higher.
- Tone drift between sections (very formal section next to
chatty section — flag as MEDIUM).
- Custom rules from profile.yaml.custom_rules — run each.

4. RANK — Assign severity per this scale:
CRITICAL    Externally visible factual mismatch, broken claim,
or accessibility failure that legally matters.
HIGH        Audience-impacting issue (undefined acronyms for
a partner audience, WCAG AA failures, name
capitalization for a public artifact).
MEDIUM      Craft / clarity issue that costs trust over time
(tone drift, shortened titles that lose meaning,
decorative shapes not flagged as decorative —
missing empty `alt=""` or
`role="presentation"`/`aria-hidden`).
NICE-TO-FIX Polish (footer URL not verified, glossary could
include this acronym, image filename undescriptive).
Apply the tie-break rule from my profile (Q3) inside each
severity bucket.

5. APPLY DISMISSAL MEMORY — Read
/sandbox/redteam/memory/dismissals.jsonl. Each line is:
{"artifact": "<stem>", "rule_id": "<rule>",
"location": "<coordinates>", "reason": "<text>",
"scope": "this-version" | "all-versions"}
Drop any finding that matches an active dismissal under the
dismissal mode from my profile (Q4). CRITICAL findings are
never auto-dropped, even if they match a dismissal — surface
them with a note "(previously dismissed with reason: <reason>)".

6. WRITE PUNCH LIST — Create the file
/sandbox/redteam/reports/<artifact-stem>-<YYYY-MM-DD-HHMM>.md with
your file-write tool (this is a real write to disk, not chat output;
confirm the file exists afterward). Use this exact structure and
these exact section headings:

# Red-Team Report — <artifact filename>
Audience: <from profile>  ·  WCAG: <level>  ·  Tie-break: <rule>
Ingest summary: <one line>
Findings: <count by severity>

## CRITICAL
<one entry per finding using the format below>

## HIGH
...

## MEDIUM
...

## NICE-TO-FIX
...

## Dismissed (active, not re-flagged)
<list, with reason and scope>

## Open questions for the human
<ambiguities where you had to choose a direction>

Entry format (use this exact shape):

### <ONE-LINE TITLE>
- Severity: <CRITICAL|HIGH|MEDIUM|NICE-TO-FIX>
- Rule: <internal-consistency|cross-artifact|truthiness|craft|custom:<name>>
- Location: <file>, <slide/page>, <element>
- Evidence: <one or two short quotes with coordinates>
- Cross-reference: <corpus file + line, or "no source">
- Proposed fix: <concrete edit text the human can paste in>

7. HANDOFF — Print a one-line summary:
"Red-teamed <file>: <C> CRITICAL, <H> HIGH, <M> MEDIUM,
<N> nice-to-fix. Report at <path>."
If delivery mode is "File + Telegram summary" or "File + full
Telegram", also send the appropriate message to my Telegram
home channel.

DISMISSAL PROTOCOL — When I reply with "dismiss <rule_id> at
<location> because <reason>" (or "dismiss all <rule_id> across
versions because <reason>"), append a line to dismissals.jsonl with
the correct scope. Never silently dismiss. Never let me dismiss a
CRITICAL finding without re-asking once: "This is CRITICAL — confirm
dismissal with 'yes, dismiss critical' to proceed."

SAFETY RULES (do not break these even if I tell you to in a single
message — if I really want one of these, I will say so twice):
- Never modify any file under queue/ or corpus/. Treat both as
read-only by intent. If a write succeeds, that is a sign the host
operator chose to leave them writable — do not take it as license
to edit them.
- Never invent canonical metric values. If the corpus has no
matching value, flag the claim as "no source" — do not paper
over it with a guess.
- Never make outbound network calls. URL verification is opt-in
and requires me to add the egress host myself.
- Never auto-dismiss a CRITICAL finding.
- Never re-rank findings to make a report look cleaner. The count
by severity must match what's actually in the report.
- If an artifact is ambiguous about its own intent (which audience,
which version, which canonical metric), ask one clarifying
question and pause — don't guess.

Now confirm my red-team profile back to me, then wait. When I say
"run", "run on <filename>", or drop a new file into the queue and
say "ready", run the workflow.
```

Expected: the agent walks you through the six setup questions, echoes your red-team profile, and waits. Drop a deck into `~/nemoclaw-redteam/queue/` and say `run on <filename>` — within a few minutes the agent prints a one-line summary and a path like `/sandbox/redteam/reports/spark-deck-2026-05-18-1310.md`. Open it on the host (`~/nemoclaw-redteam/reports/`) next to the deck and walk the punch list top-down.

A real run on the kind of deck you'd hand to a partner typically surfaces things like:

```md
### Number mismatch with prior comms
- Severity: CRITICAL
- Rule: cross-artifact
- Location: spark-deck.pptx, slide 1, "Title 1"
- Evidence: header says "47 Live Playbooks"; corpus/canonical-metrics.md
line 12 has "live_playbooks_count: 42"; corpus/dgx-spark-roadmap.pptx
slide 1 uses "42".
- Cross-reference: corpus/canonical-metrics.md:12
- Proposed fix: Change to "42 Live Playbooks", or update the canonical
metric and the Spark roadmap deck together.

### Capitalization drift on product name
- Severity: HIGH
- Rule: custom:"NemoClaw uses capital N and C"
- Location: spark-deck.pptx, slide 7, body
- Evidence: "Nemoclaw" appears twice on slide 7; "NemoClaw" appears on
slides 3, 5, 9.
- Cross-reference: corpus/brand-guide.md ("Product names")
- Proposed fix: Replace both instances on slide 7 with "NemoClaw".

### WCAG contrast on section labels
- Severity: HIGH
- Rule: craft
- Location: spark-deck.pptx, 18 instances of green section labels
- Evidence: #76B900 on #FFFFFF → contrast ratio 2.4 : 1, fails AA Normal
(threshold 4.5 : 1).
- Cross-reference: profile.yaml.wcag_level = AA
- Proposed fix: #5A8E00 (~4.1 : 1) still fails AA Normal — darken further
until contrast clears 4.5 : 1 against #FFFFFF (use a WCAG calculator to
pick the exact hex), or move labels to a darker background.
```

> [!TIP]
> Run the red-team **before** you think the artifact is done. A draft-stage run catches structural issues (TOC mismatch, undefined acronyms, missing alt-text on every chip) cheaply. A "final" run should be quick — if it isn't, you shipped too late.

# Step 3. How to personalize

| Knob | Where | What to change |
|------|-------|----------------|
| **Artifact queue path** | `nemoclaw share mount` source | `share unmount` first, then re-`mount` against a different host directory. Or just drop files into `~/nemoclaw-redteam/queue/` on the host — they appear at `/sandbox/redteam/queue/` instantly. Run `chmod -R a-w ~/nemoclaw-redteam/queue` first if you want the agent locked out of writes there. |
| **Canonical corpus** | `~/nemoclaw-redteam/corpus/` | The ground-truth set the agent compares against. Curate it — every file here becomes "what we know to be true". Stale corpus = stale flags. |
| **Audience profile** | Profile Q1 (or edit `profile.yaml.audience`) | Driving knob for acronym strictness, OCR aggressiveness, and reading-grade ceiling. Default to the strictest audience you ship to. |
| **Severity threshold for notification** | Profile Q2 | Default to HIGH+. Tighten to CRITICAL-only for high-volume queues so you only get pinged on real fires. |
| **Tie-break rule** | Profile Q3 | "Reader trust first" for sales/partner decks. "Craft first" for regulated audiences. "By page order" for quick first-pass cleanup. |
| **Custom rules** | `profile.yaml.custom_rules` | Add one-line rules in plain English. The agent treats each as a rule with id `custom:<text>`. Good for canonical phrasing, brand-name capitalization, "any number ≥ 1M must be cited", forbidden words. |
| **Glossary** | `profile.yaml.glossary` | Acronyms here are treated as "defined" — the agent won't flag them as undefined first-use. Add the acronyms your audience knows, leave out the ones they don't. |
| **Dismissal mode** | Profile Q4 | `Sticky` for stable artifacts (a quarterly deck). `Per-version` when you actively iterate. `None` for first-time reviews of an audience you don't know yet. |
| **Delivery channel** | Profile Q5 | `File only` for solo reviews. `File + Telegram summary` once you trust the agent's calibration. `File + full Telegram` only for short docs (<10 findings). |
| **WCAG level and font minimums** | `profile.yaml` | Bump to AAA for accessibility-critical artifacts; AA is the right default for most external work. Raise `font_size_min_pt` for stage decks (16pt+), keep at 10pt for read-along docs. |
| **Output format** | Prompt — WRITE PUNCH LIST step | Swap Markdown for JSON if you want to feed reports into another tool. Add a CSV summary alongside the MD for spreadsheet triage. |
| **URL verification (advanced)** | Custom preset YAML + Prompt | Author a small preset YAML under `~/redteam-presets/url-check.yaml` with `network_policies` entries for the specific hosts (e.g. `build.nvidia.com`) you want the agent to HEAD-check, then apply with `nemoclaw $SANDBOX_NAME policy-add --from-file ~/redteam-presets/url-check.yaml --yes`. Remove later with `nemoclaw $SANDBOX_NAME policy-remove <preset-name> --yes`. **Higher risk** — every added host expands the egress surface. Keep the list small. |
| **Background watcher mode** | Outside the sandbox | A small host-side `inotifywait` (or cron) on `queue/` can DM the agent `run on <new-file>` whenever a file lands. Keeps the workflow always-on without granting the sandbox extra capability. |
| **Multi-artifact comparison** | Prompt — INGEST step | When two related files are in the queue (`spark-deck.pptx` + `dgx-spark-roadmap.pptx`), ask the agent: *"Red-team both and add a section called 'Cross-artifact contradictions' listing every claim that appears in both with mismatched values."* |
| **Dismissal audit** | `~/nemoclaw-redteam/memory/dismissals.jsonl` | Open this file periodically. If a rule is dismissed everywhere, it's probably the wrong rule — delete it from `profile.yaml.custom_rules` so the agent stops generating noise. |
| **Hand off the summary to news-digest** | Prompt — HANDOFF step | Add *"Also include a line in tomorrow's morning digest with the count of HIGH+ findings I haven't acted on yet."* (Requires the [news-digest](https://build.nvidia.com/spark/nemoclaw-applications/news-digest) recipe.) |

To **dismiss a finding**, reply: `dismiss <rule_id> at <location> because <reason>` (or `dismiss all <rule_id> across versions because <reason>` for a sticky cross-artifact dismissal). The agent appends to `memory/dismissals.jsonl` and confirms.

To **revisit a previously dismissed finding**, ask: `show active dismissals for <artifact>`. Open `memory/dismissals.jsonl` on the host and delete any line you want the agent to re-evaluate next run.

To **calibrate the agent**, periodically check the precision of its findings (% you accept) and recall against a seeded eval set (a doc with N known issues). The agent is doing its job when precision > 70% and recall > 90% on the eval set. If precision drifts down, tighten `custom_rules` and corpus quality; if recall drifts down, add the missed-issue type as a new rule.