Skip to content

Commit 5bb79e0

Browse files
committed
Fix 8 confirmed issues from static-review investigation
1. (High) dedupe MODE-OFF collapse: stable_key fallback now requires line-inclusive code_paths match (not path-only), preventing two distinct same-file bugs with same title+CWE from collapsing. Same- batch Step 3 also tightened to require line-inclusive match. 2. (Medium) KB_SNAPSHOT format/claims: architecture's comment-wrapped marker vs threat-model's bare header documented correctly; false reader claims fixed (report does NOT read KB_SNAPSHOT; planner does NOT read per-file markers; critic reads state field as primary). 3. (Low) schema.json active_snapshot description: absent=MODE-OFF (verdicts allowed), not DEGRADED. snapshot_pinned=false=HALT only when active_snapshot IS present. 4. (Low) vcs_info.snapshot_id description: marked redundant with active_snapshot.snapshot_id; default meta-agent doesn't write it. 5. (Medium) meta-agent crash-resume: step reference fixed (step 5 is state write, not step 6 which is GC). 6. (High) meta-agent dirty checks: .mantis_snapshots/ and workspace/ added to ALL per-VCS exclude sets (git, hg, multi-vcs); previously missing, causing silent sync suppression when state_root is inside the target repo. 7. (Medium) architecture parent-rollup: expanded from 1-hop to 2-hop to match plan's dependency-aware fan-out depth. 8. (Medium) report: render possible_duplicate_of as advisory note in finding entries (was written by dedupe but invisible everywhere). No .py committed. mdformat clean. Schema valid. TAG=agy CONV=aab01c59-a0bc-4e12-b8d8-07a15022423a Change-Id: I4f06f3ff50c353f30f557d46645906df470531ae
1 parent 0196fe9 commit 5bb79e0

7 files changed

Lines changed: 136 additions & 89 deletions

File tree

‎mantis-architecture/SKILL.md‎

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,14 @@ VCS):**
176176
`CODE_ROOT`; carry forward all other KB entries unchanged (they were
177177
built against the same code, just a different snapshot ID). Re-stamp
178178
`KB_SNAPSHOT: CUR` on every (re)written file.
179-
- **Parent-rollup:** When invalidating a KB entry for changed file F,
180-
also invalidate any KB entry that REFERENCES F (e.g., an entity that
181-
imports F's module). This ensures downstream architectural analysis is
182-
updated when a dependency changes.
179+
- **Parent-rollup (2-hop, matching plan's fan-out):** When
180+
invalidating a KB entry for changed file F, also invalidate any KB
181+
entry that REFERENCES F directly (1-hop) AND any entry that
182+
references a 1-hop dependent of F (2-hop). This matches
183+
`mantis-plan`'s dependency-aware fan-out (which expands up to 2
184+
hops), ensuring that a grandchild entity (H imports G, G imports
185+
changed F) is not carried forward stale and later fed as a
186+
`kb_reference` while its dependency has changed.
183187
- **Guardrail:** If ANY uncertainty arises (can't determine which KB
184188
entries map to which source files, the KB structure is ambiguous, or
185189
changed_files is empty but KB_ID != CUR), fall back to full rebuild
@@ -256,8 +260,14 @@ VCS):**
256260
FIRST line of `index.md`, each `entities/*.md`, and each
257261
`vulnerabilities/*.md` exactly `<!-- KB_SNAPSHOT: <SNAPSHOT_ID> -->`
258262
(substitute `CUR` from step 0b; it is an HTML comment so it does not
259-
render). This is how the next pass's freshness gate (step 0b) and
260-
downstream readers (Planner, Threat Modeler) detect drift.
263+
render). This is how the next pass's freshness gate (step 0b) detects
264+
drift. (Note: `mantis-threat-model` writes a bare `KB_SNAPSHOT:` header on
265+
`THREAT_MODEL.md` only, not the comment-wrapped marker; the freshness gate
266+
reads `kb_snapshot_id` from state as its primary source and the file marker
267+
as a secondary check. `mantis-critic` reads the `KB_SNAPSHOT:` marker on
268+
the first line of `THREAT_MODEL.md` first, falling back to `kb_snapshot_id`
269+
in state if the marker is absent. Neither `mantis-plan` nor `mantis-report`
270+
reads per-file KB_SNAPSHOT markers.)
261271

262272
- **`AS_OF` tags (REQUIRED when running the freshness gate, i.e. HALT or
263273
PINNED; never write a timeless verdict):** Any assertion DERIVED FROM A

‎mantis-critic/SKILL.md‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,11 @@ Execute the critic evaluation as follows:
146146
sources (try in order, first match wins):
147147

148148
1. The literal `KB_SNAPSHOT:` token on the FIRST line of
149-
`workspace/kb/THREAT_MODEL.md` (this is what the producers — architecture
150-
and threat-model — actually write; do NOT look for `kb_snapshot_id:` or
151-
`Snapshot:` — those tokens are never written and the gate would never
152-
match).
149+
`workspace/kb/THREAT_MODEL.md` (threat-model writes this as a bare header;
150+
architecture writes comment-wrapped `<!-- KB_SNAPSHOT: ... -->` on each KB
151+
file). For architecture files, scan for the `KB_SNAPSHOT:` substring
152+
inside the comment. Do NOT look for `kb_snapshot_id:` or `Snapshot:` —
153+
those tokens are never written and the gate would never match.
153154
2. Else the `kb_snapshot_id` value in `workspace/.mantis_state.json` (which
154155
architecture writes in its state-stamp step).
155156
3. Else `""` (no prior KB provenance). The blanket mass-mark below is gated

‎mantis-dedupe/SKILL.md‎

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,14 @@ Execute your task as follows:
204204
is false at the PASS level (`active_snapshot.snapshot_pinned` — it is NOT a
205205
per-finding field) and Block B is uninformative — fall back to today's
206206
dedup by `signature` if present, else `stable_key` = normalized_title +
207-
first `code_paths` entry (path only). This preserves dedup for targets that
207+
first `code_paths` entry **including its trailing `:line`**
208+
(line-inclusive, same as Step 2). Rationale: stripping `:line` would
209+
collapse two DISTINCT bugs in the same file (e.g. `parser.c:100` vs
210+
`parser.c:900`) with the same title+CWE into one — silently deleting a real
211+
finding. Note: `signature` itself strips `:line` by design (it is a coarse
212+
identity for cross-pass lineage, not a dedup key); this fallback therefore
213+
prefers `signature` only when `stable_key`'s line-inclusive match ALSO
214+
agrees, never on `signature` alone. This preserves dedup for targets that
208215
can never MATCH. When `active_snapshot` IS present but unpinned (HALT
209216
mode), this exception does NOT fire: keep the snapshot-gated behavior above
210217
(NOT_MATCHED → keep ACTIVE + `possible_duplicate_of`, never `DUPLICATE`).
@@ -213,9 +220,11 @@ Execute your task as follows:
213220
ALWAYS kept active (never trashed), regardless of mode.
214221

215222
3. **Filter Duplicate Findings in Current Batch:** Check the current findings
216-
against each other to find duplicates (using `code_paths` and `title`
217-
similarities). If multiple findings refer to the exact same flaw or highly
218-
overlapping code paths, they must be merged.
223+
against each other to find duplicates. Two findings are duplicates ONLY if
224+
they share the same `code_paths` entry **line-inclusively** (WITH trailing
225+
`:line`) AND have the same or highly similar title. If multiple findings
226+
refer to the exact same flaw at the same location, they must be merged.
227+
Findings at different lines in the same file are DISTINCT — never merge them.
219228

220229
4. **Map/Reduce Chunking Strategy (For Scale):** If there are many finding files
221230
(e.g., > 20 items), use a Map/Reduce approach to group them by target file or

‎mantis-meta-agent/SKILL.md‎

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -150,16 +150,18 @@ Execute your orchestration duties in a continuous loop:
150150
(Else pass-1 summary pollution makes the tree permanently "dirty" and
151151
sync silently never runs.) STEP 1 - FRESH dirty/ahead pre-check (NEVER
152152
rely on last pass's vcs_info): git : dirty if
153-
`git status --porcelain -- ':(exclude)**/mantis-summary.md' ':(exclude)**/*.bak-*'`
153+
`git status --porcelain -- ':(exclude)**/mantis-summary.md' ':(exclude)**/*.bak-*' ':(exclude)workspace/' ':(exclude).mantis_snapshots/'`
154154
is non-empty; ahead if `git rev-list --count @{u}..HEAD 2>/dev/null`
155155
errors or > 0; detached if `git symbolic-ref -q HEAD` errors. hg : dirty
156-
if `hg status -X '**/mantis-summary.md' -X 'workspace/**'` non-empty.
157-
multi-vcs : dirty if `repo forall -c 'git status --porcelain'` produces
158-
ANY output. If dirty OR ahead OR detached OR no-upstream -> DO NOT SYNC;
159-
log "sync skipped: local changes / detached / no upstream"; keep the
160-
tree. STEP 2 - SYNC (only if STEP 1 found clean AND on a tracked
161-
branch): git : git fetch && git merge --ff-only hg : hg pull && hg
162-
update --check multi-vcs : repo sync -c none / unknown : do NOT sync.
156+
if
157+
`hg status -X '**/mantis-summary.md' -X '*.bak-*' -X 'workspace/**' -X '.mantis_snapshots/**'`
158+
non-empty. multi-vcs : dirty if
159+
`repo forall -c "git status --porcelain -- ':(exclude)**/mantis-summary.md' ':(exclude)**/*.bak-*' ':(exclude)workspace/' ':(exclude).mantis_snapshots/'"`
160+
produces ANY output. If dirty OR ahead OR detached OR no-upstream -> DO
161+
NOT SYNC; log "sync skipped: local changes / detached / no upstream";
162+
keep the tree. STEP 2 - SYNC (only if STEP 1 found clean AND on a
163+
tracked branch): git : git fetch && git merge --ff-only hg : hg pull &&
164+
hg update --check multi-vcs : repo sync -c none / unknown : do NOT sync.
163165
STEP 3 - POST-SYNC INTEGRITY (git, if applicable): git submodule update
164166
--init --recursive ; if .gitattributes uses filter=lfs, git lfs pull. If
165167
either is needed but unavailable/fails -> force content_hash into
@@ -214,8 +216,8 @@ Execute your orchestration duties in a continuous loop:
214216
before any copy/detect): if `state.active_snapshot.pass == N` on entry,
215217
REUSE that dir (no re-copy/re-pin). If `snapshot_pinned` was true but
216218
the dir is now missing -> STOP and yield to the user (never re-pin to a
217-
possibly-drifted live tree). If reusing, skip steps 1–5 and go straight
218-
to step 6 (state write).
219+
possibly-drifted live tree). If reusing, skip steps 1–4 and go straight
220+
to step 5 (state write + snapshot_history append).
219221

220222
1. Detect vcs_info (existing meta-agent detection). VCS_ID = commit_hash
221223
(git/hg) / manifest revision (multi-vcs) / "" (else).

‎mantis-report/SKILL.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ Execute the reporting stage as follows:
212212
are visible as such. First-seen = the lowest `pass_number` in the finding's
213213
`history` entries (or the pass_number of the lowest-numbered archive dir
214214
that contains it); current state = the copy you kept from the fold above.
215+
- **Duplicate Advisory:** If the finding has a `possible_duplicate_of` field
216+
(set by `mantis-dedupe` when a cross-pass candidate was NOT_MATCHED), emit
217+
an advisory note:
218+
`Possibly related to finding <UUID> (cross-pass candidate; snapshots differ — not confirmed duplicate).`
219+
This makes the advisory regression-pointer visible to the stakeholder.
215220
- **Discovery Snapshot:** Emit `Discovery Snapshot: <discovery_commit>` for
216221
the finding. If `discovery_commit` is missing or empty, emit
217222
`Discovery Snapshot: (legacy — not recorded)`. Never omit or drop the

0 commit comments

Comments
 (0)