| 1 | contextRegion misplacement | Nests contextRegion as a child of region | contextRegion is a sibling property on physicalLocation, same level as region | JSON1005 (schema) |
| 2 | Invented exploitability values | Uses unconfirmed, unknown, low, medium, high, or other freeform strings | Closed vocabulary: demonstrated, poc, theoretical only | AI2014 |
| 3 | Missing message.markdown | Provides message.text only | Both text and markdown are required; markdown carries the structured narrative | AI1005 |
| 4 | ai/origin at result level | Places ai/origin on result.properties | ai/origin is a run-level property only (run.properties) | AI1006 |
| 5 | Partial ai/ key coverage* | Emits 4–6 of the 8 keys, typically missing ai/evidence, ai/redacted, ai/fullLogLocation | All 8 keys must be accounted for across the run (some are conditional, e.g., ai/redacted only on redacted logs) | AI-PROFILE |
| 6 | rank as string | Emits "rank": "65" (string) | rank is a number (0.0–100.0), not a string | JSON1005 (schema) |
| 7 | Missing versionControlProvenance | Omits entirely or provides repositoryUri without revisionId | At least one entry with both repositoryUri and revisionId | AI1004 |
| 8 | Invented ai/* keys | Adds ai/confidence, ai/severity, ai/model, etc. | Exactly 8 defined keys under ai/ namespace; tool-specific data goes under tool namespace | AI-PROFILE |
| 9 | kind omitted | Relies on default | Explicit kind: "fail" for vulnerability findings | Schema best practice |
| 10 | All-or-nothing violation | Some results have ai/exploitability, others don't | If any result declares it, every result must | AI2014 consistency |
| 11 | Execution narrative in ai/handoff | Puts dead-end analysis, model selection, and confidence self-assessment in ai/handoff | Execution narrative belongs in toolExecutionNotifications; ai/handoff is for remediation context only | AI2012 (ai/handoff scope) |
| 12 | Configuration gaps as prose | Describes data access or permission issues in ai/handoff or message.text | Configuration gaps belong in toolConfigurationNotifications (inline on the emit-invocations payload) | — |
| 13 | Missing notification descriptors | Emits notifications without registering descriptors in tool.driver.notifications[] | Notification descriptors must be registered for the descriptor.id to resolve | AI2017 |
| 14 | Editorializing notification ids | Uses AI/EXEC/DECISION, <toolName>/EXEC/..., or other prefixed ids | Notification descriptor ids name the concern only (e.g. DECISION, DATA-ACCESS-DENIED); the array (toolExecutionNotifications vs toolConfigurationNotifications) encodes the kind, tool.driver.name encodes the emitter | — |
| 15 | Zero-based line numbers | Emits startLine: 0 or other 0-based coordinates | SARIF line numbers are 1-based (startLine ≥ 1). 0 is invalid per JSON schema | JSON1008 |
| 16 | threadFlowLocation index dangling | Uses threadFlow.locations[].index to reference runs[].threadFlowLocations but never populates that top-level array | Either populate runs[].threadFlowLocations[] or use inline location objects on each threadFlowLocation | SARIF1009 |
| 17 | Missing ai/attackerPosition | Omits attacker position entirely or on some results | Must appear on every result if present on any (all-or-nothing). Use "unclear" if genuinely unknown | AI2015 |
| 18 | Missing rule helpUri | Emits rules[] without helpUri | Every rule should include helpUri linking to documentation (CWE URL, internal doc, etc.) | SARIF2012 |
| 19 | Non-conventional rule IDs | Uses tool-specific prefixes like ACME-CPP-001 | Rule IDs should follow conventional patterns; CWE-based IDs preferred for interoperability | SARIF2009 |