Conversation
rng1995
left a comment
There was a problem hiding this comment.
[SkillSpector Review]
Hi @Harbor404, thank you for tackling all three meta-review defects from #667 in one coherent change, especially the false "LLM degraded" caused by coverage findings!
Value and readiness: The meta_review_required snapshot fixes the false degraded status: AE1/AE7 coverage findings added during finalization no longer require a meta call. The updated test_reference_destination_kinds expectation matches #667, and analysis_completeness still reports the real coverage gap. The duplicate-location guard is a sound conservative choice. No LLM response can remove a finding or lower its severity or confidence: confirmation uses max(...), and llm-unconfirmed has no scoring consumer in src/. However, the new finding_id binding accepts whatever id the model returns without checking it. That can move an assessment onto a different rule's finding, or drop an answer the model did give. In addition, the new evidence fields copy free-form model text onto every reviewed finding, which changes terminal and Markdown output for every LLM scan. Not ready to merge yet.
Material findings
-
[Blocker]
src/skillspector/nodes/meta_analyzer.py:439-442, 472, 493: id binding is trusted without checks, and it is the only binding used when an id is present.- An item that carries a
finding_idis bound to that finding without checking that the item'spattern_id/start_linematch it, or that the id was part of the item's batch. Suppose a batch holds an SC4 finding at line 4 and an AST1 finding at line 9, and the model swaps their ids. The AST1 finding then gets the SC4 assessment's text as itsmessage/explanation/remediation. If the model leavesexplanationempty, line 493 builds the default text fromassessment.get("pattern_id"), so another rule's default explanation lands on this finding. Main could not mix up rules this way, because its keys includedrule_id. - An item whose id matches no finding (for example, one hex digit wrong in the 40-character id) hits
continueat line 442 and is never indexed by location. The finding it reviewed is then reported asmissing, even though the model answered with the correct pattern and line.
Please index every item both by id and by location. Accept the id only when it belongs to that batch's
batch.findingsand itsrule_id(andstart_line, when given) match. Otherwise, fall back to the location path, keeping the duplicate guard. Build default text fromf.rule_id. Please add tests for swapped ids, an unknown id with a correct location, and an id from another batch. (Finding ids are per-runuuid4values, and each batch prompt lists only its own ids. So I see no practical way for a prompt-injected batch to target another batch's finding; the batch check is defense in depth.) - An item that carries a
-
[Blocker]
meta_analyzer.py:300-305:_review_evidencecopies the model'sconfidence,explanationandremediationintoevidencefor every reviewed finding. The reports print evidence in full: the terminal output atreport.py:1072-1074(even though message and remediation there are cut to 60 and 150 characters), and Markdown atreport.py:1518-1521(each value inside an inline code span). Every finding in an LLM scan therefore gains an Evidence line that repeats the model's explanation and remediation. Confirmed findings already carry that text asmessage/explanation/remediation. For disagreed or low-confidence findings, the model's rebuttal, which prompt injection in the scanned skill can steer ("documented installer, safe to run"), now appears under "Evidence" next to a deterministic finding. #667 only asks to distinguish the outcomes, andllm_review_outcomealready does that. Please record onlyllm_review_outcome(andllm_review_confidence, if wanted) and drop the copied explanation and remediation. If the PIC wants the model's reasoning for non-confirmed findings, put it in a clearly labelled field with a length limit. -
[Non-blocking]
meta_analyzer.py:331-334, 872-875, 910-914: the failure paths now add thellm-unconfirmedtag through_mark_review_failed. On main, findings whose review failed stayed untagged. The PR description says the existing tag behaviour is kept, but this changes it. Two effects:- Consumers that act on the tag will treat an LLM outage as model disagreement, which is the conflation #667 asks to remove.
suppression.finding_fingerprinthashestags, so a baseline written with--no-llmno longer matches findings from a run whose LLM call failed. On main, those two outputs produce the same fingerprint.
Please leave failure paths untagged and rely on
llm_review_outcome=failed, or document the new meaning of the tag. -
[Non-blocking]
meta_analyzer.py:441and the location dictionaries: if a finding receives several assessments, the last batch wins. This happens with chunked files:findings_in_rangeplaces a finding in every chunk that contains its start line, and chunks overlap by 50 lines. Main kept any confirmation. Now a later chunk without the relevant context can flip a confirmed finding todisagreed. Please merge deterministically (confirmed > low-confidence > disagreed) and add a test. -
[Non-blocking]
cli.py:2252: the transitive merge buildsmerged_result = {**initial_result, ...}, someta_review_requiredreflects only the root scan, whileeffective_finding_idsis merged from all children. Consider OR-ing the child flags so the field means the same thing in transitive scans. -
[Non-blocking] Tests and docs:
test_finding_clones_preserve_security_metadata_and_confidencechanged fromevidence == original.evidenceto two spot checks. Please assert that the original evidence is preserved as a subset (returned.evidence.items() >= original.evidence.items()).- Please add a
semantic_runtimetest for a legacy result (no flag) whose only findings are taggedcoverage. - The
apply_filterandmeta_analyzerdocstrings still describe matching by(file, rule_id)only.
PIC tradeoffs: First, should the model's reasoning for disagreed or low-confidence findings appear in reports at all (item 2)? Showing it helps triage, but it puts text a scanned skill can influence next to deterministic findings. Second, should llm-unconfirmed also mean "review failed" (item 3)?
Verification and gaps: I traced apply_filter, every return path of the meta_analyzer node, finalize_inspection_ledger, semantic_runtime._has_effective_findings, the transitive merge in cli.py, evidence rendering in report.py, and suppression.finding_fingerprint. I confirmed that finding ids come from finding-{uuid4().hex} (models.py:118-120) and that _file is set by parse_response, not by the model. Tests were not run locally per policy. CI: all 6 checks green. Overlap: no file overlap with Harbor404's #700, #701 or #703-#706. #703 also changes the finding output shape (models.py/report.py, different fields), so the two output changes should be coordinated.
Decision: Changes Requested (reviewed head b8c17dc42a83d0cee94fc47863e0184d43c6cff5)
| if not pattern_id or not item.get("is_vulnerability", False): | ||
| finding_id = item.get("finding_id") | ||
| if finding_id: | ||
| assessments_by_id[str(finding_id)] = item |
There was a problem hiding this comment.
[Blocker] The id is accepted without checking that the item's pattern_id/start_line match the finding, or that the id belongs to this batch. If the model swaps ids between an SC4 finding and an AST1 finding, the AST1 finding is relabelled with the SC4 assessment, and line 493 builds the default text from the item's pattern_id. An unknown or mistyped id hits continue here and is never indexed by location, so the reviewed finding is reported as missing. Please validate the id against batch.findings and the finding's rule_id/start_line, fall back to location matching on mismatch, and build default text from f.rule_id.
There was a problem hiding this comment.
Fixed in 01e6cc9. Every item is now indexed by both id and location. An id is accepted only when it belongs to batch.findings and its pattern/file/start (and end when both are present) match that finding; otherwise the item falls back to the unambiguous location path. Default explanation/remediation now use f.rule_id. Added swapped-id, unknown-id-with-correct-location, and cross-batch-id regressions.
| """Preserve original evidence while recording the per-finding review outcome.""" | ||
| evidence = {**finding.evidence, "llm_review_outcome": outcome} | ||
| if item is not None: | ||
| for field in ("confidence", "explanation", "remediation"): |
There was a problem hiding this comment.
[Blocker] This copies the model's explanation/remediation into evidence on every reviewed finding. Terminal output (report.py:1072-1074) and Markdown (report.py:1518-1521) print evidence in full, so every finding in an LLM scan gains an Evidence line that repeats the model's text. Disagreed findings would show a rebuttal that prompt injection in the scanned skill can steer. Please keep only llm_review_outcome (and llm_review_confidence, if wanted).
There was a problem hiding this comment.
Fixed in 01e6cc9. _review_evidence now retains only llm_review_outcome and the numeric llm_review_confidence; model-provided explanation and remediation are not copied into evidence. A regression asserts neither llm_review_explanation nor llm_review_remediation appears.
|
Additional non-blocking items are addressed in |
Signed-off-by: Harbor404 <2657212322@qq.com>
Bind ids only when they match the submitted batch location, merge overlapping verdicts deterministically, retain only outcome/confidence evidence, keep failed reviews untagged, and OR transitive meta-review flags. Signed-off-by: Harbor404 <2657212322@qq.com>
01e6cc9 to
c4d34b8
Compare
|
Updated the branch on top of current The lint failure was a |
Summary
Fixes #667.
Three related meta-review defects: an assessment could be attached to the wrong
finding, the report could not distinguish "model disagreed" from "review never
happened", and findings added during finalization could make the review phase
look failed when nothing was ever required of it.
Signed-off-by: Harbor404 2657212322@qq.com
Changes
Bind an assessment to the exact finding
MetaAnalyzerFindinggains an optionalfinding_id; the prompt and therendered findings include it and ask for it back.
apply_filter()prefersfinding_id. Responses without one keep the legacylocation fallback. When several findings share the same exact location and the
response carries no id, the result is marked
missinginstead of guessing abinding.
Distinguish review outcomes
Each finding's
evidencenow recordsllm_review_outcome=confirmed|disagreed|low-confidence|missing|failedwhilekeeping the existing
llm-unconfirmedbehavior for compatibility, so a reportcan tell a model disagreement from an omitted decision from a failed call.
Stop finalization from faking a review failure
MetaAnalyzerResponseandSkillspectorStategain ameta_review_requiredsnapshot, recorded when the analyzer actually ran.
afterwards no longer demand a successful
meta_analyzercall.semantic_runtimeprefers the snapshot and, for legacy state, ignorespost-hoc findings tagged
coverage. Genuine review gaps still surface throughfinding evidence and the ledger.
Testing
mypypasses onmeta_analyzer.py,semantic_runtime.py, andstate.py.Checking
finalize_inspection_ledger.pyalone hits a pre-existing error atline 124 that is outside this diff.
Notes for Reviewer
finding_id. Older responsesstill work through the unique-location fallback; a non-unique location without
an id is conservatively reported as
missingrather than mis-attached.llm_review_explanation/llm_review_remediationare added as evidencefields only — scoring is unchanged.