google/mantis-report
>- Generates a human-readable security review packet compiled from confirmed findings and exploit chains. Use at the end of a review cycle to produce stakeholder-facing documentation. Don't use for auditing code or verifying patches directly.
npx skills add https://github.com/google/mantis --skill mantis-report
Security Reporting Expert. Synthesizes complex, technical finding logs into a
high-quality, human-readable review packet for developers and stakeholders.
/mantis-reportconfirmed findings and exploit chains.
workspace/findings/*.json (all active finding files for this pass).workspace/archive/findings_pass_*/*.json (archived findings from priorpasses) and legacy workspace/archive/loop*_findings/*.json. The report is
a campaign-wide view: this pass's full findings plus carried-forward
findings that prior passes stopped retrying (e.g. hit the retry cap) so they
do not vanish from the report.
workspace/.mantis_state.json (to track current loop pass, and to readvcs_info and active_snapshot {root, snapshot_id, snapshot_pinned}
for provenance).
discovery_commit,repro_snapshot_id, patch_base_snapshot. When any is absent/empty it is
rendered as "(not recorded)" — never a reason to drop a finding.
workspace/report/review_packet_pass_<N>_<snapshot_tag>.md (pass- andsnapshot-tagged markdown report). Falls back to the unsuffixed
review_packet_pass_<N>.md on a legacy pass with no recorded snapshot.
workspace/report/review_packet-latest.md.workspace/findings/ orworkspace/archive/. The report is a campaign-wide view: it reports the
current state of every unresolved finding discovered in this or any prior
pass, de-duplicated to each finding's latest state. A confirmed-but-unfixed
finding that plan stopped carrying back (e.g. it hit the 2-attempt retry
cap) does NOT vanish — it appears here at its most recent archived state.
review_packet-latest.md. Re-running the SAME pass on the SAME snapshot
updates the same tagged file. Re-running the same pass number on a DIFFERENT
snapshot writes a DISTINCT file (the <snapshot_tag> suffix prevents
cross-snapshot overwrite). Legacy passes with no recorded snapshot keep the
unsuffixed review_packet_pass_<N>.md name and overwrite in place, exactly
as before.
Step 0 — Locator Resolution.
LOCATOR RESOLUTION (before reading ANY target code or artifact):
0. ROLE: If this skill NEVER reads target source (report, calibrate, reflect),
you are a FINDINGS-ONLY stage: skip steps 2-6; still read active_snapshot from
state for provenance/annotation; NEVER stop merely because a code root is unset.
1. Determine CODE_ROOT, in this priority order:
a. If --target_root is passed on THIS invocation, CODE_ROOT = --target_root.
It is AUTHORITATIVE and OVERRIDES SNAPSHOT_ROOT and the state fallback
(used when a caller hands you a prepared tree, e.g. a patched shadow).
b. Else if --snapshot_root (or SNAPSHOT_ROOT) is passed, use it.
c. Else read state_root/workspace/.mantis_state.json (state_root from
--state_root if passed, else ./workspace/... relative to the current dir)
-> active_snapshot.root / .snapshot_id / .snapshot_pinned.
d. Else (no arg AND no readable active_snapshot): CODE_ROOT = current directory,
treat snapshot_pinned = false (MODE-OFF). Do NOT stop.
2. SENTINEL CHECK (only if snapshot_pinned is true AND you did NOT take path 1a):
verify CODE_ROOT/.mantis_snapshot_id exists and equals SNAPSHOT_ID. If missing
or different -> STOP "snapshot sentinel mismatch". (A --target_root tree (1a) is
deliberately mutated and is sentinel-EXEMPT.)
3. PATH FIELDS:
- SNAPSHOT-RELATIVE (read under CODE_ROOT): code_paths entries; plan target_files
that are file paths. Strip ONLY a trailing ":<digits>". A code_paths entry
containing "://" is a URL/endpoint, NOT a file read. A code_paths entry that is
NOT of the form <existing-path>:<integer> is a non-source LOCATOR
(symbol/offset/endpoint): only check that the artifact/symbol exists; skip ALL
line-range and line-existence logic.
- STATE-RELATIVE (read/write under state_root/workspace, NEVER prefix CODE_ROOT):
kb_references, repro_file_path, reattack_file_path, helper scripts, report
files, and all state/findings JSON.
4. Never WRITE under CODE_ROOT when snapshot_pinned is true. Any command that
compiles, generates, or writes artifacts MUST run in a PRIVATE SHADOW copy
(mktemp -d from CODE_ROOT), never with cwd=CODE_ROOT. Read-only inspection may
cd into CODE_ROOT.
5. VCS-METADATA CARVE-OUT: history-log extraction and any VCS diff/blame command
run in the LIVE repository root (which still has .git/.hg/.repo), NOT CODE_ROOT
(the snapshot copy strips VCS metadata). Do NOT stop merely because CODE_ROOT
lacks .git/.hg/.repo.
6. Every shell command uses ABSOLUTE paths and sets its own working directory on
that call. Do NOT assume the working directory persists between calls.
Then the following FINDINGS-ONLY notes apply to the Reporter:
steps 2–6, does NOT require or resolve a CODE_ROOT, and NEVER stops because a
code root or sentinel is unset.
active_snapshot (root, snapshot_id, snapshot_pinned)and vcs_info from workspace/.mantis_state.json for provenance — used to
build the header, the top banners, and the output file name.
workspace/findings/*.json,workspace/.mantis_state.json, workspace/report/*, workspace/archive/*)
is STATE-RELATIVE and is NEVER prefixed with CODE_ROOT.
Compile a professional Markdown report detailing the verified/reproduced
vulnerabilities and exploit chains.
Execute the reporting stage as follows:
(newest-first fold; no script).** Build a working set keyed by finding
identity, each finding once at its most-recent state:
SAME-BUG PREDICATE (used for ALL current↔archived dedup, fold, and
suppression in this stage; over-reporting is always safe here, hiding a real
finding is never acceptable): two findings are the SAME BUG only if EITHER
(i) they have the exact same id (UUID); OR (ii) ALL THREE hold — they share
a non-empty lineage_id, they share a non-empty signature, AND at least
one code_paths entry compared WITH its trailing :line (line-inclusive) is
identical between them. Otherwise they are DISTINCT — render BOTH. NEVER
treat two findings as the same bug on lineage_id alone or signature
alone: both are coarser than a bug's true identity (basename-derived lineage
can link two distinct same-named files; a signature strips the line number
so it collides between distinct same-file bugs), and collapsing on either
alone can silently drop a real finding.
Design note (re-anchoring vs fold): mantis-plan's re-anchoring (Phase
2\) only provides a line hint that steers RE-DISCOVER — it does NOT rewrite
the carried finding's code_paths. A re-discovered finding surfaces at its
NEW line on the current snapshot. Because the fold predicate requires a
line-inclusive code_paths match, the re-discovered finding will NOT fold
with its ancestor (the line numbers differ). This is SAFE over-reporting —
both entries render, the current one shows the new location. A future phase
could relax the predicate to path-only matching when BOTH findings carry the
same signature AND lineage_id AND the ancestor's line is confirmed absent
on the current snapshot, but the conservative line-inclusive match is used
today to prevent any silent-drop risk.
workspace/findings/*.json and add every active findingto the working set, keyed by its id (UUID) — all remediation statuses,
so this pass's VERIFIED_SECURE/MITIGATION_PROPOSED fixes still render
with their patches in Categories 1/2. NEVER collapse two ACTIVE findings,
even if they share a lineage_id or signature — keep each active id
as its own entry.
workspace/archive/findings_pass_<N>/ (and legacyloop<N>_findings/) in descending pass order. Add an archived finding
only if it is still OPEN (see 4) AND NO finding already in the working set
is the SAME BUG as it (per the predicate above) AND no earlier
(higher-pass) archived copy of the SAME BUG was already added. First copy
met = latest state; ignore later same-bug copies in lower dirs. (An
archived finding is suppressed ONLY when a genuine same-bug supersedes it;
a mere lineage_id or signature coincidence does NOT suppress it.)
(predicate ii) as a current finding, the current finding supersedes it —
show a single entry at the current (most-recent) state; do not also render
the archived copy. When the predicate is NOT satisfied (different
signature, e.g. a file rename, or lineage_id absent), render both as
separate entries (safe over-reporting, never hiding).
FAILS the actionable predicate (step 4 below) but the archived ancestor
would have PASSED it (e.g., the ancestor was reproduced and OPEN, but
the current pass's reproduce resulted in not_attempted due to a
transient build/environment failure on the new snapshot), do NOT let the
transient downgrade suppress the confirmed-open bug. Instead, keep the
ancestor's last confirmed state visible in the report (render it with a
note "Open — repro pending on new snapshot") so a previously-confirmed
bug can never silently disappear from the report. The current finding's
newer metadata (e.g., updated code_paths line numbers) may still be
attached as an annotation, but the verdict/status shown must be the
ancestor's last confirmed state, never the transient downgrade.
include a finding only if it is an exploit chain (constituent_findings
present, or "Exploit Chain" in title/history) OR repro_status is
reproduced OR (repro_status is statically_confirmed AND it carries
empirical execution evidence — an external stack trace, sanitizer trace
(ASan/UBSan/MSan/TSan), or crash log). Do not include false positives,
NON_VIABLE, DUPLICATE, failed_to_reproduce, or ordinary
statically_confirmed findings lacking empirical traces.
finding is "still open" if it satisfies (4) AND patch_status is NOT
VERIFIED_SECURE/MITIGATION_PROPOSED AND status is not
FALSE_POSITIVE/DUPLICATE AND production_viability is not
NON_VIABLE. (Active findings are loaded regardless of patch_status so
this pass's fixes still appear; archived fixed findings are not re-listed
every pass — use the optional "Resolved this campaign" rollup for a
cumulative fixed view.)
whole archive at once.
"LOW" fromthe main report body. You must place these lower-priority issues into a
separate, dedicated "Appendix: Low Priority Findings" section at the very
end of the report, keeping the main report focused on high-risk issues.
and Qualitative Priority.
first discovered in vs. the current pass (e.g.
discovered pass 2 · still open as of pass 7), so carried-over findings
are visible as such. First-seen = the lowest pass_number in the finding's
history entries (or the pass_number of the lowest-numbered archive dir
that contains it); current state = the copy you kept from the fold above.
possible_duplicate_of field(set by mantis-dedupe when a cross-pass candidate was NOT_MATCHED), emit
an advisory note:
Possibly related to finding <UUID> (cross-pass candidate; snapshots differ — not confirmed duplicate).
This makes the advisory regression-pointer visible to the stakeholder.
Discovery Snapshot: <discovery_commit> forthe finding. If discovery_commit is missing or empty, emit
Discovery Snapshot: (legacy — not recorded). Never omit or drop the
finding because this field is absent.
the concrete impact on the system.
repro_file_path) and execution command(run_command).
trigger (repro_output).
was gathered on: Evidence base: <repro_snapshot_id>. If
repro_snapshot_id is missing or empty, write
Evidence base: (not recorded). Do NOT assume it equals the header/pass
snapshot.
reasoning),production viability reasoning (critic_reasoning), and outrage factor
analysis (outrage_commentary).
patch_diff) and re-attack status to prove thefix is resilient.
Apply against: <patch_base_snapshot>. If patch_base_snapshot is
missing or empty, write Apply against: (not recorded). If
patch_base_snapshot is present and differs from this finding's
discovery_commit, add a one-line caution that the diff was generated
against a different snapshot and may not apply cleanly to the discovery
snapshot.
description, PoC script/command, and reproducer logs) to the report, you
must scan and redact any hardcoded API keys, tokens, credentials, PII
(names, emails, phone numbers), internal hostnames/domain names, and overly
weaponized payload parameters, replacing them with standard placeholders
like <REDACTED_SECRET>, <REDACTED_PII>, <REDACTED_INTERNAL_HOST>, or
<REDACTED_PAYLOAD> to ensure the report is safe for broader distribution.
Executive Summary):
**Snapshot provenance banners — emit these BEFORE item 1 below, at the very
top of the report, each as its own separated blockquote, in this order:**
a. Non-authoritative / HALT banner (3-state rule). Read
active_snapshot from workspace/.mantis_state.json. - If
active_snapshot is absent (MODE-OFF — no --sync was requested,
today's default): do NOT emit this banner. The run is byte-for-byte today's
behavior; the report's VERIFIED_SECURE findings and other verdicts are
valid as today. Emitting a NON-AUTHORITATIVE banner here would contradict
the verdicts the same run produces. - If active_snapshot IS present but
snapshot_pinned is false (HALT mode): emit as the FIRST line of the
report:
> WARNING — NON-AUTHORITATIVE RESULTS: The target could not be pinned to an immutable snapshot for this pass (HALT mode: the tree raced or was too big / live / copy-failed). Findings may not correspond to a stable, reproducible tree, and the ABSENCE of findings in this report does NOT indicate the target is secure. Treat all results as provisional.
Omit this banner when active_snapshot is absent (MODE-OFF) OR when
active_snapshot.snapshot_pinned is true (PINNED).
b. Dirty working tree warning. If vcs_info.dirty is true, emit:
> WARNING — DIRTY WORKING TREE: The target had uncommitted local modifications when it was scanned. Results reflect that exact working tree (captured by content hash), NOT a clean committed revision. The recorded commit alone will not reproduce this state.
c. Mixed-snapshot banner. Let HEADER_SID =
active_snapshot.snapshot_id. If HEADER_SID is present and non-empty AND
any INCLUDED finding has a discovery_commit that is missing, empty, or
not string-equal to HEADER_SID, emit:
> WARNING — MIXED SNAPSHOTS: This report combines findings discovered on different code snapshots (e.g. findings retried from earlier passes). The pass snapshot is <HEADER_SID>. Consult each finding's "Discovery Snapshot" before acting; line numbers and code context may differ between snapshots.
Compare snapshot IDs as EXACT strings only — no fuzzy or prefix match.
"vcs_info"in workspace/.mantis_state.json:
"vcs_type" is "git", show:Target Version: Git branch [branch] at commit [commit_hash] [(dirty) if dirty is true].
"vcs_type" is "hg", show:Target Version: Mercurial branch [branch] at revision [commit_hash] [(dirty) if dirty is true].
"vcs_type" is "multi-vcs", show:Target Version: Multi-VCS (repo) manifest [revision] [(dirty) if dirty is true].
"vcs_type" is "none", show:Target Version: None (No version control detected).
"vcs_type" is "unknown", or if vcs_info is missing, show:Target Version: Unknown (VCS detection failed/error).
After the Target Version: line, append the pass snapshot identity on a
second line:
active_snapshot.snapshot_id is present and non-empty, write:Snapshot ID: [snapshot_id] (pinned: [snapshot_pinned]).
active_snapshot is absent or snapshot_id is empty, write:Snapshot ID: (legacy — snapshot not recorded). This is display-only
provenance; it does not gate or drop any finding.
automatically generated by Mantis AI. All findings and patches are
AI-generated and must be manually verified by a security or subject
matter expert before deployment or disclosure.”*
table and the main body of the report by grouping findings. **Exploit
chains MUST be excluded from these main groups and reported ONLY in their
dedicated "Exploit Chains (Not End-to-End Reproduced)" section.** For
standard (non-chain) findings, group them into three distinct categories
based on their remediation status (strictly mutually exclusive):
patch_status is "VERIFIED_SECURE".
patch_status is in
["MITIGATION_PROPOSED", "VERIFICATION_INCOMPLETE"] OR (patch_diff is
present AND patch_status is unset/empty).
patch_status is in ["VERIFICATION_FAILED", "ERROR"] OR (patch_diff
is not present AND patch_status is unset/empty).
"Exploit Chains (Not End-to-End Reproduced)" specifically for exploit
chains. Document each chain finding here, listing its title, qualitative
priority, risk score, and detailing its constituent findings (their IDs and
individual status). Do not mix exploit chains with standard findings in
Categories 1, 2, or 3.
review_packet.md fileon every execution. Instead, determine the current run/pass number N of
the pipeline (resolved from "pass_number" in
workspace/.mantis_state.json. If missing or invalid, scan
workspace/archive/ for folders matching findings_pass_N or
loopN_findings and resolve N to max_found + 1, defaulting to 1 if no
archives exist). Then derive <snapshot_tag> from
active_snapshot.snapshot_id: take the snapshot ID exactly as stored and
replace every character NOT in [A-Za-z0-9.-] with a single underscore _
(do NOT truncate — the result stays well under any filename length limit).
Write the report to:
workspace/report/review_packet_pass_<N>_<snapshot_tag>.md whenactive_snapshot.snapshot_id is present and non-empty (e.g.
review_packet_pass_1_content_9f86d081884c...md). The <snapshot_tag>
suffix guarantees a reused pass number run against a DIFFERENT snapshot
writes a distinct file and cannot overwrite the earlier packet.
workspace/report/review_packet_pass_<N>.md (no suffix — exactly thelegacy name) when active_snapshot is absent or snapshot_id is empty,
preserving today's backward-compatible behavior.
update a symlink or write a copy of THAT exact file (whether or not it
carries a <snapshot_tag> suffix) to
workspace/report/review_packet-latest.md, so the latest version is always
reachable. The review_packet-latest.md name is unchanged and remains the
stable entry point for any downstream consumer.
metadata, and syntax-highlighted code blocks for logs and diffs.
included findings, their priority, and their risk scores.
filtering to findings where patch_status is VERIFIED_SECURE or
MITIGATION_PROPOSED. This gives stakeholders a visible "what got fixed"
view alongside the open findings.
reproduction attempt count is at the cap. The count is NOT a field on the
finding JSON; it lives in the cache file
state_root/workspace/archive/.repro_attempts.json, keyed by each
finding's signature (or, if signature is absent, by its computed
stable_key = normalized_title + "@" + primary_file_path — same key
selection as mantis-reproduce). For each open finding in the working
set, look up its signature (or stable_key fallback) in that cache;
include the finding in this callout if the cached value is at the retry
cap. Read the cached value per the schema's value-shape rule: a bare
integer V means {count: V, last_snapshot: UNKNOWN}; an object means
{count: V.count, last_snapshot: V.last_snapshot or UNKNOWN}. If the
cache file is missing or the finding's key is absent, treat its count as
0 (do not list it here). These are items the planner stopped carrying
> [!NOTE] De-dup caveat: de-dup is by finding identity, per the
> SAME-BUG PREDICATE at the top of this stage: two findings fold only if
> EITHER (i) they share the exact same id (UUID), OR (ii) ALL THREE hold —
> a shared non-empty lineage_id, a shared non-empty signature, AND at
> least one line-inclusive code_paths match. NEVER fold on lineage_id
> alone or on signature alone (basename-derived lineage can link two
> distinct same-named files; a signature strips the line number so it
> collides between distinct same-file bugs) — collapsing on either alone can
> silently drop a real finding. A bug re-discovered under a new UUID that
> does NOT satisfy predicate (ii) — a regression, a file rename that shifts
> the line, or a non-deterministic re-find — lists as a SEPARATE entry from
> its archived ancestor: over-reporting (safe), never hiding. With stable
> finding signatures and lineage tracking landed (Phase 3), a re-discovered
> finding folds into its ancestor's single entry ONLY when predicate (ii) is
> fully satisfied; the UUID-only match remains the safe branch for
> legacy/un-upgraded findings.
review_packet-latest.md is authoritative: Note thatreview_packet-latest.md is now the authoritative current open state of
the whole campaign (not just the latest pass). The per-pass
review_packet_pass_<N>_<snapshot_tag>.md files remain as-is for
historical reference.
Take google/mantis-report from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.