Skip to main content
Glama

EchelonGraph CVE & Exposure

Check an SBOM against the advisory corpus

check_sbom
Read-onlyIdempotent

Check a dependency list against EchelonGraph's advisory corpus, one verdict per component. For container images and Kubernetes pods, the input is an SBOM of each image, or its purls. Pass purls (package URLs, up to 2,000 distinct, 10,000 with repeats) or sbom (a CycloneDX or SPDX JSON document, up to 5,000,000 characters). This server reads the document's purls; only they are sent to the API, in POST bodies of at most 200, never in a URL; the document itself is not sent on. Run from npm, this server is on your machine; over the hosted endpoint (mcp.echelongraph.io) it is EchelonGraph's, and the document is the request body, accepted up to 6 MiB. data.results holds one row per component sent, in order, with verdict (affected, not_affected, undetermined or not_assessed), not_assessed_reason, cve_ids, matched_package and matched_via (a deb or apk purl's upstream qualifier matches as its source package); each match carries fixed_in (the next unaffected version) or null with fixed_in_reason. data.summary counts the verdicts. not_affected is the only clean verdict: not_assessed (no verdict, as for a deb, apk or rpm purl without a distro qualifier naming its release, which EchelonGraph does not guess) and undetermined are not, and the note counts both. The API allows 1,200 components a minute per caller; on a 429 the tool waits out Retry-After, up to 50 seconds a call, then answers what it has, the rest in data.not_sent_purls, not clean. Input with more than 2,000 distinct purls is refused, not truncated. Past 30,000 characters of JSON, the first text block holds data cut to fit, and the note says what the cut leaves out and where to read it (TEXT CUT); so is structured data (DATA CUT), unless full: true. Cut, each row keeps index, purl, verdict, not_assessed_reason and cve_ids at least, each match its fixed_in while kept; not_affected rows leave the text first, then not_assessed ones; not_sent_purls keeps its first 10.

Input Schema

NameRequiredDescriptionDefault
fullNotrue: data in the structured result is the answer whole, however long. Without it, a structured result past 30,000 characters of JSON has its data cut (DATA CUT): in an order the note states, the entries of its longest lists keep only some of their fields, and then the lists only some of their entries; data_truncated is true, and truncated gives each list cut with its total, the number returned and whether its entries are thinned. Whole data is for a program; a client that sizes structured results, as Claude Code does against MAX_MCP_OUTPUT_TOKENS, can refuse an answer past its cap.
sbomNoa CycloneDX JSON or SPDX JSON document, as JSON text or as an object; its purls are read by this MCP server and only they are sent to the API; over the hosted endpoint (mcp.echelongraph.io) the document is the request body
purlsNopackage URLs to check, e.g. pkg:npm/lodash@4.17.20 or pkg:deb/debian/openssl@3.0.11-1~deb12u1?distro=debian-12 (up to 2,000 distinct, sent in batches of 200), in a list of at most 10,000 entries, repeats included

Output Schema

NameRequiredDescriptionDefault

No arguments

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed4 schema fields changed
    • changedInput schema / properties / purls / description
      Previous value: -"package URLs to check, e.g. pkg:npm/lodash@4.17.20 or pkg:deb/debian/openssl@3.0.11-1~deb12u1?distro=debian-12 (up to 2,000 distinct, sent in batches of 200)"New value: +"package URLs to check, e.g. pkg:npm/lodash@4.17.20 or pkg:deb/debian/openssl@3.0.11-1~deb12u1?distro=debian-12 (up to 2,000 distinct, sent in batches of 200), in a list of at most 10,000 entries, repeats included"
    • addedInput schema / properties / purls / maxItems
      Added value: +10000
    • changedInput schema / properties / sbom / anyOf
      Previous value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "additionalProperties": {},
      -    "propertyNames": {
      -      "type": "string"
      -    },
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "type": "string"
      +  },
      +  {
      +    "additionalProperties": {},
      +    "maxProperties": 64,
      +    "propertyNames": {
      +      "type": "string"
      +    },
      +    "type": "object"
      +  }
      +]
    • changedOutput schema / oneOf
      Previous value: -[
      -  {
      -    "additionalProperties": {},
      -    "properties": {
      -      "coverage": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "properties": {
      -              "batch_size": {
      -                "description": "The most purls one request carries (the API's cap per request).",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches": {
      -                "description": "Requests the distinct purls make, at batch_size each.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches_sent": {
      -                "description": "Of those, the ones the API answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "components_in_document": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      -              },
      -              "distinct_purls": {
      -                "description": "Distinct purls to check: sent plus not_sent.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "duplicates_removed": {
      -                "description": "Purls that appeared more than once and were sent once.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "input": {
      -                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      -                "enum": [
      -                  "purls",
      -                  "cyclonedx",
      -                  "spdx"
      -                ],
      -                "type": "string"
      -              },
      -              "not_assessed": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Of those sent, the components with no verdict, as the answer counts them."
      -              },
      -              "not_sent": {
      -                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "not_sent_reason": {
      -                "anyOf": [
      -                  {
      -                    "enum": [
      -                      "time_budget",
      -                      "rate_limited",
      -                      "request_failed"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      -              },
      -              "partial": {
      -                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "rate_limit_waits": {
      -                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "sent": {
      -                "description": "Distinct purls sent and answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "waited_ms": {
      -                "description": "Milliseconds spent in those waits.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "with_purl": {
      -                "description": "Of those, the ones carrying a purl.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "without_purl": {
      -                "description": "The ones without a purl: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              }
      -            },
      -            "required": [
      -              "input",
      -              "components_in_document",
      -              "with_purl",
      -              "without_purl",
      -              "duplicates_removed",
      -              "distinct_purls",
      -              "batch_size",
      -              "batches",
      -              "batches_sent",
      -              "sent",
      -              "not_sent",
      -              "not_sent_reason",
      -              "rate_limit_waits",
      -              "waited_ms",
      -              "not_assessed",
      -              "partial"
      -            ],
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "What the answer covers; null where the answer says nothing about it."
      -      },
      -      "data": {
      -        "additionalProperties": {},
      -        "properties": {
      -          "answered_at": {
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "components": {
      -            "type": [
      -              "number",
      -              "null"
      -            ]
      -          },
      -          "match_layer": {
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "not_sent_purls": {
      -            "anyOf": [
      -              {
      -                "items": {
      -                  "type": "string"
      -                },
      -                "type": "array"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "description": "The distinct purls not sent (coverage.not_sent_reason says why): not checked, and not clean. Present only when some were not sent."
      -          },
      -          "results": {
      -            "items": {
      -              "additionalProperties": {},
      -              "properties": {
      -                "advisories_considered": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "assessed": {
      -                  "description": "Whether the matcher produced a verdict for this component. false is never clean.",
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "candidates_capped": {
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "capped": {
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "code": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "cve_ids": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "type": "string"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "ecosystem": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "error": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "index": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "input_kind": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matched_package": {
      -                  "description": "The package name the advisories were looked up by (#2836): the purl's own name, or, for a deb or apk purl with an upstream qualifier, the source package it names, which is how Debian and Alpine file advisories.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matched_version_from": {
      -                  "description": "Upstream rows: upstream when the qualifier named a version (upstream=name@version) and it was used, purl when the purl's own version was.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matched_via": {
      -                  "description": "Which name matched_package is: purl_name (the purl's name), upstream (the purl's upstream qualifier) or coordinates (the component's package).",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matches": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "additionalProperties": {},
      -                        "properties": {
      -                          "cve_id": {
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          },
      -                          "epss_score": {
      -                            "description": "The CVE's EPSS score (FIRST), as EchelonGraph's record holds it; null when it has none.",
      -                            "type": [
      -                              "number",
      -                              "null"
      -                            ]
      -                          },
      -                          "fixed_in": {
      -                            "description": "The lowest version above this component's that no affected interval of the CVE's record for the package holds, always above it: the fixed bound of the interval that holds the component's version, moved past any overlapping interval that still holds that bound (fixed_in_detail then names it). null when no such version is on record (an interval on the way records no fix: it ends at last_affected, or has no end) or no interval holds the version, with fixed_in_reason saying which.",
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          },
      -                          "fixed_in_detail": {
      -                            "description": "Set when fixed_in is not the fixed bound of the interval that holds the component's version: the overlapping intervals of the same record it moved past, which still hold that bound.",
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          },
      -                          "fixed_in_reason": {
      -                            "description": "Why fixed_in is null; absent when fixed_in is a version.",
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          },
      -                          "interval": {
      -                            "anyOf": [
      -                              {
      -                                "additionalProperties": {},
      -                                "properties": {
      -                                  "fixed": {
      -                                    "type": [
      -                                      "string",
      -                                      "null"
      -                                    ]
      -                                  },
      -                                  "introduced": {
      -                                    "type": [
      -                                      "string",
      -                                      "null"
      -                                    ]
      -                                  },
      -                                  "last_affected": {
      -                                    "type": [
      -                                      "string",
      -                                      "null"
      -                                    ]
      -                                  }
      -                                },
      -                                "type": "object"
      -                              },
      -                              {
      -                                "type": "null"
      -                              }
      -                            ],
      -                            "description": "The advisory interval that holds this component's version (fixed is exclusive, last_affected inclusive); absent when no interval holds it."
      -                          },
      -                          "kev_listed": {
      -                            "description": "Whether the CVE is in CISA's Known Exploited Vulnerabilities catalog, as EchelonGraph's record holds it.",
      -                            "type": [
      -                              "boolean",
      -                              "null"
      -                            ]
      -                          },
      -                          "match_reason": {
      -                            "description": "Why the advisory applies, with the advisory interval the version falls inside: \"[A, B)\" names that interval's fixed bound B (another interval of the advisory can still hold B; fixed_in is the version past them all), \"[A, B]\" the last affected version B. On an upstream row it ends by naming the source package and the purl name.",
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          }
      -                        },
      -                        "type": "object"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "matches_truncated": {
      -                  "additionalProperties": false,
      -                  "description": "Present only on a row whose matches the structured result's cut (DATA CUT) shortened or left out: total, the row's matches in the answer; returned, the ones data keeps (its first, highest effective_score first); and, while they fit, of all its matches, the CVE IDs KEV-listed (kev_listed_cve_ids) or used in ransomware (ransomware_cve_ids), and the distinct fixed_in versions (fixed_in_versions). The same call with full: true returns every match.",
      -                  "properties": {
      -                    "fixed_in_versions": {
      -                      "items": {
      -                        "type": "string"
      -                      },
      -                      "type": "array"
      -                    },
      -                    "kev_listed_cve_ids": {
      -                      "items": {
      -                        "type": "string"
      -                      },
      -                      "type": "array"
      -                    },
      -                    "ransomware_cve_ids": {
      -                      "items": {
      -                        "type": "string"
      -                      },
      -                      "type": "array"
      -                    },
      -                    "returned": {
      -                      "maximum": 9007199254740991,
      -                      "minimum": 0,
      -                      "type": "integer"
      -                    },
      -                    "total": {
      -                      "maximum": 9007199254740991,
      -                      "minimum": 0,
      -                      "type": "integer"
      -                    }
      -                  },
      -                  "required": [
      -                    "total",
      -                    "returned"
      -                  ],
      -                  "type": "object"
      -                },
      -                "not_affected_count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "not_assessed_reason": {
      -                  "description": "Why assessed is false: package_not_in_advisory_corpus, no_decidable_advisory, advisory_lookup_failed, candidate_window_truncated, time_budget, distro_release_unknown, purl_type_unsupported, version_missing or invalid_component.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "package": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "purl": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "purl_name": {
      -                  "description": "Upstream rows: the purl's own (binary) package name, not looked up.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "undetermined": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "additionalProperties": {},
      -                        "properties": {
      -                          "cve_id": {
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          }
      -                        },
      -                        "type": "object"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "undetermined_count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "verdict": {
      -                  "description": "affected (count > 0); not_affected (assessed, no match, nothing undetermined: the only clean verdict); undetermined (advisories name the package, at least one could not be decided, none matched: not clean); not_assessed (no verdict: not clean).",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "version": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                }
      -              },
      -              "type": "object"
      -            },
      -            "type": "array"
      -          },
      -          "summary": {
      -            "additionalProperties": {},
      -            "properties": {
      -              "affected": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "components": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "corpus_cache_max_age_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "elapsed_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "lookups": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_affected": {
      -                "description": "Components with a decided, clean verdict.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_assessed": {
      -                "description": "Components with no verdict: not clean.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_assessed_by_reason": {
      -                "anyOf": [
      -                  {
      -                    "additionalProperties": {},
      -                    "properties": {
      -                      "advisory_lookup_failed": {
      -                        "type": "number"
      -                      },
      -                      "candidate_window_truncated": {
      -                        "type": "number"
      -                      },
      -                      "distro_release_unknown": {
      -                        "description": "deb, apk or rpm purls without a distro qualifier naming the release, which EchelonGraph does not guess.",
      -                        "type": "number"
      -                      },
      -                      "invalid_component": {
      -                        "type": "number"
      -                      },
      -                      "no_decidable_advisory": {
      -                        "type": "number"
      -                      },
      -                      "package_not_in_advisory_corpus": {
      -                        "type": "number"
      -                      },
      -                      "purl_type_unsupported": {
      -                        "type": "number"
      -                      },
      -                      "time_budget": {
      -                        "description": "Components the batch's time budget ran out before: not clean; check them again.",
      -                        "type": "number"
      -                      },
      -                      "version_missing": {
      -                        "type": "number"
      -                      }
      -                    },
      -                    "type": "object"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "The not_assessed components, counted by not_assessed_reason."
      -              },
      -              "partial": {
      -                "description": "true when the time budget ran out before every component was looked up.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "time_budget_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "undetermined": {
      -                "description": "Components whose advisories could not all be decided and none matched: not clean.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              }
      -            },
      -            "type": "object"
      -          }
      -        },
      -        "type": "object"
      -      },
      -      "data_truncated": {
      -        "description": "false: data is the answer whole. true: the structured result would pass 30,000 characters of JSON with data whole, so data is cut (DATA CUT): the entries of some of its lists keep only some of their fields, or the lists only some of their entries, truncated gives each, and no string is shortened. The same call with full: true returns data whole.",
      -        "type": "boolean"
      -      },
      -      "freshness": {
      -        "description": "The producing radar's last completed check (last_run_at), where the API serves one.",
      -        "type": "null"
      -      },
      -      "measured_at": {
      -        "anyOf": [
      -          {
      -            "description": "An RFC 3339 instant.",
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "When the underlying observation was made, as the API states it; null when the answer does not say or holds no observation."
      -      },
      -      "method": {
      -        "description": "How the numbers were produced.",
      -        "type": "string"
      -      },
      -      "notes": {
      -        "description": "Caveats, one sentence each.",
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "state": {
      -        "description": "measured: a measurement of what was asked; an exposure count is measured only with measured_at and method. not_assessed: the answer holds no dated measurement of what was asked, so no count in it is presented as one; it can still relay a count, as what the source holds on record, undated, and its notes (and exposure_state, where the result carries it) say what each count is.",
      -        "enum": [
      -          "measured",
      -          "not_assessed"
      -        ],
      -        "type": "string"
      -      },
      -      "truncated": {
      -        "additionalProperties": {
      -          "additionalProperties": false,
      -          "properties": {
      -            "returned": {
      -              "maximum": 9007199254740991,
      -              "minimum": 0,
      -              "type": "integer"
      -            },
      -            "thinned": {
      -              "const": true,
      -              "type": "boolean"
      -            },
      -            "total": {
      -              "maximum": 9007199254740991,
      -              "minimum": 0,
      -              "type": "integer"
      -            }
      -          },
      -          "required": [
      -            "total",
      -            "returned"
      -          ],
      -          "type": "object"
      -        },
      -        "description": "Present when data_truncated is true: each list of data that was cut, by its path in data (a dot between the names of an object and a list inside it), with total, its entries in the answer, returned, the entries data holds, and thinned, true when each of them keeps only some of its fields.",
      -        "propertyNames": {
      -          "type": "string"
      -        },
      -        "type": "object"
      -      }
      -    },
      -    "required": [
      -      "state",
      -      "measured_at",
      -      "method",
      -      "coverage",
      -      "freshness",
      -      "notes",
      -      "data_truncated",
      -      "data"
      -    ],
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "coverage": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "properties": {
      -              "batch_size": {
      -                "description": "The most purls one request carries (the API's cap per request).",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches": {
      -                "description": "Requests the distinct purls make, at batch_size each.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches_sent": {
      -                "description": "Of those, the ones the API answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "components_in_document": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      -              },
      -              "distinct_purls": {
      -                "description": "Distinct purls to check: sent plus not_sent.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "duplicates_removed": {
      -                "description": "Purls that appeared more than once and were sent once.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "input": {
      -                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      -                "enum": [
      -                  "purls",
      -                  "cyclonedx",
      -                  "spdx"
      -                ],
      -                "type": "string"
      -              },
      -              "not_assessed": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Of those sent, the components with no verdict, as the answer counts them."
      -              },
      -              "not_sent": {
      -                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "not_sent_reason": {
      -                "anyOf": [
      -                  {
      -                    "enum": [
      -                      "time_budget",
      -                      "rate_limited",
      -                      "request_failed"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      -              },
      -              "partial": {
      -                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "rate_limit_waits": {
      -                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "sent": {
      -                "description": "Distinct purls sent and answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "waited_ms": {
      -                "description": "Milliseconds spent in those waits.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "with_purl": {
      -                "description": "Of those, the ones carrying a purl.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "without_purl": {
      -                "description": "The ones without a purl: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              }
      -            },
      -            "required": [
      -              "input",
      -              "components_in_document",
      -              "with_purl",
      -              "without_purl",
      -              "duplicates_removed",
      -              "distinct_purls",
      -              "batch_size",
      -              "batches",
      -              "batches_sent",
      -              "sent",
      -              "not_sent",
      -              "not_sent_reason",
      -              "rate_limit_waits",
      -              "waited_ms",
      -              "not_assessed",
      -              "partial"
      -            ],
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ]
      -      },
      -      "error": {
      -        "additionalProperties": false,
      -        "properties": {
      -          "kind": {
      -            "description": "What failed: the request (network, timeout), the answer (http, not_json, not_object, unexpected_shape), the input, or this server.",
      -            "enum": [
      -              "network",
      -              "timeout",
      -              "http",
      -              "not_json",
      -              "not_object",
      -              "invalid_input",
      -              "internal",
      -              "unexpected_shape",
      -              "radars"
      -            ],
      -            "type": "string"
      -          },
      -          "message": {
      -            "description": "The cause: the API's own message, or what went wrong.",
      -            "type": "string"
      -          },
      -          "path": {
      -            "description": "The API path requested, when a request was made.",
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "status": {
      -            "anyOf": [
      -              {
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "description": "The HTTP status, when the API answered one."
      -          }
      -        },
      -        "required": [
      -          "kind",
      -          "path",
      -          "status",
      -          "message"
      -        ],
      -        "type": "object"
      -      },
      -      "freshness": {
      -        "type": "null"
      -      },
      -      "measured_at": {
      -        "type": "null"
      -      },
      -      "method": {
      -        "type": "null"
      -      },
      -      "notes": {
      -        "description": "Caveats, one sentence each.",
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "state": {
      -        "description": "failed: the lookup did not complete. invalid_input: the input was refused, so nothing was looked up. Neither is a finding.",
      -        "enum": [
      -          "failed",
      -          "invalid_input"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "state",
      -      "measured_at",
      -      "method",
      -      "coverage",
      -      "freshness",
      -      "notes",
      -      "error"
      -    ],
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": {},
      +    "properties": {
      +      "coverage": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": {},
      +            "properties": {
      +              "batch_size": {
      +                "description": "The most purls one request carries (the API's cap per request).",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches": {
      +                "description": "Requests the distinct purls make, at batch_size each.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches_sent": {
      +                "description": "Of those, the ones the API answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "components_in_document": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      +              },
      +              "distinct_purls": {
      +                "description": "Distinct purls to check: sent plus not_sent.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "duplicates_removed": {
      +                "description": "Purls that appeared more than once and were sent once.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "input": {
      +                "anyOf": [
      +                  {
      +                    "enum": [
      +                      "purls",
      +                      "cyclonedx",
      +                      "spdx"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  {
      +                    "type": "string"
      +                  }
      +                ],
      +                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document."
      +              },
      +              "not_assessed": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Of those sent, the components with no verdict, as the answer counts them."
      +              },
      +              "not_sent": {
      +                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "not_sent_reason": {
      +                "anyOf": [
      +                  {
      +                    "anyOf": [
      +                      {
      +                        "enum": [
      +                          "time_budget",
      +                          "rate_limited",
      +                          "request_failed"
      +                        ],
      +                        "type": "string"
      +                      },
      +                      {
      +                        "type": "string"
      +                      }
      +                    ]
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      +              },
      +              "partial": {
      +                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "rate_limit_waits": {
      +                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "sent": {
      +                "description": "Distinct purls sent and answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "waited_ms": {
      +                "description": "Milliseconds spent in those waits.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "with_purl": {
      +                "description": "Of those, the ones carrying a purl.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "without_purl": {
      +                "description": "The ones without a purl: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              }
      +            },
      +            "required": [
      +              "input",
      +              "components_in_document",
      +              "with_purl",
      +              "without_purl",
      +              "duplicates_removed",
      +              "distinct_purls",
      +              "batch_size",
      +              "batches",
      +              "batches_sent",
      +              "sent",
      +              "not_sent",
      +              "not_sent_reason",
      +              "rate_limit_waits",
      +              "waited_ms",
      +              "not_assessed",
      +              "partial"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "What the answer covers; null where the answer says nothing about it."
      +      },
      +      "data": {
      +        "additionalProperties": {},
      +        "properties": {
      +          "answered_at": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "components": {
      +            "type": [
      +              "number",
      +              "null"
      +            ]
      +          },
      +          "match_layer": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "not_sent_purls": {
      +            "anyOf": [
      +              {
      +                "items": {
      +                  "type": "string"
      +                },
      +                "type": "array"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "description": "The distinct purls not sent (coverage.not_sent_reason says why): not checked, and not clean. Present only when some were not sent."
      +          },
      +          "results": {
      +            "items": {
      +              "additionalProperties": {},
      +              "properties": {
      +                "advisories_considered": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "assessed": {
      +                  "description": "Whether the matcher produced a verdict for this component. false is never clean.",
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "candidates_capped": {
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "capped": {
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "code": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "cve_ids": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "type": "string"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "ecosystem": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "error": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "index": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "input_kind": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matched_package": {
      +                  "description": "The package name the advisories were looked up by (#2836): the purl's own name, or, for a deb or apk purl with an upstream qualifier, the source package it names, which is how Debian and Alpine file advisories.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matched_version_from": {
      +                  "description": "Upstream rows: upstream when the qualifier named a version (upstream=name@version) and it was used, purl when the purl's own version was.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matched_via": {
      +                  "description": "Which name matched_package is: purl_name (the purl's name), upstream (the purl's upstream qualifier) or coordinates (the component's package).",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matches": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "additionalProperties": {},
      +                        "properties": {
      +                          "cve_id": {
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          },
      +                          "epss_score": {
      +                            "description": "The CVE's EPSS score (FIRST), as EchelonGraph's record holds it; null when it has none.",
      +                            "type": [
      +                              "number",
      +                              "null"
      +                            ]
      +                          },
      +                          "fixed_in": {
      +                            "description": "The lowest version above this component's that no affected interval of the CVE's record for the package holds, always above it: the fixed bound of the interval that holds the component's version, moved past any overlapping interval that still holds that bound (fixed_in_detail then names it). null when no such version is on record (an interval on the way records no fix: it ends at last_affected, or has no end) or no interval holds the version, with fixed_in_reason saying which.",
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          },
      +                          "fixed_in_detail": {
      +                            "description": "Set when fixed_in is not the fixed bound of the interval that holds the component's version: the overlapping intervals of the same record it moved past, which still hold that bound.",
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          },
      +                          "fixed_in_reason": {
      +                            "description": "Why fixed_in is null; absent when fixed_in is a version.",
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          },
      +                          "interval": {
      +                            "anyOf": [
      +                              {
      +                                "additionalProperties": {},
      +                                "properties": {
      +                                  "fixed": {
      +                                    "type": [
      +                                      "string",
      +                                      "null"
      +                                    ]
      +                                  },
      +                                  "introduced": {
      +                                    "type": [
      +                                      "string",
      +                                      "null"
      +                                    ]
      +                                  },
      +                                  "last_affected": {
      +                                    "type": [
      +                                      "string",
      +                                      "null"
      +                                    ]
      +                                  }
      +                                },
      +                                "type": "object"
      +                              },
      +                              {
      +                                "type": "null"
      +                              }
      +                            ],
      +                            "description": "The advisory interval that holds this component's version (fixed is exclusive, last_affected inclusive); absent when no interval holds it."
      +                          },
      +                          "kev_listed": {
      +                            "description": "Whether the CVE is in CISA's Known Exploited Vulnerabilities catalog, as EchelonGraph's record holds it.",
      +                            "type": [
      +                              "boolean",
      +                              "null"
      +                            ]
      +                          },
      +                          "match_reason": {
      +                            "description": "Why the advisory applies, with the advisory interval the version falls inside: \"[A, B)\" names that interval's fixed bound B (another interval of the advisory can still hold B; fixed_in is the version past them all), \"[A, B]\" the last affected version B. On an upstream row it ends by naming the source package and the purl name.",
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          }
      +                        },
      +                        "type": "object"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "matches_truncated": {
      +                  "additionalProperties": {},
      +                  "description": "Present only on a row whose matches the structured result's cut (DATA CUT) shortened or left out: total, the row's matches in the answer; returned, the ones data keeps (chosen used in ransomware first, then the other KEV-listed ones, then highest effective_score, and listed in the API's order); and, while they fit, of all its matches, the CVE IDs KEV-listed (kev_listed_cve_ids) or used in ransomware (ransomware_cve_ids), and the distinct fixed_in versions (fixed_in_versions). The same call with full: true returns every match.",
      +                  "properties": {
      +                    "fixed_in_versions": {
      +                      "items": {
      +                        "type": "string"
      +                      },
      +                      "type": "array"
      +                    },
      +                    "kev_listed_cve_ids": {
      +                      "items": {
      +                        "type": "string"
      +                      },
      +                      "type": "array"
      +                    },
      +                    "ransomware_cve_ids": {
      +                      "items": {
      +                        "type": "string"
      +                      },
      +                      "type": "array"
      +                    },
      +                    "returned": {
      +                      "maximum": 9007199254740991,
      +                      "minimum": 0,
      +                      "type": "integer"
      +                    },
      +                    "total": {
      +                      "maximum": 9007199254740991,
      +                      "minimum": 0,
      +                      "type": "integer"
      +                    }
      +                  },
      +                  "required": [
      +                    "total",
      +                    "returned"
      +                  ],
      +                  "type": "object"
      +                },
      +                "not_affected_count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "not_assessed_reason": {
      +                  "description": "Why assessed is false: package_not_in_advisory_corpus, no_decidable_advisory, advisory_lookup_failed, candidate_window_truncated, time_budget, distro_release_unknown, purl_type_unsupported, version_missing or invalid_component.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "package": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "purl": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "purl_name": {
      +                  "description": "Upstream rows: the purl's own (binary) package name, not looked up.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "undetermined": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "additionalProperties": {},
      +                        "properties": {
      +                          "cve_id": {
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          }
      +                        },
      +                        "type": "object"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "undetermined_count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "verdict": {
      +                  "description": "affected (count > 0); not_affected (assessed, no match, nothing undetermined: the only clean verdict); undetermined (advisories name the package, at least one could not be decided, none matched: not clean); not_assessed (no verdict: not clean).",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "version": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                }
      +              },
      +              "type": "object"
      +            },
      +            "type": "array"
      +          },
      +          "summary": {
      +            "additionalProperties": {},
      +            "properties": {
      +              "affected": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "components": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "corpus_cache_max_age_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "elapsed_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "lookups": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_affected": {
      +                "description": "Components with a decided, clean verdict.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_assessed": {
      +                "description": "Components with no verdict: not clean.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_assessed_by_reason": {
      +                "anyOf": [
      +                  {
      +                    "additionalProperties": {},
      +                    "properties": {
      +                      "advisory_lookup_failed": {
      +                        "type": "number"
      +                      },
      +                      "candidate_window_truncated": {
      +                        "type": "number"
      +                      },
      +                      "distro_release_unknown": {
      +                        "description": "deb, apk or rpm purls without a distro qualifier naming the release, which EchelonGraph does not guess.",
      +                        "type": "number"
      +                      },
      +                      "invalid_component": {
      +                        "type": "number"
      +                      },
      +                      "no_decidable_advisory": {
      +                        "type": "number"
      +                      },
      +                      "package_not_in_advisory_corpus": {
      +                        "type": "number"
      +                      },
      +                      "purl_type_unsupported": {
      +                        "type": "number"
      +                      },
      +                      "time_budget": {
      +                        "description": "Components the batch's time budget ran out before: not clean; check them again.",
      +                        "type": "number"
      +                      },
      +                      "version_missing": {
      +                        "type": "number"
      +                      }
      +                    },
      +                    "type": "object"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "The not_assessed components, counted by not_assessed_reason."
      +              },
      +              "partial": {
      +                "description": "true when the time budget ran out before every component was looked up.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "time_budget_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "undetermined": {
      +                "description": "Components whose advisories could not all be decided and none matched: not clean.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              }
      +            },
      +            "type": "object"
      +          }
      +        },
      +        "type": "object"
      +      },
      +      "data_truncated": {
      +        "description": "false: data is the answer whole. true: the structured result would pass 30,000 characters of JSON with data whole, so data is cut (DATA CUT): the entries of some of its lists keep only some of their fields, or the lists only some of their entries, truncated gives each, and no string is shortened. The same call with full: true returns data whole.",
      +        "type": "boolean"
      +      },
      +      "freshness": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": {},
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "The producing radar's last completed check (last_run_at), where the API serves one."
      +      },
      +      "measured_at": {
      +        "anyOf": [
      +          {
      +            "description": "An RFC 3339 instant.",
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "When the underlying observation was made, as the API states it; null when the answer does not say or holds no observation."
      +      },
      +      "method": {
      +        "description": "How the numbers were produced.",
      +        "type": "string"
      +      },
      +      "notes": {
      +        "description": "Caveats, one sentence each.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "state": {
      +        "description": "measured: a measurement of what was asked; an exposure count is measured only with measured_at and method. not_assessed: the answer holds no dated measurement of what was asked, so no count in it is presented as one; it can still relay a count, as what the source holds on record, undated, and its notes (and exposure_state, where the result carries it) say what each count is.",
      +        "enum": [
      +          "measured",
      +          "not_assessed"
      +        ],
      +        "type": "string"
      +      },
      +      "truncated": {
      +        "additionalProperties": {
      +          "additionalProperties": {},
      +          "properties": {
      +            "returned": {
      +              "maximum": 9007199254740991,
      +              "minimum": 0,
      +              "type": "integer"
      +            },
      +            "thinned": {
      +              "const": true,
      +              "type": "boolean"
      +            },
      +            "total": {
      +              "maximum": 9007199254740991,
      +              "minimum": 0,
      +              "type": "integer"
      +            }
      +          },
      +          "required": [
      +            "total",
      +            "returned"
      +          ],
      +          "type": "object"
      +        },
      +        "description": "Present when data_truncated is true: each list of data that was cut, by its path in data (a dot between the names of an object and a list inside it), with total, its entries in the answer, returned, the entries data holds, and thinned, true when each of them keeps only some of its fields.",
      +        "propertyNames": {
      +          "type": "string"
      +        },
      +        "type": "object"
      +      }
      +    },
      +    "required": [
      +      "state",
      +      "measured_at",
      +      "method",
      +      "coverage",
      +      "freshness",
      +      "notes",
      +      "data_truncated",
      +      "data"
      +    ],
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "coverage": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "properties": {
      +              "batch_size": {
      +                "description": "The most purls one request carries (the API's cap per request).",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches": {
      +                "description": "Requests the distinct purls make, at batch_size each.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches_sent": {
      +                "description": "Of those, the ones the API answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "components_in_document": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      +              },
      +              "distinct_purls": {
      +                "description": "Distinct purls to check: sent plus not_sent.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "duplicates_removed": {
      +                "description": "Purls that appeared more than once and were sent once.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "input": {
      +                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      +                "enum": [
      +                  "purls",
      +                  "cyclonedx",
      +                  "spdx"
      +                ],
      +                "type": "string"
      +              },
      +              "not_assessed": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Of those sent, the components with no verdict, as the answer counts them."
      +              },
      +              "not_sent": {
      +                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "not_sent_reason": {
      +                "anyOf": [
      +                  {
      +                    "enum": [
      +                      "time_budget",
      +                      "rate_limited",
      +                      "request_failed"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      +              },
      +              "partial": {
      +                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "rate_limit_waits": {
      +                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "sent": {
      +                "description": "Distinct purls sent and answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "waited_ms": {
      +                "description": "Milliseconds spent in those waits.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "with_purl": {
      +                "description": "Of those, the ones carrying a purl.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "without_purl": {
      +                "description": "The ones without a purl: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              }
      +            },
      +            "required": [
      +              "input",
      +              "components_in_document",
      +              "with_purl",
      +              "without_purl",
      +              "duplicates_removed",
      +              "distinct_purls",
      +              "batch_size",
      +              "batches",
      +              "batches_sent",
      +              "sent",
      +              "not_sent",
      +              "not_sent_reason",
      +              "rate_limit_waits",
      +              "waited_ms",
      +              "not_assessed",
      +              "partial"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ]
      +      },
      +      "error": {
      +        "additionalProperties": false,
      +        "properties": {
      +          "kind": {
      +            "description": "What failed: the request (network, timeout), the answer (http, not_json, not_object, unexpected_shape), the input, or this server.",
      +            "enum": [
      +              "network",
      +              "timeout",
      +              "http",
      +              "not_json",
      +              "not_object",
      +              "invalid_input",
      +              "internal",
      +              "unexpected_shape",
      +              "radars"
      +            ],
      +            "type": "string"
      +          },
      +          "message": {
      +            "description": "The cause: the API's own message, or what went wrong.",
      +            "type": "string"
      +          },
      +          "path": {
      +            "description": "The API path requested, when a request was made.",
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "status": {
      +            "anyOf": [
      +              {
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "description": "The HTTP status, when the API answered one."
      +          }
      +        },
      +        "required": [
      +          "kind",
      +          "path",
      +          "status",
      +          "message"
      +        ],
      +        "type": "object"
      +      },
      +      "freshness": {
      +        "type": "null"
      +      },
      +      "measured_at": {
      +        "type": "null"
      +      },
      +      "method": {
      +        "type": "null"
      +      },
      +      "notes": {
      +        "description": "Caveats, one sentence each.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "state": {
      +        "description": "failed: the lookup did not complete. invalid_input: the input was refused, so nothing was looked up. Neither is a finding.",
      +        "enum": [
      +          "failed",
      +          "invalid_input"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "state",
      +      "measured_at",
      +      "method",
      +      "coverage",
      +      "freshness",
      +      "notes",
      +      "error"
      +    ],
      +    "type": "object"
      +  }
      +]
  2. Changed1 schema field changed
    • changedOutput schema / oneOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "coverage": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "properties": {
      -              "batch_size": {
      -                "description": "The most purls one request carries (the API's cap per request).",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches": {
      -                "description": "Requests the distinct purls make, at batch_size each.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches_sent": {
      -                "description": "Of those, the ones the API answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "components_in_document": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      -              },
      -              "distinct_purls": {
      -                "description": "Distinct purls to check: sent plus not_sent.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "duplicates_removed": {
      -                "description": "Purls that appeared more than once and were sent once.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "input": {
      -                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      -                "enum": [
      -                  "purls",
      -                  "cyclonedx",
      -                  "spdx"
      -                ],
      -                "type": "string"
      -              },
      -              "not_assessed": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Of those sent, the components with no verdict, as the answer counts them."
      -              },
      -              "not_sent": {
      -                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "not_sent_reason": {
      -                "anyOf": [
      -                  {
      -                    "enum": [
      -                      "time_budget",
      -                      "rate_limited",
      -                      "request_failed"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      -              },
      -              "partial": {
      -                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "rate_limit_waits": {
      -                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "sent": {
      -                "description": "Distinct purls sent and answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "waited_ms": {
      -                "description": "Milliseconds spent in those waits.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "with_purl": {
      -                "description": "Of those, the ones carrying a purl.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "without_purl": {
      -                "description": "The ones without a purl: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              }
      -            },
      -            "required": [
      -              "input",
      -              "components_in_document",
      -              "with_purl",
      -              "without_purl",
      -              "duplicates_removed",
      -              "distinct_purls",
      -              "batch_size",
      -              "batches",
      -              "batches_sent",
      -              "sent",
      -              "not_sent",
      -              "not_sent_reason",
      -              "rate_limit_waits",
      -              "waited_ms",
      -              "not_assessed",
      -              "partial"
      -            ],
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "What the answer covers; null where the answer says nothing about it."
      -      },
      -      "data": {
      -        "additionalProperties": {},
      -        "properties": {
      -          "answered_at": {
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "components": {
      -            "type": [
      -              "number",
      -              "null"
      -            ]
      -          },
      -          "match_layer": {
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "not_sent_purls": {
      -            "anyOf": [
      -              {
      -                "items": {
      -                  "type": "string"
      -                },
      -                "type": "array"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "description": "The distinct purls not sent (coverage.not_sent_reason says why): not checked, and not clean. Present only when some were not sent."
      -          },
      -          "results": {
      -            "items": {
      -              "additionalProperties": {},
      -              "properties": {
      -                "advisories_considered": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "assessed": {
      -                  "description": "Whether the matcher produced a verdict for this component. false is never clean.",
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "candidates_capped": {
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "capped": {
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "code": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "cve_ids": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "type": "string"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "ecosystem": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "error": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "index": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "input_kind": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matched_package": {
      -                  "description": "The package name the advisories were looked up by (#2836): the purl's own name, or, for a deb or apk purl with an upstream qualifier, the source package it names, which is how Debian and Alpine file advisories.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matched_version_from": {
      -                  "description": "Upstream rows: upstream when the qualifier named a version (upstream=name@version) and it was used, purl when the purl's own version was.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matched_via": {
      -                  "description": "Which name matched_package is: purl_name (the purl's name), upstream (the purl's upstream qualifier) or coordinates (the component's package).",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matches": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "additionalProperties": {},
      -                        "properties": {
      -                          "cve_id": {
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          },
      -                          "epss_score": {
      -                            "description": "The CVE's EPSS score (FIRST), as EchelonGraph's record holds it; null when it has none.",
      -                            "type": [
      -                              "number",
      -                              "null"
      -                            ]
      -                          },
      -                          "fixed_in": {
      -                            "description": "The lowest version above this component's that no affected interval of the CVE's record for the package holds, always above it: the fixed bound of the interval that holds the component's version, moved past any overlapping interval that still holds that bound (fixed_in_detail then names it). null when no such version is on record (an interval on the way records no fix: it ends at last_affected, or has no end) or no interval holds the version, with fixed_in_reason saying which.",
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          },
      -                          "fixed_in_detail": {
      -                            "description": "Set when fixed_in is not the fixed bound of the interval that holds the component's version: the overlapping intervals of the same record it moved past, which still hold that bound.",
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          },
      -                          "fixed_in_reason": {
      -                            "description": "Why fixed_in is null; absent when fixed_in is a version.",
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          },
      -                          "interval": {
      -                            "anyOf": [
      -                              {
      -                                "additionalProperties": {},
      -                                "properties": {
      -                                  "fixed": {
      -                                    "type": [
      -                                      "string",
      -                                      "null"
      -                                    ]
      -                                  },
      -                                  "introduced": {
      -                                    "type": [
      -                                      "string",
      -                                      "null"
      -                                    ]
      -                                  },
      -                                  "last_affected": {
      -                                    "type": [
      -                                      "string",
      -                                      "null"
      -                                    ]
      -                                  }
      -                                },
      -                                "type": "object"
      -                              },
      -                              {
      -                                "type": "null"
      -                              }
      -                            ],
      -                            "description": "The advisory interval that holds this component's version (fixed is exclusive, last_affected inclusive); absent when no interval holds it."
      -                          },
      -                          "kev_listed": {
      -                            "description": "Whether the CVE is in CISA's Known Exploited Vulnerabilities catalog, as EchelonGraph's record holds it.",
      -                            "type": [
      -                              "boolean",
      -                              "null"
      -                            ]
      -                          },
      -                          "match_reason": {
      -                            "description": "Why the advisory applies, with the advisory interval the version falls inside: \"[A, B)\" names that interval's fixed bound B (another interval of the advisory can still hold B; fixed_in is the version past them all), \"[A, B]\" the last affected version B. On an upstream row it ends by naming the source package and the purl name.",
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          }
      -                        },
      -                        "type": "object"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "not_affected_count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "not_assessed_reason": {
      -                  "description": "Why assessed is false: package_not_in_advisory_corpus, no_decidable_advisory, advisory_lookup_failed, candidate_window_truncated, time_budget, distro_release_unknown, purl_type_unsupported, version_missing or invalid_component.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "package": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "purl": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "purl_name": {
      -                  "description": "Upstream rows: the purl's own (binary) package name, not looked up.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "undetermined": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "additionalProperties": {},
      -                        "properties": {
      -                          "cve_id": {
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          }
      -                        },
      -                        "type": "object"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "undetermined_count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "verdict": {
      -                  "description": "affected (count > 0); not_affected (assessed, no match, nothing undetermined: the only clean verdict); undetermined (advisories name the package, at least one could not be decided, none matched: not clean); not_assessed (no verdict: not clean).",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "version": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                }
      -              },
      -              "type": "object"
      -            },
      -            "type": "array"
      -          },
      -          "summary": {
      -            "additionalProperties": {},
      -            "properties": {
      -              "affected": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "components": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "corpus_cache_max_age_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "elapsed_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "lookups": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_affected": {
      -                "description": "Components with a decided, clean verdict.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_assessed": {
      -                "description": "Components with no verdict: not clean.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_assessed_by_reason": {
      -                "anyOf": [
      -                  {
      -                    "additionalProperties": {},
      -                    "properties": {
      -                      "advisory_lookup_failed": {
      -                        "type": "number"
      -                      },
      -                      "candidate_window_truncated": {
      -                        "type": "number"
      -                      },
      -                      "distro_release_unknown": {
      -                        "description": "deb, apk or rpm purls without a distro qualifier naming the release, which EchelonGraph does not guess.",
      -                        "type": "number"
      -                      },
      -                      "invalid_component": {
      -                        "type": "number"
      -                      },
      -                      "no_decidable_advisory": {
      -                        "type": "number"
      -                      },
      -                      "package_not_in_advisory_corpus": {
      -                        "type": "number"
      -                      },
      -                      "purl_type_unsupported": {
      -                        "type": "number"
      -                      },
      -                      "time_budget": {
      -                        "description": "Components the batch's time budget ran out before: not clean; check them again.",
      -                        "type": "number"
      -                      },
      -                      "version_missing": {
      -                        "type": "number"
      -                      }
      -                    },
      -                    "type": "object"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "The not_assessed components, counted by not_assessed_reason."
      -              },
      -              "partial": {
      -                "description": "true when the time budget ran out before every component was looked up.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "time_budget_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "undetermined": {
      -                "description": "Components whose advisories could not all be decided and none matched: not clean.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              }
      -            },
      -            "type": "object"
      -          }
      -        },
      -        "type": "object"
      -      },
      -      "data_truncated": {
      -        "description": "false: data is the answer whole. true: the structured result would pass 30,000 characters of JSON with data whole, so data is cut (DATA CUT): the entries of some of its lists keep only some of their fields, or the lists only some of their entries, truncated gives each, and no string is shortened. The same call with full: true returns data whole.",
      -        "type": "boolean"
      -      },
      -      "freshness": {
      -        "description": "The producing radar's last completed check (last_run_at), where the API serves one.",
      -        "type": "null"
      -      },
      -      "measured_at": {
      -        "anyOf": [
      -          {
      -            "description": "An RFC 3339 instant.",
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "When the underlying observation was made, as the API states it; null when the answer does not say or holds no observation."
      -      },
      -      "method": {
      -        "description": "How the numbers were produced.",
      -        "type": "string"
      -      },
      -      "notes": {
      -        "description": "Caveats, one sentence each.",
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "state": {
      -        "description": "measured: a measurement of what was asked; an exposure count is measured only with measured_at and method. not_assessed: the answer holds no dated measurement of what was asked, so no count in it is presented as one; it can still relay a count, as what the source holds on record, undated, and its notes (and exposure_state, where the result carries it) say what each count is.",
      -        "enum": [
      -          "measured",
      -          "not_assessed"
      -        ],
      -        "type": "string"
      -      },
      -      "truncated": {
      -        "additionalProperties": {
      -          "additionalProperties": false,
      -          "properties": {
      -            "returned": {
      -              "maximum": 9007199254740991,
      -              "minimum": 0,
      -              "type": "integer"
      -            },
      -            "thinned": {
      -              "const": true,
      -              "type": "boolean"
      -            },
      -            "total": {
      -              "maximum": 9007199254740991,
      -              "minimum": 0,
      -              "type": "integer"
      -            }
      -          },
      -          "required": [
      -            "total",
      -            "returned"
      -          ],
      -          "type": "object"
      -        },
      -        "description": "Present when data_truncated is true: each list of data that was cut, by its path in data (a dot between the names of an object and a list inside it), with total, its entries in the answer, returned, the entries data holds, and thinned, true when each of them keeps only some of its fields.",
      -        "propertyNames": {
      -          "type": "string"
      -        },
      -        "type": "object"
      -      }
      -    },
      -    "required": [
      -      "state",
      -      "measured_at",
      -      "method",
      -      "coverage",
      -      "freshness",
      -      "notes",
      -      "data_truncated",
      -      "data"
      -    ],
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "coverage": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "properties": {
      -              "batch_size": {
      -                "description": "The most purls one request carries (the API's cap per request).",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches": {
      -                "description": "Requests the distinct purls make, at batch_size each.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches_sent": {
      -                "description": "Of those, the ones the API answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "components_in_document": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      -              },
      -              "distinct_purls": {
      -                "description": "Distinct purls to check: sent plus not_sent.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "duplicates_removed": {
      -                "description": "Purls that appeared more than once and were sent once.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "input": {
      -                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      -                "enum": [
      -                  "purls",
      -                  "cyclonedx",
      -                  "spdx"
      -                ],
      -                "type": "string"
      -              },
      -              "not_assessed": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Of those sent, the components with no verdict, as the answer counts them."
      -              },
      -              "not_sent": {
      -                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "not_sent_reason": {
      -                "anyOf": [
      -                  {
      -                    "enum": [
      -                      "time_budget",
      -                      "rate_limited",
      -                      "request_failed"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      -              },
      -              "partial": {
      -                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "rate_limit_waits": {
      -                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "sent": {
      -                "description": "Distinct purls sent and answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "waited_ms": {
      -                "description": "Milliseconds spent in those waits.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "with_purl": {
      -                "description": "Of those, the ones carrying a purl.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "without_purl": {
      -                "description": "The ones without a purl: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              }
      -            },
      -            "required": [
      -              "input",
      -              "components_in_document",
      -              "with_purl",
      -              "without_purl",
      -              "duplicates_removed",
      -              "distinct_purls",
      -              "batch_size",
      -              "batches",
      -              "batches_sent",
      -              "sent",
      -              "not_sent",
      -              "not_sent_reason",
      -              "rate_limit_waits",
      -              "waited_ms",
      -              "not_assessed",
      -              "partial"
      -            ],
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ]
      -      },
      -      "error": {
      -        "additionalProperties": false,
      -        "properties": {
      -          "kind": {
      -            "description": "What failed: the request (network, timeout), the answer (http, not_json, not_object, unexpected_shape), the input, or this server.",
      -            "enum": [
      -              "network",
      -              "timeout",
      -              "http",
      -              "not_json",
      -              "not_object",
      -              "invalid_input",
      -              "internal",
      -              "unexpected_shape",
      -              "radars"
      -            ],
      -            "type": "string"
      -          },
      -          "message": {
      -            "description": "The cause: the API's own message, or what went wrong.",
      -            "type": "string"
      -          },
      -          "path": {
      -            "description": "The API path requested, when a request was made.",
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "status": {
      -            "anyOf": [
      -              {
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "description": "The HTTP status, when the API answered one."
      -          }
      -        },
      -        "required": [
      -          "kind",
      -          "path",
      -          "status",
      -          "message"
      -        ],
      -        "type": "object"
      -      },
      -      "freshness": {
      -        "type": "null"
      -      },
      -      "measured_at": {
      -        "type": "null"
      -      },
      -      "method": {
      -        "type": "null"
      -      },
      -      "notes": {
      -        "description": "Caveats, one sentence each.",
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "state": {
      -        "description": "failed: the lookup did not complete. invalid_input: the input was refused, so nothing was looked up. Neither is a finding.",
      -        "enum": [
      -          "failed",
      -          "invalid_input"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "state",
      -      "measured_at",
      -      "method",
      -      "coverage",
      -      "freshness",
      -      "notes",
      -      "error"
      -    ],
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": {},
      +    "properties": {
      +      "coverage": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "properties": {
      +              "batch_size": {
      +                "description": "The most purls one request carries (the API's cap per request).",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches": {
      +                "description": "Requests the distinct purls make, at batch_size each.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches_sent": {
      +                "description": "Of those, the ones the API answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "components_in_document": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      +              },
      +              "distinct_purls": {
      +                "description": "Distinct purls to check: sent plus not_sent.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "duplicates_removed": {
      +                "description": "Purls that appeared more than once and were sent once.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "input": {
      +                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      +                "enum": [
      +                  "purls",
      +                  "cyclonedx",
      +                  "spdx"
      +                ],
      +                "type": "string"
      +              },
      +              "not_assessed": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Of those sent, the components with no verdict, as the answer counts them."
      +              },
      +              "not_sent": {
      +                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "not_sent_reason": {
      +                "anyOf": [
      +                  {
      +                    "enum": [
      +                      "time_budget",
      +                      "rate_limited",
      +                      "request_failed"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      +              },
      +              "partial": {
      +                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "rate_limit_waits": {
      +                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "sent": {
      +                "description": "Distinct purls sent and answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "waited_ms": {
      +                "description": "Milliseconds spent in those waits.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "with_purl": {
      +                "description": "Of those, the ones carrying a purl.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "without_purl": {
      +                "description": "The ones without a purl: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              }
      +            },
      +            "required": [
      +              "input",
      +              "components_in_document",
      +              "with_purl",
      +              "without_purl",
      +              "duplicates_removed",
      +              "distinct_purls",
      +              "batch_size",
      +              "batches",
      +              "batches_sent",
      +              "sent",
      +              "not_sent",
      +              "not_sent_reason",
      +              "rate_limit_waits",
      +              "waited_ms",
      +              "not_assessed",
      +              "partial"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "What the answer covers; null where the answer says nothing about it."
      +      },
      +      "data": {
      +        "additionalProperties": {},
      +        "properties": {
      +          "answered_at": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "components": {
      +            "type": [
      +              "number",
      +              "null"
      +            ]
      +          },
      +          "match_layer": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "not_sent_purls": {
      +            "anyOf": [
      +              {
      +                "items": {
      +                  "type": "string"
      +                },
      +                "type": "array"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "description": "The distinct purls not sent (coverage.not_sent_reason says why): not checked, and not clean. Present only when some were not sent."
      +          },
      +          "results": {
      +            "items": {
      +              "additionalProperties": {},
      +              "properties": {
      +                "advisories_considered": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "assessed": {
      +                  "description": "Whether the matcher produced a verdict for this component. false is never clean.",
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "candidates_capped": {
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "capped": {
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "code": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "cve_ids": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "type": "string"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "ecosystem": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "error": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "index": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "input_kind": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matched_package": {
      +                  "description": "The package name the advisories were looked up by (#2836): the purl's own name, or, for a deb or apk purl with an upstream qualifier, the source package it names, which is how Debian and Alpine file advisories.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matched_version_from": {
      +                  "description": "Upstream rows: upstream when the qualifier named a version (upstream=name@version) and it was used, purl when the purl's own version was.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matched_via": {
      +                  "description": "Which name matched_package is: purl_name (the purl's name), upstream (the purl's upstream qualifier) or coordinates (the component's package).",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matches": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "additionalProperties": {},
      +                        "properties": {
      +                          "cve_id": {
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          },
      +                          "epss_score": {
      +                            "description": "The CVE's EPSS score (FIRST), as EchelonGraph's record holds it; null when it has none.",
      +                            "type": [
      +                              "number",
      +                              "null"
      +                            ]
      +                          },
      +                          "fixed_in": {
      +                            "description": "The lowest version above this component's that no affected interval of the CVE's record for the package holds, always above it: the fixed bound of the interval that holds the component's version, moved past any overlapping interval that still holds that bound (fixed_in_detail then names it). null when no such version is on record (an interval on the way records no fix: it ends at last_affected, or has no end) or no interval holds the version, with fixed_in_reason saying which.",
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          },
      +                          "fixed_in_detail": {
      +                            "description": "Set when fixed_in is not the fixed bound of the interval that holds the component's version: the overlapping intervals of the same record it moved past, which still hold that bound.",
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          },
      +                          "fixed_in_reason": {
      +                            "description": "Why fixed_in is null; absent when fixed_in is a version.",
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          },
      +                          "interval": {
      +                            "anyOf": [
      +                              {
      +                                "additionalProperties": {},
      +                                "properties": {
      +                                  "fixed": {
      +                                    "type": [
      +                                      "string",
      +                                      "null"
      +                                    ]
      +                                  },
      +                                  "introduced": {
      +                                    "type": [
      +                                      "string",
      +                                      "null"
      +                                    ]
      +                                  },
      +                                  "last_affected": {
      +                                    "type": [
      +                                      "string",
      +                                      "null"
      +                                    ]
      +                                  }
      +                                },
      +                                "type": "object"
      +                              },
      +                              {
      +                                "type": "null"
      +                              }
      +                            ],
      +                            "description": "The advisory interval that holds this component's version (fixed is exclusive, last_affected inclusive); absent when no interval holds it."
      +                          },
      +                          "kev_listed": {
      +                            "description": "Whether the CVE is in CISA's Known Exploited Vulnerabilities catalog, as EchelonGraph's record holds it.",
      +                            "type": [
      +                              "boolean",
      +                              "null"
      +                            ]
      +                          },
      +                          "match_reason": {
      +                            "description": "Why the advisory applies, with the advisory interval the version falls inside: \"[A, B)\" names that interval's fixed bound B (another interval of the advisory can still hold B; fixed_in is the version past them all), \"[A, B]\" the last affected version B. On an upstream row it ends by naming the source package and the purl name.",
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          }
      +                        },
      +                        "type": "object"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "matches_truncated": {
      +                  "additionalProperties": false,
      +                  "description": "Present only on a row whose matches the structured result's cut (DATA CUT) shortened or left out: total, the row's matches in the answer; returned, the ones data keeps (its first, highest effective_score first); and, while they fit, of all its matches, the CVE IDs KEV-listed (kev_listed_cve_ids) or used in ransomware (ransomware_cve_ids), and the distinct fixed_in versions (fixed_in_versions). The same call with full: true returns every match.",
      +                  "properties": {
      +                    "fixed_in_versions": {
      +                      "items": {
      +                        "type": "string"
      +                      },
      +                      "type": "array"
      +                    },
      +                    "kev_listed_cve_ids": {
      +                      "items": {
      +                        "type": "string"
      +                      },
      +                      "type": "array"
      +                    },
      +                    "ransomware_cve_ids": {
      +                      "items": {
      +                        "type": "string"
      +                      },
      +                      "type": "array"
      +                    },
      +                    "returned": {
      +                      "maximum": 9007199254740991,
      +                      "minimum": 0,
      +                      "type": "integer"
      +                    },
      +                    "total": {
      +                      "maximum": 9007199254740991,
      +                      "minimum": 0,
      +                      "type": "integer"
      +                    }
      +                  },
      +                  "required": [
      +                    "total",
      +                    "returned"
      +                  ],
      +                  "type": "object"
      +                },
      +                "not_affected_count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "not_assessed_reason": {
      +                  "description": "Why assessed is false: package_not_in_advisory_corpus, no_decidable_advisory, advisory_lookup_failed, candidate_window_truncated, time_budget, distro_release_unknown, purl_type_unsupported, version_missing or invalid_component.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "package": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "purl": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "purl_name": {
      +                  "description": "Upstream rows: the purl's own (binary) package name, not looked up.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "undetermined": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "additionalProperties": {},
      +                        "properties": {
      +                          "cve_id": {
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          }
      +                        },
      +                        "type": "object"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "undetermined_count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "verdict": {
      +                  "description": "affected (count > 0); not_affected (assessed, no match, nothing undetermined: the only clean verdict); undetermined (advisories name the package, at least one could not be decided, none matched: not clean); not_assessed (no verdict: not clean).",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "version": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                }
      +              },
      +              "type": "object"
      +            },
      +            "type": "array"
      +          },
      +          "summary": {
      +            "additionalProperties": {},
      +            "properties": {
      +              "affected": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "components": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "corpus_cache_max_age_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "elapsed_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "lookups": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_affected": {
      +                "description": "Components with a decided, clean verdict.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_assessed": {
      +                "description": "Components with no verdict: not clean.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_assessed_by_reason": {
      +                "anyOf": [
      +                  {
      +                    "additionalProperties": {},
      +                    "properties": {
      +                      "advisory_lookup_failed": {
      +                        "type": "number"
      +                      },
      +                      "candidate_window_truncated": {
      +                        "type": "number"
      +                      },
      +                      "distro_release_unknown": {
      +                        "description": "deb, apk or rpm purls without a distro qualifier naming the release, which EchelonGraph does not guess.",
      +                        "type": "number"
      +                      },
      +                      "invalid_component": {
      +                        "type": "number"
      +                      },
      +                      "no_decidable_advisory": {
      +                        "type": "number"
      +                      },
      +                      "package_not_in_advisory_corpus": {
      +                        "type": "number"
      +                      },
      +                      "purl_type_unsupported": {
      +                        "type": "number"
      +                      },
      +                      "time_budget": {
      +                        "description": "Components the batch's time budget ran out before: not clean; check them again.",
      +                        "type": "number"
      +                      },
      +                      "version_missing": {
      +                        "type": "number"
      +                      }
      +                    },
      +                    "type": "object"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "The not_assessed components, counted by not_assessed_reason."
      +              },
      +              "partial": {
      +                "description": "true when the time budget ran out before every component was looked up.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "time_budget_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "undetermined": {
      +                "description": "Components whose advisories could not all be decided and none matched: not clean.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              }
      +            },
      +            "type": "object"
      +          }
      +        },
      +        "type": "object"
      +      },
      +      "data_truncated": {
      +        "description": "false: data is the answer whole. true: the structured result would pass 30,000 characters of JSON with data whole, so data is cut (DATA CUT): the entries of some of its lists keep only some of their fields, or the lists only some of their entries, truncated gives each, and no string is shortened. The same call with full: true returns data whole.",
      +        "type": "boolean"
      +      },
      +      "freshness": {
      +        "description": "The producing radar's last completed check (last_run_at), where the API serves one.",
      +        "type": "null"
      +      },
      +      "measured_at": {
      +        "anyOf": [
      +          {
      +            "description": "An RFC 3339 instant.",
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "When the underlying observation was made, as the API states it; null when the answer does not say or holds no observation."
      +      },
      +      "method": {
      +        "description": "How the numbers were produced.",
      +        "type": "string"
      +      },
      +      "notes": {
      +        "description": "Caveats, one sentence each.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "state": {
      +        "description": "measured: a measurement of what was asked; an exposure count is measured only with measured_at and method. not_assessed: the answer holds no dated measurement of what was asked, so no count in it is presented as one; it can still relay a count, as what the source holds on record, undated, and its notes (and exposure_state, where the result carries it) say what each count is.",
      +        "enum": [
      +          "measured",
      +          "not_assessed"
      +        ],
      +        "type": "string"
      +      },
      +      "truncated": {
      +        "additionalProperties": {
      +          "additionalProperties": false,
      +          "properties": {
      +            "returned": {
      +              "maximum": 9007199254740991,
      +              "minimum": 0,
      +              "type": "integer"
      +            },
      +            "thinned": {
      +              "const": true,
      +              "type": "boolean"
      +            },
      +            "total": {
      +              "maximum": 9007199254740991,
      +              "minimum": 0,
      +              "type": "integer"
      +            }
      +          },
      +          "required": [
      +            "total",
      +            "returned"
      +          ],
      +          "type": "object"
      +        },
      +        "description": "Present when data_truncated is true: each list of data that was cut, by its path in data (a dot between the names of an object and a list inside it), with total, its entries in the answer, returned, the entries data holds, and thinned, true when each of them keeps only some of its fields.",
      +        "propertyNames": {
      +          "type": "string"
      +        },
      +        "type": "object"
      +      }
      +    },
      +    "required": [
      +      "state",
      +      "measured_at",
      +      "method",
      +      "coverage",
      +      "freshness",
      +      "notes",
      +      "data_truncated",
      +      "data"
      +    ],
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "coverage": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "properties": {
      +              "batch_size": {
      +                "description": "The most purls one request carries (the API's cap per request).",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches": {
      +                "description": "Requests the distinct purls make, at batch_size each.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches_sent": {
      +                "description": "Of those, the ones the API answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "components_in_document": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      +              },
      +              "distinct_purls": {
      +                "description": "Distinct purls to check: sent plus not_sent.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "duplicates_removed": {
      +                "description": "Purls that appeared more than once and were sent once.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "input": {
      +                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      +                "enum": [
      +                  "purls",
      +                  "cyclonedx",
      +                  "spdx"
      +                ],
      +                "type": "string"
      +              },
      +              "not_assessed": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Of those sent, the components with no verdict, as the answer counts them."
      +              },
      +              "not_sent": {
      +                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "not_sent_reason": {
      +                "anyOf": [
      +                  {
      +                    "enum": [
      +                      "time_budget",
      +                      "rate_limited",
      +                      "request_failed"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      +              },
      +              "partial": {
      +                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "rate_limit_waits": {
      +                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "sent": {
      +                "description": "Distinct purls sent and answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "waited_ms": {
      +                "description": "Milliseconds spent in those waits.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "with_purl": {
      +                "description": "Of those, the ones carrying a purl.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "without_purl": {
      +                "description": "The ones without a purl: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              }
      +            },
      +            "required": [
      +              "input",
      +              "components_in_document",
      +              "with_purl",
      +              "without_purl",
      +              "duplicates_removed",
      +              "distinct_purls",
      +              "batch_size",
      +              "batches",
      +              "batches_sent",
      +              "sent",
      +              "not_sent",
      +              "not_sent_reason",
      +              "rate_limit_waits",
      +              "waited_ms",
      +              "not_assessed",
      +              "partial"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ]
      +      },
      +      "error": {
      +        "additionalProperties": false,
      +        "properties": {
      +          "kind": {
      +            "description": "What failed: the request (network, timeout), the answer (http, not_json, not_object, unexpected_shape), the input, or this server.",
      +            "enum": [
      +              "network",
      +              "timeout",
      +              "http",
      +              "not_json",
      +              "not_object",
      +              "invalid_input",
      +              "internal",
      +              "unexpected_shape",
      +              "radars"
      +            ],
      +            "type": "string"
      +          },
      +          "message": {
      +            "description": "The cause: the API's own message, or what went wrong.",
      +            "type": "string"
      +          },
      +          "path": {
      +            "description": "The API path requested, when a request was made.",
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "status": {
      +            "anyOf": [
      +              {
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "description": "The HTTP status, when the API answered one."
      +          }
      +        },
      +        "required": [
      +          "kind",
      +          "path",
      +          "status",
      +          "message"
      +        ],
      +        "type": "object"
      +      },
      +      "freshness": {
      +        "type": "null"
      +      },
      +      "measured_at": {
      +        "type": "null"
      +      },
      +      "method": {
      +        "type": "null"
      +      },
      +      "notes": {
      +        "description": "Caveats, one sentence each.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "state": {
      +        "description": "failed: the lookup did not complete. invalid_input: the input was refused, so nothing was looked up. Neither is a finding.",
      +        "enum": [
      +          "failed",
      +          "invalid_input"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "state",
      +      "measured_at",
      +      "method",
      +      "coverage",
      +      "freshness",
      +      "notes",
      +      "error"
      +    ],
      +    "type": "object"
      +  }
      +]
  3. Changed2 schema fields changed
    • addedInput schema / properties / full
      Added value: +{
      +  "description": "true: data in the structured result is the answer whole, however long. Without it, a structured result past 30,000 characters of JSON has its data cut (DATA CUT): in an order the note states, the entries of its longest lists keep only some of their fields, and then the lists only some of their entries; data_truncated is true, and truncated gives each list cut with its total, the number returned and whether its entries are thinned. Whole data is for a program; a client that sizes structured results, as Claude Code does against MAX_MCP_OUTPUT_TOKENS, can refuse an answer past its cap.",
      +  "type": "boolean"
      +}
    • changedOutput schema / oneOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "coverage": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "properties": {
      -              "batch_size": {
      -                "description": "The most purls one request carries (the API's cap per request).",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches": {
      -                "description": "Requests the distinct purls make, at batch_size each.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches_sent": {
      -                "description": "Of those, the ones the API answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "components_in_document": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      -              },
      -              "distinct_purls": {
      -                "description": "Distinct purls to check: sent plus not_sent.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "duplicates_removed": {
      -                "description": "Purls that appeared more than once and were sent once.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "input": {
      -                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      -                "enum": [
      -                  "purls",
      -                  "cyclonedx",
      -                  "spdx"
      -                ],
      -                "type": "string"
      -              },
      -              "not_assessed": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Of those sent, the components with no verdict, as the answer counts them."
      -              },
      -              "not_sent": {
      -                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "not_sent_reason": {
      -                "anyOf": [
      -                  {
      -                    "enum": [
      -                      "time_budget",
      -                      "rate_limited",
      -                      "request_failed"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      -              },
      -              "partial": {
      -                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "rate_limit_waits": {
      -                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "sent": {
      -                "description": "Distinct purls sent and answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "waited_ms": {
      -                "description": "Milliseconds spent in those waits.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "with_purl": {
      -                "description": "Of those, the ones carrying a purl.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "without_purl": {
      -                "description": "The ones without a purl: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              }
      -            },
      -            "required": [
      -              "input",
      -              "components_in_document",
      -              "with_purl",
      -              "without_purl",
      -              "duplicates_removed",
      -              "distinct_purls",
      -              "batch_size",
      -              "batches",
      -              "batches_sent",
      -              "sent",
      -              "not_sent",
      -              "not_sent_reason",
      -              "rate_limit_waits",
      -              "waited_ms",
      -              "not_assessed",
      -              "partial"
      -            ],
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "What the answer covers; null where the answer says nothing about it."
      -      },
      -      "data": {
      -        "additionalProperties": {},
      -        "properties": {
      -          "answered_at": {
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "components": {
      -            "type": [
      -              "number",
      -              "null"
      -            ]
      -          },
      -          "match_layer": {
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "not_sent_purls": {
      -            "anyOf": [
      -              {
      -                "items": {
      -                  "type": "string"
      -                },
      -                "type": "array"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "description": "The distinct purls not sent (coverage.not_sent_reason says why): not checked, and not clean. Present only when some were not sent."
      -          },
      -          "results": {
      -            "items": {
      -              "additionalProperties": {},
      -              "properties": {
      -                "advisories_considered": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "assessed": {
      -                  "description": "Whether the matcher produced a verdict for this component. false is never clean.",
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "candidates_capped": {
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "capped": {
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "code": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "cve_ids": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "type": "string"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "ecosystem": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "error": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "index": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "input_kind": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matched_package": {
      -                  "description": "The package name the advisories were looked up by (#2836): the purl's own name, or, for a deb or apk purl with an upstream qualifier, the source package it names, which is how Debian and Alpine file advisories.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matched_version_from": {
      -                  "description": "Upstream rows: upstream when the qualifier named a version (upstream=name@version) and it was used, purl when the purl's own version was.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matched_via": {
      -                  "description": "Which name matched_package is: purl_name (the purl's name), upstream (the purl's upstream qualifier) or coordinates (the component's package).",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matches": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "additionalProperties": {},
      -                        "properties": {
      -                          "cve_id": {
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          },
      -                          "epss_score": {
      -                            "description": "The CVE's EPSS score (FIRST), as EchelonGraph's record holds it; null when it has none.",
      -                            "type": [
      -                              "number",
      -                              "null"
      -                            ]
      -                          },
      -                          "fixed_in": {
      -                            "description": "The fixed bound of the advisory interval that holds this component's version, always above it. null when that interval records no fix (it ends at last_affected, or has no end) or no interval holds the version, with fixed_in_reason saying which.",
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          },
      -                          "fixed_in_reason": {
      -                            "description": "Why fixed_in is null; absent when fixed_in is a version.",
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          },
      -                          "interval": {
      -                            "anyOf": [
      -                              {
      -                                "additionalProperties": {},
      -                                "properties": {
      -                                  "fixed": {
      -                                    "type": [
      -                                      "string",
      -                                      "null"
      -                                    ]
      -                                  },
      -                                  "introduced": {
      -                                    "type": [
      -                                      "string",
      -                                      "null"
      -                                    ]
      -                                  },
      -                                  "last_affected": {
      -                                    "type": [
      -                                      "string",
      -                                      "null"
      -                                    ]
      -                                  }
      -                                },
      -                                "type": "object"
      -                              },
      -                              {
      -                                "type": "null"
      -                              }
      -                            ],
      -                            "description": "The advisory interval that holds this component's version (fixed is exclusive, last_affected inclusive); absent when no interval holds it."
      -                          },
      -                          "kev_listed": {
      -                            "description": "Whether the CVE is in CISA's Known Exploited Vulnerabilities catalog, as EchelonGraph's record holds it.",
      -                            "type": [
      -                              "boolean",
      -                              "null"
      -                            ]
      -                          },
      -                          "match_reason": {
      -                            "description": "Why the advisory applies, with the advisory interval the version falls inside: \"[A, B)\" names the fixed version B, \"[A, B]\" the last affected version B. On an upstream row it ends by naming the source package and the purl name.",
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          }
      -                        },
      -                        "type": "object"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "not_affected_count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "not_assessed_reason": {
      -                  "description": "Why assessed is false: package_not_in_advisory_corpus, no_decidable_advisory, advisory_lookup_failed, candidate_window_truncated, time_budget, distro_release_unknown, purl_type_unsupported, version_missing or invalid_component.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "package": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "purl": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "purl_name": {
      -                  "description": "Upstream rows: the purl's own (binary) package name, not looked up.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "undetermined": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "additionalProperties": {},
      -                        "properties": {
      -                          "cve_id": {
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          }
      -                        },
      -                        "type": "object"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "undetermined_count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "verdict": {
      -                  "description": "affected (count > 0); not_affected (assessed, no match, nothing undetermined: the only clean verdict); undetermined (advisories name the package, at least one could not be decided, none matched: not clean); not_assessed (no verdict: not clean).",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "version": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                }
      -              },
      -              "type": "object"
      -            },
      -            "type": "array"
      -          },
      -          "summary": {
      -            "additionalProperties": {},
      -            "properties": {
      -              "affected": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "components": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "corpus_cache_max_age_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "elapsed_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "lookups": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_affected": {
      -                "description": "Components with a decided, clean verdict.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_assessed": {
      -                "description": "Components with no verdict: not clean.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_assessed_by_reason": {
      -                "anyOf": [
      -                  {
      -                    "additionalProperties": {},
      -                    "properties": {
      -                      "advisory_lookup_failed": {
      -                        "type": "number"
      -                      },
      -                      "candidate_window_truncated": {
      -                        "type": "number"
      -                      },
      -                      "distro_release_unknown": {
      -                        "description": "deb, apk or rpm purls without a distro qualifier naming the release, which EchelonGraph does not guess.",
      -                        "type": "number"
      -                      },
      -                      "invalid_component": {
      -                        "type": "number"
      -                      },
      -                      "no_decidable_advisory": {
      -                        "type": "number"
      -                      },
      -                      "package_not_in_advisory_corpus": {
      -                        "type": "number"
      -                      },
      -                      "purl_type_unsupported": {
      -                        "type": "number"
      -                      },
      -                      "time_budget": {
      -                        "description": "Components the batch's time budget ran out before: not clean; check them again.",
      -                        "type": "number"
      -                      },
      -                      "version_missing": {
      -                        "type": "number"
      -                      }
      -                    },
      -                    "type": "object"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "The not_assessed components, counted by not_assessed_reason."
      -              },
      -              "partial": {
      -                "description": "true when the time budget ran out before every component was looked up.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "time_budget_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "undetermined": {
      -                "description": "Components whose advisories could not all be decided and none matched: not clean.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              }
      -            },
      -            "type": "object"
      -          }
      -        },
      -        "type": "object"
      -      },
      -      "freshness": {
      -        "description": "The producing radar's last completed check (last_run_at), where the API serves one.",
      -        "type": "null"
      -      },
      -      "measured_at": {
      -        "anyOf": [
      -          {
      -            "description": "An RFC 3339 instant.",
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "When the underlying observation was made, as the API states it; null when the answer does not say or holds no observation."
      -      },
      -      "method": {
      -        "description": "How the numbers were produced.",
      -        "type": "string"
      -      },
      -      "notes": {
      -        "description": "Caveats, one sentence each.",
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "state": {
      -        "description": "measured: a measurement of what was asked; an exposure count is measured only with measured_at and method. not_assessed: the answer holds no dated measurement of what was asked, so no count in it is presented as one; it can still relay a count, as what the source holds on record, undated, and its notes (and exposure_state, where the result carries it) say what each count is.",
      -        "enum": [
      -          "measured",
      -          "not_assessed"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "state",
      -      "measured_at",
      -      "method",
      -      "coverage",
      -      "freshness",
      -      "notes",
      -      "data"
      -    ],
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "coverage": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "properties": {
      -              "batch_size": {
      -                "description": "The most purls one request carries (the API's cap per request).",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches": {
      -                "description": "Requests the distinct purls make, at batch_size each.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches_sent": {
      -                "description": "Of those, the ones the API answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "components_in_document": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      -              },
      -              "distinct_purls": {
      -                "description": "Distinct purls to check: sent plus not_sent.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "duplicates_removed": {
      -                "description": "Purls that appeared more than once and were sent once.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "input": {
      -                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      -                "enum": [
      -                  "purls",
      -                  "cyclonedx",
      -                  "spdx"
      -                ],
      -                "type": "string"
      -              },
      -              "not_assessed": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Of those sent, the components with no verdict, as the answer counts them."
      -              },
      -              "not_sent": {
      -                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "not_sent_reason": {
      -                "anyOf": [
      -                  {
      -                    "enum": [
      -                      "time_budget",
      -                      "rate_limited",
      -                      "request_failed"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      -              },
      -              "partial": {
      -                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "rate_limit_waits": {
      -                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "sent": {
      -                "description": "Distinct purls sent and answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "waited_ms": {
      -                "description": "Milliseconds spent in those waits.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "with_purl": {
      -                "description": "Of those, the ones carrying a purl.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "without_purl": {
      -                "description": "The ones without a purl: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              }
      -            },
      -            "required": [
      -              "input",
      -              "components_in_document",
      -              "with_purl",
      -              "without_purl",
      -              "duplicates_removed",
      -              "distinct_purls",
      -              "batch_size",
      -              "batches",
      -              "batches_sent",
      -              "sent",
      -              "not_sent",
      -              "not_sent_reason",
      -              "rate_limit_waits",
      -              "waited_ms",
      -              "not_assessed",
      -              "partial"
      -            ],
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ]
      -      },
      -      "error": {
      -        "additionalProperties": false,
      -        "properties": {
      -          "kind": {
      -            "description": "What failed: the request (network, timeout), the answer (http, not_json, not_object, unexpected_shape), the input, or this server.",
      -            "enum": [
      -              "network",
      -              "timeout",
      -              "http",
      -              "not_json",
      -              "not_object",
      -              "invalid_input",
      -              "internal",
      -              "unexpected_shape",
      -              "radars"
      -            ],
      -            "type": "string"
      -          },
      -          "message": {
      -            "description": "The cause: the API's own message, or what went wrong.",
      -            "type": "string"
      -          },
      -          "path": {
      -            "description": "The API path requested, when a request was made.",
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "status": {
      -            "anyOf": [
      -              {
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "description": "The HTTP status, when the API answered one."
      -          }
      -        },
      -        "required": [
      -          "kind",
      -          "path",
      -          "status",
      -          "message"
      -        ],
      -        "type": "object"
      -      },
      -      "freshness": {
      -        "type": "null"
      -      },
      -      "measured_at": {
      -        "type": "null"
      -      },
      -      "method": {
      -        "type": "null"
      -      },
      -      "notes": {
      -        "description": "Caveats, one sentence each.",
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "state": {
      -        "description": "failed: the lookup did not complete. invalid_input: the input was refused, so nothing was looked up. Neither is a finding.",
      -        "enum": [
      -          "failed",
      -          "invalid_input"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "state",
      -      "measured_at",
      -      "method",
      -      "coverage",
      -      "freshness",
      -      "notes",
      -      "error"
      -    ],
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "coverage": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "properties": {
      +              "batch_size": {
      +                "description": "The most purls one request carries (the API's cap per request).",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches": {
      +                "description": "Requests the distinct purls make, at batch_size each.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches_sent": {
      +                "description": "Of those, the ones the API answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "components_in_document": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      +              },
      +              "distinct_purls": {
      +                "description": "Distinct purls to check: sent plus not_sent.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "duplicates_removed": {
      +                "description": "Purls that appeared more than once and were sent once.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "input": {
      +                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      +                "enum": [
      +                  "purls",
      +                  "cyclonedx",
      +                  "spdx"
      +                ],
      +                "type": "string"
      +              },
      +              "not_assessed": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Of those sent, the components with no verdict, as the answer counts them."
      +              },
      +              "not_sent": {
      +                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "not_sent_reason": {
      +                "anyOf": [
      +                  {
      +                    "enum": [
      +                      "time_budget",
      +                      "rate_limited",
      +                      "request_failed"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      +              },
      +              "partial": {
      +                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "rate_limit_waits": {
      +                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "sent": {
      +                "description": "Distinct purls sent and answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "waited_ms": {
      +                "description": "Milliseconds spent in those waits.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "with_purl": {
      +                "description": "Of those, the ones carrying a purl.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "without_purl": {
      +                "description": "The ones without a purl: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              }
      +            },
      +            "required": [
      +              "input",
      +              "components_in_document",
      +              "with_purl",
      +              "without_purl",
      +              "duplicates_removed",
      +              "distinct_purls",
      +              "batch_size",
      +              "batches",
      +              "batches_sent",
      +              "sent",
      +              "not_sent",
      +              "not_sent_reason",
      +              "rate_limit_waits",
      +              "waited_ms",
      +              "not_assessed",
      +              "partial"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "What the answer covers; null where the answer says nothing about it."
      +      },
      +      "data": {
      +        "additionalProperties": {},
      +        "properties": {
      +          "answered_at": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "components": {
      +            "type": [
      +              "number",
      +              "null"
      +            ]
      +          },
      +          "match_layer": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "not_sent_purls": {
      +            "anyOf": [
      +              {
      +                "items": {
      +                  "type": "string"
      +                },
      +                "type": "array"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "description": "The distinct purls not sent (coverage.not_sent_reason says why): not checked, and not clean. Present only when some were not sent."
      +          },
      +          "results": {
      +            "items": {
      +              "additionalProperties": {},
      +              "properties": {
      +                "advisories_considered": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "assessed": {
      +                  "description": "Whether the matcher produced a verdict for this component. false is never clean.",
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "candidates_capped": {
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "capped": {
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "code": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "cve_ids": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "type": "string"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "ecosystem": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "error": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "index": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "input_kind": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matched_package": {
      +                  "description": "The package name the advisories were looked up by (#2836): the purl's own name, or, for a deb or apk purl with an upstream qualifier, the source package it names, which is how Debian and Alpine file advisories.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matched_version_from": {
      +                  "description": "Upstream rows: upstream when the qualifier named a version (upstream=name@version) and it was used, purl when the purl's own version was.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matched_via": {
      +                  "description": "Which name matched_package is: purl_name (the purl's name), upstream (the purl's upstream qualifier) or coordinates (the component's package).",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matches": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "additionalProperties": {},
      +                        "properties": {
      +                          "cve_id": {
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          },
      +                          "epss_score": {
      +                            "description": "The CVE's EPSS score (FIRST), as EchelonGraph's record holds it; null when it has none.",
      +                            "type": [
      +                              "number",
      +                              "null"
      +                            ]
      +                          },
      +                          "fixed_in": {
      +                            "description": "The lowest version above this component's that no affected interval of the CVE's record for the package holds, always above it: the fixed bound of the interval that holds the component's version, moved past any overlapping interval that still holds that bound (fixed_in_detail then names it). null when no such version is on record (an interval on the way records no fix: it ends at last_affected, or has no end) or no interval holds the version, with fixed_in_reason saying which.",
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          },
      +                          "fixed_in_detail": {
      +                            "description": "Set when fixed_in is not the fixed bound of the interval that holds the component's version: the overlapping intervals of the same record it moved past, which still hold that bound.",
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          },
      +                          "fixed_in_reason": {
      +                            "description": "Why fixed_in is null; absent when fixed_in is a version.",
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          },
      +                          "interval": {
      +                            "anyOf": [
      +                              {
      +                                "additionalProperties": {},
      +                                "properties": {
      +                                  "fixed": {
      +                                    "type": [
      +                                      "string",
      +                                      "null"
      +                                    ]
      +                                  },
      +                                  "introduced": {
      +                                    "type": [
      +                                      "string",
      +                                      "null"
      +                                    ]
      +                                  },
      +                                  "last_affected": {
      +                                    "type": [
      +                                      "string",
      +                                      "null"
      +                                    ]
      +                                  }
      +                                },
      +                                "type": "object"
      +                              },
      +                              {
      +                                "type": "null"
      +                              }
      +                            ],
      +                            "description": "The advisory interval that holds this component's version (fixed is exclusive, last_affected inclusive); absent when no interval holds it."
      +                          },
      +                          "kev_listed": {
      +                            "description": "Whether the CVE is in CISA's Known Exploited Vulnerabilities catalog, as EchelonGraph's record holds it.",
      +                            "type": [
      +                              "boolean",
      +                              "null"
      +                            ]
      +                          },
      +                          "match_reason": {
      +                            "description": "Why the advisory applies, with the advisory interval the version falls inside: \"[A, B)\" names that interval's fixed bound B (another interval of the advisory can still hold B; fixed_in is the version past them all), \"[A, B]\" the last affected version B. On an upstream row it ends by naming the source package and the purl name.",
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          }
      +                        },
      +                        "type": "object"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "not_affected_count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "not_assessed_reason": {
      +                  "description": "Why assessed is false: package_not_in_advisory_corpus, no_decidable_advisory, advisory_lookup_failed, candidate_window_truncated, time_budget, distro_release_unknown, purl_type_unsupported, version_missing or invalid_component.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "package": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "purl": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "purl_name": {
      +                  "description": "Upstream rows: the purl's own (binary) package name, not looked up.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "undetermined": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "additionalProperties": {},
      +                        "properties": {
      +                          "cve_id": {
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          }
      +                        },
      +                        "type": "object"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "undetermined_count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "verdict": {
      +                  "description": "affected (count > 0); not_affected (assessed, no match, nothing undetermined: the only clean verdict); undetermined (advisories name the package, at least one could not be decided, none matched: not clean); not_assessed (no verdict: not clean).",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "version": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                }
      +              },
      +              "type": "object"
      +            },
      +            "type": "array"
      +          },
      +          "summary": {
      +            "additionalProperties": {},
      +            "properties": {
      +              "affected": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "components": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "corpus_cache_max_age_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "elapsed_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "lookups": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_affected": {
      +                "description": "Components with a decided, clean verdict.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_assessed": {
      +                "description": "Components with no verdict: not clean.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_assessed_by_reason": {
      +                "anyOf": [
      +                  {
      +                    "additionalProperties": {},
      +                    "properties": {
      +                      "advisory_lookup_failed": {
      +                        "type": "number"
      +                      },
      +                      "candidate_window_truncated": {
      +                        "type": "number"
      +                      },
      +                      "distro_release_unknown": {
      +                        "description": "deb, apk or rpm purls without a distro qualifier naming the release, which EchelonGraph does not guess.",
      +                        "type": "number"
      +                      },
      +                      "invalid_component": {
      +                        "type": "number"
      +                      },
      +                      "no_decidable_advisory": {
      +                        "type": "number"
      +                      },
      +                      "package_not_in_advisory_corpus": {
      +                        "type": "number"
      +                      },
      +                      "purl_type_unsupported": {
      +                        "type": "number"
      +                      },
      +                      "time_budget": {
      +                        "description": "Components the batch's time budget ran out before: not clean; check them again.",
      +                        "type": "number"
      +                      },
      +                      "version_missing": {
      +                        "type": "number"
      +                      }
      +                    },
      +                    "type": "object"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "The not_assessed components, counted by not_assessed_reason."
      +              },
      +              "partial": {
      +                "description": "true when the time budget ran out before every component was looked up.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "time_budget_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "undetermined": {
      +                "description": "Components whose advisories could not all be decided and none matched: not clean.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              }
      +            },
      +            "type": "object"
      +          }
      +        },
      +        "type": "object"
      +      },
      +      "data_truncated": {
      +        "description": "false: data is the answer whole. true: the structured result would pass 30,000 characters of JSON with data whole, so data is cut (DATA CUT): the entries of some of its lists keep only some of their fields, or the lists only some of their entries, truncated gives each, and no string is shortened. The same call with full: true returns data whole.",
      +        "type": "boolean"
      +      },
      +      "freshness": {
      +        "description": "The producing radar's last completed check (last_run_at), where the API serves one.",
      +        "type": "null"
      +      },
      +      "measured_at": {
      +        "anyOf": [
      +          {
      +            "description": "An RFC 3339 instant.",
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "When the underlying observation was made, as the API states it; null when the answer does not say or holds no observation."
      +      },
      +      "method": {
      +        "description": "How the numbers were produced.",
      +        "type": "string"
      +      },
      +      "notes": {
      +        "description": "Caveats, one sentence each.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "state": {
      +        "description": "measured: a measurement of what was asked; an exposure count is measured only with measured_at and method. not_assessed: the answer holds no dated measurement of what was asked, so no count in it is presented as one; it can still relay a count, as what the source holds on record, undated, and its notes (and exposure_state, where the result carries it) say what each count is.",
      +        "enum": [
      +          "measured",
      +          "not_assessed"
      +        ],
      +        "type": "string"
      +      },
      +      "truncated": {
      +        "additionalProperties": {
      +          "additionalProperties": false,
      +          "properties": {
      +            "returned": {
      +              "maximum": 9007199254740991,
      +              "minimum": 0,
      +              "type": "integer"
      +            },
      +            "thinned": {
      +              "const": true,
      +              "type": "boolean"
      +            },
      +            "total": {
      +              "maximum": 9007199254740991,
      +              "minimum": 0,
      +              "type": "integer"
      +            }
      +          },
      +          "required": [
      +            "total",
      +            "returned"
      +          ],
      +          "type": "object"
      +        },
      +        "description": "Present when data_truncated is true: each list of data that was cut, by its path in data (a dot between the names of an object and a list inside it), with total, its entries in the answer, returned, the entries data holds, and thinned, true when each of them keeps only some of its fields.",
      +        "propertyNames": {
      +          "type": "string"
      +        },
      +        "type": "object"
      +      }
      +    },
      +    "required": [
      +      "state",
      +      "measured_at",
      +      "method",
      +      "coverage",
      +      "freshness",
      +      "notes",
      +      "data_truncated",
      +      "data"
      +    ],
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "coverage": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "properties": {
      +              "batch_size": {
      +                "description": "The most purls one request carries (the API's cap per request).",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches": {
      +                "description": "Requests the distinct purls make, at batch_size each.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches_sent": {
      +                "description": "Of those, the ones the API answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "components_in_document": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      +              },
      +              "distinct_purls": {
      +                "description": "Distinct purls to check: sent plus not_sent.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "duplicates_removed": {
      +                "description": "Purls that appeared more than once and were sent once.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "input": {
      +                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      +                "enum": [
      +                  "purls",
      +                  "cyclonedx",
      +                  "spdx"
      +                ],
      +                "type": "string"
      +              },
      +              "not_assessed": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Of those sent, the components with no verdict, as the answer counts them."
      +              },
      +              "not_sent": {
      +                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "not_sent_reason": {
      +                "anyOf": [
      +                  {
      +                    "enum": [
      +                      "time_budget",
      +                      "rate_limited",
      +                      "request_failed"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      +              },
      +              "partial": {
      +                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "rate_limit_waits": {
      +                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "sent": {
      +                "description": "Distinct purls sent and answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "waited_ms": {
      +                "description": "Milliseconds spent in those waits.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "with_purl": {
      +                "description": "Of those, the ones carrying a purl.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "without_purl": {
      +                "description": "The ones without a purl: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              }
      +            },
      +            "required": [
      +              "input",
      +              "components_in_document",
      +              "with_purl",
      +              "without_purl",
      +              "duplicates_removed",
      +              "distinct_purls",
      +              "batch_size",
      +              "batches",
      +              "batches_sent",
      +              "sent",
      +              "not_sent",
      +              "not_sent_reason",
      +              "rate_limit_waits",
      +              "waited_ms",
      +              "not_assessed",
      +              "partial"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ]
      +      },
      +      "error": {
      +        "additionalProperties": false,
      +        "properties": {
      +          "kind": {
      +            "description": "What failed: the request (network, timeout), the answer (http, not_json, not_object, unexpected_shape), the input, or this server.",
      +            "enum": [
      +              "network",
      +              "timeout",
      +              "http",
      +              "not_json",
      +              "not_object",
      +              "invalid_input",
      +              "internal",
      +              "unexpected_shape",
      +              "radars"
      +            ],
      +            "type": "string"
      +          },
      +          "message": {
      +            "description": "The cause: the API's own message, or what went wrong.",
      +            "type": "string"
      +          },
      +          "path": {
      +            "description": "The API path requested, when a request was made.",
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "status": {
      +            "anyOf": [
      +              {
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "description": "The HTTP status, when the API answered one."
      +          }
      +        },
      +        "required": [
      +          "kind",
      +          "path",
      +          "status",
      +          "message"
      +        ],
      +        "type": "object"
      +      },
      +      "freshness": {
      +        "type": "null"
      +      },
      +      "measured_at": {
      +        "type": "null"
      +      },
      +      "method": {
      +        "type": "null"
      +      },
      +      "notes": {
      +        "description": "Caveats, one sentence each.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "state": {
      +        "description": "failed: the lookup did not complete. invalid_input: the input was refused, so nothing was looked up. Neither is a finding.",
      +        "enum": [
      +          "failed",
      +          "invalid_input"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "state",
      +      "measured_at",
      +      "method",
      +      "coverage",
      +      "freshness",
      +      "notes",
      +      "error"
      +    ],
      +    "type": "object"
      +  }
      +]
  4. Changed1 schema field changed
    • changedOutput schema / oneOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "coverage": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "properties": {
      -              "batch_size": {
      -                "description": "The most purls one request carries (the API's cap per request).",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches": {
      -                "description": "Requests the distinct purls make, at batch_size each.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches_sent": {
      -                "description": "Of those, the ones the API answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "components_in_document": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      -              },
      -              "distinct_purls": {
      -                "description": "Distinct purls to check: sent plus not_sent.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "duplicates_removed": {
      -                "description": "Purls that appeared more than once and were sent once.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "input": {
      -                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      -                "enum": [
      -                  "purls",
      -                  "cyclonedx",
      -                  "spdx"
      -                ],
      -                "type": "string"
      -              },
      -              "not_assessed": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Of those sent, the components with no verdict, as the answer counts them."
      -              },
      -              "not_sent": {
      -                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "not_sent_reason": {
      -                "anyOf": [
      -                  {
      -                    "enum": [
      -                      "time_budget",
      -                      "rate_limited",
      -                      "request_failed"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      -              },
      -              "partial": {
      -                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "rate_limit_waits": {
      -                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "sent": {
      -                "description": "Distinct purls sent and answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "waited_ms": {
      -                "description": "Milliseconds spent in those waits.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "with_purl": {
      -                "description": "Of those, the ones carrying a purl.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "without_purl": {
      -                "description": "The ones without a purl: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              }
      -            },
      -            "required": [
      -              "input",
      -              "components_in_document",
      -              "with_purl",
      -              "without_purl",
      -              "duplicates_removed",
      -              "distinct_purls",
      -              "batch_size",
      -              "batches",
      -              "batches_sent",
      -              "sent",
      -              "not_sent",
      -              "not_sent_reason",
      -              "rate_limit_waits",
      -              "waited_ms",
      -              "not_assessed",
      -              "partial"
      -            ],
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "What the answer covers; null where the answer says nothing about it."
      -      },
      -      "data": {
      -        "additionalProperties": {},
      -        "properties": {
      -          "answered_at": {
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "components": {
      -            "type": [
      -              "number",
      -              "null"
      -            ]
      -          },
      -          "match_layer": {
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "not_sent_purls": {
      -            "anyOf": [
      -              {
      -                "items": {
      -                  "type": "string"
      -                },
      -                "type": "array"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "description": "The distinct purls not sent (coverage.not_sent_reason says why): not checked, and not clean. Present only when some were not sent."
      -          },
      -          "results": {
      -            "items": {
      -              "additionalProperties": {},
      -              "properties": {
      -                "advisories_considered": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "assessed": {
      -                  "description": "Whether the matcher produced a verdict for this component. false is never clean.",
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "candidates_capped": {
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "capped": {
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "code": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "cve_ids": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "type": "string"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "ecosystem": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "error": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "index": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "input_kind": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matches": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "additionalProperties": {},
      -                        "properties": {
      -                          "cve_id": {
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          }
      -                        },
      -                        "type": "object"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "not_affected_count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "not_assessed_reason": {
      -                  "description": "Why assessed is false: package_not_in_advisory_corpus, no_decidable_advisory, advisory_lookup_failed, candidate_window_truncated, time_budget, distro_release_unknown, purl_type_unsupported, version_missing or invalid_component.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "package": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "purl": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "undetermined": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "additionalProperties": {},
      -                        "properties": {
      -                          "cve_id": {
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          }
      -                        },
      -                        "type": "object"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "undetermined_count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "verdict": {
      -                  "description": "affected (count > 0); not_affected (assessed, no match, nothing undetermined: the only clean verdict); undetermined (advisories name the package, at least one could not be decided, none matched: not clean); not_assessed (no verdict: not clean).",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "version": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                }
      -              },
      -              "type": "object"
      -            },
      -            "type": "array"
      -          },
      -          "summary": {
      -            "additionalProperties": {},
      -            "properties": {
      -              "affected": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "components": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "corpus_cache_max_age_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "elapsed_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "lookups": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_affected": {
      -                "description": "Components with a decided, clean verdict.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_assessed": {
      -                "description": "Components with no verdict: not clean.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_assessed_by_reason": {
      -                "anyOf": [
      -                  {
      -                    "additionalProperties": {},
      -                    "properties": {
      -                      "advisory_lookup_failed": {
      -                        "type": "number"
      -                      },
      -                      "candidate_window_truncated": {
      -                        "type": "number"
      -                      },
      -                      "distro_release_unknown": {
      -                        "description": "deb, apk or rpm purls without a distro qualifier naming the release, which EchelonGraph does not guess.",
      -                        "type": "number"
      -                      },
      -                      "invalid_component": {
      -                        "type": "number"
      -                      },
      -                      "no_decidable_advisory": {
      -                        "type": "number"
      -                      },
      -                      "package_not_in_advisory_corpus": {
      -                        "type": "number"
      -                      },
      -                      "purl_type_unsupported": {
      -                        "type": "number"
      -                      },
      -                      "time_budget": {
      -                        "description": "Components the batch's time budget ran out before: not clean; check them again.",
      -                        "type": "number"
      -                      },
      -                      "version_missing": {
      -                        "type": "number"
      -                      }
      -                    },
      -                    "type": "object"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "The not_assessed components, counted by not_assessed_reason."
      -              },
      -              "partial": {
      -                "description": "true when the time budget ran out before every component was looked up.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "time_budget_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "undetermined": {
      -                "description": "Components whose advisories could not all be decided and none matched: not clean.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              }
      -            },
      -            "type": "object"
      -          }
      -        },
      -        "type": "object"
      -      },
      -      "freshness": {
      -        "description": "The producing radar's last completed check (last_run_at), where the API serves one.",
      -        "type": "null"
      -      },
      -      "measured_at": {
      -        "anyOf": [
      -          {
      -            "description": "An RFC 3339 instant.",
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "When the underlying observation was made, as the API states it; null when the answer does not say or holds no observation."
      -      },
      -      "method": {
      -        "description": "How the numbers were produced.",
      -        "type": "string"
      -      },
      -      "notes": {
      -        "description": "Caveats, one sentence each.",
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "state": {
      -        "description": "measured: a measurement of what was asked; an exposure count is measured only with measured_at and method. not_assessed: the answer holds no dated measurement of what was asked, so no count in it is presented as one; it can still relay a count, as what the source holds on record, undated, and its notes (and exposure_state, where the result carries it) say what each count is.",
      -        "enum": [
      -          "measured",
      -          "not_assessed"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "state",
      -      "measured_at",
      -      "method",
      -      "coverage",
      -      "freshness",
      -      "notes",
      -      "data"
      -    ],
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "coverage": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "properties": {
      -              "batch_size": {
      -                "description": "The most purls one request carries (the API's cap per request).",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches": {
      -                "description": "Requests the distinct purls make, at batch_size each.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches_sent": {
      -                "description": "Of those, the ones the API answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "components_in_document": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      -              },
      -              "distinct_purls": {
      -                "description": "Distinct purls to check: sent plus not_sent.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "duplicates_removed": {
      -                "description": "Purls that appeared more than once and were sent once.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "input": {
      -                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      -                "enum": [
      -                  "purls",
      -                  "cyclonedx",
      -                  "spdx"
      -                ],
      -                "type": "string"
      -              },
      -              "not_assessed": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Of those sent, the components with no verdict, as the answer counts them."
      -              },
      -              "not_sent": {
      -                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "not_sent_reason": {
      -                "anyOf": [
      -                  {
      -                    "enum": [
      -                      "time_budget",
      -                      "rate_limited",
      -                      "request_failed"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      -              },
      -              "partial": {
      -                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "rate_limit_waits": {
      -                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "sent": {
      -                "description": "Distinct purls sent and answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "waited_ms": {
      -                "description": "Milliseconds spent in those waits.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "with_purl": {
      -                "description": "Of those, the ones carrying a purl.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "without_purl": {
      -                "description": "The ones without a purl: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              }
      -            },
      -            "required": [
      -              "input",
      -              "components_in_document",
      -              "with_purl",
      -              "without_purl",
      -              "duplicates_removed",
      -              "distinct_purls",
      -              "batch_size",
      -              "batches",
      -              "batches_sent",
      -              "sent",
      -              "not_sent",
      -              "not_sent_reason",
      -              "rate_limit_waits",
      -              "waited_ms",
      -              "not_assessed",
      -              "partial"
      -            ],
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ]
      -      },
      -      "error": {
      -        "additionalProperties": false,
      -        "properties": {
      -          "kind": {
      -            "description": "What failed: the request (network, timeout), the answer (http, not_json, not_object, unexpected_shape), the input, or this server.",
      -            "enum": [
      -              "network",
      -              "timeout",
      -              "http",
      -              "not_json",
      -              "not_object",
      -              "invalid_input",
      -              "internal",
      -              "unexpected_shape",
      -              "radars"
      -            ],
      -            "type": "string"
      -          },
      -          "message": {
      -            "description": "The cause: the API's own message, or what went wrong.",
      -            "type": "string"
      -          },
      -          "path": {
      -            "description": "The API path requested, when a request was made.",
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "status": {
      -            "anyOf": [
      -              {
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "description": "The HTTP status, when the API answered one."
      -          }
      -        },
      -        "required": [
      -          "kind",
      -          "path",
      -          "status",
      -          "message"
      -        ],
      -        "type": "object"
      -      },
      -      "freshness": {
      -        "type": "null"
      -      },
      -      "measured_at": {
      -        "type": "null"
      -      },
      -      "method": {
      -        "type": "null"
      -      },
      -      "notes": {
      -        "description": "Caveats, one sentence each.",
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "state": {
      -        "description": "failed: the lookup did not complete. invalid_input: the input was refused, so nothing was looked up. Neither is a finding.",
      -        "enum": [
      -          "failed",
      -          "invalid_input"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "state",
      -      "measured_at",
      -      "method",
      -      "coverage",
      -      "freshness",
      -      "notes",
      -      "error"
      -    ],
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "coverage": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "properties": {
      +              "batch_size": {
      +                "description": "The most purls one request carries (the API's cap per request).",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches": {
      +                "description": "Requests the distinct purls make, at batch_size each.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches_sent": {
      +                "description": "Of those, the ones the API answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "components_in_document": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      +              },
      +              "distinct_purls": {
      +                "description": "Distinct purls to check: sent plus not_sent.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "duplicates_removed": {
      +                "description": "Purls that appeared more than once and were sent once.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "input": {
      +                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      +                "enum": [
      +                  "purls",
      +                  "cyclonedx",
      +                  "spdx"
      +                ],
      +                "type": "string"
      +              },
      +              "not_assessed": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Of those sent, the components with no verdict, as the answer counts them."
      +              },
      +              "not_sent": {
      +                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "not_sent_reason": {
      +                "anyOf": [
      +                  {
      +                    "enum": [
      +                      "time_budget",
      +                      "rate_limited",
      +                      "request_failed"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      +              },
      +              "partial": {
      +                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "rate_limit_waits": {
      +                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "sent": {
      +                "description": "Distinct purls sent and answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "waited_ms": {
      +                "description": "Milliseconds spent in those waits.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "with_purl": {
      +                "description": "Of those, the ones carrying a purl.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "without_purl": {
      +                "description": "The ones without a purl: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              }
      +            },
      +            "required": [
      +              "input",
      +              "components_in_document",
      +              "with_purl",
      +              "without_purl",
      +              "duplicates_removed",
      +              "distinct_purls",
      +              "batch_size",
      +              "batches",
      +              "batches_sent",
      +              "sent",
      +              "not_sent",
      +              "not_sent_reason",
      +              "rate_limit_waits",
      +              "waited_ms",
      +              "not_assessed",
      +              "partial"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "What the answer covers; null where the answer says nothing about it."
      +      },
      +      "data": {
      +        "additionalProperties": {},
      +        "properties": {
      +          "answered_at": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "components": {
      +            "type": [
      +              "number",
      +              "null"
      +            ]
      +          },
      +          "match_layer": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "not_sent_purls": {
      +            "anyOf": [
      +              {
      +                "items": {
      +                  "type": "string"
      +                },
      +                "type": "array"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "description": "The distinct purls not sent (coverage.not_sent_reason says why): not checked, and not clean. Present only when some were not sent."
      +          },
      +          "results": {
      +            "items": {
      +              "additionalProperties": {},
      +              "properties": {
      +                "advisories_considered": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "assessed": {
      +                  "description": "Whether the matcher produced a verdict for this component. false is never clean.",
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "candidates_capped": {
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "capped": {
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "code": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "cve_ids": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "type": "string"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "ecosystem": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "error": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "index": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "input_kind": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matched_package": {
      +                  "description": "The package name the advisories were looked up by (#2836): the purl's own name, or, for a deb or apk purl with an upstream qualifier, the source package it names, which is how Debian and Alpine file advisories.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matched_version_from": {
      +                  "description": "Upstream rows: upstream when the qualifier named a version (upstream=name@version) and it was used, purl when the purl's own version was.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matched_via": {
      +                  "description": "Which name matched_package is: purl_name (the purl's name), upstream (the purl's upstream qualifier) or coordinates (the component's package).",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matches": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "additionalProperties": {},
      +                        "properties": {
      +                          "cve_id": {
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          },
      +                          "epss_score": {
      +                            "description": "The CVE's EPSS score (FIRST), as EchelonGraph's record holds it; null when it has none.",
      +                            "type": [
      +                              "number",
      +                              "null"
      +                            ]
      +                          },
      +                          "fixed_in": {
      +                            "description": "The fixed bound of the advisory interval that holds this component's version, always above it. null when that interval records no fix (it ends at last_affected, or has no end) or no interval holds the version, with fixed_in_reason saying which.",
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          },
      +                          "fixed_in_reason": {
      +                            "description": "Why fixed_in is null; absent when fixed_in is a version.",
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          },
      +                          "interval": {
      +                            "anyOf": [
      +                              {
      +                                "additionalProperties": {},
      +                                "properties": {
      +                                  "fixed": {
      +                                    "type": [
      +                                      "string",
      +                                      "null"
      +                                    ]
      +                                  },
      +                                  "introduced": {
      +                                    "type": [
      +                                      "string",
      +                                      "null"
      +                                    ]
      +                                  },
      +                                  "last_affected": {
      +                                    "type": [
      +                                      "string",
      +                                      "null"
      +                                    ]
      +                                  }
      +                                },
      +                                "type": "object"
      +                              },
      +                              {
      +                                "type": "null"
      +                              }
      +                            ],
      +                            "description": "The advisory interval that holds this component's version (fixed is exclusive, last_affected inclusive); absent when no interval holds it."
      +                          },
      +                          "kev_listed": {
      +                            "description": "Whether the CVE is in CISA's Known Exploited Vulnerabilities catalog, as EchelonGraph's record holds it.",
      +                            "type": [
      +                              "boolean",
      +                              "null"
      +                            ]
      +                          },
      +                          "match_reason": {
      +                            "description": "Why the advisory applies, with the advisory interval the version falls inside: \"[A, B)\" names the fixed version B, \"[A, B]\" the last affected version B. On an upstream row it ends by naming the source package and the purl name.",
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          }
      +                        },
      +                        "type": "object"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "not_affected_count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "not_assessed_reason": {
      +                  "description": "Why assessed is false: package_not_in_advisory_corpus, no_decidable_advisory, advisory_lookup_failed, candidate_window_truncated, time_budget, distro_release_unknown, purl_type_unsupported, version_missing or invalid_component.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "package": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "purl": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "purl_name": {
      +                  "description": "Upstream rows: the purl's own (binary) package name, not looked up.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "undetermined": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "additionalProperties": {},
      +                        "properties": {
      +                          "cve_id": {
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          }
      +                        },
      +                        "type": "object"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "undetermined_count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "verdict": {
      +                  "description": "affected (count > 0); not_affected (assessed, no match, nothing undetermined: the only clean verdict); undetermined (advisories name the package, at least one could not be decided, none matched: not clean); not_assessed (no verdict: not clean).",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "version": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                }
      +              },
      +              "type": "object"
      +            },
      +            "type": "array"
      +          },
      +          "summary": {
      +            "additionalProperties": {},
      +            "properties": {
      +              "affected": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "components": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "corpus_cache_max_age_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "elapsed_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "lookups": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_affected": {
      +                "description": "Components with a decided, clean verdict.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_assessed": {
      +                "description": "Components with no verdict: not clean.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_assessed_by_reason": {
      +                "anyOf": [
      +                  {
      +                    "additionalProperties": {},
      +                    "properties": {
      +                      "advisory_lookup_failed": {
      +                        "type": "number"
      +                      },
      +                      "candidate_window_truncated": {
      +                        "type": "number"
      +                      },
      +                      "distro_release_unknown": {
      +                        "description": "deb, apk or rpm purls without a distro qualifier naming the release, which EchelonGraph does not guess.",
      +                        "type": "number"
      +                      },
      +                      "invalid_component": {
      +                        "type": "number"
      +                      },
      +                      "no_decidable_advisory": {
      +                        "type": "number"
      +                      },
      +                      "package_not_in_advisory_corpus": {
      +                        "type": "number"
      +                      },
      +                      "purl_type_unsupported": {
      +                        "type": "number"
      +                      },
      +                      "time_budget": {
      +                        "description": "Components the batch's time budget ran out before: not clean; check them again.",
      +                        "type": "number"
      +                      },
      +                      "version_missing": {
      +                        "type": "number"
      +                      }
      +                    },
      +                    "type": "object"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "The not_assessed components, counted by not_assessed_reason."
      +              },
      +              "partial": {
      +                "description": "true when the time budget ran out before every component was looked up.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "time_budget_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "undetermined": {
      +                "description": "Components whose advisories could not all be decided and none matched: not clean.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              }
      +            },
      +            "type": "object"
      +          }
      +        },
      +        "type": "object"
      +      },
      +      "freshness": {
      +        "description": "The producing radar's last completed check (last_run_at), where the API serves one.",
      +        "type": "null"
      +      },
      +      "measured_at": {
      +        "anyOf": [
      +          {
      +            "description": "An RFC 3339 instant.",
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "When the underlying observation was made, as the API states it; null when the answer does not say or holds no observation."
      +      },
      +      "method": {
      +        "description": "How the numbers were produced.",
      +        "type": "string"
      +      },
      +      "notes": {
      +        "description": "Caveats, one sentence each.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "state": {
      +        "description": "measured: a measurement of what was asked; an exposure count is measured only with measured_at and method. not_assessed: the answer holds no dated measurement of what was asked, so no count in it is presented as one; it can still relay a count, as what the source holds on record, undated, and its notes (and exposure_state, where the result carries it) say what each count is.",
      +        "enum": [
      +          "measured",
      +          "not_assessed"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "state",
      +      "measured_at",
      +      "method",
      +      "coverage",
      +      "freshness",
      +      "notes",
      +      "data"
      +    ],
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "coverage": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "properties": {
      +              "batch_size": {
      +                "description": "The most purls one request carries (the API's cap per request).",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches": {
      +                "description": "Requests the distinct purls make, at batch_size each.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches_sent": {
      +                "description": "Of those, the ones the API answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "components_in_document": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      +              },
      +              "distinct_purls": {
      +                "description": "Distinct purls to check: sent plus not_sent.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "duplicates_removed": {
      +                "description": "Purls that appeared more than once and were sent once.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "input": {
      +                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      +                "enum": [
      +                  "purls",
      +                  "cyclonedx",
      +                  "spdx"
      +                ],
      +                "type": "string"
      +              },
      +              "not_assessed": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Of those sent, the components with no verdict, as the answer counts them."
      +              },
      +              "not_sent": {
      +                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "not_sent_reason": {
      +                "anyOf": [
      +                  {
      +                    "enum": [
      +                      "time_budget",
      +                      "rate_limited",
      +                      "request_failed"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      +              },
      +              "partial": {
      +                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "rate_limit_waits": {
      +                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "sent": {
      +                "description": "Distinct purls sent and answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "waited_ms": {
      +                "description": "Milliseconds spent in those waits.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "with_purl": {
      +                "description": "Of those, the ones carrying a purl.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "without_purl": {
      +                "description": "The ones without a purl: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              }
      +            },
      +            "required": [
      +              "input",
      +              "components_in_document",
      +              "with_purl",
      +              "without_purl",
      +              "duplicates_removed",
      +              "distinct_purls",
      +              "batch_size",
      +              "batches",
      +              "batches_sent",
      +              "sent",
      +              "not_sent",
      +              "not_sent_reason",
      +              "rate_limit_waits",
      +              "waited_ms",
      +              "not_assessed",
      +              "partial"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ]
      +      },
      +      "error": {
      +        "additionalProperties": false,
      +        "properties": {
      +          "kind": {
      +            "description": "What failed: the request (network, timeout), the answer (http, not_json, not_object, unexpected_shape), the input, or this server.",
      +            "enum": [
      +              "network",
      +              "timeout",
      +              "http",
      +              "not_json",
      +              "not_object",
      +              "invalid_input",
      +              "internal",
      +              "unexpected_shape",
      +              "radars"
      +            ],
      +            "type": "string"
      +          },
      +          "message": {
      +            "description": "The cause: the API's own message, or what went wrong.",
      +            "type": "string"
      +          },
      +          "path": {
      +            "description": "The API path requested, when a request was made.",
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "status": {
      +            "anyOf": [
      +              {
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "description": "The HTTP status, when the API answered one."
      +          }
      +        },
      +        "required": [
      +          "kind",
      +          "path",
      +          "status",
      +          "message"
      +        ],
      +        "type": "object"
      +      },
      +      "freshness": {
      +        "type": "null"
      +      },
      +      "measured_at": {
      +        "type": "null"
      +      },
      +      "method": {
      +        "type": "null"
      +      },
      +      "notes": {
      +        "description": "Caveats, one sentence each.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "state": {
      +        "description": "failed: the lookup did not complete. invalid_input: the input was refused, so nothing was looked up. Neither is a finding.",
      +        "enum": [
      +          "failed",
      +          "invalid_input"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "state",
      +      "measured_at",
      +      "method",
      +      "coverage",
      +      "freshness",
      +      "notes",
      +      "error"
      +    ],
      +    "type": "object"
      +  }
      +]
  5. Changed1 schema field changed
    • changedInput schema / properties / sbom / description
      Previous value: -"a CycloneDX JSON or SPDX JSON document, as JSON text or as an object; its purls are read here and only they are sent"New value: +"a CycloneDX JSON or SPDX JSON document, as JSON text or as an object; its purls are read by this MCP server and only they are sent to the API; over the hosted endpoint (mcp.echelongraph.io) the document is the request body"
  6. Changed1 schema field changed
    • changedOutput schema / oneOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "coverage": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "properties": {
      -              "batch_size": {
      -                "description": "The most purls one request carries (the API's cap per request).",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches": {
      -                "description": "Requests the distinct purls make, at batch_size each.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches_sent": {
      -                "description": "Of those, the ones the API answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "components_in_document": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      -              },
      -              "distinct_purls": {
      -                "description": "Distinct purls to check: sent plus not_sent.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "duplicates_removed": {
      -                "description": "Purls that appeared more than once and were sent once.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "input": {
      -                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      -                "enum": [
      -                  "purls",
      -                  "cyclonedx",
      -                  "spdx"
      -                ],
      -                "type": "string"
      -              },
      -              "not_assessed": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Of those sent, the components with no verdict, as the answer counts them."
      -              },
      -              "not_sent": {
      -                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "not_sent_reason": {
      -                "anyOf": [
      -                  {
      -                    "enum": [
      -                      "time_budget",
      -                      "rate_limited",
      -                      "request_failed"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      -              },
      -              "partial": {
      -                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "rate_limit_waits": {
      -                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "sent": {
      -                "description": "Distinct purls sent and answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "waited_ms": {
      -                "description": "Milliseconds spent in those waits.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "with_purl": {
      -                "description": "Of those, the ones carrying a purl.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "without_purl": {
      -                "description": "The ones without a purl: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              }
      -            },
      -            "required": [
      -              "input",
      -              "components_in_document",
      -              "with_purl",
      -              "without_purl",
      -              "duplicates_removed",
      -              "distinct_purls",
      -              "batch_size",
      -              "batches",
      -              "batches_sent",
      -              "sent",
      -              "not_sent",
      -              "not_sent_reason",
      -              "rate_limit_waits",
      -              "waited_ms",
      -              "not_assessed",
      -              "partial"
      -            ],
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "What the answer covers; null where the answer says nothing about it."
      -      },
      -      "data": {
      -        "additionalProperties": {},
      -        "properties": {
      -          "answered_at": {
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "components": {
      -            "type": [
      -              "number",
      -              "null"
      -            ]
      -          },
      -          "match_layer": {
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "not_sent_purls": {
      -            "anyOf": [
      -              {
      -                "items": {
      -                  "type": "string"
      -                },
      -                "type": "array"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "description": "The distinct purls not sent (coverage.not_sent_reason says why): not checked, and not clean. Present only when some were not sent."
      -          },
      -          "results": {
      -            "items": {
      -              "additionalProperties": {},
      -              "properties": {
      -                "advisories_considered": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "assessed": {
      -                  "description": "Whether the matcher produced a verdict for this component. false is never clean.",
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "candidates_capped": {
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "capped": {
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "code": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "cve_ids": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "type": "string"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "ecosystem": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "error": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "index": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "input_kind": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matches": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "additionalProperties": {},
      -                        "properties": {
      -                          "cve_id": {
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          }
      -                        },
      -                        "type": "object"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "not_affected_count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "not_assessed_reason": {
      -                  "description": "Why assessed is false: package_not_in_advisory_corpus, no_decidable_advisory, advisory_lookup_failed, candidate_window_truncated, time_budget, distro_release_unknown, purl_type_unsupported, version_missing or invalid_component.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "package": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "purl": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "undetermined": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "additionalProperties": {},
      -                        "properties": {
      -                          "cve_id": {
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          }
      -                        },
      -                        "type": "object"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "undetermined_count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "verdict": {
      -                  "description": "affected (count > 0); not_affected (assessed, no match, nothing undetermined: the only clean verdict); undetermined (advisories name the package, at least one could not be decided, none matched: not clean); not_assessed (no verdict: not clean).",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "version": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                }
      -              },
      -              "type": "object"
      -            },
      -            "type": "array"
      -          },
      -          "summary": {
      -            "additionalProperties": {},
      -            "properties": {
      -              "affected": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "components": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "corpus_cache_max_age_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "elapsed_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "lookups": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_affected": {
      -                "description": "Components with a decided, clean verdict.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_assessed": {
      -                "description": "Components with no verdict: not clean.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_assessed_by_reason": {
      -                "anyOf": [
      -                  {
      -                    "additionalProperties": {},
      -                    "properties": {
      -                      "advisory_lookup_failed": {
      -                        "type": "number"
      -                      },
      -                      "candidate_window_truncated": {
      -                        "type": "number"
      -                      },
      -                      "distro_release_unknown": {
      -                        "description": "deb, apk or rpm purls without a distro qualifier naming the release, which EchelonGraph does not guess.",
      -                        "type": "number"
      -                      },
      -                      "invalid_component": {
      -                        "type": "number"
      -                      },
      -                      "no_decidable_advisory": {
      -                        "type": "number"
      -                      },
      -                      "package_not_in_advisory_corpus": {
      -                        "type": "number"
      -                      },
      -                      "purl_type_unsupported": {
      -                        "type": "number"
      -                      },
      -                      "time_budget": {
      -                        "description": "Components the batch's time budget ran out before: not clean; check them again.",
      -                        "type": "number"
      -                      },
      -                      "version_missing": {
      -                        "type": "number"
      -                      }
      -                    },
      -                    "type": "object"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "The not_assessed components, counted by not_assessed_reason."
      -              },
      -              "partial": {
      -                "description": "true when the time budget ran out before every component was looked up.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "time_budget_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "undetermined": {
      -                "description": "Components whose advisories could not all be decided and none matched: not clean.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              }
      -            },
      -            "type": "object"
      -          }
      -        },
      -        "type": "object"
      -      },
      -      "freshness": {
      -        "description": "The producing radar's last completed check (last_run_at), where the API serves one.",
      -        "type": "null"
      -      },
      -      "measured_at": {
      -        "anyOf": [
      -          {
      -            "description": "An RFC 3339 instant.",
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "When the underlying observation was made, as the API states it; null when the answer does not say or holds no observation."
      -      },
      -      "method": {
      -        "description": "How the numbers were produced.",
      -        "type": "string"
      -      },
      -      "notes": {
      -        "description": "Caveats, one sentence each.",
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "state": {
      -        "description": "measured: a measurement of what was asked; an exposure count is measured only with measured_at and method. not_assessed: the answer holds no dated measurement of what was asked, so no count in it is presented as one; it can still relay a count, as what the source holds on record, undated, and its notes (and exposure_state, where the result carries it) say what each count is.",
      -        "enum": [
      -          "measured",
      -          "not_assessed"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "state",
      -      "measured_at",
      -      "method",
      -      "coverage",
      -      "freshness",
      -      "notes",
      -      "data"
      -    ],
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "coverage": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "properties": {
      -              "batch_size": {
      -                "description": "The most purls one request carries (the API's cap per request).",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches": {
      -                "description": "Requests the distinct purls make, at batch_size each.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches_sent": {
      -                "description": "Of those, the ones the API answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "components_in_document": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      -              },
      -              "distinct_purls": {
      -                "description": "Distinct purls to check: sent plus not_sent.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "duplicates_removed": {
      -                "description": "Purls that appeared more than once and were sent once.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "input": {
      -                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      -                "enum": [
      -                  "purls",
      -                  "cyclonedx",
      -                  "spdx"
      -                ],
      -                "type": "string"
      -              },
      -              "not_assessed": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Of those sent, the components with no verdict, as the answer counts them."
      -              },
      -              "not_sent": {
      -                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "not_sent_reason": {
      -                "anyOf": [
      -                  {
      -                    "enum": [
      -                      "time_budget",
      -                      "rate_limited",
      -                      "request_failed"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      -              },
      -              "partial": {
      -                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "rate_limit_waits": {
      -                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "sent": {
      -                "description": "Distinct purls sent and answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "waited_ms": {
      -                "description": "Milliseconds spent in those waits.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "with_purl": {
      -                "description": "Of those, the ones carrying a purl.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "without_purl": {
      -                "description": "The ones without a purl: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              }
      -            },
      -            "required": [
      -              "input",
      -              "components_in_document",
      -              "with_purl",
      -              "without_purl",
      -              "duplicates_removed",
      -              "distinct_purls",
      -              "batch_size",
      -              "batches",
      -              "batches_sent",
      -              "sent",
      -              "not_sent",
      -              "not_sent_reason",
      -              "rate_limit_waits",
      -              "waited_ms",
      -              "not_assessed",
      -              "partial"
      -            ],
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ]
      -      },
      -      "error": {
      -        "additionalProperties": false,
      -        "properties": {
      -          "kind": {
      -            "description": "What failed: the request (network, timeout), the answer (http, not_json, not_object, unexpected_shape), the input, or this server.",
      -            "enum": [
      -              "network",
      -              "timeout",
      -              "http",
      -              "not_json",
      -              "not_object",
      -              "invalid_input",
      -              "internal",
      -              "unexpected_shape",
      -              "radars"
      -            ],
      -            "type": "string"
      -          },
      -          "message": {
      -            "description": "The cause: the API's own message, or what went wrong.",
      -            "type": "string"
      -          },
      -          "path": {
      -            "description": "The API path requested, when a request was made.",
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "status": {
      -            "anyOf": [
      -              {
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "description": "The HTTP status, when the API answered one."
      -          }
      -        },
      -        "required": [
      -          "kind",
      -          "path",
      -          "status",
      -          "message"
      -        ],
      -        "type": "object"
      -      },
      -      "freshness": {
      -        "type": "null"
      -      },
      -      "measured_at": {
      -        "type": "null"
      -      },
      -      "method": {
      -        "type": "null"
      -      },
      -      "notes": {
      -        "description": "Caveats, one sentence each.",
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "state": {
      -        "description": "failed: the lookup did not complete. invalid_input: the input was refused, so nothing was looked up. Neither is a finding.",
      -        "enum": [
      -          "failed",
      -          "invalid_input"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "state",
      -      "measured_at",
      -      "method",
      -      "coverage",
      -      "freshness",
      -      "notes",
      -      "error"
      -    ],
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "coverage": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "properties": {
      +              "batch_size": {
      +                "description": "The most purls one request carries (the API's cap per request).",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches": {
      +                "description": "Requests the distinct purls make, at batch_size each.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches_sent": {
      +                "description": "Of those, the ones the API answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "components_in_document": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      +              },
      +              "distinct_purls": {
      +                "description": "Distinct purls to check: sent plus not_sent.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "duplicates_removed": {
      +                "description": "Purls that appeared more than once and were sent once.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "input": {
      +                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      +                "enum": [
      +                  "purls",
      +                  "cyclonedx",
      +                  "spdx"
      +                ],
      +                "type": "string"
      +              },
      +              "not_assessed": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Of those sent, the components with no verdict, as the answer counts them."
      +              },
      +              "not_sent": {
      +                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "not_sent_reason": {
      +                "anyOf": [
      +                  {
      +                    "enum": [
      +                      "time_budget",
      +                      "rate_limited",
      +                      "request_failed"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      +              },
      +              "partial": {
      +                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "rate_limit_waits": {
      +                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "sent": {
      +                "description": "Distinct purls sent and answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "waited_ms": {
      +                "description": "Milliseconds spent in those waits.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "with_purl": {
      +                "description": "Of those, the ones carrying a purl.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "without_purl": {
      +                "description": "The ones without a purl: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              }
      +            },
      +            "required": [
      +              "input",
      +              "components_in_document",
      +              "with_purl",
      +              "without_purl",
      +              "duplicates_removed",
      +              "distinct_purls",
      +              "batch_size",
      +              "batches",
      +              "batches_sent",
      +              "sent",
      +              "not_sent",
      +              "not_sent_reason",
      +              "rate_limit_waits",
      +              "waited_ms",
      +              "not_assessed",
      +              "partial"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "What the answer covers; null where the answer says nothing about it."
      +      },
      +      "data": {
      +        "additionalProperties": {},
      +        "properties": {
      +          "answered_at": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "components": {
      +            "type": [
      +              "number",
      +              "null"
      +            ]
      +          },
      +          "match_layer": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "not_sent_purls": {
      +            "anyOf": [
      +              {
      +                "items": {
      +                  "type": "string"
      +                },
      +                "type": "array"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "description": "The distinct purls not sent (coverage.not_sent_reason says why): not checked, and not clean. Present only when some were not sent."
      +          },
      +          "results": {
      +            "items": {
      +              "additionalProperties": {},
      +              "properties": {
      +                "advisories_considered": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "assessed": {
      +                  "description": "Whether the matcher produced a verdict for this component. false is never clean.",
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "candidates_capped": {
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "capped": {
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "code": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "cve_ids": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "type": "string"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "ecosystem": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "error": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "index": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "input_kind": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matches": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "additionalProperties": {},
      +                        "properties": {
      +                          "cve_id": {
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          }
      +                        },
      +                        "type": "object"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "not_affected_count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "not_assessed_reason": {
      +                  "description": "Why assessed is false: package_not_in_advisory_corpus, no_decidable_advisory, advisory_lookup_failed, candidate_window_truncated, time_budget, distro_release_unknown, purl_type_unsupported, version_missing or invalid_component.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "package": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "purl": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "undetermined": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "additionalProperties": {},
      +                        "properties": {
      +                          "cve_id": {
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          }
      +                        },
      +                        "type": "object"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "undetermined_count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "verdict": {
      +                  "description": "affected (count > 0); not_affected (assessed, no match, nothing undetermined: the only clean verdict); undetermined (advisories name the package, at least one could not be decided, none matched: not clean); not_assessed (no verdict: not clean).",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "version": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                }
      +              },
      +              "type": "object"
      +            },
      +            "type": "array"
      +          },
      +          "summary": {
      +            "additionalProperties": {},
      +            "properties": {
      +              "affected": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "components": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "corpus_cache_max_age_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "elapsed_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "lookups": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_affected": {
      +                "description": "Components with a decided, clean verdict.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_assessed": {
      +                "description": "Components with no verdict: not clean.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_assessed_by_reason": {
      +                "anyOf": [
      +                  {
      +                    "additionalProperties": {},
      +                    "properties": {
      +                      "advisory_lookup_failed": {
      +                        "type": "number"
      +                      },
      +                      "candidate_window_truncated": {
      +                        "type": "number"
      +                      },
      +                      "distro_release_unknown": {
      +                        "description": "deb, apk or rpm purls without a distro qualifier naming the release, which EchelonGraph does not guess.",
      +                        "type": "number"
      +                      },
      +                      "invalid_component": {
      +                        "type": "number"
      +                      },
      +                      "no_decidable_advisory": {
      +                        "type": "number"
      +                      },
      +                      "package_not_in_advisory_corpus": {
      +                        "type": "number"
      +                      },
      +                      "purl_type_unsupported": {
      +                        "type": "number"
      +                      },
      +                      "time_budget": {
      +                        "description": "Components the batch's time budget ran out before: not clean; check them again.",
      +                        "type": "number"
      +                      },
      +                      "version_missing": {
      +                        "type": "number"
      +                      }
      +                    },
      +                    "type": "object"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "The not_assessed components, counted by not_assessed_reason."
      +              },
      +              "partial": {
      +                "description": "true when the time budget ran out before every component was looked up.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "time_budget_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "undetermined": {
      +                "description": "Components whose advisories could not all be decided and none matched: not clean.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              }
      +            },
      +            "type": "object"
      +          }
      +        },
      +        "type": "object"
      +      },
      +      "freshness": {
      +        "description": "The producing radar's last completed check (last_run_at), where the API serves one.",
      +        "type": "null"
      +      },
      +      "measured_at": {
      +        "anyOf": [
      +          {
      +            "description": "An RFC 3339 instant.",
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "When the underlying observation was made, as the API states it; null when the answer does not say or holds no observation."
      +      },
      +      "method": {
      +        "description": "How the numbers were produced.",
      +        "type": "string"
      +      },
      +      "notes": {
      +        "description": "Caveats, one sentence each.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "state": {
      +        "description": "measured: a measurement of what was asked; an exposure count is measured only with measured_at and method. not_assessed: the answer holds no dated measurement of what was asked, so no count in it is presented as one; it can still relay a count, as what the source holds on record, undated, and its notes (and exposure_state, where the result carries it) say what each count is.",
      +        "enum": [
      +          "measured",
      +          "not_assessed"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "state",
      +      "measured_at",
      +      "method",
      +      "coverage",
      +      "freshness",
      +      "notes",
      +      "data"
      +    ],
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "coverage": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "properties": {
      +              "batch_size": {
      +                "description": "The most purls one request carries (the API's cap per request).",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches": {
      +                "description": "Requests the distinct purls make, at batch_size each.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches_sent": {
      +                "description": "Of those, the ones the API answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "components_in_document": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      +              },
      +              "distinct_purls": {
      +                "description": "Distinct purls to check: sent plus not_sent.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "duplicates_removed": {
      +                "description": "Purls that appeared more than once and were sent once.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "input": {
      +                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      +                "enum": [
      +                  "purls",
      +                  "cyclonedx",
      +                  "spdx"
      +                ],
      +                "type": "string"
      +              },
      +              "not_assessed": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Of those sent, the components with no verdict, as the answer counts them."
      +              },
      +              "not_sent": {
      +                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "not_sent_reason": {
      +                "anyOf": [
      +                  {
      +                    "enum": [
      +                      "time_budget",
      +                      "rate_limited",
      +                      "request_failed"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      +              },
      +              "partial": {
      +                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "rate_limit_waits": {
      +                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "sent": {
      +                "description": "Distinct purls sent and answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "waited_ms": {
      +                "description": "Milliseconds spent in those waits.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "with_purl": {
      +                "description": "Of those, the ones carrying a purl.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "without_purl": {
      +                "description": "The ones without a purl: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              }
      +            },
      +            "required": [
      +              "input",
      +              "components_in_document",
      +              "with_purl",
      +              "without_purl",
      +              "duplicates_removed",
      +              "distinct_purls",
      +              "batch_size",
      +              "batches",
      +              "batches_sent",
      +              "sent",
      +              "not_sent",
      +              "not_sent_reason",
      +              "rate_limit_waits",
      +              "waited_ms",
      +              "not_assessed",
      +              "partial"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ]
      +      },
      +      "error": {
      +        "additionalProperties": false,
      +        "properties": {
      +          "kind": {
      +            "description": "What failed: the request (network, timeout), the answer (http, not_json, not_object, unexpected_shape), the input, or this server.",
      +            "enum": [
      +              "network",
      +              "timeout",
      +              "http",
      +              "not_json",
      +              "not_object",
      +              "invalid_input",
      +              "internal",
      +              "unexpected_shape",
      +              "radars"
      +            ],
      +            "type": "string"
      +          },
      +          "message": {
      +            "description": "The cause: the API's own message, or what went wrong.",
      +            "type": "string"
      +          },
      +          "path": {
      +            "description": "The API path requested, when a request was made.",
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "status": {
      +            "anyOf": [
      +              {
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "description": "The HTTP status, when the API answered one."
      +          }
      +        },
      +        "required": [
      +          "kind",
      +          "path",
      +          "status",
      +          "message"
      +        ],
      +        "type": "object"
      +      },
      +      "freshness": {
      +        "type": "null"
      +      },
      +      "measured_at": {
      +        "type": "null"
      +      },
      +      "method": {
      +        "type": "null"
      +      },
      +      "notes": {
      +        "description": "Caveats, one sentence each.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "state": {
      +        "description": "failed: the lookup did not complete. invalid_input: the input was refused, so nothing was looked up. Neither is a finding.",
      +        "enum": [
      +          "failed",
      +          "invalid_input"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "state",
      +      "measured_at",
      +      "method",
      +      "coverage",
      +      "freshness",
      +      "notes",
      +      "error"
      +    ],
      +    "type": "object"
      +  }
      +]
  7. Changed2 schema fields changed
    • changedInput schema / properties / purls / description
      Previous value: -"package URLs to check, e.g. pkg:npm/lodash@4.17.20 or pkg:deb/debian/openssl@3.0.11-1~deb12u1?distro=debian-12 (up to 200)"New value: +"package URLs to check, e.g. pkg:npm/lodash@4.17.20 or pkg:deb/debian/openssl@3.0.11-1~deb12u1?distro=debian-12 (up to 2,000 distinct, sent in batches of 200)"
    • changedOutput schema / oneOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "coverage": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "properties": {
      -              "components_in_document": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      -              },
      -              "duplicates_removed": {
      -                "description": "Purls that appeared more than once and were sent once.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "input": {
      -                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      -                "enum": [
      -                  "purls",
      -                  "cyclonedx",
      -                  "spdx"
      -                ],
      -                "type": "string"
      -              },
      -              "not_assessed": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Of those sent, the components with no verdict, as the answer counts them."
      -              },
      -              "partial": {
      -                "description": "The answer's summary.partial: true when the time budget ran out first.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "sent": {
      -                "description": "Distinct purls sent and checked.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "with_purl": {
      -                "description": "Of those, the ones carrying a purl.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "without_purl": {
      -                "description": "The ones without a purl: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              }
      -            },
      -            "required": [
      -              "input",
      -              "components_in_document",
      -              "with_purl",
      -              "without_purl",
      -              "duplicates_removed",
      -              "sent",
      -              "not_assessed",
      -              "partial"
      -            ],
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "What the answer covers; null where the answer says nothing about it."
      -      },
      -      "data": {
      -        "additionalProperties": {},
      -        "properties": {
      -          "answered_at": {
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "components": {
      -            "type": [
      -              "number",
      -              "null"
      -            ]
      -          },
      -          "match_layer": {
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "results": {
      -            "items": {
      -              "additionalProperties": {},
      -              "properties": {
      -                "advisories_considered": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "assessed": {
      -                  "description": "Whether the matcher produced a verdict for this component. false is never clean.",
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "candidates_capped": {
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "capped": {
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "code": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "cve_ids": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "type": "string"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "ecosystem": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "error": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "index": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "input_kind": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matches": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "additionalProperties": {},
      -                        "properties": {
      -                          "cve_id": {
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          }
      -                        },
      -                        "type": "object"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "not_affected_count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "not_assessed_reason": {
      -                  "description": "Why assessed is false: package_not_in_advisory_corpus, no_decidable_advisory, advisory_lookup_failed, candidate_window_truncated, time_budget, distro_release_unknown, purl_type_unsupported, version_missing or invalid_component.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "package": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "purl": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "undetermined": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "additionalProperties": {},
      -                        "properties": {
      -                          "cve_id": {
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          }
      -                        },
      -                        "type": "object"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "undetermined_count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "verdict": {
      -                  "description": "affected (count > 0); not_affected (assessed, no match, nothing undetermined: the only clean verdict); undetermined (advisories name the package, at least one could not be decided, none matched: not clean); not_assessed (no verdict: not clean).",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "version": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                }
      -              },
      -              "type": "object"
      -            },
      -            "type": "array"
      -          },
      -          "summary": {
      -            "additionalProperties": {},
      -            "properties": {
      -              "affected": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "components": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "corpus_cache_max_age_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "elapsed_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "lookups": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_affected": {
      -                "description": "Components with a decided, clean verdict.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_assessed": {
      -                "description": "Components with no verdict: not clean.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_assessed_by_reason": {
      -                "anyOf": [
      -                  {
      -                    "additionalProperties": {},
      -                    "properties": {
      -                      "advisory_lookup_failed": {
      -                        "type": "number"
      -                      },
      -                      "candidate_window_truncated": {
      -                        "type": "number"
      -                      },
      -                      "distro_release_unknown": {
      -                        "description": "deb, apk or rpm purls without a distro qualifier naming the release, which EchelonGraph does not guess.",
      -                        "type": "number"
      -                      },
      -                      "invalid_component": {
      -                        "type": "number"
      -                      },
      -                      "no_decidable_advisory": {
      -                        "type": "number"
      -                      },
      -                      "package_not_in_advisory_corpus": {
      -                        "type": "number"
      -                      },
      -                      "purl_type_unsupported": {
      -                        "type": "number"
      -                      },
      -                      "time_budget": {
      -                        "description": "Components the batch's time budget ran out before: not clean; check them again.",
      -                        "type": "number"
      -                      },
      -                      "version_missing": {
      -                        "type": "number"
      -                      }
      -                    },
      -                    "type": "object"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "The not_assessed components, counted by not_assessed_reason."
      -              },
      -              "partial": {
      -                "description": "true when the time budget ran out before every component was looked up.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "time_budget_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "undetermined": {
      -                "description": "Components whose advisories could not all be decided and none matched: not clean.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              }
      -            },
      -            "type": "object"
      -          }
      -        },
      -        "type": "object"
      -      },
      -      "freshness": {
      -        "description": "The producing radar's last completed check (last_run_at), where the API serves one.",
      -        "type": "null"
      -      },
      -      "measured_at": {
      -        "anyOf": [
      -          {
      -            "description": "An RFC 3339 instant.",
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "When the underlying observation was made, as the API states it; null when the answer does not say or holds no observation."
      -      },
      -      "method": {
      -        "description": "How the numbers were produced.",
      -        "type": "string"
      -      },
      -      "notes": {
      -        "description": "Caveats, one sentence each.",
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "state": {
      -        "description": "measured: a measurement of what was asked; an exposure count is measured only with measured_at and method. not_assessed: the answer holds no dated measurement of what was asked, so no count in it is presented as one; it can still relay a count, as what the source holds on record, undated, and its notes (and exposure_state, where the result carries it) say what each count is.",
      -        "enum": [
      -          "measured",
      -          "not_assessed"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "state",
      -      "measured_at",
      -      "method",
      -      "coverage",
      -      "freshness",
      -      "notes",
      -      "data"
      -    ],
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "coverage": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "properties": {
      -              "components_in_document": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      -              },
      -              "duplicates_removed": {
      -                "description": "Purls that appeared more than once and were sent once.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "input": {
      -                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      -                "enum": [
      -                  "purls",
      -                  "cyclonedx",
      -                  "spdx"
      -                ],
      -                "type": "string"
      -              },
      -              "not_assessed": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Of those sent, the components with no verdict, as the answer counts them."
      -              },
      -              "partial": {
      -                "description": "The answer's summary.partial: true when the time budget ran out first.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "sent": {
      -                "description": "Distinct purls sent and checked.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "with_purl": {
      -                "description": "Of those, the ones carrying a purl.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "without_purl": {
      -                "description": "The ones without a purl: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              }
      -            },
      -            "required": [
      -              "input",
      -              "components_in_document",
      -              "with_purl",
      -              "without_purl",
      -              "duplicates_removed",
      -              "sent",
      -              "not_assessed",
      -              "partial"
      -            ],
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ]
      -      },
      -      "error": {
      -        "additionalProperties": false,
      -        "properties": {
      -          "kind": {
      -            "description": "What failed: the request (network, timeout), the answer (http, not_json, not_object, unexpected_shape), the input, or this server.",
      -            "enum": [
      -              "network",
      -              "timeout",
      -              "http",
      -              "not_json",
      -              "not_object",
      -              "invalid_input",
      -              "internal",
      -              "unexpected_shape",
      -              "radars"
      -            ],
      -            "type": "string"
      -          },
      -          "message": {
      -            "description": "The cause: the API's own message, or what went wrong.",
      -            "type": "string"
      -          },
      -          "path": {
      -            "description": "The API path requested, when a request was made.",
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "status": {
      -            "anyOf": [
      -              {
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "description": "The HTTP status, when the API answered one."
      -          }
      -        },
      -        "required": [
      -          "kind",
      -          "path",
      -          "status",
      -          "message"
      -        ],
      -        "type": "object"
      -      },
      -      "freshness": {
      -        "type": "null"
      -      },
      -      "measured_at": {
      -        "type": "null"
      -      },
      -      "method": {
      -        "type": "null"
      -      },
      -      "notes": {
      -        "description": "Caveats, one sentence each.",
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "state": {
      -        "description": "failed: the lookup did not complete. invalid_input: the input was refused, so nothing was looked up. Neither is a finding.",
      -        "enum": [
      -          "failed",
      -          "invalid_input"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "state",
      -      "measured_at",
      -      "method",
      -      "coverage",
      -      "freshness",
      -      "notes",
      -      "error"
      -    ],
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "coverage": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "properties": {
      +              "batch_size": {
      +                "description": "The most purls one request carries (the API's cap per request).",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches": {
      +                "description": "Requests the distinct purls make, at batch_size each.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches_sent": {
      +                "description": "Of those, the ones the API answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "components_in_document": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      +              },
      +              "distinct_purls": {
      +                "description": "Distinct purls to check: sent plus not_sent.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "duplicates_removed": {
      +                "description": "Purls that appeared more than once and were sent once.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "input": {
      +                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      +                "enum": [
      +                  "purls",
      +                  "cyclonedx",
      +                  "spdx"
      +                ],
      +                "type": "string"
      +              },
      +              "not_assessed": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Of those sent, the components with no verdict, as the answer counts them."
      +              },
      +              "not_sent": {
      +                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "not_sent_reason": {
      +                "anyOf": [
      +                  {
      +                    "enum": [
      +                      "time_budget",
      +                      "rate_limited",
      +                      "request_failed"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      +              },
      +              "partial": {
      +                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "rate_limit_waits": {
      +                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "sent": {
      +                "description": "Distinct purls sent and answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "waited_ms": {
      +                "description": "Milliseconds spent in those waits.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "with_purl": {
      +                "description": "Of those, the ones carrying a purl.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "without_purl": {
      +                "description": "The ones without a purl: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              }
      +            },
      +            "required": [
      +              "input",
      +              "components_in_document",
      +              "with_purl",
      +              "without_purl",
      +              "duplicates_removed",
      +              "distinct_purls",
      +              "batch_size",
      +              "batches",
      +              "batches_sent",
      +              "sent",
      +              "not_sent",
      +              "not_sent_reason",
      +              "rate_limit_waits",
      +              "waited_ms",
      +              "not_assessed",
      +              "partial"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "What the answer covers; null where the answer says nothing about it."
      +      },
      +      "data": {
      +        "additionalProperties": {},
      +        "properties": {
      +          "answered_at": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "components": {
      +            "type": [
      +              "number",
      +              "null"
      +            ]
      +          },
      +          "match_layer": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "not_sent_purls": {
      +            "anyOf": [
      +              {
      +                "items": {
      +                  "type": "string"
      +                },
      +                "type": "array"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "description": "The distinct purls not sent (coverage.not_sent_reason says why): not checked, and not clean. Present only when some were not sent."
      +          },
      +          "results": {
      +            "items": {
      +              "additionalProperties": {},
      +              "properties": {
      +                "advisories_considered": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "assessed": {
      +                  "description": "Whether the matcher produced a verdict for this component. false is never clean.",
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "candidates_capped": {
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "capped": {
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "code": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "cve_ids": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "type": "string"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "ecosystem": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "error": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "index": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "input_kind": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matches": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "additionalProperties": {},
      +                        "properties": {
      +                          "cve_id": {
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          }
      +                        },
      +                        "type": "object"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "not_affected_count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "not_assessed_reason": {
      +                  "description": "Why assessed is false: package_not_in_advisory_corpus, no_decidable_advisory, advisory_lookup_failed, candidate_window_truncated, time_budget, distro_release_unknown, purl_type_unsupported, version_missing or invalid_component.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "package": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "purl": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "undetermined": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "additionalProperties": {},
      +                        "properties": {
      +                          "cve_id": {
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          }
      +                        },
      +                        "type": "object"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "undetermined_count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "verdict": {
      +                  "description": "affected (count > 0); not_affected (assessed, no match, nothing undetermined: the only clean verdict); undetermined (advisories name the package, at least one could not be decided, none matched: not clean); not_assessed (no verdict: not clean).",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "version": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                }
      +              },
      +              "type": "object"
      +            },
      +            "type": "array"
      +          },
      +          "summary": {
      +            "additionalProperties": {},
      +            "properties": {
      +              "affected": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "components": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "corpus_cache_max_age_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "elapsed_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "lookups": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_affected": {
      +                "description": "Components with a decided, clean verdict.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_assessed": {
      +                "description": "Components with no verdict: not clean.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_assessed_by_reason": {
      +                "anyOf": [
      +                  {
      +                    "additionalProperties": {},
      +                    "properties": {
      +                      "advisory_lookup_failed": {
      +                        "type": "number"
      +                      },
      +                      "candidate_window_truncated": {
      +                        "type": "number"
      +                      },
      +                      "distro_release_unknown": {
      +                        "description": "deb, apk or rpm purls without a distro qualifier naming the release, which EchelonGraph does not guess.",
      +                        "type": "number"
      +                      },
      +                      "invalid_component": {
      +                        "type": "number"
      +                      },
      +                      "no_decidable_advisory": {
      +                        "type": "number"
      +                      },
      +                      "package_not_in_advisory_corpus": {
      +                        "type": "number"
      +                      },
      +                      "purl_type_unsupported": {
      +                        "type": "number"
      +                      },
      +                      "time_budget": {
      +                        "description": "Components the batch's time budget ran out before: not clean; check them again.",
      +                        "type": "number"
      +                      },
      +                      "version_missing": {
      +                        "type": "number"
      +                      }
      +                    },
      +                    "type": "object"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "The not_assessed components, counted by not_assessed_reason."
      +              },
      +              "partial": {
      +                "description": "true when the time budget ran out before every component was looked up.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "time_budget_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "undetermined": {
      +                "description": "Components whose advisories could not all be decided and none matched: not clean.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              }
      +            },
      +            "type": "object"
      +          }
      +        },
      +        "type": "object"
      +      },
      +      "freshness": {
      +        "description": "The producing radar's last completed check (last_run_at), where the API serves one.",
      +        "type": "null"
      +      },
      +      "measured_at": {
      +        "anyOf": [
      +          {
      +            "description": "An RFC 3339 instant.",
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "When the underlying observation was made, as the API states it; null when the answer does not say or holds no observation."
      +      },
      +      "method": {
      +        "description": "How the numbers were produced.",
      +        "type": "string"
      +      },
      +      "notes": {
      +        "description": "Caveats, one sentence each.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "state": {
      +        "description": "measured: a measurement of what was asked; an exposure count is measured only with measured_at and method. not_assessed: the answer holds no dated measurement of what was asked, so no count in it is presented as one; it can still relay a count, as what the source holds on record, undated, and its notes (and exposure_state, where the result carries it) say what each count is.",
      +        "enum": [
      +          "measured",
      +          "not_assessed"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "state",
      +      "measured_at",
      +      "method",
      +      "coverage",
      +      "freshness",
      +      "notes",
      +      "data"
      +    ],
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "coverage": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "properties": {
      +              "batch_size": {
      +                "description": "The most purls one request carries (the API's cap per request).",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches": {
      +                "description": "Requests the distinct purls make, at batch_size each.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches_sent": {
      +                "description": "Of those, the ones the API answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "components_in_document": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      +              },
      +              "distinct_purls": {
      +                "description": "Distinct purls to check: sent plus not_sent.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "duplicates_removed": {
      +                "description": "Purls that appeared more than once and were sent once.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "input": {
      +                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      +                "enum": [
      +                  "purls",
      +                  "cyclonedx",
      +                  "spdx"
      +                ],
      +                "type": "string"
      +              },
      +              "not_assessed": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Of those sent, the components with no verdict, as the answer counts them."
      +              },
      +              "not_sent": {
      +                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "not_sent_reason": {
      +                "anyOf": [
      +                  {
      +                    "enum": [
      +                      "time_budget",
      +                      "rate_limited",
      +                      "request_failed"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      +              },
      +              "partial": {
      +                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "rate_limit_waits": {
      +                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "sent": {
      +                "description": "Distinct purls sent and answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "waited_ms": {
      +                "description": "Milliseconds spent in those waits.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "with_purl": {
      +                "description": "Of those, the ones carrying a purl.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "without_purl": {
      +                "description": "The ones without a purl: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              }
      +            },
      +            "required": [
      +              "input",
      +              "components_in_document",
      +              "with_purl",
      +              "without_purl",
      +              "duplicates_removed",
      +              "distinct_purls",
      +              "batch_size",
      +              "batches",
      +              "batches_sent",
      +              "sent",
      +              "not_sent",
      +              "not_sent_reason",
      +              "rate_limit_waits",
      +              "waited_ms",
      +              "not_assessed",
      +              "partial"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ]
      +      },
      +      "error": {
      +        "additionalProperties": false,
      +        "properties": {
      +          "kind": {
      +            "description": "What failed: the request (network, timeout), the answer (http, not_json, not_object, unexpected_shape), the input, or this server.",
      +            "enum": [
      +              "network",
      +              "timeout",
      +              "http",
      +              "not_json",
      +              "not_object",
      +              "invalid_input",
      +              "internal",
      +              "unexpected_shape",
      +              "radars"
      +            ],
      +            "type": "string"
      +          },
      +          "message": {
      +            "description": "The cause: the API's own message, or what went wrong.",
      +            "type": "string"
      +          },
      +          "path": {
      +            "description": "The API path requested, when a request was made.",
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "status": {
      +            "anyOf": [
      +              {
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "description": "The HTTP status, when the API answered one."
      +          }
      +        },
      +        "required": [
      +          "kind",
      +          "path",
      +          "status",
      +          "message"
      +        ],
      +        "type": "object"
      +      },
      +      "freshness": {
      +        "type": "null"
      +      },
      +      "measured_at": {
      +        "type": "null"
      +      },
      +      "method": {
      +        "type": "null"
      +      },
      +      "notes": {
      +        "description": "Caveats, one sentence each.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "state": {
      +        "description": "failed: the lookup did not complete. invalid_input: the input was refused, so nothing was looked up. Neither is a finding.",
      +        "enum": [
      +          "failed",
      +          "invalid_input"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "state",
      +      "measured_at",
      +      "method",
      +      "coverage",
      +      "freshness",
      +      "notes",
      +      "error"
      +    ],
      +    "type": "object"
      +  }
      +]
  8. First observed

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare read-only/idempotent, but the description adds substantial behavioral detail beyond them: only purls are sent to the API (never the document, never in a URL), batching at 200 per POST, a 1,200-component/min rate limit, 429 handling with Retry-After up to 50 seconds, refusal rather than truncation above 2,000 distinct purls, and the TEXT CUT/DATA CUT truncation scheme with row-retention order. This is exactly the operational context an agent needs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose is front-loaded and nearly every sentence carries operational detail (limits, truncation, verdict meaning) rather than filler. The density makes it a wall of run-on sentences, which costs readability, but little could be removed without losing real guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite an output schema existing, the description supplies the interpretation rules an agent cannot get from the schema alone: not_affected is the only clean verdict, not_assessed/undetermined are not, fixed_in semantics, and the truncation/rate-limit failure modes. Nothing material is missing for correct invocation and result reading.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema does most of the work. The description still adds semantics beyond it: purls up to 2,000 distinct (10,000 with repeats), sbom as CycloneDX or SPDX JSON up to 5,000,000 characters, a 6 MiB hosted-endpoint body limit, and that only the document's purls are read and sent. A 5 is not warranted because much of this is already in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence states a specific verb and resource — check a dependency list / SBOM against EchelonGraph's advisory corpus, one verdict per component — and names the concrete input forms (CycloneDX/SPDX SBOM or purls) for container images and Kubernetes pods. It does not explicitly distinguish itself from siblings such as scan_manifest or check_affected, so it falls short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear usage context: use it for container images and Kubernetes pods, supplying an SBOM of each image or its purls. It also explains what 'clean' means (only not_affected), which guides interpretation. It never names an alternative tool or an exclusion condition, so not a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.