mantis-dedupe
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDeduplicator (/mantis-dedupe)
Deduplicator (/mantis-dedupe)
System Goal
系统目标
Duplicate Finding Merger. Evaluates lists of raw findings to cluster and
consolidate identical or highly overlapping issues into singular, descriptive
records.
重复检测结果合并工具。评估原始检测结果列表,将相同或高度重叠的问题聚类并合并为单一、描述清晰的记录。
Command Definition
命令定义
- Command:
/mantis-dedupe - Description: Consolidates raw security findings to eliminate redundant reports.
- Arguments (optional; supplied by the orchestrator, consumed by Block A):
/
--snapshot_root/--snapshot_id. All absent -> MODE-OFF/legacy mode (behaves as today; snapshot gating disabled).--state_root
- 命令:
/mantis-dedupe - 描述: 合并原始安全检测结果,消除冗余报告。
- 参数(可选;由编排器提供,供Block A使用):
/
--snapshot_root/--snapshot_id。若全部缺失则进入MODE-OFF/传统模式(与当前行为一致;禁用快照门控)。--state_root
Input/Output Contract
输入/输出约定
- Reads:
- (raw finding JSON files, ignoring
workspace/findings/)..trash/ - and
workspace/archive/findings_pass_*/*.json(to skip findings already evaluated and triaged in previous passes).workspace/archive/loop*_findings/*.json - (to track current loop pass).
workspace/.mantis_state.json
- Writes:
- Moves duplicate findings to after setting
workspace/findings/.trash/and"status": "DUPLICATE"."duplicate_of" - Sets (soft, non-terminal) on NOT_MATCHED matches and stamps
"possible_duplicate_of"on current findings that lack it. Reads"discovery_commit"/active_snapshotfromsnapshot_pinned..mantis_state.json - Appends transaction logs to .
workspace/.tx_log.jsonl - Generates/executes merging script .
workspace/helpers/merge_findings.py - Updates primary finding (merges fields and history).
workspace/findings/<primary_id>.json
- Moves duplicate findings to
- Preconditions:
- must exist and contain finding files.
workspace/findings/
- Idempotency Guarantee:
- Cross-references against archived findings in to filter out any findings already processed in previous passes of this run. Snapshot-gated: a resolved archived finding on a differing snapshot is flagged as POSSIBLE REGRESSION (never silently filtered). Logs transactions to
workspace/archive/to support tracking and potential rollbacks. Deterministic merging rules implemented inworkspace/.tx_log.jsonl.merge_findings.py
- Cross-references against archived findings in
- 读取:
- (原始检测结果JSON文件,忽略
workspace/findings/目录)。.trash/ - 和
workspace/archive/findings_pass_*/*.json(用于跳过之前轮次中已评估和分类的检测结果)。workspace/archive/loop*_findings/*.json - (用于跟踪当前循环轮次)。
workspace/.mantis_state.json
- 写入:
- 将重复检测结果设置和
"status": "DUPLICATE"字段后,移动至"duplicate_of"目录。workspace/findings/.trash/ - 对未匹配的结果设置(软标记,非终结性),并为缺少
"possible_duplicate_of"字段的当前检测结果添加该字段。从"discovery_commit"读取.mantis_state.json/active_snapshot信息。snapshot_pinned - 向追加事务日志。
workspace/.tx_log.jsonl - 生成并执行合并脚本。
workspace/helpers/merge_findings.py - 更新主检测结果文件(合并字段和历史记录)。
workspace/findings/<primary_id>.json
- 将重复检测结果设置
- 前置条件:
- 目录必须存在且包含检测结果文件。
workspace/findings/
- 幂等性保证:
- 与中的归档检测结果进行交叉引用,过滤掉本次运行中之前轮次已处理的检测结果。 快照门控:不同快照上已解决的归档检测结果会被标记为POSSIBLE REGRESSION(不会被静默过滤)。将事务记录到
workspace/archive/以支持跟踪和潜在回滚。 在workspace/.tx_log.jsonl中实现确定性合并规则。merge_findings.py
- 与
Instructions
操作说明
Step 0: Locator Resolution (run first)
步骤0:定位器解析(首先执行)
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 perScenario 2): ifmantis-pipeline-adapteris present ANDactive_snapshot, treat the snapshot as STALE for this pass — STOP "stale active_snapshot: pass mismatch" or degrade as HALT (active_snapshot.pass != state.pass_numbereffectively false: no authoritative verdicts, Block B NOT_MATCHED, reproducesnapshot_pinned). This catches a custom harness that preservednot_attemptedacross 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 isactive_snapshot-only, notsnapshot_id-aware).pass
Notes: , , ,
and are STATE-RELATIVE (under
--state_root). Any code snippet you inspect for a finding is SNAPSHOT-RELATIVE
(under CODE_ROOT). Never write under CODE_ROOT.
workspace/findings/workspace/archive/workspace/.tx_log.jsonlworkspace/helpers/.mantis_state.jsonReview a list of security findings and merge duplicate findings that refer to
the exact same security flaw or adjacent code paths.
Execute your task as follows:
-
Load Raw Findings & Archived Findings Queue:
- List the contents of the directory and read the files in
. If the directory is empty or does not exist, notify the user and exit.
workspace/findings/ - Important: Ignore hidden files and directories (such as the subdirectory) when listing or processing findings.
.trash/ - Locate and load all archived finding JSON files from previous loop passes,
if they exist, under and
workspace/archive/findings_pass_*/*.json. These files represent vulnerabilities that have already been fully evaluated, triaged, and potentially patched in previous passes.workspace/archive/loop*_findings/*.json - Important: Do NOT read or deduplicate against
(VCS history), as we want to catch regressions if old bugs were reintroduced.
workspace/historical_learnings.jsonl
- List the contents of the directory and read the files in
-
Filter Loop Duplicates (snapshot-gated). First, stampon any CURRENT finding that lacks it, using
discovery_commitfromactive_snapshot.snapshot_id(skip when unpinned). Then, for each current finding that matches an archived finding (by.mantis_state.json+code_pathssimilarity), run:titleSignature-based candidate matching (Phase 3) — TIGHTENS, never replaces:may only PROMOTE a pair to "candidate for the pairwise snapshot check"; it may NEVER by itself cause a hard DUPLICATE/trash. A pair is a candidate for the Pairwise Snapshot Match Check below ONLY if it satisfies BOTH:signature- it matches under today's +
code_pathssimilarity, comparingtitleentries line-inclusively (WITH their trailingcode_paths); AND:line - (when both findings have a ) their
signaturefields are equal. Asignaturematch WITHOUT thesignature+code_pathsagreement is NOT a duplicate — at most a softtitle(keep the finding ACTIVE), never a trash. Rationale:possible_duplicate_ofstrips the line number and all-but-firstsignatureentry, so two DISTINCT bugs in the same file (e.g.code_pathsvsparser.c:100) with the same title+CWE share oneparser.c:900; trashing on signature alone would silently delete a real finding. If EITHER lackssignature, use today'ssignature+code_pathssimilarity matching unchanged.title
PAIRWISE SNAPSHOT MATCH CHECK (decides MATCHED vs NOT_MATCHED) — compares the CURRENT finding'sagainst the ARCHIVED finding'sdiscovery_commitfor this pair (NOT againstdiscovery_commit):SNAPSHOT_ID- If is false AND there is NO
snapshot_pinnedin state (MODE-OFF) -> NOT_MATCHED. Stop. (In HALT —active_snapshotpresent butactive_snapshot— do NOT short-circuit here; fall through to the pairwise comparison below, which will be NOT_MATCHED because the current finding'ssnapshot_pinned=falseis adiscovery_commitid that will not equal the archived one.)live: - Read the CURRENT finding's and the ARCHIVED finding's
discovery_commit:discovery_commit- If EITHER is missing, empty, or the literal -> NOT_MATCHED.
"MIXED" - If they are NOT byte-for-byte equal to each other -> NOT_MATCHED.
- If they ARE byte-for-byte equal to each other (both present, non-MIXED)
-> 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 : 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.) Note: this is a PAIRWISE check (current vs archived), NOT a check against the global
discovery_commit— dedupe stamps the current finding'sSNAPSHOT_IDtodiscovery_commitin Step 2 above, so a check againstSNAPSHOT_IDwould always be MATCHED and would trash reintroduced/regression bugs as DUPLICATE.SNAPSHOT_ID
- If EITHER is missing, empty, or the literal
Then, using the idempotency rule (Input/Output Contract → Idempotency Guarantee) to avoid double-writes, decide mechanically:- MATCHED (both present and equal): soft-delete the current finding as a
loop-duplicate exactly as before — set and
"status": "DUPLICATE", clear"duplicate_of": "<archived_uuid>"if present, ensurepossible_duplicate_of, move it there, and log amkdir -p workspace/findings/.trash/transaction inloop_filter. If the current finding lacksworkspace/.tx_log.jsonlbut the archived finding has one, inherit the archived finding'slineage_idonto the current finding before moving it (so the lineage chain is preserved across the merge).lineage_id - NOT_MATCHED (differ, or either absent): do NOT set and do NOT move to trash. Keep the current finding ACTIVE and set
DUPLICATE(a soft, non-terminal hint). If the current finding lacks"possible_duplicate_of": "<archived_uuid>"but the archived finding has one, inherit the archived finding'slineage_idonto the current finding (so the lineage chain is preserved for report folding even when the findings are on different snapshots).lineage_id
[!IMPORTANT] STATUS & DUPLICATE INVARIANTS:- A finding MUST NOT carry both and
duplicate_ofpointing to the same target UUID.possible_duplicate_of - MUST NOT coexist with
status = "DUPLICATE"pointing to the same target UUID.possible_duplicate_of - Under NOT_MATCHED (outside the MODE-OFF fallback exception), the
finding's MUST remain active (e.g.
status,VALID,PROVISIONALLY_VALID),NEEDS_RESEARCHMUST NOT be set, and the finding MUST NOT be moved toduplicate_of. Setting.trash/is a non-terminal hint only.possible_duplicate_of
- POSSIBLE REGRESSION: if the archived match has a RESOLVED status
(in {
patch_status,VERIFIED_SECURE} ORMITIGATION_PROPOSED==statusORFALSE_POSITIVE==production_viability) AND the pair is NOT MATCHED, keep the current finding ACTIVE, add a history note "POSSIBLE REGRESSION vs <archived_uuid>", and do NOT filter it. (A reverted fix re-discovered on new code must never be trashed.)NON_VIABLE - EXACT-UUID retry exception (unchanged): if the current finding has the EXACT SAME UUID as the archived one, it was intentionally copied back for a retry — do NOT filter it (keep as-is).
- Permanently-unpinned exception (MODE-OFF only — no ): if there is NO
active_snapshotin state (MODE-OFF = today's default; a target with no snapshot boundary, e.g. a live endpoint),active_snapshotis false at the PASS level (snapshot_pinned— it is NOT a per-finding field) and Block B is uninformative — fall back to today's dedup byactive_snapshot.snapshot_pinnedif present, elsesignature= normalized_title + firststable_keyentry including its trailingcode_paths(line-inclusive, same as Step 2). Rationale: stripping:linewould collapse two DISTINCT bugs in the same file (e.g.:linevsparser.c:100) with the same title+CWE into one — silently deleting a real finding. Note:parser.c:900itself stripssignatureby design (it is a coarse identity for cross-pass lineage, not a dedup key); this fallback therefore prefers:lineonly whensignature's line-inclusive match ALSO agrees, never onstable_keyalone. This preserves dedup for targets that can never MATCH. WhensignatureIS present but unpinned (HALT mode), this exception does NOT fire: keep the snapshot-gated behavior above (NOT_MATCHED → keep ACTIVE +active_snapshot, neverpossible_duplicate_of). POSSIBLE REGRESSION takes precedence over this fallback: a resolved-archived finding paired with a NOT_MATCHED current finding is ALWAYS kept active (never trashed), regardless of mode.DUPLICATE
- it matches under today's
-
Filter Duplicate Findings in Current Batch: Check the current findings against each other to find duplicates. Two findings are duplicates ONLY if they share the sameentry line-inclusively (WITH trailing
code_paths) AND have the same or highly similar title. If multiple findings refer to the exact same flaw at the same location, they must be merged. Findings at different lines in the same file are DISTINCT — never merge them.:line -
Map/Reduce Chunking Strategy (For Scale): If there are many finding files (e.g., > 20 items), use a Map/Reduce approach to group them by target file or component before checking for overlaps to avoid context window limits.
-
Token-Optimized Consolidation and Merging: To minimize LLM output tokens and prevent data loss, do not manually rewrite or output the merged JSON files in your response. Instead, follow this pattern:
- Identify Duplicates: Internally map which findings are duplicates of a primary finding.
- Reusable Deterministic Scripting (versioned): Write a reusable helper
script (e.g. ) whose FIRST LINE is exactly
workspace/helpers/merge_findings.py. Before reusing an existing helper, grep its first lines for# MANTIS_HELPER_VERSION = 2; if that marker is absent or a different integer (a helper left by an older pipeline version), REGENERATE the helper. Only reuse it when the marker matches. The script must follow these deterministic rules:MANTIS_HELPER_VERSION = 2- Title: Pick the most comprehensive and descriptive title.
- ID: Preserve the unique of the primary finding being kept.
"id" - Severity: Pick the highest severity level specified among the merged items.
- Privileges Required: Inherit the most severe privilege requirement
(priority: >
NONE>LOW).HIGH - Attacker Position: Inherit the most critical position requirement
(priority: >
EXTERNAL>INTERNAL_NETWORK>IN_CLUSTER>LOCAL>HOST_SYSTEM>SUPPLY_CHAIN>PHYSICAL_TEMPORARY).PHYSICAL_LONG_TERM - User Interaction: Inherit the most severe user interaction
requirement (priority: >
NONE).REQUIRED - Code Paths: Collect and deduplicate all file paths and line numbers into a single unique array.
- Description, Mitigation, & Impact: Concatenate cleanly.
- History: Concatenate and preserve all entries from the merged findings. Append a new entry to the
"history"array for this merge action conforming to the schema (containing"history","stage": "dedupe","action": "merge","details": "Merged duplicate findings: [comma-separated-ids]", and"pass_number": <current_pass_number>)."timestamp": "<current_iso8601_timestamp>" - Unknown keys & provenance (MANDATORY): The script MUST copy through
EVERY key it does not explicitly handle (including ,
discovery_commit,repro_snapshot_id,patch_base_snapshot,possible_duplicate_of,signature,lineage_id) from the primary finding onto the merged object — never drop unknown fields. It MUST REFUSE to merge two findings whosecwevalues differ (they describe different code versions); leave them separate and log the refusal. When merging findings that all share onediscovery_commit, preserve it unchanged.discovery_commit
- Execute the Script: Run your script to update the primary finding's
file () on disk.
workspace/findings/<primary_id>.json
-
Transactional Staged Clean Up: Do not permanently delete redundant files. Ensure the trash directory exists (e.g.,). Before moving, the script must update the duplicate finding files, setting
mkdir -p workspace/findings/.trash/and"status": "DUPLICATE". Move the merged duplicate"duplicate_of": "<primary_uuid>"files to the trash staging directory (.json). For every file moved, append a transaction record toworkspace/findings/.trash/.workspace/.tx_log.jsonlTransaction Log Schema Format (
)workspace/.tx_log.jsonlEach line must be a self-contained JSON object documenting the transaction:json{"timestamp": "2026-07-14T15:13:00Z", "action": "loop_filter | dedupe_merge", "primary_uuid": "[UUID] (or null for loop_filter)", "moved_uuid": "[UUID]"}This cleans up the directory for downstream stages while preserving rollback capability.
When complete, notify the user.
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 perScenario 2): ifmantis-pipeline-adapteris present ANDactive_snapshot, treat the snapshot as STALE for this pass — STOP "stale active_snapshot: pass mismatch" or degrade as HALT (active_snapshot.pass != state.pass_numbereffectively false: no authoritative verdicts, Block B NOT_MATCHED, reproducesnapshot_pinned). This catches a custom harness that preservednot_attemptedacross 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 isactive_snapshot-only, notsnapshot_id-aware).pass
注意:、、、
和 均为STATE-RELATIVE(位于下)。为检测结果检查的任何代码片段均为SNAPSHOT-RELATIVE(位于CODE_ROOT下)。请勿在CODE_ROOT下写入内容。
workspace/findings/workspace/archive/workspace/.tx_log.jsonlworkspace/helpers/.mantis_state.json--state_root审核安全检测结果列表,合并指向完全相同安全漏洞或相邻代码路径的重复检测结果。
按以下步骤执行任务:
-
加载原始检测结果与归档检测结果队列:
- 列出目录内容并读取中的文件。若目录为空或不存在,通知用户并退出。
workspace/findings/ - 重要提示: 列出或处理检测结果时,忽略隐藏文件和目录(如子目录)。
.trash/ - 定位并加载之前循环轮次中所有已归档的检测结果JSON文件(若存在),路径为和
workspace/archive/findings_pass_*/*.json。这些文件代表之前轮次中已完全评估、分类并可能已修复的漏洞。workspace/archive/loop*_findings/*.json - 重要提示: 请勿读取或针对(版本控制系统历史)进行去重,因为我们需要捕获旧漏洞重新引入的回归情况。
workspace/historical_learnings.jsonl
- 列出目录内容并读取
-
过滤循环重复项(快照门控)。 首先,使用中的
.mantis_state.json为所有缺少active_snapshot.snapshot_id字段的当前检测结果添加该字段(未固定快照时跳过)。然后,对于每个与归档检测结果匹配(通过discovery_commit+code_paths相似度)的当前检测结果,执行:title基于签名的候选匹配(阶段3)——仅收紧规则,绝不替代:仅可将配对提升为“候选以进行成对快照检查”;绝不能仅通过signature直接标记为硬DUPLICATE/移入回收站。仅当同时满足以下两个条件时,配对才符合下方成对快照匹配检查的候选要求:signature- 符合当前的+
code_paths相似度匹配规则,对比title条目时包含行号(包括末尾的code_paths);并且:line - (当两个检测结果均包含字段时)它们的
signature字段完全相等。仅signature匹配但signature+code_paths不匹配的情况不属于重复——最多标记为软title(保持检测结果为ACTIVE状态),绝不移入回收站。理由:possible_duplicate_of会去除行号和除第一个外的所有signature条目,因此同一文件中两个不同的漏洞(如code_pathsvsparser.c:100)若标题和CWE相同,会共享同一个parser.c:900;仅基于签名去重会静默删除真实的检测结果。若任一检测结果缺少signature,则直接使用当前的signature+code_paths相似度匹配规则。title
成对快照匹配检查(决定MATCHED vs NOT_MATCHED)——对比该配对中当前检测结果的与归档检测结果的discovery_commit(而非与全局discovery_commit对比):SNAPSHOT_ID- 若为false且状态中无
snapshot_pinned(MODE-OFF)→ NOT_MATCHED。停止操作。(在HALT模式下——存在active_snapshot但active_snapshot——请勿在此处短路;继续执行下方的成对对比,由于当前检测结果的snapshot_pinned=false为discovery_commit格式的ID,与归档检测结果的ID不相等,因此结果为NOT_MATCHED。)live: - 读取当前检测结果的和归档检测结果的
discovery_commit:discovery_commit- 若任一字段缺失、为空或为字面量→ NOT_MATCHED。
"MIXED" - 若两者并非逐字节完全相等 → NOT_MATCHED。
- 若两者逐字节完全相等(均存在且非MIXED)→ MATCHED。没有其他途径可判定为MATCHED;绝不进行模糊对比。全局“默认字段并继续”的向后兼容规则不适用于:缺失即判定为NOT_MATCHED。(没有单独的“脏数据”门控:脏树的SNAPSHOT_ID已嵌入工作树内容哈希,因此同轮次的检测结果会匹配,跨轮次的裸提交检测结果则不会匹配。)注意:这是成对检查(当前 vs 归档),而非与全局
discovery_commit的检查——去重步骤会在步骤2中将当前检测结果的SNAPSHOT_ID标记为discovery_commit,因此与SNAPSHOT_ID的检查始终会判定为MATCHED,从而将重新引入/回归的漏洞误标记为DUPLICATE并删除。SNAPSHOT_ID
- 若任一字段缺失、为空或为字面量
然后,使用幂等性规则(输入/输出约定 → 幂等性保证)避免重复写入,机械性地做出判定:- MATCHED(两者均存在且相等):将当前检测结果作为循环重复项进行软删除——设置和
"status": "DUPLICATE",若存在"duplicate_of": "<archived_uuid>"则清除该字段,确保possible_duplicate_of目录存在,将文件移至该目录,并在mkdir -p workspace/findings/.trash/中记录workspace/.tx_log.jsonl事务。若当前检测结果缺少loop_filter但归档检测结果包含该字段,在移动前将归档检测结果的lineage_id继承到当前检测结果中(以保留合并后的 lineage 链)。lineage_id - NOT_MATCHED(两者不同,或任一缺失):不设置标记,不移入回收站。保持当前检测结果为ACTIVE状态,并设置
DUPLICATE(软标记,仅作为提示)。若当前检测结果缺少"possible_duplicate_of": "<archived_uuid>"但归档检测结果包含该字段,将归档检测结果的lineage_id继承到当前检测结果中(以便在检测结果位于不同快照时,仍能保留 lineage 链用于报告折叠)。lineage_id
[!IMPORTANT] 状态与重复项不变量:- 检测结果不得同时包含指向同一目标UUID的和
duplicate_of字段。possible_duplicate_of - 不得与指向同一目标UUID的
status = "DUPLICATE"字段共存。possible_duplicate_of - 在NOT_MATCHED情况下(除MODE-OFF回退例外),检测结果的必须保持活跃状态(如
status、VALID、PROVISIONALLY_VALID),不得设置NEEDS_RESEARCH,且不得移入duplicate_of目录。设置.trash/仅作为非终结性提示。possible_duplicate_of
- POSSIBLE REGRESSION: 若归档匹配项的状态为已解决(属于{
patch_status,VERIFIED_SECURE} 或MITIGATION_PROPOSED==status或FALSE_POSITIVE==production_viability)且配对结果为NOT_MATCHED,保持当前检测结果为ACTIVE状态,添加历史记录备注"POSSIBLE REGRESSION vs <archived_uuid>",且不得过滤该结果。(新代码中重新发现的已回滚修复绝不能被删除。)NON_VIABLE - 精确UUID重试例外(保持不变): 若当前检测结果的UUID与归档检测结果完全相同,说明是有意复制回来进行重试——请勿过滤该结果(保持原样)。
- 永久未固定例外(仅MODE-OFF模式——无): 若状态中无
active_snapshot(MODE-OFF = 当前默认模式;无快照边界的目标,如实时端点),active_snapshot在PASS级别为false(snapshot_pinned——这并非每个检测结果的字段)且Block B无有效信息——回退到当前基于active_snapshot.snapshot_pinned的去重规则(若存在signature),否则使用signature= 标准化标题 + 第一个stable_key条目包括末尾的code_paths(包含行号,与步骤2相同)。理由:去除:line会将同一文件中两个不同的漏洞(如:linevsparser.c:100)若标题和CWE相同则合并为一个——静默删除真实的检测结果。注意:parser.c:900本身设计为去除signature(用于跨轮次lineage的粗粒度标识,而非去重键);因此仅当:line的包含行号的匹配也一致时,才优先使用stable_key,绝不单独基于signature去重。这为永远无法MATCH的目标保留了去重功能。当存在signature但未固定(HALT模式)时,该例外不生效:保持上述快照门控行为(NOT_MATCHED → 保持ACTIVE +active_snapshot,绝不标记为possible_duplicate_of)。 POSSIBLE REGRESSION优先于此回退规则: 已解决的归档检测结果与NOT_MATCHED的当前检测结果配对时,始终保持活跃状态(绝不删除),无论处于何种模式。DUPLICATE
- 符合当前的
-
过滤当前批次中的重复检测结果: 将当前检测结果两两对比,找出重复项。仅当两个检测结果包含完全相同的条目包含行号(包括末尾的
code_paths)且标题相同或高度相似时,才判定为重复项。若多个检测结果指向同一位置的完全相同漏洞,必须进行合并。同一文件中不同行的检测结果为不同项——绝不能合并。:line -
Map/Reduce分块策略(针对大规模场景): 若检测结果文件数量较多(如超过20个),使用Map/Reduce方法按目标文件或组件分组,再检查重叠情况,避免超出上下文窗口限制。
-
优化Token的合并与整合: 为最小化LLM输出Token并防止数据丢失,请勿在响应中手动重写或输出合并后的JSON文件。请遵循以下流程:
- 识别重复项: 在内部映射哪些检测结果是主检测结果的重复项。
- 可复用的确定性脚本(版本化): 编写可复用的辅助脚本(如),其第一行必须为
workspace/helpers/merge_findings.py。复用现有辅助脚本前,检查其首行是否包含# MANTIS_HELPER_VERSION = 2;若标记缺失或为不同整数(由旧版本流水线留下的脚本),则重新生成辅助脚本。仅当标记匹配时才可复用。脚本必须遵循以下确定性规则:MANTIS_HELPER_VERSION = 2- 标题: 选择最全面、描述最清晰的标题。
- ID: 保留要保留的主检测结果的唯一。
"id" - 严重程度: 选择合并项中最高的严重级别。
- 所需权限: 继承最严格的权限要求(优先级:>
NONE>LOW)。HIGH - 攻击者位置: 继承最关键的位置要求(优先级:>
EXTERNAL>INTERNAL_NETWORK>IN_CLUSTER>LOCAL>HOST_SYSTEM>SUPPLY_CHAIN>PHYSICAL_TEMPORARY)。PHYSICAL_LONG_TERM - 用户交互: 继承最严格的用户交互要求(优先级:>
NONE)。REQUIRED - 代码路径: 收集并去重所有文件路径和行号,合并为单一唯一数组。
- 描述、缓解措施与影响: 清晰地拼接内容。
- 历史记录: 拼接并保留所有合并检测结果的条目。在
"history"数组中添加一条新的合并操作记录,符合以下模式(包含"history"、"stage": "dedupe"、"action": "merge"、"details": "Merged duplicate findings: [逗号分隔的ID列表]"和"pass_number": <当前轮次编号>)。"timestamp": "<当前ISO8601时间戳>" - 未知键与来源(必填): 脚本必须将主检测结果中所有未明确处理的键(包括、
discovery_commit、repro_snapshot_id、patch_base_snapshot、possible_duplicate_of、signature、lineage_id)复制到合并后的对象中——绝不丢弃未知字段。若两个检测结果的cwe值不同(描述不同的代码版本),脚本必须拒绝合并;保留它们并记录拒绝操作。合并所有共享同一discovery_commit的检测结果时,保持该字段不变。discovery_commit
- 执行脚本: 运行脚本以更新磁盘上的主检测结果文件()。
workspace/findings/<primary_id>.json
-
事务性阶段清理: 请勿永久删除冗余文件。确保回收站目录存在(如)。移动前,脚本必须更新重复检测结果文件,设置
mkdir -p workspace/findings/.trash/和"status": "DUPLICATE"。将合并后的重复"duplicate_of": "<primary_uuid>"文件移至回收站暂存目录(.json)。对于每个移动的文件,向workspace/findings/.trash/追加一条事务记录。workspace/.tx_log.jsonl事务日志格式(
)workspace/.tx_log.jsonl每一行必须是一个独立的JSON对象,记录事务信息:json{"timestamp": "2026-07-14T15:13:00Z", "action": "loop_filter | dedupe_merge", "primary_uuid": "[UUID] (or null for loop_filter)", "moved_uuid": "[UUID]"}此操作可为下游阶段清理目录,同时保留回滚能力。
完成后,通知用户。