{
  "$comment": "Evidence base for /landscape. Two independent methods per tool: `probe` = aoi-probe run against the installed binary on a credential-free, network-free command; `docs` = primary documentation and source. Where both ran, they agreed. Do not hand-edit findings \u2014 regenerate the probe half with tools/aoi-probe/corpus.sh.",
  "generated": "2026-07-30",
  "probe_version": "0.1.0",
  "method": {
    "probe": "aoi-probe run locally against the installed binary. Every command is credential-free and network-free so the run reproduces on any machine.",
    "docs": "Primary vendor documentation and upstream source, read directly. Used for tools whose flagship mode needs an environment (a cluster, a token, a daemon) that a reproducible probe cannot assume.",
    "kind": {
      "stream": "the tool's primary streaming/bulk machine-output mode",
      "query": "a data-returning command in the tool's machine-output mode",
      "meta": "only a version/metadata command was reachable without credentials"
    }
  },
  "axes": {
    "ndjson": "One complete JSON object per line, so a consumer can process incrementally.",
    "discriminator": "A field whose value names the record type, so a consumer dispatches on shape rather than guessing.",
    "format_version": "A version OF THE OUTPUT FORMAT carried in the output itself. Deliberately distinguished from the version of the tool or of the subject being described \u2014 across this table, `version` means all three different things.",
    "terminal_event": "A distinct final record meaning 'the run finished', so end-of-stream is distinguishable from a truncated stream.",
    "success_boolean": "An explicit boolean on the terminal record. An integer exit status does not count: it requires out-of-band knowledge of a code table.",
    "offline_discovery": "Can the tool be asked, offline and unauthenticated, to describe its own output?",
    "stability_promise": "Does the documentation make an explicit compatibility commitment about the machine output?"
  },
  "tools": [
    {
      "name": "cargo",
      "mechanism": "--message-format json",
      "kind": "stream",
      "verified_by": [
        "probe",
        "docs"
      ],
      "probe_argv": "cargo build --message-format json",
      "ndjson": true,
      "discriminator": "reason",
      "discriminator_note": "Two differently-spelled discriminators in ONE stream: `reason` at the envelope level, `$message_type` nested inside embedded rustc diagnostics.",
      "format_version": null,
      "terminal_event": "build-finished",
      "success_boolean": "success",
      "offline_discovery": "partial",
      "stability_promise": "scoped-elsewhere",
      "stability_note": "TRAP: cargo's 'the format is stable and versioned' promise covers `cargo metadata`, NOT the `--message-format json` build stream. The build stream carries no version at all.",
      "gap": "No version declaration anywhere in the stream. A consumer cannot branch on format version.",
      "source": "https://doc.rust-lang.org/cargo/reference/external-tools.html"
    },
    {
      "name": "terraform",
      "mechanism": "-json on plan/apply/init",
      "kind": "stream",
      "verified_by": [
        "probe",
        "docs"
      ],
      "probe_argv": "terraform plan -json -no-color",
      "ndjson": true,
      "discriminator": "type",
      "format_version": "ui",
      "format_version_note": "Carried inside an event typed `version`, under a key called `ui` \u2014 invisible to any key-name heuristic. Docs say 1.0 as of TF 1.1.0; observed 1.1 in 1.5.7, consistent with their documented minor-bump rule.",
      "terminal_event": null,
      "terminal_event_note": "`change_summary` is emitted on success only. A failed apply ends on `apply_errored` + `diagnostic` with no terminal message, so failure is indistinguishable from a killed process. Probing `plan` confirms the last event is `outputs` \u2014 unique, but not a completion marker.",
      "success_boolean": null,
      "offline_discovery": false,
      "stability_promise": "explicit",
      "stability_note": "Strongest JSON promise in the survey: increment minor for backward-compatible additions, ignore unrecognized properties, increment major for breaking changes, reject unsupported major versions.",
      "gap": "No terminal event on failure, and no success boolean at any time.",
      "source": "https://developer.hashicorp.com/terraform/internals/machine-readable-ui"
    },
    {
      "name": "ripgrep",
      "mechanism": "--json",
      "kind": "stream",
      "verified_by": [
        "probe",
        "docs"
      ],
      "probe_argv": "rg --json <pattern> <file>",
      "ndjson": true,
      "discriminator": "type",
      "discriminator_values": [
        "begin",
        "match",
        "context",
        "end",
        "summary"
      ],
      "format_version": null,
      "terminal_event": "summary",
      "success_boolean": null,
      "terminal_event_note": "A genuine terminal event, but it carries only `elapsed_total` and `stats`. A clean no-match run is indistinguishable from an aborted one. Key order is also unstable \u2014 `type` is the last key on the summary line.",
      "offline_discovery": "man-page-only",
      "stability_promise": "weak",
      "stability_note": "Only 'there are four different types of messages (and this number may expand over time)'.",
      "gap": "No version field of any kind, and no success indicator on the terminal event.",
      "source": "https://docs.rs/grep-printer/latest/grep_printer/struct.JSON.html"
    },
    {
      "name": "git trace2",
      "mechanism": "GIT_TRACE2_EVENT",
      "kind": "stream",
      "verified_by": [
        "docs"
      ],
      "ndjson": true,
      "discriminator": "event",
      "format_version": "evt",
      "format_version_note": "Observed \"4\", with a documented bump rule: increment when event types are added or fields removed; additive field changes do not require an increment.",
      "terminal_event": "atexit",
      "success_boolean": null,
      "terminal_event_note": "`atexit` is genuinely last (after `exit`), but carries `code` as an integer, not a boolean.",
      "offline_discovery": false,
      "stability_promise": null,
      "channel_note": "Emitted on a separate channel (stderr/fd/file), so it structurally cannot mix with stdout \u2014 the one tool that solves stream pollution by construction.",
      "gap": "Integer code rather than a boolean; no stability promise and no offline documentation (api-trace2 ships no man page).",
      "source": "https://git-scm.com/docs/api-trace2"
    },
    {
      "name": "git",
      "mechanism": "status --porcelain=v2",
      "kind": "stream",
      "verified_by": [
        "probe",
        "docs"
      ],
      "probe_argv": "git status --porcelain=v2",
      "ndjson": false,
      "json": false,
      "discriminator": "leading sigil character (#, 1, 2, u, ?, !)",
      "format_version": null,
      "format_version_note": "The version lives in the FLAG (--porcelain=v2), not the output. A consumer cannot detect which version it was handed.",
      "terminal_event": null,
      "success_boolean": null,
      "offline_discovery": "man-page-only",
      "stability_promise": "explicit",
      "stability_note": "Strongest promise in the entire survey \u2014 and it belongs to a format that is not JSON: 'will remain stable across Git versions and regardless of user configuration.'",
      "gap": "Not JSON at all, and no in-band version. Arguably the most-invoked tool in any agent shell session.",
      "source": "https://git-scm.com/docs/git-status"
    },
    {
      "name": "pytest-reportlog",
      "mechanism": "--report-log=FILE",
      "kind": "stream",
      "verified_by": [
        "docs"
      ],
      "ndjson": true,
      "discriminator": "$report_type",
      "format_version": "pytest_version",
      "format_version_note": "On line 1 (SessionStart). It is the tool version, not a format version, but it is at least branchable.",
      "terminal_event": "SessionFinish",
      "success_boolean": null,
      "terminal_event_note": "Carries `exitstatus` as an integer, with no summary counts.",
      "offline_discovery": false,
      "stability_promise": "consumer-obligation",
      "stability_note": "Closest thing to a forward-compatibility rule in the survey, phrased as a consumer duty: ignore unrecognized reports and unknown keys.",
      "gap": "File-only \u2014 it can never write to stdout, so it cannot be piped.",
      "source": "https://github.com/pytest-dev/pytest-reportlog"
    },
    {
      "name": "docker",
      "mechanism": "--format json / system events --format json",
      "kind": "meta",
      "verified_by": [
        "probe",
        "docs"
      ],
      "probe_argv": "docker version --format json",
      "ndjson": "varies",
      "discriminator": "Type + Action",
      "format_version": null,
      "terminal_event": null,
      "success_boolean": null,
      "offline_discovery": false,
      "stability_promise": null,
      "gap": "Three different output shapes under one --format json flag: `events`/`ps` are NDJSON, `inspect` is a JSON array, `version` is a single object. The tool does not agree with itself.",
      "source": "https://docs.docker.com/reference/cli/docker/system/events/"
    },
    {
      "name": "kubectl",
      "mechanism": "-o json",
      "kind": "meta",
      "verified_by": [
        "probe",
        "docs"
      ],
      "probe_argv": "kubectl version --client -o json",
      "ndjson": false,
      "discriminator": "kind",
      "format_version": "apiVersion",
      "terminal_event": null,
      "success_boolean": null,
      "offline_discovery": false,
      "offline_discovery_note": "`explain` and `api-resources` hit the cluster discovery API \u2014 credentials and network required.",
      "stability_promise": "explicit",
      "stability_note": "Kubernetes deprecation policy: API elements may only be removed by incrementing the API group version; objects must round-trip between versions without information loss.",
      "gap": "Buffered single document, no terminal event, no success boolean, no offline schema discovery.",
      "source": "https://kubernetes.io/docs/reference/using-api/deprecation-policy/"
    },
    {
      "name": "syft",
      "mechanism": "-o syft-json",
      "kind": "query",
      "verified_by": [
        "docs"
      ],
      "ndjson": false,
      "discriminator": "metadataType",
      "format_version": "schema.version",
      "format_version_note": "SchemaVer, with 111 published versioned schema files \u2014 best-in-class versioning among buffered formats.",
      "terminal_event": null,
      "success_boolean": null,
      "offline_discovery": "partial",
      "offline_discovery_note": "Schemas are public and credential-free, but `schema.url` requires network AND points at `main` rather than a release tag.",
      "stability_promise": "explicit",
      "stability_note": "TRAP: this promise is Syft's alone. Grype's only published schemas cover `grype db search`, never `grype -o json`. Do not conflate them.",
      "gap": "A single buffered document \u2014 structurally cannot stream, so no terminal event and no success boolean.",
      "source": "https://github.com/anchore/syft/blob/main/schema/json/README.md"
    },
    {
      "name": "gh",
      "mechanism": "--json f1,f2 (fields mandatory)",
      "kind": "query",
      "verified_by": [
        "docs"
      ],
      "ndjson": false,
      "discriminator": null,
      "format_version": null,
      "terminal_event": null,
      "success_boolean": null,
      "offline_discovery": true,
      "offline_discovery_note": "Best capability-discovery story in the survey: a bare --json enumerates valid field names, verified working with an invalid token, outside a repo, exit 0. It publishes names but no types.",
      "stability_promise": null,
      "gap": "Buffered array, no envelope, no version, no completion signal.",
      "source": "https://cli.github.com/manual/gh_help_formatting"
    },
    {
      "name": "aws-cli",
      "mechanism": "--output json",
      "kind": "query",
      "verified_by": [
        "docs"
      ],
      "ndjson": false,
      "discriminator": null,
      "format_version": null,
      "terminal_event": null,
      "success_boolean": null,
      "offline_discovery": "partial",
      "offline_discovery_note": "Ships 428 botocore service-2.json models on disk with apiVersion and full output shapes \u2014 credential-free and offline. They describe the API, not the CLI envelope.",
      "stability_promise": "anti-promise",
      "stability_note": "Docs warn the opposite: 'resources might have key-value elements added or removed in future updates'.",
      "gap": "No envelope, no version, no completion signal.",
      "source": "https://docs.aws.amazon.com/cli/latest/userguide/cli-usage-output-format.html"
    },
    {
      "name": "journalctl",
      "mechanism": "-o json / json-seq / json-pretty",
      "kind": "stream",
      "verified_by": [
        "docs"
      ],
      "ndjson": "varies",
      "ndjson_note": "`json` is NDJSON; `json-seq` is RFC 7464 with a 0x1E prefix that naive line readers choke on; `json-pretty` is multi-line and NOT line-delimited. Three framings, one flag family.",
      "discriminator": null,
      "format_version": null,
      "terminal_event": null,
      "success_boolean": null,
      "offline_discovery": "man-page-only",
      "stability_promise": "negative",
      "stability_note": "The only stability statement is a negative one: 'The format of the cursor is private and subject to change.' Forward-compat guidance is a consumer obligation: skip unknown fields.",
      "gap": "`--show-cursor` emits a non-JSON `-- cursor: \u2026` trailer onto stdout.",
      "source": "https://systemd.io/JOURNAL_EXPORT_FORMATS/"
    },
    {
      "name": "stripe-cli",
      "mechanism": "listen --format json",
      "kind": "stream",
      "verified_by": [
        "docs"
      ],
      "ndjson": "tty-dependent",
      "ndjson_note": "Framing depends on whether stdout is a TTY: non-TTY yields compact NDJSON, TTY yields pretty JSON with ANSI. Plus a blank line after every record.",
      "discriminator": "type",
      "format_version": null,
      "format_version_note": "`api_version` is the Stripe API data version, not an envelope version, and v2/thin events lack it.",
      "terminal_event": null,
      "success_boolean": null,
      "offline_discovery": "partial",
      "stability_promise": null,
      "gap": "Stdout is not pure: EndpointResponse lines, POST errors, and logs-tail warnings print regardless of --format.",
      "source": "https://docs.stripe.com/cli/listen"
    },
    {
      "name": "ansible",
      "mechanism": "ANSIBLE_STDOUT_CALLBACK=ansible.posix.json (no flag)",
      "kind": "query",
      "verified_by": [
        "docs"
      ],
      "ndjson": false,
      "discriminator": null,
      "format_version": null,
      "terminal_event": null,
      "success_boolean": null,
      "terminal_event_note": "`stats` gives per-host counters, not a boolean \u2014 the consumer must reduce across hosts.",
      "offline_discovery": false,
      "stability_promise": "anti-promise",
      "stability_note": "Shape changes under the `free` and `host_pinned` strategies.",
      "gap": "Buffered and emitted only at run end, so an aborted run produces nothing at all. `-v` writes to stdout by default and corrupts the document.",
      "source": "https://docs.ansible.com/ansible/latest/collections/ansible/posix/json_callback.html"
    },
    {
      "name": "eslint",
      "mechanism": "-f json",
      "kind": "query",
      "verified_by": [
        "docs"
      ],
      "ndjson": false,
      "discriminator": null,
      "format_version": null,
      "terminal_event": null,
      "success_boolean": null,
      "offline_discovery": "partial",
      "offline_discovery_note": "Ships TypeScript types (LintResult) offline, but no JSON Schema of the report.",
      "stability_promise": null,
      "stability_note": "The semver policy covers formatter EXISTENCE, not payload shape.",
      "gap": "A bare array with no envelope and no totals \u2014 the consumer must sum errorCount. An inherited TIMING env var injects a text table into stdout.",
      "source": "https://eslint.org/docs/latest/use/formatters/"
    },
    {
      "name": "trivy",
      "mechanism": "-f json",
      "kind": "meta",
      "verified_by": [
        "probe",
        "docs"
      ],
      "probe_argv": "trivy version -f json",
      "ndjson": false,
      "discriminator": null,
      "format_version": "SchemaVersion",
      "format_version_note": "An integer (observed 2) \u2014 but it appears in doc examples with no explanation and no documented policy.",
      "terminal_event": null,
      "success_boolean": null,
      "offline_discovery": false,
      "stability_promise": null,
      "gap": "A version number with no policy attached is not a compatibility contract.",
      "source": "https://trivy.dev/latest/docs/configuration/reporting/"
    }
  ],
  "findings": {
    "discriminator_convergence": {
      "claim": "12 of 22 surveyed tools independently invented a type discriminator, in 9 mutually incompatible spellings.",
      "spellings": [
        "type",
        "reason",
        "event",
        "Type+Action",
        "kind",
        "$report_type",
        "$message_type",
        "metadataType",
        "leading sigil char",
        "level"
      ],
      "sharpest_example": "cargo carries TWO differently-spelled discriminators in one stream: `reason` at the envelope level and `$message_type` inside embedded rustc diagnostics."
    },
    "envelope_sigil_convergence": {
      "claim": "Three tools independently invented a sigil to mark envelope keys apart from payload keys. All three chose a different sigil.",
      "spellings": {
        "terraform": "@ (@level, @message, @module, @timestamp)",
        "pytest/rustc": "$ ($report_type, $message_type)",
        "journalctl": "__ and _ (__CURSOR, _BOOT_ID)"
      }
    },
    "version_means_three_things": {
      "claim": "A consumer reading `.version` gets a different KIND of answer from each tool.",
      "examples": {
        "semgrep": "the tool's version",
        "npm ls": "the described project's version",
        "cargo metadata": "the output format's version"
      }
    },
    "success_boolean_scarcity": {
      "claim": "1 of 22 tools emits a terminal event with an explicit success boolean: cargo's build-finished.success.",
      "runners_up": "Everyone else who signals completion downgrades to an integer requiring an out-of-band code table: git trace2 `code`, pytest-reportlog `exitstatus`, pytest-json-report `exitcode`. Ansible gives counters. ESLint makes you sum errorCount."
    },
    "offline_schema_discovery": {
      "claim": "No tool in the survey can be asked, offline and unauthenticated, to describe its own output.",
      "closest": "gh enumerates field NAMES offline but publishes no types; aws ships 428 API models that describe the API rather than the CLI envelope; syft publishes 111 schemas but its own schema.url needs network and points at main."
    },
    "headline": {
      "claim": "No surveyed tool has all four of {NDJSON, type discriminator, in-band format version, terminal event with an explicit success boolean}. Cargo and terraform each have three.",
      "cross_validation": "Established twice by independent methods: an empirical aoi-probe run against installed binaries, and a documentation/source survey. Both arrived at the same result."
    }
  }
}