{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://neurarch.com/schema/precheck-attestation-v1.json",
  "title": "Neurarch pre-check record (in-toto Statement v1, test-result v0.1 predicate)",
  "description": "What `neurarch ... --attest <out.json>` writes. An in-toto Statement v1 whose predicate is in-toto's test-result v0.1 (result, configuration, url, passedTests, warnedTests, failedTests). Everything Neurarch-specific sits in the `neurarchPrecheck` extension field, which in-toto's parsing rules require consumers to ignore when they do not know it. The first subject is the model as OpenSSF model-transparency names it (model name, sha256 over the file digests in manifest order); the rest are the model's files, one per subject.",
  "type": "object",
  "required": ["_type", "subject", "predicateType", "predicate"],
  "properties": {
    "_type": { "const": "https://in-toto.io/Statement/v1" },
    "subject": {
      "type": "array",
      "minItems": 2,
      "items": { "$ref": "#/definitions/subject" }
    },
    "predicateType": { "const": "https://in-toto.io/attestation/test-result/v0.1" },
    "predicate": { "$ref": "#/definitions/predicate" }
  },
  "definitions": {
    "digestSet": {
      "description": "in-toto DigestSet: algorithm name to lowercase hex. Neurarch always writes sha256.",
      "type": "object",
      "minProperties": 1,
      "required": ["sha256"],
      "properties": { "sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" } },
      "additionalProperties": { "type": "string", "pattern": "^[0-9a-f]+$" }
    },
    "subject": {
      "description": "in-toto ResourceDescriptor as a Statement subject: a name and a digest.",
      "type": "object",
      "required": ["name", "digest"],
      "properties": {
        "name": { "type": "string", "minLength": 1 },
        "digest": { "$ref": "#/definitions/digestSet" }
      }
    },
    "resourceDescriptor": {
      "description": "in-toto ResourceDescriptor: at least one of uri, digest or content.",
      "type": "object",
      "anyOf": [{ "required": ["uri"] }, { "required": ["digest"] }, { "required": ["content"] }],
      "properties": {
        "name": { "type": "string" },
        "uri": { "type": "string", "minLength": 1 },
        "digest": { "$ref": "#/definitions/digestSet" },
        "content": { "type": "string" },
        "downloadLocation": { "type": "string" },
        "mediaType": { "type": "string" },
        "annotations": { "type": "object" }
      }
    },
    "testName": { "type": "string", "pattern": "^[a-z][a-z0-9-]*(/[A-Za-z0-9_.:-]+)?$" },
    "predicate": {
      "type": "object",
      "required": ["result", "configuration", "neurarchPrecheck"],
      "properties": {
        "result": { "enum": ["PASSED", "WARNED", "FAILED"] },
        "configuration": {
          "description": "The CLI (as a purl) and every file whose code or tables decided a verdict, by digest; the repo's .neurarch.yml and --source file when the run read them.",
          "type": "array",
          "minItems": 1,
          "items": { "$ref": "#/definitions/resourceDescriptor" }
        },
        "url": { "type": "string", "minLength": 1, "description": "Only when --attest-url was passed (for example the CI run)." },
        "passedTests": { "type": "array", "items": { "$ref": "#/definitions/testName" }, "uniqueItems": true },
        "warnedTests": { "type": "array", "items": { "$ref": "#/definitions/testName" }, "uniqueItems": true },
        "failedTests": { "type": "array", "items": { "$ref": "#/definitions/testName" }, "uniqueItems": true },
        "neurarchPrecheck": { "$ref": "#/definitions/extension" }
      }
    },
    "verdict": { "enum": ["pass", "warn", "block"] },
    "extension": {
      "type": "object",
      "additionalProperties": false,
      "required": ["schema", "command", "argv", "tool", "exitCode", "verdicts", "findings", "subjectSource"],
      "properties": {
        "schema": { "const": "https://neurarch.com/schema/precheck-attestation-v1.json" },
        "command": { "enum": ["check", "layout", "serve-check", "policy-apply"] },
        "model": { "type": "string", "description": "The model reference as given: a Hugging Face id or a graph path." },
        "argv": { "type": "array", "items": { "type": "string" }, "description": "The command line after the record flags were removed, including any defaults the policy file filled in." },
        "tool": {
          "type": "object",
          "required": ["name", "version"],
          "additionalProperties": false,
          "properties": { "name": { "const": "neurarch" }, "version": { "type": "string" } }
        },
        "exitCode": { "type": "integer", "minimum": 0, "description": "What the CLI exited with, after the policy when one ran. Non-zero always pairs with result FAILED." },
        "verdicts": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "params": { "type": "object", "properties": { "derived": { "type": "number" } } },
            "training": {
              "type": "object",
              "properties": {
                "peakGb": { "type": "number" },
                "upperGb": { "type": "number" },
                "smallestCard": { "type": ["string", "null"] },
                "smallestCardAtEstimate": { "type": ["string", "null"] },
                "seqLen": { "type": "integer" },
                "attention": { "type": "string" },
                "estCostUsd": { "type": "number" }
              }
            },
            "inference": {
              "type": "object",
              "properties": {
                "peakGb": { "type": "number" },
                "smallestCard": { "type": ["string", "null"] },
                "batch": { "type": "integer" },
                "promptTokens": { "type": "integer" },
                "genTokens": { "type": "integer" },
                "precision": { "type": "string" }
              }
            },
            "serve": {
              "type": "object",
              "required": ["verdict"],
              "properties": {
                "verdict": { "$ref": "#/definitions/verdict" },
                "engine": { "type": "string" },
                "engineVersion": { "type": ["string", "null"] },
                "engineVersionAssumed": { "type": "boolean" }
              }
            },
            "layout": {
              "type": "object",
              "required": ["verdict"],
              "properties": { "verdict": { "$ref": "#/definitions/verdict" } }
            },
            "dataFit": { "type": "object", "required": ["verdict"], "properties": { "verdict": { "$ref": "#/definitions/verdict" } } },
            "change": { "type": "object", "required": ["gate"], "properties": { "gate": { "enum": ["approved", "refused"] } } }
          }
        },
        "findings": {
          "type": "array",
          "items": {
            "type": "object",
            "required": ["rule", "checker", "severity", "title"],
            "additionalProperties": false,
            "properties": {
              "rule": { "$ref": "#/definitions/testName" },
              "checker": { "type": "string" },
              "severity": { "enum": ["block", "warn", "info"] },
              "title": { "type": "string" },
              "effective": { "enum": ["block", "warn", "info", "off"] },
              "gates": { "type": "boolean" },
              "waived": { "const": true },
              "baseline": { "enum": ["new", "existing"] }
            }
          }
        },
        "subjectSource": {
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "modelName", "serialization", "modelSigningPredicateType", "rootDigest", "files"],
          "properties": {
            "kind": { "enum": ["directory", "hub", "file"] },
            "modelName": { "type": "string", "minLength": 1 },
            "repo": { "type": "string" },
            "revision": { "type": "string", "pattern": "^[0-9a-f]{40}$" },
            "serialization": {
              "description": "What model-transparency records as `serialization` for the same manifest.",
              "type": "object",
              "required": ["method", "hash_type", "allow_symlinks", "ignore_paths"],
              "properties": {
                "method": { "const": "files" },
                "hash_type": { "const": "sha256" },
                "allow_symlinks": { "type": "boolean" },
                "ignore_paths": { "type": "array", "items": { "type": "string" } }
              }
            },
            "modelSigningPredicateType": { "const": "https://model_signing/signature/v1.0" },
            "rootDigest": { "$ref": "#/definitions/digestSet" },
            "files": { "type": "integer", "minimum": 1 }
          }
        },
        "time": { "type": "string", "description": "Only when --attest-time was passed. Never read from the clock." }
      }
    }
  }
}
