{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://docs.mutastic.io/schemas/events/v1.json",
  "title": "mutastic events, v1",
  "description": "One event of `mutastic run --format=jsonl`: each line of its output is one JSON object, keyed on its `reason`. A run writes `run-started` first, then `mutant-finished` for each mutant, `mutant-revised` for each mutant run again once a test turned out to fail on its own, then `run-finished`. `mutastic explain ID --format=jsonl` writes the same three for its one mutant, then `mutant-explained`. From 1.0.0, within v1, events only gain fields, event reasons and enum values: read what you know, and ignore a field or an event you don't. See https://docs.mutastic.io/reference/schemas/.",
  "type": "object",
  "required": ["schema", "edition", "reason"],
  "properties": {
    "schema": { "$ref": "#/$defs/schema" },
    "edition": { "$ref": "#/$defs/edition" },
    "reason": {
      "type": "string",
      "description": "What the event is: `run-started`, `mutant-finished`, `mutant-revised` or `run-finished`; from `mutastic next`, `next-item` and `next-finished`; from `mutastic explain`, `mutant-explained` after the run's. Ignore an event whose reason you don't know: a later v1 may add one."
    }
  },
  "allOf": [
    {
      "if": { "properties": { "reason": { "const": "run-started" } }, "required": ["reason"] },
      "then": { "$ref": "#/$defs/runStarted" }
    },
    {
      "if": { "properties": { "reason": { "const": "mutant-finished" } }, "required": ["reason"] },
      "then": { "$ref": "#/$defs/mutantFinished" }
    },
    {
      "if": { "properties": { "reason": { "const": "mutant-revised" } }, "required": ["reason"] },
      "then": { "$ref": "#/$defs/mutantFinished" }
    },
    {
      "if": { "properties": { "reason": { "const": "run-finished" } }, "required": ["reason"] },
      "then": { "$ref": "#/$defs/runFinished" }
    },
    {
      "if": { "properties": { "reason": { "const": "next-item" } }, "required": ["reason"] },
      "then": { "$ref": "#/$defs/mutantFinished", "required": ["ids", "rank"] }
    },
    {
      "if": { "properties": { "reason": { "const": "next-finished" } }, "required": ["reason"] },
      "then": { "$ref": "#/$defs/nextFinished" }
    },
    {
      "if": { "properties": { "reason": { "const": "mutant-explained" } }, "required": ["reason"] },
      "then": { "$ref": "#/$defs/mutantExplained" }
    }
  ],
  "$defs": {
    "schema": {
      "const": "mutastic.events/v1",
      "description": "The format and its major version. A change that would break a reader comes as v2, with a schema of its own."
    },
    "edition": {
      "enum": ["java", "rust"],
      "description": "Which mutastic wrote the event: `java` (for Java and Kotlin, on the JVM) or `rust`. Every event has it. What this schema requires only of the Java edition, `run-finished`'s `jdk`, every Java event has."
    },
    "status": {
      "enum": ["Killed", "Survived", "Timeout", "NoCoverage", "Ignored", "RuntimeError"],
      "description": "What happened to a mutant, in the mutation-testing report schema's words: `Killed` (caught: a test failed), `Survived` (missed: every test that reaches it passed), `Timeout` (the tests didn't finish, which counts as caught), `NoCoverage` (no test reaches its line), `Ignored` (skipped, and left out of the score) or `RuntimeError` (the Rust edition only: the tests crashed the process where the crash may be undefined behaviour, or where the standard library's own check of an unsafe precondition stopped it, so no test can be said to have caught it; left out of the score)."
    },
    "toolchain": {
      "type": "object",
      "description": "What compiled and ran the code under test, in words every edition shares.",
      "required": ["name", "version"],
      "properties": {
        "name": {
          "type": "string",
          "description": "`jdk` for the Java edition, `rustc` for Rust's."
        },
        "version": {
          "type": "string",
          "description": "Its version as the edition knows it: the Java feature release the tests ran on, `\"17\"`; rustc's version, `\"1.91.0\"`."
        }
      }
    },
    "score": {
      "type": ["number", "null"],
      "description": "A percentage to one decimal place, or null when there was nothing to score."
    },
    "count": {
      "type": "integer",
      "minimum": 0
    },
    "test": {
      "type": "string",
      "description": "A test, as its class and method: `com.example.PricingTest#bigOrdersGetTenPercentOff`."
    },
    "mutantId": {
      "type": "string",
      "pattern": "^m[0-9a-f]{8}$",
      "description": "A mutant's id, as people type it: `m855c8e3c`."
    },
    "runStarted": {
      "type": "object",
      "description": "The first event: the run has started.",
      "required": ["schema", "reason", "project", "rule", "decline"],
      "properties": {
        "schema": { "$ref": "#/$defs/schema" },
        "reason": { "const": "run-started" },
        "project": {
          "type": "string",
          "description": "The project's name."
        },
        "rule": {
          "type": "string",
          "description": "The rule a fix follows, for an agent: change tests only."
        },
        "decline": {
          "type": "string",
          "description": "When not to write a test, for an agent: a mutant whose case can't happen is proposed for a person to ignore instead."
        },
        "edition": { "$ref": "#/$defs/edition" },
        "profile": {
          "type": "object",
          "description": "The Rust edition only: the build settings that decide what a mutant means, so runs can be compared. mutastic builds with a profile of its own, with overflow checks and debug assertions on.",
          "properties": {
            "name": { "type": "string", "description": "The Cargo profile mutastic built with: `mutastic`." },
            "optLevel": { "description": "Cargo's `opt-level`, as Cargo reports it: `\"0\"`, `\"3\"`, `\"s\"`." },
            "overflowChecks": { "type": "boolean", "description": "Whether integer overflow panics." },
            "debugAssertions": { "type": "boolean", "description": "Whether `debug_assert!` runs." }
          }
        },
        "share": {
          "type": "object",
          "description": "In a run of one share of the mutants (`--shard=N/M`): which share, and what it ran on, so `run --from-shards` puts together only the shares of one run.",
          "required": ["number", "of", "version", "code"],
          "properties": {
            "number": { "type": "integer", "minimum": 1, "description": "Which share: N." },
            "of": { "type": "integer", "minimum": 1, "description": "How many shares the run's mutants were dealt into: M." },
            "version": { "type": "string", "description": "The mutastic that ran it." },
            "code": { "type": "string", "description": "A hash of the module's compiled code and tests: the same in every share of one run." }
          }
        },
        "unsafe": {
          "enum": ["skip", "enclosing", "all"],
          "description": "The Rust edition only: which code near `unsafe` was mutated. `skip`: neither unsafe code nor a safe function holding an `unsafe` block; `enclosing`: those functions too, but not the code inside the block; `all`: everything. See `nearUnsafe`, and `run-finished`'s `notMutated`."
        }
      }
    },
    "mutantFinished": {
      "type": "object",
      "description": "One mutant's result, written as it finishes (`mutant-finished`). With `--tests-from`, every mutant's comes at the end, once all the tests have run. A mutant a test reached that turned out to fail on its own, with no mutant in, runs again without that test once every mutant has its first result, and comes again as `mutant-revised`, with the same fields: that result replaces the first.",
      "required": [
        "schema",
        "reason",
        "id",
        "fingerprint",
        "status",
        "operator",
        "location",
        "mutation",
        "coveredBy",
        "testsRun",
        "durationMs"
      ],
      "properties": {
        "schema": { "$ref": "#/$defs/schema" },
        "reason": { "enum": ["mutant-finished", "mutant-revised", "next-item"] },
        "edition": { "$ref": "#/$defs/edition" },
        "id": { "$ref": "#/$defs/mutantId" },
        "fingerprint": {
          "type": "string",
          "pattern": "^[0-9a-f]{16}$",
          "description": "The mutant's fingerprint, from its class, method, site and change: the id is `m` and its first eight digits."
        },
        "status": { "$ref": "#/$defs/status" },
        "operator": {
          "type": "string",
          "description": "The operator that made it: `mutastic/boundary`, `stream/remove-stage`."
        },
        "docs": {
          "type": "string",
          "description": "Where docs.mutastic.io describes the operator, in the docs of the mutastic that ran: in the Java edition its row on the operators page, `https://docs.mutastic.io/concepts/operators/#boundary`; in Rust's its section in the Rust docs, `https://docs.mutastic.io/rust/operators/conditions/#boundary`. Only for mutastic's own operators: a plugin's has none."
        },
        "location": {
          "type": "object",
          "description": "Where the mutant is.",
          "required": ["file", "line", "class", "method"],
          "properties": {
            "file": {
              "type": "string",
              "description": "The source file, relative to the project: `src/main/java/com/example/pricing/Pricing.java`. Where no source directory has it, the path the class's source would have."
            },
            "line": {
              "type": "integer",
              "minimum": 1,
              "description": "The line, from 1."
            },
            "column": {
              "type": "integer",
              "minimum": 1,
              "description": "The column, from 1. The Rust edition only."
            },
            "class": {
              "type": "string",
              "description": "What holds the method. In the Java edition, the class by its binary name: `com.example.pricing.Pricing`, `com.example.Outer$Inner`. In Rust's, the path of the item that holds the function: `pricing::Pricing`, or the crate's name for a free function."
            },
            "method": {
              "type": "string",
              "description": "The method. In the Java edition, by name and JVM descriptor: `discount(I)I`. In Rust's, the function's name: `discount`."
            }
          }
        },
        "mutation": {
          "type": "object",
          "description": "What the mutant changes.",
          "required": ["description", "applied"],
          "properties": {
            "original": {
              "type": "string",
              "description": "The source the change is to, where it's known: `>=`."
            },
            "replacement": {
              "type": "string",
              "description": "What that source becomes, where the change has one: `>`."
            },
            "description": {
              "type": "string",
              "description": "The change in words: \"`>=` becomes `>`\"."
            },
            "applied": {
              "type": "object",
              "description": "The change as the engine makes it, and at what level: in the Java edition to the compiled instruction, `{\"level\": \"bytecode\", \"text\": \"IF_ICMPLT -> IF_ICMPLE\"}`; in Rust's to the source, the whole site before and after, `{\"level\": \"source\", \"text\": \"x < 0 -> true\"}`. Every mutant has it, in both editions.",
              "required": ["level", "text"],
              "properties": {
                "level": {
                  "type": "string",
                  "description": "What the engine changes: `bytecode`, the compiled instructions (the Java edition), or `source`, the code before it's compiled (the Rust edition). `llvm-ir` is kept for an engine that changes the compiler's intermediate code. A later v1 may add a level, so it's a string rather than a closed list: accept a level you don't know, and treat its `text` as opaque."
                },
                "text": {
                  "type": "string",
                  "description": "The change at that level, before and after: `IF_ICMPLT -> IF_ICMPLE`, `x < 0 -> true`. Its form depends on the level: read it as opaque text, for people."
                }
              }
            }
          }
        },
        "statusReason": {
          "type": "string",
          "description": "Why it was skipped, kind first: `arid: trivial getter of name`. Only for a skipped mutant."
        },
        "skipKind": {
          "enum": ["generated", "equivalent", "unviable", "arid", "user"],
          "description": "The kind of reason it was skipped, the first word of `statusReason`: code the compiler wrote, a change that can't be told apart, one that can't run, code not worth a test, or the project's own decision. Only with `statusReason`."
        },
        "coveredBy": {
          "type": "array",
          "items": { "$ref": "#/$defs/test" },
          "description": "The tests that reach the mutant's line: its own test class's first (PricingTest's, for Pricing), then the rest, each by name, whatever order they ran in. Empty when none does, or when it was skipped."
        },
        "caughtBy": {
          "$ref": "#/$defs/test",
          "description": "The test that caught it, in the word `changedSinceLastRun` uses. Only when a test did: a timeout names none, and nor does a crash the Rust edition counts as caught, which `caughtWith` describes."
        },
        "caughtWith": {
          "type": "string",
          "description": "What caught it failed with: in the Java edition the exception's class, `org.opentest4j.AssertionFailedError`; in Rust's the panic or the crash, `crash: SIGABRT`."
        },
        "caughtByAll": {
          "type": "array",
          "items": { "$ref": "#/$defs/test" },
          "description": "Every covering test that catches it, in the order they ran, the first being `caughtBy`: only with `--kill-matrix`, and only when one did."
        },
        "nearUnsafe": {
          "type": "string",
          "description": "The Rust edition only: why the mutant is near unsafe code, when it is (`called from unsafe code`, `in a module with unsafe code`, `in a function with an unsafe block`, `inside unsafe code`). It ran on its own, and a crash isn't counted as caught (`RuntimeError`)."
        },
        "failureOrigin": {
          "enum": ["assertion", "test", "code", "unknown"],
          "description": "Where the failure came from: `assertion` (a check failed), `test` (the test's own code threw), `code` (the code under test threw) or `unknown`."
        },
        "unconfirmedCatchBy": {
          "$ref": "#/$defs/test",
          "description": "A test whose catch didn't repeat when run again (`--confirm-catches`): likely flaky, and its catch doesn't count."
        },
        "falseCatchBy": {
          "$ref": "#/$defs/test",
          "description": "A test that failed against the mutant and then failed on its own with no mutant in, so its catch wasn't the mutant's: the mutant ran again without it, and this result is what the other covering tests made of it. Only when the test that caught it turned out that way: in `mutant-revised`, or with `--tests-from` in the `mutant-finished` that comes at the end."
        },
        "inputs": {
          "type": "object",
          "description": "The whole numbers the covering tests passed to the mutant's method, by parameter name: `{\"total\": [50, 250]}`. Only when there were any.",
          "additionalProperties": {
            "type": "array",
            "items": { "type": "integer" }
          }
        },
        "suggestedTest": {
          "type": "object",
          "description": "A test that would catch it, found by running a call with and without the mutant. Only when one was found.",
          "required": ["testClass", "name", "code", "call", "today", "withMutant"],
          "properties": {
            "testClass": {
              "type": "string",
              "description": "The test class to add it to, by simple name: `PricingTest`."
            },
            "name": {
              "type": "string",
              "description": "The test method's name: `discountWithTotal100`."
            },
            "code": {
              "type": "string",
              "description": "The test, as source, lines joined by `\\n`."
            },
            "call": {
              "type": "string",
              "description": "The call the test makes, in the mutated class's language: `new Pricing().discount(100)`."
            },
            "today": {
              "type": "string",
              "description": "What the call gives today, as source: `10`, `true`, `\"text\"`, or `throws java.lang.IllegalStateException`. What the code does, not necessarily what it should."
            },
            "withMutant": {
              "type": "string",
              "description": "What it gives with the mutant in, the same way, or `doesn't return`."
            }
          }
        },
        "suggestion": {
          "type": "object",
          "description": "The call `suggestedTest` was found by, whole, or for a mutant no test reaches the call a first test could start from: what a card is written from. Only when one was found.",
          "required": ["call", "parameters", "arguments", "receiver", "today", "withMutant"],
          "properties": {
            "call": { "type": "string", "description": "The call, in the mutated class's language: `new Pricing().discount(100)`." },
            "parameters": { "type": "array", "items": { "type": "string" }, "description": "The method's parameter names, in order." },
            "arguments": { "type": "array", "items": { "type": "integer" }, "description": "The arguments, one for each parameter." },
            "receiver": {
              "type": "array",
              "description": "The instances made to call the method on, outermost first; empty for a static method.",
              "items": {
                "type": "object",
                "required": ["type", "arguments"],
                "properties": {
                  "type": { "type": "string", "description": "The class's simple name: `Pricing`." },
                  "arguments": { "type": "array", "items": { "type": "integer" }, "description": "Its constructor's arguments." },
                  "parameters": { "type": "array", "items": { "type": "string" }, "description": "The constructor's names for its arguments, in order; empty when they aren't known. Written from 1.0.0." }
                }
              }
            },
            "today": { "type": "string", "description": "What the call gives today, as `suggestedTest` has it." },
            "withMutant": { "type": "string", "description": "What it gives with the mutant in, as `suggestedTest` has it." },
            "named": {
              "type": "object",
              "description": "The value the call is at the edge of, when the source names it, which the test is named after. Only then. Written from 1.0.0.",
              "required": ["name", "note"],
              "properties": {
                "name": { "type": "string", "description": "A constant's or a field's name, as the source has it: `FREE_FROM`, `freeFrom`." },
                "note": { "type": "string", "description": "What the test's comment says of it: `FREE_FROM = 100`, or `100 is Pricing's FREE_FROM` for a constant the test can't use; empty if nothing." },
                "local": { "type": "string", "description": "The line the test declares the value in, when it passes it twice: `int freeFrom = 20;`." },
                "meaning": { "type": "string", "description": "What the name is, and its value where it's known: `FREE_FROM (Discounts.FREE_FROM = 100)`, `freeFrom = 50 (Delivery's field, from new Delivery(50))`. Optional." }
              }
            },
            "builtBy": { "type": "string", "description": "The covering test the call makes its receiver as, `DeliveryTest#smallOrders`, when it makes it the same way. Optional." }
          }
        },
        "testsRun": {
          "$ref": "#/$defs/count",
          "description": "How many covering tests ran before the result was known."
        },
        "durationMs": {
          "$ref": "#/$defs/count",
          "description": "How long the mutant took, in milliseconds."
        },
        "doneWhen": {
          "type": "string",
          "description": "The command that proves a fix: `mutastic verify m855c8e3c samples/pricing passes`. Only for a missed mutant."
        },
        "reproduce": {
          "type": "string",
          "description": "The command that runs it again on its own. Only for a missed mutant."
        },
        "ignore": {
          "type": "string",
          "description": "The command that ignores it, with a reason. Only for a missed mutant, and always there for one."
        },
        "propose": {
          "type": "string",
          "description": "The command that proposes ignoring it, for a person to accept: what an agent runs instead of `ignore`. Only for a missed mutant, and always there for one."
        },
        "rendered": {
          "type": "string",
          "description": "The mutant's entry as `--format=agent` writes it. Only for a missed mutant. Its words are for reading, and change between releases."
        },
        "ids": {
          "type": "array",
          "items": { "$ref": "#/$defs/mutantId" },
          "description": "Only in `next-item`: every mutant on the item's line, the lead (this event's mutant) first. One test usually catches them all."
        },
        "rank": {
          "type": "object",
          "description": "Only in `next-item`: where the item stands in `mutastic next`'s queue, and what the order went by.",
          "required": ["position", "order", "factors"],
          "properties": {
            "position": { "type": "integer", "minimum": 1, "description": "1 for the head of the queue." },
            "order": {
              "type": "string",
              "description": "Which order ranked it. `run-1` is the run's own: new misses before a baseline's, then a condition or removal before other changes, then the line more tests reach, then source order. It changes only with a release."
            },
            "factors": {
              "type": "array",
              "description": "What the order went by, in the order it's applied.",
              "items": {
                "type": "object",
                "required": ["signal", "value", "why"],
                "properties": {
                  "signal": {
                    "type": "string",
                    "description": "`baseline` (only where there's a baseline), `operator` or `coveringTests`. A later v1 may add signals."
                  },
                  "value": {
                    "type": ["string", "integer"],
                    "description": "What it was ranked on: `new` or `legacy`; `condition-or-removal` or `other`; how many tests reach the line."
                  },
                  "why": { "type": "string", "description": "The factor in words, as the human format prints it." }
                }
              }
            }
          }
        }
      },
      "allOf": [
        {
          "if": { "properties": { "status": { "const": "Survived" } }, "required": ["status"] },
          "then": { "required": ["doneWhen", "reproduce", "ignore", "propose", "rendered"] }
        }
      ]
    },
    "flakyEvidence": {
      "type": "object",
      "description": "What one flaky test failed with, and what it reaches that changes from run to run.",
      "required": ["test", "failure", "reaches", "reachesInTest", "outside"],
      "properties": {
        "test": { "$ref": "#/$defs/test" },
        "failure": {
          "type": "array",
          "items": { "type": "string" },
          "description": "The exception it failed with and its causes, outermost first, by class name. Empty when the worker didn't say."
        },
        "reaches": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Calls in the code it ran that give a different answer each run: `new Random()`, `Instant.now`."
        },
        "reachesInTest": {
          "type": "array",
          "items": { "type": "string" },
          "description": "The same, in the test's own class."
        },
        "failsAlone": {
          "const": true,
          "description": "It passed among the other tests and failed on its own, with no mutant in. Only there when it did."
        },
        "outside": {
          "type": "boolean",
          "description": "Whether it failed with something from outside the code: the network, a database, a channel or a file."
        }
      }
    },
    "flip": {
      "type": "object",
      "description": "A mutant caught in one run and not the other.",
      "required": ["id", "before", "after"],
      "properties": {
        "id": { "$ref": "#/$defs/mutantId" },
        "before": { "$ref": "#/$defs/status" },
        "after": { "$ref": "#/$defs/status" },
        "caughtBy": {
          "$ref": "#/$defs/test",
          "description": "The test that caught it, in whichever run it was caught. Not there for a timeout."
        }
      }
    },
    "likelyFlaky": {
      "type": "object",
      "description": "A test that's likely flaky, the mutants whose results it changed, and what named it.",
      "required": ["test", "mutants", "reasons"],
      "properties": {
        "test": { "$ref": "#/$defs/test" },
        "mutants": {
          "type": "array",
          "items": { "$ref": "#/$defs/mutantId" }
        },
        "reasons": {
          "type": "array",
          "items": { "enum": ["changed-since-last-run", "catch-not-repeated"] },
          "description": "`changed-since-last-run`: with the same code and mutastic, a mutant it caught changed result. `catch-not-repeated`: its catch didn't repeat under `--confirm-catches`."
        }
      }
    },
    "unrelatedCatch": {
      "type": "object",
      "description": "A test that caught mutants in several unrelated methods, each time with the same exception thrown in its own code: it may be flaky, or just call a lot of code.",
      "required": ["test", "failure", "mutants"],
      "properties": {
        "test": { "$ref": "#/$defs/test" },
        "failure": {
          "type": "string",
          "description": "The exception it threw each time, by class name."
        },
        "mutants": {
          "type": "array",
          "items": { "$ref": "#/$defs/mutantId" }
        }
      }
    },
    "runFinished": {
      "type": "object",
      "description": "The last event: the totals, the score and verdict, and how the run compares with the last one.",
      "required": [
        "schema",
        "reason",
        "mutants",
        "killed",
        "survived",
        "timeout",
        "noCoverage",
        "ignored",
        "toolchain",
        "score",
        "threshold",
        "passed",
        "coveredScore",
        "tests",
        "workers",
        "workersStarted",
        "coverageMs",
        "flaky",
        "flakyEvidence",
        "likelyFlaky",
        "unrelatedCatches",
        "reports",
        "staleIgnores",
        "durationMs"
      ],
      "properties": {
        "schema": { "$ref": "#/$defs/schema" },
        "reason": { "const": "run-finished" },
        "edition": { "$ref": "#/$defs/edition" },
        "jdk": {
          "type": "integer",
          "description": "The Java feature release the tests ran on: `17`. The Java edition only, and always there in it; `toolchain` says the same in words every edition shares."
        },
        "toolchain": {
          "$ref": "#/$defs/toolchain",
          "description": "What compiled and ran the code under test: `{\"name\": \"jdk\", \"version\": \"17\"}`."
        },
        "mutants": { "$ref": "#/$defs/count", "description": "How many mutants there were, skipped ones too." },
        "killed": { "$ref": "#/$defs/count", "description": "How many were caught." },
        "survived": { "$ref": "#/$defs/count", "description": "How many were missed." },
        "timeout": { "$ref": "#/$defs/count", "description": "How many timed out, which counts as caught." },
        "noCoverage": { "$ref": "#/$defs/count", "description": "How many no test reaches." },
        "ignored": { "$ref": "#/$defs/count", "description": "How many were skipped." },
        "runtimeError": { "$ref": "#/$defs/count", "description": "The Rust edition only: how many crashed in a way no test can be said to have caught, out of the score (`RuntimeError`)." },
        "notMutated": {
          "type": "object",
          "description": "The Rust edition only: places mutastic found and chose not to mutate, by why.",
          "properties": {
            "unsafe": { "$ref": "#/$defs/count", "description": "Mutants not made because their code is near `unsafe` and `run-started`'s `unsafe` setting leaves it out." },
            "cfg": { "$ref": "#/$defs/count", "description": "Mutants not made because their code is under a `cfg` that's off in this build (another platform, or a feature not enabled), so it's never compiled: not counted as not covered. Optional." }
          }
        },
        "build": {
          "type": "object",
          "description": "The Rust edition only: how the mutants were compiled.",
          "properties": {
            "rounds": { "$ref": "#/$defs/count", "description": "How many times the crate was compiled with mutants in." },
            "unviable": { "$ref": "#/$defs/count", "description": "How many mutants didn't compile, and were skipped." },
            "setupMs": { "$ref": "#/$defs/count", "description": "How long reading the package and building it took, before the coverage pass, in milliseconds, of `durationMs`. Optional." }
          }
        },
        "score": {
          "$ref": "#/$defs/score",
          "description": "The share of mutants caught, skipped ones left out, as a percentage to one decimal place; null when there was nothing to score."
        },
        "threshold": {
          "type": "integer",
          "description": "The score the run had to reach to pass."
        },
        "passed": {
          "type": "boolean",
          "description": "Whether the score reached the threshold. For a run of mutants asked for by id (`--only`), whether every one was caught. With `failOnNew`, whether no mutant no test catches was new: the threshold isn't applied."
        },
        "failOnNew": {
          "const": true,
          "description": "The run was judged on its new misses (`run --fail-on-new`), not on its score: it passes while every mutant no test catches, missed or not covered, is in the project's `.mutastic/baseline.yaml`. Only for such a run. Optional."
        },
        "newMisses": {
          "type": "array",
          "items": { "$ref": "#/$defs/mutantId" },
          "description": "With `failOnNew`, the mutants no test catches that the baseline doesn't hold, by id: what failed the run. Only with `failOnNew`."
        },
        "inBaseline": {
          "$ref": "#/$defs/count",
          "description": "With `failOnNew`, how many mutants no test catches the baseline holds: missed before, and still missed. Only with `failOnNew`."
        },
        "only": {
          "type": ["string", "null"],
          "description": "For a run of mutants asked for by id (`--only`), the first one found; null when none was. Only for such a run."
        },
        "coveredScore": {
          "$ref": "#/$defs/score",
          "description": "The score of the code some test reaches: how well the tests check what they run."
        },
        "tests": { "$ref": "#/$defs/count", "description": "How many tests the project has." },
        "workers": { "$ref": "#/$defs/count", "description": "How many test workers ran side by side: in the Java edition each is a JVM, in Rust's a thread." },
        "workersStarted": { "$ref": "#/$defs/count", "description": "How many test workers started in all: one for each worker, and one more after each was stopped for taking too long. The Rust edition never stops one, so it's `workers` there." },
        "coverageMs": { "$ref": "#/$defs/count", "description": "How long the coverage pass took, in milliseconds, of `durationMs`." },
        "flaky": {
          "type": "array",
          "items": { "$ref": "#/$defs/test" },
          "description": "Tests that failed when first run and passed when run again: left out of every mutant's run."
        },
        "flakyEvidence": {
          "type": "array",
          "items": { "$ref": "#/$defs/flakyEvidence" },
          "description": "For each flaky test the run found out about, why it may be flaky."
        },
        "previousScore": {
          "$ref": "#/$defs/score",
          "description": "The last run's score. Only when there was a last run, under the same operators."
        },
        "sameCodeAsLastRun": {
          "type": "boolean",
          "description": "Whether the code and tests are byte for byte what the last run had. Only with `previousScore`."
        },
        "sameMutasticAsLastRun": {
          "type": "boolean",
          "description": "Whether the same mutastic ran last time. Only with `previousScore`."
        },
        "changedSinceLastRun": {
          "type": "array",
          "items": { "$ref": "#/$defs/flip" },
          "description": "Mutants caught in one run and not the other. Only with `previousScore`."
        },
        "likelyFlaky": {
          "type": "array",
          "items": { "$ref": "#/$defs/likelyFlaky" },
          "description": "Tests that are likely flaky, and why."
        },
        "unrelatedCatches": {
          "type": "array",
          "items": { "$ref": "#/$defs/unrelatedCatch" },
          "description": "Tests that fail the same way whatever the change."
        },
        "reports": {
          "type": "array",
          "items": { "type": "string" },
          "description": "The report files the run wrote, relative to where it ran: `build/mutastic/mutation.json`."
        },
        "aridAudit": {
          "type": "object",
          "description": "What became of the mutants the arid filters skip, when they ran anyway (`MUTASTIC_ARID=audit`). Only in such a run.",
          "required": ["ran", "caught", "caughtByAssertion", "missed", "notCovered", "checkedIn"],
          "properties": {
            "ran": { "$ref": "#/$defs/count", "description": "How many arid mutants ran." },
            "caught": { "$ref": "#/$defs/count", "description": "How many of those were caught." },
            "caughtByAssertion": {
              "$ref": "#/$defs/count",
              "description": "How many of those a test's own assertion caught: a check on what the code returns."
            },
            "missed": { "$ref": "#/$defs/count" },
            "notCovered": { "$ref": "#/$defs/count" },
            "checkedIn": {
              "type": "array",
              "items": { "type": "string" },
              "description": "The methods of the catches by an assertion, `Board.toString()`, in the order found."
            }
          }
        },
        "ghosts": {
          "type": "array",
          "description": "Experimental, with `--experimental=ghosts`: each ghost, the call most writes like this one make inserted where this one doesn't, and what its tests did. Never in the score or the counts. Only in such a run, and empty when it found none.",
          "items": {
            "type": "object",
            "required": ["id", "experimental", "status", "file", "line", "class", "method", "field", "inserted", "rule", "writes", "peers", "coveredBy", "rendered"],
            "properties": {
              "id": { "type": "string" },
              "experimental": { "const": true },
              "status": {
                "enum": ["appeared", "passedUnseen", "notCovered"],
                "description": "`appeared`: a test failed with the call in, and expects the silence; `passedUnseen`: every test that reaches the write passed; `notCovered`: no test reaches it."
              },
              "file": { "type": "string" },
              "line": { "type": "integer" },
              "class": { "type": "string" },
              "method": { "type": "string" },
              "field": { "type": "string", "description": "The field the write is to." },
              "inserted": { "type": "string", "description": "The call inserted: `firePropertyChange(\"balance\")`." },
              "rule": {
                "enum": ["sameField", "siblingFields"],
                "description": "`sameField`: other writes to the field fire; `siblingFields`: the class's other fields fire on every write."
              },
              "writes": { "$ref": "#/$defs/count", "description": "How many writes the rule counted, this one among them." },
              "peers": {
                "type": "array",
                "description": "The writes that fire: where, and the property name they give (empty when a method of the class fires for them).",
                "items": {
                  "type": "object",
                  "required": ["method", "line", "property"],
                  "properties": {
                    "method": { "type": "string" },
                    "line": { "type": "integer" },
                    "property": { "type": "string" }
                  }
                }
              },
              "coveredBy": { "type": "array", "items": { "type": "string" } },
              "caughtBy": { "type": "string", "description": "The test that failed with the call in, for `appeared`." },
              "rendered": { "type": "string", "description": "The ghost as the agent format shows it." }
            }
          }
        },
        "scope": {
          "type": "object",
          "description": "The change the run was scoped to (`--since`). Only for such a run.",
          "required": ["since", "fileCount", "lineCount"],
          "properties": {
            "since": {
              "type": "string",
              "description": "What the change is measured from, as given: `main`."
            },
            "lineCount": {
              "$ref": "#/$defs/count",
              "description": "How many lines it touched."
            },
            "fileCount": {
              "$ref": "#/$defs/count",
              "description": "How many source files it touched."
            }
          }
        },
        "staleIgnores": {
          "type": "array",
          "items": { "type": "string" },
          "description": "The ids of ignored mutants whose line has changed since, as `.mutastic/ignores.yaml` has them: the ignore no longer holds, and they ran."
        },
        "durationMs": {
          "$ref": "#/$defs/count",
          "description": "How long the run took, in milliseconds."
        }
      },
      "allOf": [
        {
          "if": { "properties": { "edition": { "const": "java" } } },
          "then": { "required": ["jdk"] }
        }
      ]
    },
    "nextFinished": {
      "type": "object",
      "description": "The end of `mutastic next`'s answer: how many items are queued, and how many were shown.",
      "required": ["schema", "reason", "queued", "mutants", "shown", "legacy", "waiting"],
      "properties": {
        "schema": { "$ref": "#/$defs/schema" },
        "reason": { "const": "next-finished" },
        "edition": { "$ref": "#/$defs/edition" },
        "queued": { "type": "integer", "minimum": 0, "description": "How many items (lines with missed mutants) are queued: 0 when nothing missed is left." },
        "mutants": { "type": "integer", "minimum": 0, "description": "How many missed mutants are on them." },
        "shown": { "type": "integer", "minimum": 0, "description": "How many items were shown, as `--top` asked, spread over the code." },
        "legacy": { "type": "integer", "minimum": 0, "description": "How many of the queued items a committed baseline already has." },
        "waiting": { "type": "integer", "minimum": 0, "description": "How many missed mutants are left out of the queue because they are proposed for ignoring (`ignore --propose`), waiting on a person to accept them." }
      }
    },
    "mutantExplained": {
      "type": "object",
      "description": "The last event of `mutastic explain ID --format=jsonl`, after the run's `run-started`, the mutant's `mutant-finished` and `run-finished`: what the text's About this mutant says, a field for each of its rows, in the same words.",
      "required": ["schema", "reason", "id", "applied", "operator", "fingerprint", "idDescription", "idNote", "site"],
      "properties": {
        "schema": { "$ref": "#/$defs/schema" },
        "reason": { "const": "mutant-explained" },
        "edition": { "$ref": "#/$defs/edition" },
        "id": { "$ref": "#/$defs/mutantId" },
        "applied": {
          "type": "object",
          "description": "The change as the engine makes it, as `mutant-finished`'s `mutation.applied` says it, with what each edition can add: in the Java edition the compiled instruction before and after, what each does and how a jump relates to the source (`level` `bytecode`); in the Rust edition the source before and after (`level` `source`).",
          "required": ["level", "text", "original", "replacement"],
          "properties": {
            "level": { "type": "string", "description": "What the engine changes: `bytecode` (the Java edition) or `source` (the Rust edition), as `mutation.applied.level` says. A later v1 may add one: accept a level you don't know." },
            "text": { "type": "string", "description": "The change at that level, before and after, as `mutation.applied.text` has it: `IF_ICMPLT -> IF_ICMPLE`, `total >= 100 -> (total) > (100)`." },
            "original": { "type": "string", "description": "What the engine changes, at that level: `IF_ICMPLT`, or `total >= 100`." },
            "replacement": { "type": "string", "description": "What it becomes: `IF_ICMPLE`, or `(total) > (100)`." },
            "meanings": {
              "type": "array",
              "items": { "type": "string" },
              "description": "At the bytecode level, what each instruction does, the original's first, for each one there are words for: `IF_ICMPLT: jump if the first int is less than the second`."
            },
            "note": { "type": "string", "description": "At the bytecode level, for a changed conditional jump, how it relates to the source: javac usually jumps past an `if`'s body when its condition is false, so the jump tests the opposite of the source. Only when the change is to a jump." },
            "description": { "type": "string", "description": "At the source level, what the replacement is, in the text's words." }
          }
        },
        "operator": {
          "type": "object",
          "description": "The operator that made it, and what it does.",
          "required": ["id", "description"],
          "properties": {
            "id": { "type": "string", "description": "The operator's id: `mutastic/boundary`." },
            "description": { "type": "string", "description": "What it changes, and what tests that miss it don't check." }
          }
        },
        "fingerprint": {
          "type": "string",
          "pattern": "^[0-9a-f]{16}$",
          "description": "The mutant's fingerprint, from its class, method, site and change: the id is `m` and its first eight digits."
        },
        "idDescription": { "type": "string", "description": "How the id is made from the fingerprint, and the fingerprint from the mutant." },
        "idNote": { "type": "string", "description": "Why the id stays the same when code above it moves." },
        "site": {
          "type": "object",
          "description": "Which site of its method it is.",
          "required": ["method", "description"],
          "properties": {
            "ordinal": { "type": "integer", "minimum": 1, "description": "Which site, counting from 1, as `description` says they're counted. Not for a change to the class as a whole." },
            "method": { "type": "string", "description": "The method, with its JVM descriptor: `discount(I)I`." },
            "description": { "type": "string", "description": "Which site it is and how the sites are counted, in words." }
          }
        },
        "lastRun": {
          "type": "object",
          "description": "How the last full run (`run` or `next`) went for it. Only when one has been recorded.",
          "required": ["status", "description"],
          "properties": {
            "status": { "$ref": "#/$defs/status" },
            "caughtBy": { "type": "string", "description": "The test that caught it, when one did." },
            "description": { "type": "string", "description": "The last run's result, in words: `missed: every covering test still passed`." }
          }
        }
      }
    }
  }
}
