Reproducer (/mantis-reproduce)
System Goal
Integration Test Engineer. Designs crash reproducers or inputs and executes them
inside isolated sandbox environments to empirically verify bugs.
Command Definition
- Command:
/mantis-reproduce [--reattack] [--finding_id=<uuid>] [--force] [--target_root=<path>] [--state_root=<path>] [--snapshot_root=<path>] [--snapshot_id=<SNAPSHOT_ID>] [--snapshot_pinned=<true|false>]
- Description: Generates and runs crash reproducers to verify security
flaws.
- Parameters:
--reattack: When executing as part of patch verification to isolate
re-attack outcomes.
--finding_id: The specific finding UUID to reproduce. Must be provided
and is required when --reattack is specified.
--force: Override/bypass eligibility checks for targeted normal runs.
--target_root: Path to the root of the target codebase under test
(defaults to .). AUTHORITATIVE when supplied — overrides --snapshot_root
(Block A step 1a); the sentinel check is skipped for this tree (e.g. a
patched shadow during re-attack verification).
--state_root: Path to the root of the Mantis state directory containing
workspace/ (defaults to .).
--snapshot_root: Root of the pinned immutable code snapshot for this pass.
Consumed by Block A (Step 0) when --target_root is not supplied.
--snapshot_id: The SNAPSHOT_ID string of the pinned snapshot, consumed by
Block A (sentinel) and Block B (snapshot match check) in Step 0.
--snapshot_pinned: When false (set by mantis-patch during re-attack on
a patched shadow), the reproduce sub-agent MUST skip the snapshot
sentinel/match check for this invocation — the --target_root tree is
authoritative and sentinel-exempt (Block A step 1a).
Input/Output Contract
- Reads:
state_root/workspace/findings/ (viable/conditional findings).
target_root/ (Repository source files to analyze trigger paths).
state_root/workspace/archive/.repro_attempts.json.
state_root/workspace/.mantis_state.json (to track current loop pass).
- Writes:
- PoC reproduction files (e.g.
poc_[uuid].py or crash_[uuid].payload
inside state_root/workspace/reproducers/).
- If run normally: updates findings in-place under
state_root/workspace/findings/ (sets "repro_status",
"repro_file_path", "run_command", "repro_output", and appends
history). Updates status to "VALID" if provisionally valid.
- If run with
--reattack: updates findings in-place under
state_root/workspace/findings/ (sets "reattack_status",
"reattack_file_path", "reattack_run_command", "reattack_output",
"reattack_variants", and appends history with stage "reattack"). Does
not modify "repro_*" fields or "status". Exception: may atomically
downgrade patch_status per INV-1 in Step 6 (never persist
VERIFIED_SECURE alongside a non-failed_to_bypass reattack_status).
- Updates
state_root/workspace/archive/.repro_attempts.json atomically.
- Stamps
"repro_snapshot_id" / "reattack_snapshot_id" on updated findings
and stores .repro_attempts.json values as {count,last_snapshot} objects
(bare integers still read correctly).
- Preconditions:
- Findings must exist in
state_root/workspace/findings/.
- Sandbox/container runtime environment must be available.
- Idempotency Guarantee:
- Updates findings in place. Uses
state_root/workspace/archive/.repro_attempts.lock file locking and atomic
temporary file swaps (os.replace on
state_root/workspace/archive/.repro_attempts.json.tmp) to guarantee
concurrency safety and retry stability.
- Snapshot-aware: regenerates the PoC when the finding's snapshot no longer
matches; refuses to emit a negative verdict without reached-sink evidence.
Re-attack verdicts on a snapshot mismatch are governed by the C5
unpatched-baseline re-run (Step 6), which supersedes the legacy blanket
refusal — a
failed_to_bypass verdict is only written after C5 has
confirmed the unpatched baseline still triggers on the current snapshot.
Instructions
Step 0: Locator Resolution + Snapshot Match (run first)
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.
[!NOTE] CURRENT-PASS CHECK (defensive; the binding guarantee is on the
harness per mantis-pipeline-adapter Scenario 2): if active_snapshot is
present AND active_snapshot.pass != state.pass_number, treat the snapshot as
STALE for this pass — STOP "stale active_snapshot: pass mismatch" or degrade
as HALT (snapshot_pinned effectively false: no authoritative verdicts, Block
B NOT_MATCHED, reproduce not_attempted). This catches a custom harness that
preserved active_snapshot across the Stage 15 pass increment without
re-pinning. The reference meta-agent re-pins every pass, so this check never
fires there. Block B itself cannot detect this (it is snapshot_id-only, not
pass-aware).
SNAPSHOT MATCH CHECK for finding F (decides MATCHED vs NOT_MATCHED):
1. If snapshot_pinned is false -> NOT_MATCHED. Stop.
2. Read F.discovery_commit:
- missing OR empty OR the literal "MIXED" -> NOT_MATCHED.
- not exactly equal to SNAPSHOT_ID -> NOT_MATCHED.
- exactly equal to SNAPSHOT_ID -> MATCHED.
There is no other route to MATCHED; never fuzzy-compare. The global "default the
field and proceed" backward-compat rule does NOT apply to discovery_commit:
absent = NOT_MATCHED. (There is NO separate "dirty" gate: a dirty tree's
SNAPSHOT_ID already embeds the working-tree content hash, so within-pass findings
MATCH and cross-pass bare-commit findings do not.)
Notes: When invoked by the patcher with --target_root=<shadow> (a patched
copy), Block A step 1a makes that shadow the authoritative CODE_ROOT and SKIPS
the sentinel check (the shadow is deliberately mutated). Otherwise CODE_ROOT is
the pinned snapshot and the sentinel MUST match. Stamp repro_snapshot_id
(normal run) or reattack_snapshot_id (--reattack) = the current SNAPSHOT_ID
(from --snapshot_id or state active_snapshot.snapshot_id) on every finding
you update.
Write a Proof-of-Concept Reproduction Script (Repro) or raw input payload file
that reproduces a confirmed security flaw.
Execute the reproduction stage under these constraints:
Load Viable Findings:
- If
--finding_id is supplied:
- Load only that finding's file
(
state_root/workspace/findings/<uuid>.json). Exit if it does not exist.
- If
--reattack is specified: Enforce the expected patch workflow
state for the loaded finding:
- The finding's
"status" must be "VALID" or "PROVISIONALLY_VALID".
- The finding's
"repro_status" must be "reproduced".
- The finding's
"patch_status" must NOT be "MITIGATION_PROPOSED".
(VERIFIED_SECURE IS allowed: C5 below atomically downgrades it when
the re-attack outcome is not failed_to_bypass.)
- Exit with an error if these conditions are not met, explaining the
invalid state.
- If
--reattack is NOT specified (Targeted Normal Run):
- If
--force is NOT specified, enforce standard eligibility filters:
- The finding's
"status" must be "VALID" or
"PROVISIONALLY_VALID".
- The finding's
"production_viability" must be "VIABLE",
"SAMPLE_OR_TEST", or "CONDITIONAL_VIABLE".
- Exit with an error if these conditions are not met, explaining the
invalid state.
- If
--force is specified, bypass these eligibility checks.
- If
--finding_id is not supplied:
- Constraint: Exit if
--reattack is specified (it requires
--finding_id).
- Read the JSON files in the
state_root/workspace/findings/ directory.
- Strict Eligibility Filter (Normal Runs): Include only findings where:
"status" is "VALID" or "PROVISIONALLY_VALID".
"production_viability" is "VIABLE", "SAMPLE_OR_TEST", or
"CONDITIONAL_VIABLE" (or skip this viability filter if not checking
viability, but always check status).
- If no applicable findings exist, notify the user and exit.
Tier 0 — Structural Reachability Pre-Check (Advisory Queue Sorting): If a
structural code index (mantis-structural-index) is available, you MAY query
query_structural_index.py (find_callers) before authoring code to check
whether an AST call path exists from a public entrypoint to the vulnerable
sink. Use this query to prioritize candidate execution order (process
findings with verified AST reachability first).
- CRITICAL HINT-ONLY GUARDRAIL: AST reachability is a ranking HINT ONLY.
Call graphs miss macros, function pointers, dynamic dispatch, and interface
tables. An absent call path MUST NEVER reject a finding, skip reproduction,
or set
failed_to_reproduce.
Snapshot drift check: For each loaded finding, if it already has a
repro_snapshot_id and Block B (Step 0) returns NOT_MATCHED, treat any
stored PoC/offsets as STALE: regenerate the reproducer from scratch against
the current CODE_ROOT (do not reuse old line numbers/addresses). If Block B
is MATCHED you may reuse an existing PoC.
Strict Host Isolation Constraint:
- Host command execution is strictly prohibited. Do not run commands directly
on your parent host terminal using terminal/shell execution tools.
- All reproducer executions must run isolated. Use the containerization or
sandbox execution tools provided by your environment. For memory-safety
PoCs, restrict network access and file system writes as much as possible.
For logic/auth functional tests, you may enable local network services as
needed, but never expose the environment to the external internet.
Writing and Launching the Reproducer: Write a self-contained test script
(e.g., poc_[uuid].py or a C reproducer file in the same directory) or write
a raw crash input data payload (e.g., crash_[uuid].payload) that triggers
the target bug. All generated PoC/re-attack scripts and payloads MUST be
written inside the state_root/workspace/reproducers/ directory (never in
the target_root directory). You must ensure the parent directory
state_root/workspace/reproducers/ exists (e.g. using mkdir -p) before
writing any files. Analyze the code path and constraints carefully. If your
initial reproduction attempt fails, evaluate if the finding details (such as
input paths, parameters, or assumptions) are slightly incorrect based on your
observations, and adjust the finding details dynamically to attempt a fix. If
you cannot find a triggerable path after trying multiple approaches and
adjustments, abandon the attempt. Do NOT directly mark it as
failed_to_reproduce — route the abandon decision through the Step-5 Block
F (Reached-Sink Evidence) gate: if the harness provably reached the
vulnerable entrypoint but the bug did not fire, classify as
failed_to_reproduce; if evidence is absent (setup/build failure, exit 127,
"No such file", or the sink was never reached), classify as not_attempted
(retry-eligible). A raw negative from a setup/build failure burns the retry
cap and silently drops a real bug.
To run your script or payload, use the execution or containerization tools
available in your environment to execute the code safely. Select the most
appropriate runtime image and flags for the target. All compilation and
test execution commands MUST be run in a PRIVATE BUILD SHADOW, never with
Cwd=CODE_ROOT (the snapshot is read-only — Block A step 4). Before
compiling, create BUILD_ROOT=$(mktemp -d) and copy CODE_ROOT into it (e.g.
cp -a CODE_ROOT/. BUILD_ROOT/); run all compilation/test commands with
Cwd=BUILD_ROOT; delete BUILD_ROOT on teardown. Keep the generated PoC file
itself under state_root/workspace/reproducers/ (STATE-RELATIVE) and store
its ABSOLUTE path in "run_command"/"reattack_run_command".
Sanitizer compilation (C/C++ targets): When the bug class is
memory-safety or undefined-behavior, compile with
-fsanitize=address,undefined (ASan + UBSan) at a minimum. For data races,
add -fsanitize=thread (TSan) — TSan is also mutually exclusive with ASan,
so use a separate build. Use -fno-omit-frame-pointer for usable stack
traces. These flags surface bugs that would otherwise exit 0 silently (UBSan
defaults to recover-mode). MSan caveat: MemorySanitizer
(-fsanitize=memory) detects uninitialized-memory reads, but it requires the
ENTIRE dependency chain (including libc and the C++ runtime) to be
MSan-instrumented — on an arbitrary target that is usually not the case, so a
naive -fsanitize=memory build yields bogus WARNING: MemorySanitizer
traces. MSan is also mutually exclusive with ASan in one build (use a
separate build, not an additive flag). If a fully instrumented MSan
environment cannot be guaranteed, do NOT add -fsanitize=memory; fall back
to ASan+UBSan rather than trusting a possibly-bogus MSan trace. Recognizing
MSan output as evidence (Step 5) is still correct when a legitimate MSan
instrumented build is available. Sanitizer consistency guardrail: the
SAME sanitizer flags MUST be used for the baseline, attack, and re-attack
runs — a different sanitizer can mask the bug and produce a false negative
(INV-2) or false VERIFIED_SECURE (INV-1). See Block G.
{TARGET_ROOT} token substitution (numbered step):
- When writing
run_command or reattack_run_command, use the literal
token {TARGET_ROOT} for any path that references the target tree.
- At run time, reproduce substitutes
{TARGET_ROOT} with the actual root:
- On first execution:
CODE_ROOT (the snapshot or --target_root).
- On re-execution (re-attack or retry): the current
CODE_ROOT /
--target_root / active_snapshot.root.
- This ensures the stored command resolves correctly after snapshot GC —
never bake an absolute
.mantis_snapshots/pass_<N> path into the stored
command.
Execute your reproduction using the appropriate environment: If the
target is firmware, you may write a script to boot it via qemu, unicorn,
or Firmadyne. If it's a binary, you may use dynamic instrumentation or
standard execution. Use your best judgment to construct a working harness for
the artifact.
Parallel Trajectory Search vs. Tiered Iterative Reproduction:
Parallel Trajectory Search (Breadth-First): When subagents are
available, deploy concurrent workers taking diverse logical approaches to
reproduce the bug. If any trajectory succeeds, immediately adopt its
payload and discard the others to escape potential "give up" loops and
prune compute costs.
Tiered Iterative Reproduction (Depth-First Payload Refinement): Each
trajectory worker (or a single agent) uses a tiered escalation ladder
(Tier 1 -> Tier 2 -> Tier 3) to refine its trigger payload incrementally
rather than attempting a single-shot end-to-end launch.
Tiered Iterative Execution Ladder:
- Tier 1 (Micro-Harness / Sink Logic Validation): Construct a
lightweight test calling the vulnerable function/module directly to
verify that the core bug hypothesis is sound in isolation.
- Tier 2 (Subsystem / Interface Validation): Pass the payload through
input serialization, parsers, routing, and auth wrappers to verify the
input survives intermediate processing without sanitization or
truncation.
- Tier 3 (Full Sandboxed Service / E2E Validation): Execute the
self-contained PoC against the target service via public APIs inside the
isolated sandbox (Docker, QEMU, VM). Yields the authoritative
reproduced verdict per Block F.
CRITICAL STEP-4 TIER-1 HARD GATE (Fail-Closed):
- Tiers 1 and 2 are internal stepping stones only. You MUST NEVER
record
repro_status = "reproduced" or "statically_confirmed" based on
a Tier-1 or Tier-2 execution.
- If a crash can ONLY be achieved by compiling a direct-call harness
that feeds a private/static function or bypasses the public API (Step 4),
and the payload cannot be escalated to trigger through Tier 3 (the public
API / sandboxed service), you MUST TERMINATE AND RECORD
repro_status = "failed_to_reproduce" with details citing
"Internal Invariant Protection".
Attempt Cap Accounting & Local Retries:
- Sub-Tier-3 Stepping-Stone Sub-Budget: Internal Tier-1 and Tier-2
trial runs are bounded local execution steps (max 3 trial executions per
conversation) and DO NOT increment the absolute per-finding attempt
counter in
state_root/workspace/archive/.repro_attempts.json.
- Absolute Attempt Cap Counting: Only Tier-3 full sandboxed service
executions (or full end-to-end reproducer runs) increment the absolute
attempt counter toward the hard ceiling of 6 (Section 6).
- Intra-Conversation Retries: When a tier fails, inspect logs, adjust
payload parameters, fix harness setup, and retry up to 2-3 times within
the active conversation before reporting back to the orchestrator.
Step 3a: Variant Hunting (re-attack only, MANDATORY)
When invoked with --reattack, you MUST author and execute N ≥ 3
boundary-mutated variant inputs in addition to re-running the original PoC.
The schema (schema.json) literally calls this the "variant-hunting re-attack"
— merely re-running the original PoC is insufficient. Over-narrow patches that
guard the exact PoC bytes are the dominant auto-repair failure mode; variant
hunting is the zero-infra-cost defense against them.
Legacy findings: Findings with absent reattack_variants (created before
this rule existed) are re-verified under the ≥3 variant requirement on their
first --reattack pass. This is intentional — legacy VERIFIED_SECURE verdicts
are upgraded to the stricter gate — but expect one-time churn across archived
findings on the first pass that runs variant hunting.
What to generate (bug-class-aware):
Memory-safety bugs (buffer overflow, OOB read/write, UAF, integer
overflow): Author at least 3 of:
- Off-by-one:
len = bound, len = bound + 1, len = bound - 1.
- Size mutations:
len ± 1, len = 0, len = SIZE_MAX, sign flips on
signed lengths.
- Alternate paths to the same sink: If the vulnerable sink is reached via
multiple call sites, author a variant reaching it through a different path
(e.g., different API endpoint, file format variant, or protocol command).
- Type confusion / width mismatch: Exploit a different type path to the
same sink.
Non-memory-safety bugs (logic, auth, injection, SSRF, path traversal):
Hunt for actual variants of the same class:
- Alternate endpoints/parameters: Try
/api/v2/echo when PoC targets
/api/v1/echo, or alternate parameter names.
- Equivalent payloads:
..%2fetc%2fpasswd, ..\\..\\, URL-encoding,
double encoding, unicode normalization variants.
- Auth boundary variants: Different roles, empty/null tokens, alternate
privilege-escalation paths.
- Injection variants:
'; EXEC--, UNION SELECT, blind variants,
alternate injection points.
Parallel variant generation (if subagents available): You SHOULD spawn
subagents to author and test variants in parallel. Each subagent gets one
mutation strategy, writes its variant PoC, and reports whether it triggered.
Aggregate all results before setting reattack_status. If subagents are
unavailable, do them sequentially.
Execution: Write each variant as a separate script in
state_root/workspace/reproducers/ (e.g., reattack_variant_[uuid]_[N].py).
Execute each against the patched shadow (--target_root) using the same
isolation constraints as Step 3. Record each variant in the reattack_variants
array using EXACTLY the schema keys
{"description": "...", "triggered": true/false} (Step 6).
Verdict rule: reattack_status = "failed_to_bypass" requires a non-empty
reattack_variants array containing ≥ 3 valid variant inputs that ALL failed to
trigger the bug on the patched shadow. An empty or short set makes "all variants
failed" vacuously true — this is FORBIDDEN: if fewer than 3 meaningful variants
can be constructed after genuine effort, cap at VERIFICATION_INCOMPLETE
(history note insufficient_variants), NEVER failed_to_bypass. The [] case
may ONLY coexist with a non-failed_to_bypass status (e.g., C5 baseline failure
that halted before variant hunting).
Variant validity guardrail: A variant counts as a bypass (triggered = true
causing bypassed_patch) ONLY if it satisfies BOTH:
- Same vulnerability class: The variant reproduces the original bug class —
same sink function / sanitizer signature / crash type. A junk mutant (e.g.,
len=SIZE_MAX causing an unrelated OOM, an alternate endpoint 404-ing then
erroring, or a completely different crash) does NOT count as a bypass.
Discard it (set triggered = false with a description noting it was invalid)
and continue. For non-memory bugs (logic, auth, injection), compare the same
sink function or behavior (e.g., same unauthorized action succeeds, same
injection executes, reaches the same sink function) — NOT a ±line window
against a pre-patch line number (patches shift lines, and non-memory bugs
often have no precise sink line). If you cannot positively confirm a
triggering variant is a DIFFERENT bug, count it as a bypass (fail-closed:
prefer a false VERIFICATION_FAILED over a false VERIFIED_SECURE).
- Valid input per Step 4: The variant must be a valid exercise of the
public API or internal invariants — it must not rely on artificial harness
tricks (e.g., private-function direct calls with custom-allocated buffers)
that bypass the library's execution invariants.
Ideally, confirm each triggering variant still triggers on the unpatched
shadow (same baseline the original PoC ran against) to prove it exercises the
original bug rather than an artifact. If the unpatched shadow is unavailable
(e.g., snapshot mismatch), require the same-sink evidence (same sanitizer frame,
same crash address, same logic failure) as corroboration.
If ANY valid variant triggers the bug, set reattack_status = "bypassed_patch"
and apply INV-1 (downgrade VERIFIED_SECURE → VERIFICATION_FAILED).
Strict Public-API & Internal Invariant Constraints:
- Your crash reproducer should interact with the codebase through
public-facing APIs wherever possible, or strictly respect the library's
global execution invariants (such as allocator padding) to avoid generating
artificial, non-viable crashes.
- Do not declare a finding as "reproduced" if the crash can only be achieved
by compiling a direct-call harness that feeds a private/static function a
custom-allocated buffer (e.g.,
malloc(15)) that bypasses the library's
guaranteed allocator wrappers (e.g., png_malloc(rowbytes + 48)).
- If a crash cannot be triggered through the public API or with standard
allocation padding, classify the finding as
"failed_to_reproduce" due to
"Internal Invariant Protection."
Functional & Crash-Aware Validation: Analyze the output such as stdout,
stderr, and exit codes to classify reproduction success depending on the bug
class:
Before classifying ANY negative outcome (failed_to_reproduce, or in
--reattack mode failed_to_bypass), apply this gate:
REACHED-SINK EVIDENCE GATE (mechanical):
Each reproducer produces REACHED-SINK EVIDENCE via ONE channel, recorded in repro_hints:
(a) script/source harness -> write the exact bytes MANTIS_REACHED_ENTRYPOINT to a
sidecar file $SENTINEL_FILE and flush+fsync (or unbuffered write) BEFORE
invoking the sink. (A file survives a crash that truncates buffered stdout.)
(b) binary / firmware / raw-payload -> reached-sink evidence is a captured
crash backtrace or sanitizer frame (ASan/UBSan/MSan/TSan) that
explicitly names the target sink function (target-produced tracing). A
marker written by a wrapper you author BEFORE invoking the target is
SETUP EVIDENCE ONLY: it proves "launch attempted," not "sink reached,"
and does NOT qualify as reached-sink evidence. If no in-path marker
(channel a) and no target-produced backtrace/sanitizer trace
(channel b) is achievable, the sink is unreached.
EVIDENCE PRESENT (reached-sink) = (channel a) sidecar file contains
MANTIS_REACHED_ENTRYPOINT written in-path, OR (channel b) target-produced
backtrace/sanitizer output names the sink. A wrapper pre-launch marker alone is
NOT evidence present.
EVIDENCE ABSENT includes: any compiler/build nonzero exit; exit 127 (command not
found); exit 2 with a "No such file" message.
DECISION GATE (gate the DECISION, not specific verdict strings):
- Record repro_status = reproduced OR statically_confirmed ONLY if EVIDENCE is
PRESENT. If ABSENT -> repro_status = not_attempted (retry-eligible), STOP.
- In patch verification, EVIDENCE is required on the UNPATCHED baseline (Block G),
NOT on the post-patch attack run (a correct patch legitimately stops the input
before the sink).
- If NO evidence channel is achievable for this target, downgrade to
not_attempted / VERIFICATION_INCOMPLETE. NEVER synthesize the marker.
Concretely: if the run produced NO reached-sink evidence (build/setup error,
exit 127, "No such file", or the sink was never reached), record
repro_status = not_attempted (retry-eligible) — NEVER
failed_to_reproduce; and in --reattack mode leave reattack_status UNSET
with a history note "setup_failed" — NEVER failed_to_bypass. Only classify
a negative when the harness provably reached the vulnerable entrypoint and
the bug did not fire.
HALT ceiling (3-state rule): If active_snapshot is present in state but
snapshot_pinned is false (HALT mode — the tree raced or could not be
pinned), you MUST NOT record failed_to_reproduce or failed_to_bypass at
all. In HALT, the code may have drifted and a negative reproduction result
cannot be trusted as authoritative. Instead, record
repro_status = not_attempted (retry-eligible) and, in --reattack mode,
leave reattack_status UNSET with a history note "HALT mode: snapshot
unpinned, negative result suppressed". This mirrors the authoritative-verdict
prohibition that applies to all stages in HALT. (In MODE-OFF — no
active_snapshot — classify negatives normally as today.)
- Logic & Authorization Bugs: A successful reproducer is a functional
unit test or script that explicitly demonstrates the logic failure (e.g.,
an unauthorized request returns
200 OK, or a test script successfully
bypasses validation and exits with 0).
- Memory Safety & Binary Crashes: If the sandbox execution produces a
crash signal or sanitizer trace in stdout/stderr, mark the reproduction as
"reproduced". Scan stdout/stderr for sanitizer signatures regardless of
exit code — UBSan defaults to recover-mode (exit 0), so an exit-0 run can
still contain a valid UBSan trace proving the bug fired. Check for:
- AddressSanitizer (ASan) error outputs (e.g.
ERROR: AddressSanitizer).
- UndefinedBehaviorSanitizer (UBSan) runtime reports (e.g.
runtime error:, SUMMARY: UndefinedBehaviorSanitizer).
- MemorySanitizer (MSan) error outputs (e.g.
WARNING: MemorySanitizer).
MSan evidence is valid ONLY if repro_hints records that a
fully-instrumented MSan build was available (Step 3 caveat). A naive
-fsanitize=memory build without full instrumentation produces bogus
traces — do NOT classify these as "reproduced".
- ThreadSanitizer (TSan) race reports (e.g.
WARNING: ThreadSanitizer).
- Segmentation faults (SIGSEGV, exit code
139).
- Abort signals (SIGABRT, exit code
134).
- Crash or core dumps.
Token-Optimized File Updates: To minimize LLM output tokens, do not
re-emit or manually rewrite the entire JSON object in your output. Instead,
use in-place editing tools (like a short script in your preferred language,
or jq) to programmatically append the new fields to the existing
state_root/workspace/findings/<id>.json file.
Additionally, you must Update the Reproduction Attempt Cache to help the
planner track attempts efficiently:
- Maintain a JSON cache file at
state_root/workspace/archive/.repro_attempts.json. Ensure the parent
directory state_root/workspace/archive/ exists (e.g.,
mkdir -p state_root/workspace/archive/) before creating, reading, or
locking the cache file.
- Key the cache by a stable identifier that persists across loop runs even if
UUIDs are regenerated. If the finding has a
signature field, use it
directly as the cache key (it is already a deterministic content-identity
hash). If signature is absent, fall back to a computed stable key using
the finding's normalized title and its primary file path:
stable_key = normalized_title + "@" + primary_file_path.
- Compute
normalized_title by converting the title to lowercase and
removing all non-alphanumeric characters.
- Compute
primary_file_path by taking the first entry in code_paths and
stripping any line number suffixes (e.g., converting src/auth.c:120 to
src/auth.c).
- Snapshot-aware cap (value shape
{count, last_snapshot}): store each
cache value as an object {count, last_snapshot}. When reading a value
V: if V is a bare integer, treat count=V and last_snapshot=UNKNOWN; if V
is an object, use V.count / V.last_snapshot (default UNKNOWN). Before
incrementing, run Block B comparing the finding's snapshot to the current
SNAPSHOT_ID: reset count=0 ONLY when Block B is NOT_MATCHED because
the two snapshots are present and actually differ (a genuine code change
earns a fresh budget). Do NOT reset on UNKNOWN (absent/pass_/unpinned)
— that would make no-VCS targets retry forever. Additionally keep an
absolute per-finding-id attempt counter that is NEVER reset, and stop
retrying once it reaches a hard ceiling (e.g. 6) regardless of snapshot
changes.
- To prevent race conditions during concurrent executions (including locking
bypasses caused by atomic file replacement) and protect lockless readers:
- Use a separate dedicated lock file
state_root/workspace/archive/.repro_attempts.lock which is never
deleted or replaced.
- Perform updates atomically using Python's
fcntl.flock on this lock
file:
Open the lock file state_root/workspace/archive/.repro_attempts.lock
(creating it if missing) and acquire an exclusive lock (fcntl.flock
with fcntl.LOCK_EX) inside a context manager (with statement).
Read the current contents of the cache file
state_root/workspace/archive/.repro_attempts.json (treating it as {}
if missing or empty).
Increment the count field of this finding's cache-key entry — keyed by
signature if present, else stable_key, the SAME key selection defined
above (the {count, last_snapshot} object) — by 1 ONLY for Tier-3 (full
end-to-end sandboxed service) executions. Internal Tier-1 and Tier-2
stepping-stone trials MUST NOT increment count (they are governed by
the sub-budget rule in Step 3).
Write the updated JSON to a temporary file in the same directory (e.g.,
state_root/workspace/archive/.repro_attempts.json.tmp).
Atomically replace the target cache file with the temporary file (e.g.,
os.replace in Python) to ensure readers never see a truncated or
incomplete file.
Close the lock file descriptor to release the lock (automatically handled
by exiting the with context manager).
Depending on whether the --reattack flag is provided:
If run normally (no --reattack flag): You must append or update the
following on the existing object:
"repro_status" ("reproduced", "statically_confirmed",
"not_attempted", or "failed_to_reproduce").
"repro_file_path"
"run_command"
"repro_output"
"repro_snapshot_id": the current SNAPSHOT_ID this run executed against.
"repro_hints": Record compilation and sandbox execution telemetry
(e.g., sanitizers_used: ASan+UBSan, assertions_disabled: true,
build_profile: release) to provide empirical execution evidence for
/mantis-critic.
If reproduction succeeds (repro_status is evaluated as "reproduced"
or "statically_confirmed") and the finding's current "status" is
"PROVISIONALLY_VALID": BEFORE upgrading, scan the finding's
triage_checklist (if present). If ANY entry has outcome == "UNKNOWN"
(or passes == false), do NOT upgrade: leave status as
"PROVISIONALLY_VALID", still set repro_status to the success value
(reproduction DID succeed), and append a history note
upgrade-to-VALID-blocked: triage_checklist has UNKNOWN entries (re-review required).
This avoids violating the schema's VALID ⇒ no UNKNOWN allOf gate
(schema.json lines 471-507), which forbids UNKNOWN/passes:false on
any VALID non-chain finding's triage_checklist. Reproduce does NOT
touch triage_checklist entries (the checklist is review's artifact;
only review may resolve UNKNOWN entries). If triage_checklist is
absent (no reviewer history entry, e.g. a legacy finding), or NO entry
is UNKNOWN/passes:false, you must update "status" to "VALID".
An entry to the "history" array:
{
"stage": "reproduce",
"action": "reproduced",
"details": "Reproduction status evaluated as [reproduced/failed_to_reproduce] using command: [run_command]",
"pass_number": <current_pass_number>,
"timestamp": "<current_iso8601_timestamp>"
}
If run with --reattack: You must append or update the following on
the existing object (do not touch repro_* or status):
"reattack_status" ("bypassed_patch", "failed_to_bypass",
"inconclusive_baseline_changed").
"bypassed_patch": The PoC bypassed the patch and triggered the bug. If
patch_status is VERIFIED_SECURE, atomically set
patch_status = "VERIFICATION_FAILED" in the same write (a bypass
defeats the patch). This is an explicit exception to "do not touch
status" and applies to both the C5 step-3 path and same-snapshot runs.
"failed_to_bypass": The PoC was run but failed to bypass the patch. (No
downgrade needed — failed_to_bypass is the value the allOf gate
requires for VERIFIED_SECURE.)
"inconclusive_baseline_changed": The unpatched baseline was re-run (see
C5 below) and the bug NO LONGER TRIGGERS on the current unpatched code.
Do NOT claim failed_to_bypass. If patch_status is VERIFIED_SECURE,
atomically set patch_status = "VERIFICATION_INCOMPLETE" in the same
write.
INV-1 (single source — the schema's VERIFIED_SECURE allOf gate):
VERIFIED_SECURE => reattack_status must be failed_to_bypass, and
failed_to_bypass requires a non-empty reattack_variants array with ≥
3 valid variant inputs (Step 3a) that ALL failed to trigger the bug on
the patched shadow. An empty or short variant set makes "all failed"
vacuously true — this is FORBIDDEN: cap at VERIFICATION_INCOMPLETE
instead. Any other outcome MUST atomically downgrade `
…(truncated)
1---2name: mantis-reproduce3description: Generates and runs crash reproducers to verify security flaws. Use when viable findings exist and you need to write and execute a script or payload to verify the crash. Don't use for code auditing or patching.4---5
6# Reproducer (/mantis-reproduce)
7
8## System Goal
9
10Integration Test Engineer. Designs crash reproducers or inputs and executes them
11inside isolated sandbox environments to empirically verify bugs.
12
13## Command Definition
14
15- **Command:**
16 `/mantis-reproduce [--reattack] [--finding_id=<uuid>] [--force] [--target_root=<path>] [--state_root=<path>] [--snapshot_root=<path>] [--snapshot_id=<SNAPSHOT_ID>] [--snapshot_pinned=<true|false>]`
17- **Description:** Generates and runs crash reproducers to verify security
18 flaws.
19- **Parameters:**
20 - `--reattack`: When executing as part of patch verification to isolate
21 re-attack outcomes.
22 - `--finding_id`: The specific finding UUID to reproduce. **Must** be provided
23 and is required when `--reattack` is specified.
24 - `--force`: Override/bypass eligibility checks for targeted normal runs.
25 - `--target_root`: Path to the root of the target codebase under test
26 (defaults to `.`). AUTHORITATIVE when supplied — overrides `--snapshot_root`
27 (Block A step 1a); the sentinel check is skipped for this tree (e.g. a
28 patched shadow during re-attack verification).
29 - `--state_root`: Path to the root of the Mantis state directory containing
30 `workspace/` (defaults to `.`).
31 - `--snapshot_root`: Root of the pinned immutable code snapshot for this pass.
32 Consumed by Block A (Step 0) when `--target_root` is not supplied.
33 - `--snapshot_id`: The SNAPSHOT_ID string of the pinned snapshot, consumed by
34 Block A (sentinel) and Block B (snapshot match check) in Step 0.
35 - `--snapshot_pinned`: When `false` (set by `mantis-patch` during re-attack on
36 a patched shadow), the reproduce sub-agent MUST skip the snapshot
37 sentinel/match check for this invocation — the `--target_root` tree is
38 authoritative and sentinel-exempt (Block A step 1a).
39
40## Input/Output Contract
41
42- **Reads**:
43 - `state_root/workspace/findings/` (viable/conditional findings).
44 - `target_root/` (Repository source files to analyze trigger paths).
45 - `state_root/workspace/archive/.repro_attempts.json`.
46 - `state_root/workspace/.mantis_state.json` (to track current loop pass).
47- **Writes**:
48 - PoC reproduction files (e.g. `poc_[uuid].py` or `crash_[uuid].payload`
49 inside `state_root/workspace/reproducers/`).
50 - If run normally: updates findings in-place under
51 `state_root/workspace/findings/` (sets `"repro_status"`,
52 `"repro_file_path"`, `"run_command"`, `"repro_output"`, and appends
53 history). Updates status to `"VALID"` if provisionally valid.
54 - If run with `--reattack`: updates findings in-place under
55 `state_root/workspace/findings/` (sets `"reattack_status"`,
56 `"reattack_file_path"`, `"reattack_run_command"`, `"reattack_output"`,
57 `"reattack_variants"`, and appends history with stage `"reattack"`). Does
58 not modify `"repro_*"` fields or `"status"`. **Exception:** may atomically
59 downgrade `patch_status` per INV-1 in Step 6 (never persist
60 `VERIFIED_SECURE` alongside a non-`failed_to_bypass` `reattack_status`).
61 - Updates `state_root/workspace/archive/.repro_attempts.json` atomically.
62 - Stamps `"repro_snapshot_id"` / `"reattack_snapshot_id"` on updated findings
63 and stores `.repro_attempts.json` values as `{count,last_snapshot}` objects
64 (bare integers still read correctly).
65- **Preconditions**:
66 - Findings must exist in `state_root/workspace/findings/`.
67 - Sandbox/container runtime environment must be available.
68- **Idempotency Guarantee**:
69 - Updates findings in place. Uses
70 `state_root/workspace/archive/.repro_attempts.lock` file locking and atomic
71 temporary file swaps (`os.replace` on
72 `state_root/workspace/archive/.repro_attempts.json.tmp`) to guarantee
73 concurrency safety and retry stability.
74 - Snapshot-aware: regenerates the PoC when the finding's snapshot no longer
75 matches; refuses to emit a negative verdict without reached-sink evidence.
76 Re-attack verdicts on a snapshot mismatch are governed by the C5
77 unpatched-baseline re-run (Step 6), which supersedes the legacy blanket
78 refusal — a `failed_to_bypass` verdict is only written after C5 has
79 confirmed the unpatched baseline still triggers on the current snapshot.
80
81## Instructions
82
83### Step 0: Locator Resolution + Snapshot Match (run first)
84
85```
86LOCATOR RESOLUTION (before reading ANY target code or artifact):
870. ROLE: If this skill NEVER reads target source (report, calibrate, reflect),
88 you are a FINDINGS-ONLY stage: skip steps 2-6; still read active_snapshot from
89 state for provenance/annotation; NEVER stop merely because a code root is unset.
901. Determine CODE_ROOT, in this priority order:
91 a. If --target_root is passed on THIS invocation, CODE_ROOT = --target_root.
92 It is AUTHORITATIVE and OVERRIDES SNAPSHOT_ROOT and the state fallback
93 (used when a caller hands you a prepared tree, e.g. a patched shadow).
94 b. Else if --snapshot_root (or SNAPSHOT_ROOT) is passed, use it.
95 c. Else read state_root/workspace/.mantis_state.json (state_root from
96 --state_root if passed, else ./workspace/... relative to the current dir)
97 -> active_snapshot.root / .snapshot_id / .snapshot_pinned.
98 d. Else (no arg AND no readable active_snapshot): CODE_ROOT = current directory,
99 treat snapshot_pinned = false (MODE-OFF). Do NOT stop.
1002. SENTINEL CHECK (only if snapshot_pinned is true AND you did NOT take path 1a):
101 verify CODE_ROOT/.mantis_snapshot_id exists and equals SNAPSHOT_ID. If missing
102 or different -> STOP "snapshot sentinel mismatch". (A --target_root tree (1a) is
103 deliberately mutated and is sentinel-EXEMPT.)
1043. PATH FIELDS:
105 - SNAPSHOT-RELATIVE (read under CODE_ROOT): code_paths entries; plan target_files
106 that are file paths. Strip ONLY a trailing ":<digits>". A code_paths entry
107 containing "://" is a URL/endpoint, NOT a file read. A code_paths entry that is
108 NOT of the form <existing-path>:<integer> is a non-source LOCATOR
109 (symbol/offset/endpoint): only check that the artifact/symbol exists; skip ALL
110 line-range and line-existence logic.
111 - STATE-RELATIVE (read/write under state_root/workspace, NEVER prefix CODE_ROOT):
112 kb_references, repro_file_path, reattack_file_path, helper scripts, report
113 files, and all state/findings JSON.
1144. Never WRITE under CODE_ROOT when snapshot_pinned is true. Any command that
115 compiles, generates, or writes artifacts MUST run in a PRIVATE SHADOW copy
116 (mktemp -d from CODE_ROOT), never with cwd=CODE_ROOT. Read-only inspection may
117 cd into CODE_ROOT.
1185. VCS-METADATA CARVE-OUT: history-log extraction and any VCS diff/blame command
119 run in the LIVE repository root (which still has .git/.hg/.repo), NOT CODE_ROOT
120 (the snapshot copy strips VCS metadata). Do NOT stop merely because CODE_ROOT
121 lacks .git/.hg/.repo.
1226. Every shell command uses ABSOLUTE paths and sets its own working directory on
123 that call. Do NOT assume the working directory persists between calls.
124```
125
126> [!NOTE] **CURRENT-PASS CHECK (defensive; the binding guarantee is on the
127> harness per `mantis-pipeline-adapter` Scenario 2):** if `active_snapshot` is
128> present AND `active_snapshot.pass != state.pass_number`, treat the snapshot as
129> STALE for this pass — STOP "stale active_snapshot: pass mismatch" or degrade
130> as HALT (`snapshot_pinned` effectively false: no authoritative verdicts, Block
131> B NOT_MATCHED, reproduce `not_attempted`). This catches a custom harness that
132> preserved `active_snapshot` across the Stage 15 pass increment without
133> re-pinning. The reference meta-agent re-pins every pass, so this check never
134> fires there. Block B itself cannot detect this (it is `snapshot_id`-only, not
135> `pass`-aware).
136
137```
138SNAPSHOT MATCH CHECK for finding F (decides MATCHED vs NOT_MATCHED):
1391. If snapshot_pinned is false -> NOT_MATCHED. Stop.
1402. Read F.discovery_commit:
141 - missing OR empty OR the literal "MIXED" -> NOT_MATCHED.
142 - not exactly equal to SNAPSHOT_ID -> NOT_MATCHED.
143 - exactly equal to SNAPSHOT_ID -> MATCHED.
144There is no other route to MATCHED; never fuzzy-compare. The global "default the
145field and proceed" backward-compat rule does NOT apply to discovery_commit:
146absent = NOT_MATCHED. (There is NO separate "dirty" gate: a dirty tree's
147SNAPSHOT_ID already embeds the working-tree content hash, so within-pass findings
148MATCH and cross-pass bare-commit findings do not.)
149```
150
151Notes: When invoked by the patcher with `--target_root=<shadow>` (a patched
152copy), Block A step 1a makes that shadow the authoritative CODE_ROOT and SKIPS
153the sentinel check (the shadow is deliberately mutated). Otherwise CODE_ROOT is
154the pinned snapshot and the sentinel MUST match. Stamp `repro_snapshot_id`
155(normal run) or `reattack_snapshot_id` (`--reattack`) = the current SNAPSHOT_ID
156(from `--snapshot_id` or state `active_snapshot.snapshot_id`) on every finding
157you update.
158
159Write a Proof-of-Concept Reproduction Script (Repro) or raw input payload file
160that reproduces a confirmed security flaw.
161
162Execute the reproduction stage under these constraints:
163
1641. **Load Viable Findings:**
165
166 - If `--finding_id` is supplied:
167 - Load only that finding's file
168 (`state_root/workspace/findings/<uuid>.json`). Exit if it does not exist.
169 - If `--reattack` is specified: Enforce the **expected patch workflow
170 state** for the loaded finding:
171 - The finding's `"status"` must be `"VALID"` or `"PROVISIONALLY_VALID"`.
172 - The finding's `"repro_status"` must be `"reproduced"`.
173 - The finding's `"patch_status"` must NOT be `"MITIGATION_PROPOSED"`.
174 (`VERIFIED_SECURE` IS allowed: C5 below atomically downgrades it when
175 the re-attack outcome is not `failed_to_bypass`.)
176 - Exit with an error if these conditions are not met, explaining the
177 invalid state.
178 - If `--reattack` is NOT specified (Targeted Normal Run):
179 - If `--force` is NOT specified, enforce standard eligibility filters:
180 - The finding's `"status"` must be `"VALID"` or
181 `"PROVISIONALLY_VALID"`.
182 - The finding's `"production_viability"` must be `"VIABLE"`,
183 `"SAMPLE_OR_TEST"`, or `"CONDITIONAL_VIABLE"`.
184 - Exit with an error if these conditions are not met, explaining the
185 invalid state.
186 - If `--force` is specified, bypass these eligibility checks.
187 - If `--finding_id` is not supplied:
188 - **Constraint:** Exit if `--reattack` is specified (it requires
189 `--finding_id`).
190 - Read the JSON files in the `state_root/workspace/findings/` directory.
191 - **Strict Eligibility Filter (Normal Runs):** Include only findings where:
192 - `"status"` is `"VALID"` or `"PROVISIONALLY_VALID"`.
193 - `"production_viability"` is `"VIABLE"`, `"SAMPLE_OR_TEST"`, or
194 `"CONDITIONAL_VIABLE"` (or skip this viability filter if not checking
195 viability, but always check status).
196 - If no applicable findings exist, notify the user and exit.
197
198 **Tier 0 — Structural Reachability Pre-Check (Advisory Queue Sorting):** If a
199 structural code index (`mantis-structural-index`) is available, you MAY query
200 `query_structural_index.py` (`find_callers`) before authoring code to check
201 whether an AST call path exists from a public entrypoint to the vulnerable
202 sink. Use this query to **prioritize candidate execution order** (process
203 findings with verified AST reachability first).
204
205 - **CRITICAL HINT-ONLY GUARDRAIL:** AST reachability is a ranking HINT ONLY.
206 Call graphs miss macros, function pointers, dynamic dispatch, and interface
207 tables. An absent call path MUST NEVER reject a finding, skip reproduction,
208 or set `failed_to_reproduce`.
209
210 **Snapshot drift check:** For each loaded finding, if it already has a
211 `repro_snapshot_id` and Block B (Step 0) returns NOT_MATCHED, treat any
212 stored PoC/offsets as STALE: regenerate the reproducer from scratch against
213 the current CODE_ROOT (do not reuse old line numbers/addresses). If Block B
214 is MATCHED you may reuse an existing PoC.
215
2162. **Strict Host Isolation Constraint:**
217
218 - Host command execution is strictly prohibited. Do not run commands directly
219 on your parent host terminal using terminal/shell execution tools.
220 - All reproducer executions must run isolated. Use the containerization or
221 sandbox execution tools provided by your environment. For memory-safety
222 PoCs, restrict network access and file system writes as much as possible.
223 For logic/auth functional tests, you may enable local network services as
224 needed, but never expose the environment to the external internet.
225
2263. **Writing and Launching the Reproducer:** Write a self-contained test script
227 (e.g., `poc_[uuid].py` or a C reproducer file in the same directory) or write
228 a raw crash input data payload (e.g., `crash_[uuid].payload`) that triggers
229 the target bug. **All generated PoC/re-attack scripts and payloads MUST be
230 written inside the `state_root/workspace/reproducers/` directory (never in
231 the `target_root` directory).** You must ensure the parent directory
232 `state_root/workspace/reproducers/` exists (e.g. using `mkdir -p`) before
233 writing any files. Analyze the code path and constraints carefully. If your
234 initial reproduction attempt fails, evaluate if the finding details (such as
235 input paths, parameters, or assumptions) are slightly incorrect based on your
236 observations, and adjust the finding details dynamically to attempt a fix. If
237 you cannot find a triggerable path after trying multiple approaches and
238 adjustments, abandon the attempt. **Do NOT directly mark it as
239 `failed_to_reproduce`** — route the abandon decision through the Step-5 Block
240 F (Reached-Sink Evidence) gate: if the harness provably reached the
241 vulnerable entrypoint but the bug did not fire, classify as
242 `failed_to_reproduce`; if evidence is absent (setup/build failure, exit 127,
243 "No such file", or the sink was never reached), classify as `not_attempted`
244 (retry-eligible). A raw negative from a setup/build failure burns the retry
245 cap and silently drops a real bug.
246
247 To run your script or payload, use the execution or containerization tools
248 available in your environment to execute the code safely. Select the most
249 appropriate runtime image and flags for the target. **All compilation and
250 test execution commands MUST be run in a PRIVATE BUILD SHADOW, never with
251 Cwd=CODE_ROOT (the snapshot is read-only — Block A step 4).** Before
252 compiling, create `BUILD_ROOT=$(mktemp -d)` and copy CODE_ROOT into it (e.g.
253 `cp -a CODE_ROOT/. BUILD_ROOT/`); run all compilation/test commands with
254 Cwd=BUILD_ROOT; delete BUILD_ROOT on teardown. Keep the generated PoC file
255 itself under `state_root/workspace/reproducers/` (STATE-RELATIVE) and store
256 its ABSOLUTE path in `"run_command"`/`"reattack_run_command"`.
257
258 **Sanitizer compilation (C/C++ targets):** When the bug class is
259 memory-safety or undefined-behavior, compile with
260 `-fsanitize=address,undefined` (ASan + UBSan) at a minimum. For data races,
261 add `-fsanitize=thread` (TSan) — TSan is also mutually exclusive with ASan,
262 so use a separate build. Use `-fno-omit-frame-pointer` for usable stack
263 traces. These flags surface bugs that would otherwise exit 0 silently (UBSan
264 defaults to recover-mode). **MSan caveat:** MemorySanitizer
265 (`-fsanitize=memory`) detects uninitialized-memory reads, but it requires the
266 ENTIRE dependency chain (including libc and the C++ runtime) to be
267 MSan-instrumented — on an arbitrary target that is usually not the case, so a
268 naive `-fsanitize=memory` build yields bogus `WARNING: MemorySanitizer`
269 traces. MSan is also mutually exclusive with ASan in one build (use a
270 separate build, not an additive flag). If a fully instrumented MSan
271 environment cannot be guaranteed, do NOT add `-fsanitize=memory`; fall back
272 to ASan+UBSan rather than trusting a possibly-bogus MSan trace. Recognizing
273 MSan output as evidence (Step 5) is still correct when a legitimate MSan
274 instrumented build is available. **Sanitizer consistency guardrail:** the
275 SAME sanitizer flags MUST be used for the baseline, attack, and re-attack
276 runs — a different sanitizer can mask the bug and produce a false negative
277 (INV-2) or false `VERIFIED_SECURE` (INV-1). See Block G.
278
279 **{TARGET_ROOT} token substitution (numbered step):**
280
281 1. When writing `run_command` or `reattack_run_command`, use the literal
282 token `{TARGET_ROOT}` for any path that references the target tree.
283 2. At run time, reproduce substitutes `{TARGET_ROOT}` with the actual root:
284 - On first execution: `CODE_ROOT` (the snapshot or `--target_root`).
285 - On re-execution (re-attack or retry): the current `CODE_ROOT` /
286 `--target_root` / `active_snapshot.root`.
287 3. This ensures the stored command resolves correctly after snapshot GC —
288 never bake an absolute `.mantis_snapshots/pass_<N>` path into the stored
289 command.
290
291 **Execute your reproduction using the appropriate environment:** If the
292 target is firmware, you may write a script to boot it via `qemu`, `unicorn`,
293 or Firmadyne. If it's a binary, you may use dynamic instrumentation or
294 standard execution. Use your best judgment to construct a working harness for
295 the artifact.
296
297 - *Parallel Trajectory Search vs. Tiered Iterative Reproduction:*
298
299 - **Parallel Trajectory Search (Breadth-First):** When subagents are
300 available, deploy concurrent workers taking diverse logical approaches to
301 reproduce the bug. If any trajectory succeeds, immediately adopt its
302 payload and discard the others to escape potential "give up" loops and
303 prune compute costs.
304
305 - **Tiered Iterative Reproduction (Depth-First Payload Refinement):** Each
306 trajectory worker (or a single agent) uses a tiered escalation ladder
307 (Tier 1 -> Tier 2 -> Tier 3) to refine its trigger payload incrementally
308 rather than attempting a single-shot end-to-end launch.
309
310 - *Tiered Iterative Execution Ladder:*
311
312 - **Tier 1 (Micro-Harness / Sink Logic Validation):** Construct a
313 lightweight test calling the vulnerable function/module directly to
314 verify that the core bug hypothesis is sound in isolation.
315 - **Tier 2 (Subsystem / Interface Validation):** Pass the payload through
316 input serialization, parsers, routing, and auth wrappers to verify the
317 input survives intermediate processing without sanitization or
318 truncation.
319 - **Tier 3 (Full Sandboxed Service / E2E Validation):** Execute the
320 self-contained PoC against the target service via public APIs inside the
321 isolated sandbox (Docker, QEMU, VM). Yields the authoritative
322 `reproduced` verdict per Block F.
323
324 - **CRITICAL STEP-4 TIER-1 HARD GATE (Fail-Closed):**
325
326 - Tiers 1 and 2 are **internal stepping stones only**. You **MUST NEVER**
327 record `repro_status = "reproduced"` or `"statically_confirmed"` based on
328 a Tier-1 or Tier-2 execution.
329 - If a crash can **ONLY** be achieved by compiling a direct-call harness
330 that feeds a private/static function or bypasses the public API (Step 4),
331 and the payload cannot be escalated to trigger through Tier 3 (the public
332 API / sandboxed service), you **MUST TERMINATE AND RECORD**
333 `repro_status = "failed_to_reproduce"` with details citing
334 `"Internal Invariant Protection"`.
335
336 - *Attempt Cap Accounting & Local Retries:*
337
338 - **Sub-Tier-3 Stepping-Stone Sub-Budget:** Internal Tier-1 and Tier-2
339 trial runs are bounded local execution steps (max 3 trial executions per
340 conversation) and **DO NOT** increment the absolute per-finding attempt
341 counter in `state_root/workspace/archive/.repro_attempts.json`.
342 - **Absolute Attempt Cap Counting:** Only Tier-3 full sandboxed service
343 executions (or full end-to-end reproducer runs) increment the absolute
344 attempt counter toward the hard ceiling of 6 (Section 6).
345 - **Intra-Conversation Retries:** When a tier fails, inspect logs, adjust
346 payload parameters, fix harness setup, and retry up to 2-3 times within
347 the active conversation before reporting back to the orchestrator.
348
349### Step 3a: Variant Hunting (re-attack only, MANDATORY)
350
351When invoked with `--reattack`, you MUST author and execute **N ≥ 3
352boundary-mutated variant inputs** in addition to re-running the original PoC.
353The schema (`schema.json`) literally calls this the "variant-hunting re-attack"
354— merely re-running the original PoC is insufficient. Over-narrow patches that
355guard the exact PoC bytes are the dominant auto-repair failure mode; variant
356hunting is the zero-infra-cost defense against them.
357
358**Legacy findings:** Findings with absent `reattack_variants` (created before
359this rule existed) are re-verified under the ≥3 variant requirement on their
360first `--reattack` pass. This is intentional — legacy `VERIFIED_SECURE` verdicts
361are upgraded to the stricter gate — but expect one-time churn across archived
362findings on the first pass that runs variant hunting.
363
364**What to generate (bug-class-aware):**
365
366- **Memory-safety bugs (buffer overflow, OOB read/write, UAF, integer
367 overflow):** Author at least 3 of:
368
369 - **Off-by-one:** `len = bound`, `len = bound + 1`, `len = bound - 1`.
370 - **Size mutations:** `len ± 1`, `len = 0`, `len = SIZE_MAX`, sign flips on
371 signed lengths.
372 - **Alternate paths to the same sink:** If the vulnerable sink is reached via
373 multiple call sites, author a variant reaching it through a different path
374 (e.g., different API endpoint, file format variant, or protocol command).
375 - **Type confusion / width mismatch:** Exploit a different type path to the
376 same sink.
377
378- **Non-memory-safety bugs (logic, auth, injection, SSRF, path traversal):**
379 Hunt for actual variants of the same class:
380
381 - **Alternate endpoints/parameters:** Try `/api/v2/echo` when PoC targets
382 `/api/v1/echo`, or alternate parameter names.
383 - **Equivalent payloads:** `..%2fetc%2fpasswd`, `..\\..\\`, URL-encoding,
384 double encoding, unicode normalization variants.
385 - **Auth boundary variants:** Different roles, empty/null tokens, alternate
386 privilege-escalation paths.
387 - **Injection variants:** `'; EXEC--`, ` UNION SELECT`, blind variants,
388 alternate injection points.
389
390- **Parallel variant generation (if subagents available):** You SHOULD spawn
391 subagents to author and test variants in parallel. Each subagent gets one
392 mutation strategy, writes its variant PoC, and reports whether it triggered.
393 Aggregate all results before setting `reattack_status`. If subagents are
394 unavailable, do them sequentially.
395
396**Execution:** Write each variant as a separate script in
397`state_root/workspace/reproducers/` (e.g., `reattack_variant_[uuid]_[N].py`).
398Execute each against the patched shadow (`--target_root`) using the same
399isolation constraints as Step 3. Record each variant in the `reattack_variants`
400array using EXACTLY the schema keys
401`{"description": "...", "triggered": true/false}` (Step 6).
402
403**Verdict rule:** `reattack_status = "failed_to_bypass"` requires a non-empty
404`reattack_variants` array containing ≥ 3 valid variant inputs that ALL failed to
405trigger the bug on the patched shadow. An empty or short set makes "all variants
406failed" vacuously true — this is FORBIDDEN: if fewer than 3 meaningful variants
407can be constructed after genuine effort, cap at `VERIFICATION_INCOMPLETE`
408(history note `insufficient_variants`), NEVER `failed_to_bypass`. The `[]` case
409may ONLY coexist with a non-`failed_to_bypass` status (e.g., C5 baseline failure
410that halted before variant hunting).
411
412**Variant validity guardrail:** A variant counts as a bypass (`triggered = true`
413causing `bypassed_patch`) ONLY if it satisfies BOTH:
414
4151. **Same vulnerability class:** The variant reproduces the original bug class —
416 same sink function / sanitizer signature / crash type. A junk mutant (e.g.,
417 `len=SIZE_MAX` causing an unrelated OOM, an alternate endpoint 404-ing then
418 erroring, or a completely different crash) does NOT count as a bypass.
419 Discard it (set `triggered = false` with a description noting it was invalid)
420 and continue. For non-memory bugs (logic, auth, injection), compare the same
421 sink function or behavior (e.g., same unauthorized action succeeds, same
422 injection executes, reaches the same sink function) — NOT a ±line window
423 against a pre-patch line number (patches shift lines, and non-memory bugs
424 often have no precise sink line). If you cannot positively confirm a
425 triggering variant is a DIFFERENT bug, count it as a bypass (fail-closed:
426 prefer a false VERIFICATION_FAILED over a false VERIFIED_SECURE).
4272. **Valid input per Step 4:** The variant must be a valid exercise of the
428 public API or internal invariants — it must not rely on artificial harness
429 tricks (e.g., private-function direct calls with custom-allocated buffers)
430 that bypass the library's execution invariants.
431
432Ideally, confirm each triggering variant still triggers on the **unpatched**
433shadow (same baseline the original PoC ran against) to prove it exercises the
434original bug rather than an artifact. If the unpatched shadow is unavailable
435(e.g., snapshot mismatch), require the same-sink evidence (same sanitizer frame,
436same crash address, same logic failure) as corroboration.
437
438If ANY valid variant triggers the bug, set `reattack_status = "bypassed_patch"`
439and apply INV-1 (downgrade `VERIFIED_SECURE` → `VERIFICATION_FAILED`).
440
441- **Reproduction Status Classification:**
442
443 - **`reproduced`**: The PoC successfully triggered the vulnerability.
444 - **`failed_to_reproduce`**: The PoC was executed but did not trigger the
445 vulnerability.
446 - **`statically_confirmed`**: Reproduction was impossible due to environmental
447 constraints (e.g., missing hardware emulators, unavailable external
448 services) but the flaw is statically obvious (e.g., hardcoded credentials).
449 This is strongly discouraged and should only be used as a last resort.
450 - **`not_attempted`**: The reproduction stage was skipped entirely (e.g., due
451 to infrastructure setup failure, timeouts, or explicit skip configuration).
452
4534. **Strict Public-API & Internal Invariant Constraints:**
454
455 - Your crash reproducer should interact with the codebase through
456 public-facing APIs wherever possible, or strictly respect the library's
457 global execution invariants (such as allocator padding) to avoid generating
458 artificial, non-viable crashes.
459 - Do not declare a finding as "reproduced" if the crash can only be achieved
460 by compiling a direct-call harness that feeds a private/static function a
461 custom-allocated buffer (e.g., `malloc(15)`) that bypasses the library's
462 guaranteed allocator wrappers (e.g., `png_malloc(rowbytes + 48)`).
463 - If a crash cannot be triggered through the public API or with standard
464 allocation padding, classify the finding as `"failed_to_reproduce"` due to
465 "Internal Invariant Protection."
466
4675. **Functional & Crash-Aware Validation:** Analyze the output such as stdout,
468 stderr, and exit codes to classify reproduction success depending on the bug
469 class:
470
471 Before classifying ANY negative outcome (`failed_to_reproduce`, or in
472 `--reattack` mode `failed_to_bypass`), apply this gate:
473
474 ```
475 REACHED-SINK EVIDENCE GATE (mechanical):
476 Each reproducer produces REACHED-SINK EVIDENCE via ONE channel, recorded in repro_hints:
477 (a) script/source harness -> write the exact bytes MANTIS_REACHED_ENTRYPOINT to a
478 sidecar file $SENTINEL_FILE and flush+fsync (or unbuffered write) BEFORE
479 invoking the sink. (A file survives a crash that truncates buffered stdout.)
480 (b) binary / firmware / raw-payload -> reached-sink evidence is a captured
481 crash backtrace or sanitizer frame (ASan/UBSan/MSan/TSan) that
482 explicitly names the target sink function (target-produced tracing). A
483 marker written by a wrapper you author BEFORE invoking the target is
484 SETUP EVIDENCE ONLY: it proves "launch attempted," not "sink reached,"
485 and does NOT qualify as reached-sink evidence. If no in-path marker
486 (channel a) and no target-produced backtrace/sanitizer trace
487 (channel b) is achievable, the sink is unreached.
488 EVIDENCE PRESENT (reached-sink) = (channel a) sidecar file contains
489 MANTIS_REACHED_ENTRYPOINT written in-path, OR (channel b) target-produced
490 backtrace/sanitizer output names the sink. A wrapper pre-launch marker alone is
491 NOT evidence present.
492 EVIDENCE ABSENT includes: any compiler/build nonzero exit; exit 127 (command not
493 found); exit 2 with a "No such file" message.
494 DECISION GATE (gate the DECISION, not specific verdict strings):
495 - Record repro_status = reproduced OR statically_confirmed ONLY if EVIDENCE is
496 PRESENT. If ABSENT -> repro_status = not_attempted (retry-eligible), STOP.
497 - In patch verification, EVIDENCE is required on the UNPATCHED baseline (Block G),
498 NOT on the post-patch attack run (a correct patch legitimately stops the input
499 before the sink).
500 - If NO evidence channel is achievable for this target, downgrade to
501 not_attempted / VERIFICATION_INCOMPLETE. NEVER synthesize the marker.
502 ```
503
504 Concretely: if the run produced NO reached-sink evidence (build/setup error,
505 exit 127, "No such file", or the sink was never reached), record
506 `repro_status = not_attempted` (retry-eligible) — NEVER
507 `failed_to_reproduce`; and in `--reattack` mode leave `reattack_status` UNSET
508 with a history note "setup_failed" — NEVER `failed_to_bypass`. Only classify
509 a negative when the harness provably reached the vulnerable entrypoint and
510 the bug did not fire.
511
512 **HALT ceiling (3-state rule):** If `active_snapshot` is present in state but
513 `snapshot_pinned` is `false` (HALT mode — the tree raced or could not be
514 pinned), you MUST NOT record `failed_to_reproduce` or `failed_to_bypass` at
515 all. In HALT, the code may have drifted and a negative reproduction result
516 cannot be trusted as authoritative. Instead, record
517 `repro_status = not_attempted` (retry-eligible) and, in `--reattack` mode,
518 leave `reattack_status` UNSET with a history note "HALT mode: snapshot
519 unpinned, negative result suppressed". This mirrors the authoritative-verdict
520 prohibition that applies to all stages in HALT. (In MODE-OFF — no
521 `active_snapshot` — classify negatives normally as today.)
522
523 - **Logic & Authorization Bugs:** A successful reproducer is a functional
524 unit test or script that explicitly demonstrates the logic failure (e.g.,
525 an unauthorized request returns `200 OK`, or a test script successfully
526 bypasses validation and exits with `0`).
527 - **Memory Safety & Binary Crashes:** If the sandbox execution produces a
528 crash signal or sanitizer trace in stdout/stderr, mark the reproduction as
529 `"reproduced"`. **Scan stdout/stderr for sanitizer signatures regardless of
530 exit code** — UBSan defaults to recover-mode (exit 0), so an exit-0 run can
531 still contain a valid UBSan trace proving the bug fired. Check for:
532 - AddressSanitizer (ASan) error outputs (e.g. `ERROR: AddressSanitizer`).
533 - UndefinedBehaviorSanitizer (UBSan) runtime reports (e.g.
534 `runtime error:`, `SUMMARY: UndefinedBehaviorSanitizer`).
535 - MemorySanitizer (MSan) error outputs (e.g. `WARNING: MemorySanitizer`).
536 **MSan evidence is valid ONLY if `repro_hints` records that a
537 fully-instrumented MSan build was available** (Step 3 caveat). A naive
538 `-fsanitize=memory` build without full instrumentation produces bogus
539 traces — do NOT classify these as `"reproduced"`.
540 - ThreadSanitizer (TSan) race reports (e.g. `WARNING: ThreadSanitizer`).
541 - Segmentation faults (SIGSEGV, exit code `139`).
542 - Abort signals (SIGABRT, exit code `134`).
543 - Crash or core dumps.
544
5456. **Token-Optimized File Updates:** To minimize LLM output tokens, **do not
546 re-emit or manually rewrite the entire JSON object in your output.** Instead,
547 use in-place editing tools (like a short script in your preferred language,
548 or `jq`) to programmatically append the new fields to the existing
549 `state_root/workspace/findings/<id>.json` file.
550
551 Additionally, you must **Update the Reproduction Attempt Cache** to help the
552 planner track attempts efficiently:
553
554 - Maintain a JSON cache file at
555 `state_root/workspace/archive/.repro_attempts.json`. Ensure the parent
556 directory `state_root/workspace/archive/` exists (e.g.,
557 `mkdir -p state_root/workspace/archive/`) before creating, reading, or
558 locking the cache file.
559 - Key the cache by a stable identifier that persists across loop runs even if
560 UUIDs are regenerated. If the finding has a `signature` field, use it
561 directly as the cache key (it is already a deterministic content-identity
562 hash). If `signature` is absent, fall back to a computed stable key using
563 the finding's normalized title and its primary file path:
564 `stable_key = normalized_title + "@" + primary_file_path`.
565 - Compute `normalized_title` by converting the title to lowercase and
566 removing all non-alphanumeric characters.
567 - Compute `primary_file_path` by taking the first entry in `code_paths` and
568 stripping any line number suffixes (e.g., converting `src/auth.c:120` to
569 `src/auth.c`).
570 - **Snapshot-aware cap (value shape `{count, last_snapshot}`):** store each
571 cache value as an object `{count, last_snapshot}`. When reading a value
572 V: if V is a bare integer, treat count=V and last_snapshot=UNKNOWN; if V
573 is an object, use V.count / V.last_snapshot (default UNKNOWN). Before
574 incrementing, run Block B comparing the finding's snapshot to the current
575 SNAPSHOT_ID: reset `count=0` ONLY when Block B is NOT_MATCHED *because
576 the two snapshots are present and actually differ* (a genuine code change
577 earns a fresh budget). Do NOT reset on UNKNOWN (absent/`pass_`/unpinned)
578 — that would make no-VCS targets retry forever. Additionally keep an
579 absolute per-finding-id attempt counter that is NEVER reset, and stop
580 retrying once it reaches a hard ceiling (e.g. 6) regardless of snapshot
581 changes.
582 - To prevent race conditions during concurrent executions (including locking
583 bypasses caused by atomic file replacement) and protect lockless readers:
584 - Use a separate dedicated lock file
585 `state_root/workspace/archive/.repro_attempts.lock` which is never
586 deleted or replaced.
587 - Perform updates atomically using Python's `fcntl.flock` on this lock
588 file:
589 * Open the lock file `state_root/workspace/archive/.repro_attempts.lock`
590 (creating it if missing) and acquire an exclusive lock (`fcntl.flock`
591 with `fcntl.LOCK_EX`) inside a context manager (`with` statement).
592
593 * Read the current contents of the cache file
594 `state_root/workspace/archive/.repro_attempts.json` (treating it as `{}`
595 if missing or empty).
596
597 * Increment the `count` field of this finding's cache-key entry — keyed by
598 `signature` if present, else `stable_key`, the SAME key selection defined
599 above (the `{count, last_snapshot}` object) — by 1 ONLY for Tier-3 (full
600 end-to-end sandboxed service) executions. Internal Tier-1 and Tier-2
601 stepping-stone trials MUST NOT increment `count` (they are governed by
602 the sub-budget rule in Step 3).
603
604 * Write the updated JSON to a temporary file in the same directory (e.g.,
605 `state_root/workspace/archive/.repro_attempts.json.tmp`).
606
607 * Atomically replace the target cache file with the temporary file (e.g.,
608 `os.replace` in Python) to ensure readers never see a truncated or
609 incomplete file.
610
611 * Close the lock file descriptor to release the lock (automatically handled
612 by exiting the `with` context manager).
613
614 Depending on whether the `--reattack` flag is provided:
615
616 - **If run normally (no `--reattack` flag):** You must append or update the
617 following on the existing object:
618
619 - `"repro_status"` (`"reproduced"`, `"statically_confirmed"`,
620 `"not_attempted"`, or `"failed_to_reproduce"`).
621
622 - `"repro_file_path"`
623
624 - `"run_command"`
625
626 - `"repro_output"`
627
628 - `"repro_snapshot_id"`: the current SNAPSHOT_ID this run executed against.
629
630 - `"repro_hints"`: Record compilation and sandbox execution telemetry
631 (e.g., `sanitizers_used: ASan+UBSan`, `assertions_disabled: true`,
632 `build_profile: release`) to provide empirical execution evidence for
633 `/mantis-critic`.
634
635 - If reproduction succeeds (`repro_status` is evaluated as `"reproduced"`
636 or `"statically_confirmed"`) and the finding's current `"status"` is
637 `"PROVISIONALLY_VALID"`: BEFORE upgrading, scan the finding's
638 `triage_checklist` (if present). If ANY entry has `outcome == "UNKNOWN"`
639 (or `passes == false`), do NOT upgrade: leave `status` as
640 `"PROVISIONALLY_VALID"`, still set `repro_status` to the success value
641 (reproduction DID succeed), and append a history note
642 `upgrade-to-VALID-blocked: triage_checklist has UNKNOWN entries (re-review required)`.
643 This avoids violating the schema's `VALID ⇒ no UNKNOWN` allOf gate
644 (schema.json lines 471-507), which forbids `UNKNOWN`/`passes:false` on
645 any `VALID` non-chain finding's `triage_checklist`. Reproduce does NOT
646 touch `triage_checklist` entries (the checklist is review's artifact;
647 only review may resolve `UNKNOWN` entries). If `triage_checklist` is
648 absent (no `reviewer` history entry, e.g. a legacy finding), or NO entry
649 is `UNKNOWN`/`passes:false`, you **must** update `"status"` to `"VALID"`.
650
651 - An entry to the `"history"` array:
652
653 ```json
654 {
655 "stage": "reproduce",
656 "action": "reproduced",
657 "details": "Reproduction status evaluated as [reproduced/failed_to_reproduce] using command: [run_command]",
658 "pass_number": <current_pass_number>,
659 "timestamp": "<current_iso8601_timestamp>"
660 }
661 ```
662
663 - **If run with `--reattack`:** You must append or update the following on
664 the existing object (do not touch `repro_*` or `status`):
665
666 - `"reattack_status"` (`"bypassed_patch"`, `"failed_to_bypass"`,
667 `"inconclusive_baseline_changed"`).
668
669 - `"bypassed_patch"`: The PoC bypassed the patch and triggered the bug. If
670 `patch_status` is `VERIFIED_SECURE`, atomically set
671 `patch_status = "VERIFICATION_FAILED"` in the same write (a bypass
672 defeats the patch). This is an explicit exception to "do not touch
673 status" and applies to both the C5 step-3 path and same-snapshot runs.
674
675 - `"failed_to_bypass"`: The PoC was run but failed to bypass the patch. (No
676 downgrade needed — `failed_to_bypass` is the value the allOf gate
677 requires for `VERIFIED_SECURE`.)
678
679 - `"inconclusive_baseline_changed"`: The unpatched baseline was re-run (see
680 C5 below) and the bug NO LONGER TRIGGERS on the current unpatched code.
681 Do NOT claim `failed_to_bypass`. If `patch_status` is `VERIFIED_SECURE`,
682 atomically set `patch_status = "VERIFICATION_INCOMPLETE"` in the same
683 write.
684
685 - **INV-1 (single source — the schema's `VERIFIED_SECURE` allOf gate):**
686 `VERIFIED_SECURE => reattack_status` must be `failed_to_bypass`, and
687 `failed_to_bypass` requires a non-empty `reattack_variants` array with ≥
688 3 valid variant inputs (Step 3a) that ALL failed to trigger the bug on
689 the patched shadow. An empty or short variant set makes "all failed"
690 vacuously true — this is FORBIDDEN: cap at `VERIFICATION_INCOMPLETE`
691 instead. Any other outcome MUST atomically downgrade `
692
693…(truncated)