{
  "schemaVersion": 1,
  "kind": "preregistration",
  "experiment": {
    "id": "EXP 005",
    "slug": "jev-gate",
    "title": "Jev as a fast gate",
    "stationTitle": "Can a fast model gate drift from the diff alone?",
    "stationSummary": "From the diff and the rules alone, can Jev turn away architectural drift reliably enough to be a first gate, and hand what it is unsure of to an LLM reviewer?",
    "status": "pre-registered",
    "statusText": "Pre-registered — not yet run",
    "authoredOn": "2026-09-26",
    "note": "Written, hashed and published before any gate (Jev, Laya or the LLM reviewer) sees a corpus item. Nothing in this record may change after publication; a later change is a new, dated pre-registration, and the results are judged against this one."
  },
  "question": "Given only a unified diff and a plain-language statement of the architecture rules, can a System-1 typed-decision model (Jev, hosted) serve as a fast first gate that tells whether an agent-written change breaks a rule, so that a cascade which escalates its unsure calls to an LLM reviewer misses no more drift than the reviewer alone, at under half of the reviewer's cost?",
  "framing": [
    "The ground truth is the blueprint engine, bce-engine 0.3.1, run on the real tree after each change. bce is deterministic and cheap wherever a blueprint and the tree are wired in.",
    "This experiment asks a different question: whether a typed-decision model can triage a change from the diff alone, before merge, where bce is not wired in, with escalation to a heavier reviewer. It does not test replacing bce, and no result here would support using a typed-decision model instead of it.",
    "Laya, an open-weight typed-decision model, answers the same inputs and is reported against the same criteria. The claim under test is Jev's."
  ],
  "corpus": {
    "description": "A small layered TypeScript service (domain, app, infra, config) with a blueprint of import-boundary and environment-variable rules, and 60 authored changes to it (c001 to c060). Half of the changes were written to break a rule in one of five families (reverse layer import, infrastructure leak, forbidden configuration key, re-export from a governed file, dynamic import); half were written not to, and 16 of those are near-misses (an allowed import next to a forbidden one, a test-only change, a comment that mentions a rule). The ids are shuffled, so their order does not follow the families. bce's label, not the author's intent, is the truth.",
    "items": 60,
    "groundTruthRed": 30,
    "groundTruthGreen": 30,
    "intentDisagreements": 0,
    "excluded": 0,
    "corpusSha256": "a83b222a1a4a64cc81ac755c827a47009baa2bb91b036e351e71422cc8d526a9",
    "corpusSha256Meaning": "sha256 of experiments/jev-gate/corpus.sha256, which lists the sha256 of every file the labels depend on: base/, corpus/, blueprint.json, rules.txt, gate-question.json, corpus-spec.json, manifest.json and labels.json.",
    "inputsSha256": "6bfb2b8d52376cbd22c8a34f5f986fe67ad68a0c587da862ba6b56e77e966a34",
    "inputsSha256Meaning": "sha256 of experiments/jev-gate/inputs.json, the one input every gate receives for each item. It carries no labels."
  },
  "files": {
    "experiments/jev-gate/corpus.sha256": "a83b222a1a4a64cc81ac755c827a47009baa2bb91b036e351e71422cc8d526a9",
    "experiments/jev-gate/labels.json": "36af1422755e56af56f4af267411a4a8e6535d08e6fc9d7aa3734aa005e4412a",
    "experiments/jev-gate/inputs.json": "6bfb2b8d52376cbd22c8a34f5f986fe67ad68a0c587da862ba6b56e77e966a34",
    "experiments/jev-gate/inputs.sha256": "74246d8c4b1f217bf4b1d3926b6413d1a7b6dfe021071f84bd105dc181076f3d",
    "experiments/jev-gate/rules.txt": "87186d652a7cf8a4c8e71966236c4fe6837c4bd22799fc49a15d437a083b2562",
    "experiments/jev-gate/gate-question.json": "2a9a728d15305e19d2a39001134629297176f78749865d2512cd1a51d3e09764",
    "experiments/jev-gate/blueprint.json": "1648713fe91cfc0800ca0e20d5447476ce9cb8b7f52c060b863eeed6e382de07",
    "experiments/jev-gate/corpus-spec.json": "64f7ebc2d5450b6393bfbdcb9dd80b55743302d8d413216bc6e8ee1d4d0bd7e0",
    "experiments/jev-gate/bce-contract.mjs": "ca9ea8860b5fe5502c32b102c3db5fcc19e3474bdde9cc4104d3da89364f2004",
    "experiments/jev-gate/gate-input.mjs": "851de6eac679c76a1c75b8d8b194c5693c8e5469ee987106b45dc24807d87587",
    "experiments/jev-gate/laya_count.py": "485a30602c525ce12fd8b1e5bc4ef9908d25e64b1ca21279a2c4c6a139734a99",
    "experiments/laya-vs-jev/run.py": "3fa346c4450a4251d8c15ce4b04b4f5a9820fb8cf5c1bf4ab65ab2fe3eabc1f9",
    "experiments/laya-vs-jev/laya_mlx.py": "b9483641ea20e1e566947369a2c2df7011ce3260f37b8755e5c67ee4507fa70e",
    "experiments/laya-vs-jev/laya_inputs.py": "b368b2848d8a1a1d019fdb2410c0b8115deb8f97f1e92708687048baef52af85",
    "experiments/jev-gate/baselines.json": "6267bc62d57bfff5516e440f9333aa8260075cdfdf783136d2774c0dc131cf19",
    "experiments/jev-gate/baselines.mjs": "cd0b1a4cfbcdfd5c3e1a769271d83a40c0ce094e98430381bcd4497477bae9e8",
    "scripts/jev-gate-heuristic-grep.mjs": "ffce62d0dcb1f6c7575d273099c8105f4f6d0a80cd977aaff8925fe00067c524",
    "scripts/jev-gate-heuristic-lint.mjs": "b36e50d88330fa739b2fb28afe24a521400413a3a7c41ab22614a3b9886676f2",
    "experiments/jev-gate/metrics.mjs": "ec7c03e002def319b2cc81e2865242601792be116510c122f2420ddda1e24e8d",
    "scripts/jev-gate-journal.mjs": "5bfc7cd43c22203b2d11d6d75363929f246345f0837cc733eb955c5c337ffece",
    "scripts/jev-gate-prereg.mjs": "f6fab96e293aadd7d51e672a0b90e229fb6b32c6323f9d329b9bb29a1acdfa19"
  },
  "rulesText": "Architecture rules for this TypeScript service. They govern every .ts file under src/. An import is any import in the file itself: import, import type, export ... from, and import().\nFiles in src/domain must not import anything from src/app.\nFiles in src/domain must not import anything from src/infra.\nFiles in src/domain must not import anything from src/config.\nFiles in src/domain and src/app must not import the pg package.\nFiles in src/app may use infrastructure only through src/infra/index.ts; they must not import any file in a folder inside src/infra.\nFiles in src/domain, src/app and src/infra must not contain process.env anywhere, comments included.\nIn src/config, process.env.NAME is allowed only for these names: PORT, DATABASE_URL, LOG_LEVEL, SMTP_HOST, SMTP_FROM, INVOICE_PREFIX. Anywhere in a src/config file, comments included, process.env.NAME with any other name and process.env[...] are not allowed; other forms, such as destructuring or process.env?.NAME, are not covered.\n",
  "gateQuestion": {
    "type": "noul",
    "instructions": "Does this change break any of these rules?"
  },
  "stateConstruction": "state = rules.txt + \"\\n\\n\" + diff",
  "groundTruth": {
    "engine": "bce-engine",
    "version": "0.3.1",
    "extractor": "ast",
    "command": "node node_modules/bce-engine/dist/cli.js run --blueprint <bp> --ct-repo <dir> --extractor ast --out <json>",
    "labelRule": "Each change is applied to a fresh copy of the base tree, committed as the only commit of a fresh git repository and scored. Exit 0 with verdict pass, score 100 and no violations is GREEN (the change breaks no rule). Exit 1 with verdict fail, a score below 100 and at least one violation is RED (the change breaks a rule). Any other combination is an error, never a label."
  },
  "gates": {
    "jev": {
      "role": "fast gate under test",
      "provider": "jev-ai.pro, an independently operated reseller of TypeSafe's hosted System-One API (not TypeSafe itself)",
      "endpoint": "https://jev-ai.pro/api/v1/systemone",
      "model": "jev-1.13.0",
      "acceptedServedModelPrefixes": [
        "jev-1.13",
        "typesafe/jev-1.13"
      ],
      "request": "POST JSON {\"model\": \"jev-1.13.0\", \"state\": <state>, \"questions\": {\"q\": <gateQuestion>}} with a bearer key from the environment and the User-Agent of the EXP 004 runner (experiments/laya-vs-jev/run.py, call_jev).",
      "stateConstruction": "state = rules.txt + \"\\n\\n\" + the unified diff, exactly the state field of inputs.json for the item, sent unchanged.",
      "answer": "The noul answer's probability p that the statement holds, that is, that the change breaks at least one rule. A served model outside the accepted prefixes, an HTTP error, a timeout or an answer that is not of type noul is a failure.",
      "timeoutSeconds": 15,
      "order": "One call per item, sequential, in id order, never retried."
    },
    "laya": {
      "role": "open-weight comparison, reported against the same criteria",
      "repository": "https://huggingface.co/convaiinnovations/laya",
      "revision": "55cf4c4ebb4ebe31b2550e8bdf3bd21b99753851",
      "subdir": "typed-decisions",
      "fileSha256": {
        "typed-decisions/model.safetensors": "4fa56de72383a9d3efa9cfa78955733c81b9fc8067a587ca4beb82c78107a24e",
        "typed-decisions/rl_agent_config.json": "ebf0cd524d92342a6be5e48e9fca3d7c2babfb5a56ccd79d2171ef5d8c7f7be8",
        "typed-decisions/encoder/config.json": "5268d24ad3b77c8151de5dcb0762ba4391619aad9ab0bda33e36fb083cfeae6d",
        "typed-decisions/tokenizer/tokenizer.json": "6c8aaa9a542084f2457eab775d4eeb51f92a70c0fd9de28d5edb0ddec3c08d30",
        "typed-decisions/tokenizer/tokenizer_config.json": "08d4cf3ac4dca381759441b85b91a6d40e688471dcd33d15d6649eb0a9a854d1"
      },
      "revisionSource": "The same revision and file hashes as the EXP 004 recording (experiments/laya-vs-jev/run.py, HF_SHA and PINNED).",
      "backend": "The EXP 004 MLX port (experiments/laya-vs-jev/laya_mlx.py) on the Apple GPU, one call at a time.",
      "sequence": "laya_inputs.build_sequence(tokenizer, state, gateQuestion, max_len 1024, head_max_len 256) with the same state as Jev. No input may be cut: laya_count.py measures every part before the cut and refuses any input that does not fit, and the built inputs record 0 truncated items.",
      "answer": "The noul answer's probability p that the change breaks at least one rule. A refused or failed input is a failure.",
      "order": "One call per item, sequential, in id order, never retried."
    },
    "reviewer": {
      "role": "LLM reviewer baseline",
      "tool": "nina",
      "repository": "https://github.com/xhulz/nina",
      "release": "0.28.10",
      "commit": "efcc5229fc2c5d5f39b740280efd4307b8a8537b",
      "agent": "reviewer",
      "client": "Claude Code",
      "clientVersion": "2.1.280",
      "model": "claude-opus-5-5",
      "effort": "high",
      "k": 3,
      "allowedTools": [
        "Read",
        "Grep",
        "Glob",
        "Bash(git diff:*)",
        "Bash(git status:*)",
        "Bash(git show:*)",
        "Bash(git log:*)"
      ],
      "command": "claude -p <prompt> --agent reviewer --model claude-opus-5-5 --effort high --output-format json --no-session-persistence --setting-sources project --permission-mode dontAsk --allowedTools Read Grep Glob \"Bash(git diff:*)\" \"Bash(git status:*)\" \"Bash(git show:*)\" \"Bash(git log:*)\"",
      "workspace": "A fresh scratch git repository per run, outside any odin-rnd checkout, composed with the nina CLI at the pinned release (nina init, nina compose). One base commit with the neutral message \"base\" holds only the base app (without node_modules), rules.txt at the repository root, the .claude/ directory and CLAUDE.md that nina compose writes, and a .gitignore that excludes run artefacts. The change is applied with git apply and left uncommitted, so git status and git diff show exactly the change. No odin-rnd checkout, labels.json, manifest.json, baselines.json, corpus file, blueprint or bce is reachable: the working directory is that scratch repository and the tools are limited to it. No web access; no tool outside the list above.",
      "prompt": "Review the change in this repository. The change is everything that git diff shows; it has not been committed. The architecture rules this codebase must follow are in rules.txt at the repository root. Decide one thing only: does the change break any of those rules? Ignore style, naming, tests and anything else that rules.txt does not state. Do not edit any file. Explain your reasoning briefly, then write your decision on a line of its own: VERDICT: REJECTED if the change breaks at least one rule, or VERDICT: APPROVED if it breaks none.",
      "promptAuthorship": "Written for this experiment by Odin Labs. It contains no text from nina's reviewer specification.",
      "timeoutSeconds": 600,
      "order": "k runs per item, sequential, in id order, each in its own fresh workspace, never retried.",
      "hangStop": {
        "fraction": 0.2,
        "minRuns": 10,
        "rule": "Once at least minRuns reviewer runs have completed, if more than fraction of all runs so far ended in a timeout, the run stops and the design is revisited before any further call; the stop and its counts are published. Before minRuns runs, a timeout alone never stops the run. Isolation probe runs count toward neither minRuns nor the timeout fraction."
      },
      "isolationProof": "Isolation is proven before any counted reviewer run. A canary file shaped like labels.json is placed outside the scratch repository, in its parent directory, and a probe run with the pinned command, tools, model and effort is asked to read it. The probe must fail to read it; its transcript is recorded. If the canary is readable, no reviewer run is counted and the design is revisited. Probe runs are paid spend under the cap and use no corpus item."
    }
  },
  "baselines": [
    {
      "id": "heuristic-grep",
      "script": "scripts/jev-gate-heuristic-grep.mjs",
      "scriptSha256": "ffce62d0dcb1f6c7575d273099c8105f4f6d0a80cd977aaff8925fe00067c524",
      "lib": "experiments/jev-gate/baselines.mjs",
      "libSha256": "cd0b1a4cfbcdfd5c3e1a769271d83a40c0ce094e98430381bcd4497477bae9e8",
      "description": "WO-07 heur.py H1c: an added non-comment line under src/ contains process.env, 'pg', ../infra, ../app, ../config or import(",
      "scored": 60,
      "correct": 42,
      "accuracy": 0.7,
      "missedRed": 5,
      "falseReject": 13
    },
    {
      "id": "heuristic-lint",
      "script": "scripts/jev-gate-heuristic-lint.mjs",
      "scriptSha256": "b36e50d88330fa739b2fb28afe24a521400413a3a7c41ab22614a3b9886676f2",
      "lib": "experiments/jev-gate/baselines.mjs",
      "libSha256": "cd0b1a4cfbcdfd5c3e1a769271d83a40c0ce094e98430381bcd4497477bae9e8",
      "description": "line-regex linter of rules.txt lines 2-8 over added lines: relative specifiers resolved against the file path, comments skipped for imports only",
      "scored": 60,
      "correct": 59,
      "accuracy": 0.9833,
      "missedRed": 0,
      "falseReject": 1
    }
  ],
  "baselineRule": "Each baseline is a deterministic, committed script that decides every corpus item from inputs.json alone, with no model and no network. Its result on the pinned corpus is computed and recorded here before any gate call. The results report each baseline as a row that Jev has to beat, on the same items and metrics; a typed gate that does not do better than a script adds nothing the script could not.",
  "baselinesSource": "experiments/jev-gate/baselines.json keys each baseline by its id and records its script, the sha256 of that script and of the shared library experiments/jev-gate/baselines.mjs, its per-item predictions and its scores, together with the sha256 of the inputs.json and labels.json it was scored on. The validator re-hashes all of them from disk, recomputes missed RED, false rejects and correct items from the predictions, and refuses any difference. The per-item predictions stay in baselines.json, pinned by its hash, for the paired comparison.",
  "bestBaseline": {
    "rule": "The better baseline is the one with the fewest missed RED items on the pinned corpus (primary population), computed from baselines.json before any gate call; a tie goes to fewer false rejects, then to the id that sorts first. Its id is recorded here; it is null only while no baseline is recorded.",
    "id": "heuristic-lint",
    "missedRed": 0,
    "plainly": "Jev must miss 0 of 30 RED items, abstentions included, because the better baseline (heuristic-lint) misses 0.",
    "scope": "This tests whether Jev applies mechanical rules as well as a linter, not rules a linter cannot express. bce's ground truth is regex-shaped by construction, so a refutation on this criterion alone does not generalise to rules that are not mechanical. (Founder, 2026-09-26: \"Keep it, state it plainly\"; corpus review round 2, R2-2.)"
  },
  "decisionRules": {
    "typedDecision": "For Jev and Laya, the decision is REJECT (the gate predicts RED) when p is at least decisionThreshold, and ACCEPT (the gate predicts GREEN) otherwise.",
    "decisionThreshold": 0.5,
    "confidence": "For Jev and Laya, confidence = max(p, 1 - p). A confidence the service reports alongside p is recorded but not used.",
    "verdictPattern": "^VERDICT: (APPROVED|REJECTED)",
    "verdictParse": "A reviewer run's decision is read from the result text: the first line anywhere in it that matches verdictPattern (multiline, case-sensitive). APPROVED is ACCEPT and REJECTED is REJECT. A run with no matching line is an abstention.",
    "reviewerMajority": "The reviewer's decision for an item is the decision that more than half of its k runs gave (2 of 3). Anything else, including a tie and too many abstaining runs, is an abstention.",
    "abstention": "An abstention is reported separately for each gate and is never recoded into a decision. In the headline figures and the criteria it counts against the gate.",
    "failures": "A failed call (network or HTTP error, timeout, unexpected served model, malformed answer, refused input, a client error) is an abstention. Failures are kept in the record with their reason."
  },
  "cascade": {
    "fastGate": "jev",
    "escalateTo": "reviewer",
    "confidenceThreshold": 0.9,
    "rule": "For each item: when Jev decided with confidence at least confidenceThreshold, Jev's decision is final, whether ACCEPT or REJECT. Otherwise (lower confidence, or a Jev abstention) the item is escalated and the cascade takes the reviewer's decision; a reviewer abstention on an escalated item is a cascade abstention.",
    "reporting": "The escalation rate, the cascade's missed drift and false reject, and its cost per 1,000 decisions (Jev on every item plus the reviewer's k runs on escalated items only)."
  },
  "populations": {
    "primary": "Every corpus item whose bce label agrees with its author's intent and that bce did not exclude. The criteria are judged on this population.",
    "sensitivity": "Every corpus item that bce labelled, including items whose label disagrees with the author's intent. Reported beside the primary figures; not used for the criteria."
  },
  "commentOnlyRed": {
    "ids": [
      "c009",
      "c021",
      "c026",
      "c041"
    ],
    "note": "RED only because a comment names process.env in a governed layer, or an unlisted variable in config (rules.txt lines 7 and 8 cover comments). Fair under the rules, but not architectural drift in the usual sense. For each env-not-in-* rule, one of its two target items is a comment, so per-rule results for those rules are half comment artefacts. The grep baseline's misses are these items plus c024, whose double-quoted \"pg\" import was written on purpose to dodge the grep; a grep that kept comments would miss none of the comment-only items. (Corpus review round 2, R2-1 and R2-4.)"
  },
  "metrics": [
    {
      "id": "agreement",
      "definition": "Items whose decision equals bce's label, over all items in the population. An abstention counts as not agreeing."
    },
    {
      "id": "missedDrift",
      "definition": "Headline, worst case: RED items the gate ACCEPTED or abstained on, over all RED items in the population (false accept; the primary metric for a gate). An abstention on a RED item counts as missed drift."
    },
    {
      "id": "falseReject",
      "definition": "Headline, worst case: GREEN items the gate REJECTED or abstained on, over all GREEN items in the population. An abstention on a GREEN item counts as a false reject."
    },
    {
      "id": "abstentionRate",
      "definition": "Items with no decision, over all items in the population, reported separately for RED and GREEN items. Abstentions already count against the gate in the headline figures."
    },
    {
      "id": "decidedOnly",
      "definition": "Missed drift and false reject with abstentions excluded: RED items ACCEPTED over RED items the gate decided, and GREEN items REJECTED over GREEN items the gate decided. Reported beside the headline figures, not used for the criteria."
    },
    {
      "id": "missedDriftSplit",
      "definition": "Headline missed drift split into the code-level RED items and the comment-only RED items listed in commentOnlyRed. Reported beside the headline for every gate and baseline; not a criterion. The primary metric is unchanged."
    },
    {
      "id": "accuracyAtHighConfidence",
      "definition": "For Jev and Laya: among decided items with confidence at least confidenceThreshold (the cascade's threshold), the fraction whose decision equals bce's label, with the count of such items."
    },
    {
      "id": "cascade",
      "definition": "The cascade's missed drift, false reject, abstentions and escalation rate, as defined in cascade.rule."
    },
    {
      "id": "reviewerVariance",
      "definition": "Items whose k reviewer runs did not all give the same decision (an abstaining run counts as a different outcome), over all items; and each run index's missed drift and false reject on its own."
    },
    {
      "id": "latency",
      "definition": "Wall time of each call measured by our own wrapper, p50 and p90 per gate. Jev's includes the public internet."
    },
    {
      "id": "costPer1000",
      "definition": "Cost per 1,000 decisions per gate and for the cascade, each labelled with its basis (see cost)."
    },
    {
      "id": "meanCostPerChange",
      "definition": "Mean cost per change over the primary population, on the pinned price bases. Cascade: Jev's listed-price cost on every item plus the reviewer's API-equivalent cost of all k runs on the items it escalates. Reviewer alone: the reviewer's API-equivalent cost of all k runs on every item. A missing cost counts against the cascade: a Jev call or an escalated reviewer run without a recorded cost is costed at the largest per-call cost recorded for that gate in the run, and a reviewer-alone run without a recorded cost is costed at 0. If a gate the cascade uses records no cost on any call in the run, nothing can be imputed and cascade-cost is refuted. This figure is mixed-basis (see cost.comparison)."
    }
  ],
  "statistics": {
    "confidenceLevel": 0.95,
    "z": 1.959963984540054,
    "singleRate": {
      "method": "Wilson score interval",
      "formula": "(p + z^2/(2n) ± z * sqrt(p(1 - p)/n + z^2/(4n^2))) / (1 + z^2/n), with p the observed proportion of n items",
      "reference": "wilson-1927"
    },
    "pairedDifference": {
      "method": "Newcombe hybrid score interval for the difference of paired proportions (method 10, without continuity correction)",
      "formula": "On the same n RED items, for the first (the cascade, or Jev) against what it is compared with (the reviewer, or the better baseline), with an abstention counted as a miss: a = both miss, b = only the first misses, c = only the other misses, d = neither. p1 = (a + b)/n (the first), p2 = (a + c)/n (the other), theta = p1 - p2. (l1, u1) and (l2, u2) are the Wilson intervals of p1 and p2. phi = (ad - bc)/sqrt((a + b)(c + d)(a + c)(b + d)), and phi = 0 when that denominator is 0. delta = sqrt((p1 - l1)^2 - 2 phi (p1 - l1)(u2 - p2) + (u2 - p2)^2); epsilon = sqrt((u1 - p1)^2 - 2 phi (u1 - p1)(p2 - l2) + (p2 - l2)^2). The interval is (theta - delta, theta + epsilon).",
      "reference": "newcombe-1998",
      "implementation": "experiments/jev-gate/metrics.mjs (newcombePaired), the only implementation; the verdict code calls it."
    }
  },
  "criteria": [
    {
      "id": "jev-missed-drift",
      "kind": "single-rate",
      "metric": "missedDrift",
      "gate": "jev",
      "refutedWhen": "greater-than",
      "threshold": 0.1,
      "statement": "Jev misses more than 10% of drift (RED items it accepts or abstains on)."
    },
    {
      "id": "jev-false-reject",
      "kind": "single-rate",
      "metric": "falseReject",
      "gate": "jev",
      "refutedWhen": "greater-than",
      "threshold": 0.25,
      "statement": "Jev falsely rejects more than 25% of clean changes (GREEN items it rejects or abstains on)."
    },
    {
      "id": "cascade-vs-reviewer",
      "kind": "paired-difference",
      "metric": "missedDrift",
      "gate": "cascade",
      "comparedWith": "reviewer",
      "refutedWhen": "greater-than",
      "threshold": 0,
      "statement": "The cascade misses more drift than the reviewer alone, on the same RED items (an abstention counts as a miss for both)."
    },
    {
      "id": "jev-vs-best-baseline",
      "kind": "paired-difference",
      "metric": "missedDrift",
      "gate": "jev",
      "comparedWith": "bestBaseline",
      "refutedWhen": "greater-than",
      "threshold": 0,
      "statement": "Jev misses more drift than the better model-free baseline, on the same RED items (an abstention by Jev counts as a miss)."
    },
    {
      "id": "cascade-cost",
      "kind": "point-ratio",
      "metric": "meanCostPerChange",
      "gate": "cascade",
      "comparedWith": "reviewer",
      "refutedWhen": "not-below-fraction",
      "threshold": 0.5,
      "statement": "The cascade's mean cost per change is not below 50% of the reviewer-alone mean cost per change, on the same items and the pinned price bases (Jev at its listed price, the reviewer at the API-equivalent total_cost_usd it records)."
    }
  ],
  "claimRule": "The claim is refuted if any of the five criteria is refuted. The drift criteria use the headline (worst-case) figures; the cost criterion uses the pinned price bases. Laya is judged against the same two single-rate criteria and reported; its result does not decide the claim.",
  "thresholdRule": {
    "states": {
      "refuted": "refuted",
      "notEstablished": "passes, not established at this N",
      "passes": "passes"
    },
    "singleRate": "Compare the point estimate with the threshold. Refuted: the point estimate is greater than the threshold. Passes, not established at this N: the point estimate is not greater, but the upper bound of its Wilson interval is. Passes: the upper bound is not greater than the threshold either.",
    "pairedDifference": "Compare the point estimates of the headline missed drift on the same RED items. Refuted: the first (the cascade, or Jev) misses more than what it is compared with (theta greater than 0). Passes, not established at this N: it does not miss more, but the Newcombe interval of theta includes 0, as it does for a tie. Passes: it does not miss more and the interval lies entirely below 0.",
    "publication": "All three states are published as they come out, with the counts and intervals behind them.",
    "pointRatio": "The cost criterion is a deterministic calculation on recorded costs, not a sample estimate, so it has no interval and no \"not established\" state. Refuted: the cascade's mean cost per change is not below the threshold fraction of the reviewer-alone mean. Passes: it is below."
  },
  "baselineValidity": {
    "metric": "missedDrift",
    "gate": "reviewer",
    "threshold": 0.2,
    "statement": "If the reviewer itself misses more than 20% of drift (headline point estimate), the cascade comparison is reported as uninformative: it is neither refuted nor passed.",
    "consequence": "cascade-vs-reviewer is reported as uninformative"
  },
  "latency": {
    "role": "A sanity check only. Latency is reported and is not a criterion. Our wrapper measures wall time; it is one busy machine, and Jev's latency includes the network."
  },
  "cost": {
    "jev": {
      "usdPerMillionInputTokens": 0.242,
      "basis": "Listed price: the highest listed annual-plan rate on jev-ai.pro/pricing (read 2026-09-24, as in EXP 004; output tokens listed as free), multiplied by the input_tokens the response reports. Not an invoice. A response without usage has no cost figure, and the gap is reported."
    },
    "reviewer": {
      "basis": "API-equivalent: the total_cost_usd that the Claude Code client reports for each run, at list price. Not an invoice; the runs may be billed differently."
    },
    "laya": {
      "basis": "Runs locally with no per-call price. Hardware and electricity are not measured."
    },
    "comparison": "Costs on different bases are shown side by side, each with its label; they are never presented as the same kind of figure, with one named exception: meanCostPerChange, which criterion cascade-cost compares, adds Jev's listed-price cost to the reviewer's API-equivalent cost and is labelled mixed-basis wherever it appears."
  },
  "spendCap": {
    "usd": 100,
    "alreadySpentUsd": 0.3,
    "covers": "Paid gate spend only: Jev at its listed price and the reviewer at its API-equivalent cost, including the one reviewer check already run while planning (alreadySpentUsd). Agent time is outside the cap.",
    "rule": "A running ledger adds the cost of every paid call to alreadySpentUsd. Before any paid call, if the ledger plus the largest single-call cost seen so far for that gate (for the reviewer, at least alreadySpentUsd) would exceed the cap, the run stops. A stopped run is published as partial, with the ledger. A partial run, whether stopped by this cap or by the reviewer hang-stop, gives no criterion a state and decides nothing about the claim; its counts so far are published as partial."
  },
  "retryProtocol": {
    "readinessGateMaxAttempts": 10,
    "rule": "The publication readiness gate is retried at most readinessGateMaxAttempts times per publish, each attempt logged with its time and outcome. After the last failed attempt, the publish stops and the failure is reported. Timeouts and gate settings are never changed to make a retry pass.",
    "gateCalls": "Gate calls (Jev, Laya, reviewer runs) are never retried. A failed call is an abstention (decisionRules.failures)."
  },
  "clock": {
    "rule": "Every gate call records its local start and end time in UTC. Every Jev call also records the HTTP Date header of the server's response next to the local clock.",
    "notBefore": "The time this pre-registration was merged on odin-rnd main, recorded after publication. Every gate call's local start time, and every Jev Date header, must be later. A call that is not is excluded and reported."
  },
  "attribution": {
    "nina": "nina (github.com/xhulz/nina) — used with the permission of its author, as confirmed by Odin Labs",
    "ninaUrl": "https://github.com/xhulz/nina"
  },
  "limits": [
    "60 items (30 RED, 30 GREEN) give wide 95% intervals. A criterion can pass on its point estimate and still not be established at this N; the record says which.",
    "Headline figures count every abstention against the gate: a miss on a RED item, a false reject on a GREEN one. A gate that fails or declines often looks worse for it; the figures with abstentions excluded are published beside them.",
    "Four of the 30 RED items are RED only because of a comment (commentOnlyRed); that is more than the missed-drift threshold on its own. Missed drift is reported split into code-level and comment-only items beside the headline.",
    "This tests whether Jev applies mechanical rules as well as a linter, not rules a linter cannot express. bce's ground truth is regex-shaped by construction, so a refutation on this criterion alone does not generalise to rules that are not mechanical.",
    "rules.txt line 6 has two clauses and bce enforces only the second: an import from src/app of a file directly in src/infra other than index.ts is GREEN in bce. No corpus item sits on that boundary.",
    "The reviewer and the typed gates do not see the same thing. The reviewer works in an isolated scratch repository with read and git tools; Jev and Laya see only the diff and the rules as text. The comparison is between the two setups as they would be used, not between the models alone.",
    "Jev is reached through jev-ai.pro, an independently operated reseller endpoint, not directly from TypeSafe. The served model is checked on every answer.",
    "Costs are at listed or API-equivalent prices, not invoices, and the two bases differ.",
    "One author wrote the corpus and the rules. An independent review checked the corpus before these hashes were fixed.",
    "bce reads direct import edges. A breach that only happens through a file outside the governed scope is invisible to it, so that family is not in the corpus.",
    "One machine and one run. Latency describes that machine under its load at the time.",
    "Confidence here is max(p, 1 - p) for both typed gates. EXP 004 used the confidence Jev reported and, for Laya's yes/no answers, one minus the normalised entropy, so the two experiments' confidence figures are not comparable.",
    "Costs are computed from listed and API-equivalent prices on one run; the cost criterion is a point comparison with no interval, and it says nothing about invoiced prices or other volumes."
  ],
  "sources": [
    {
      "id": "bce",
      "label": "bce-engine 0.3.1 on npm",
      "url": "https://www.npmjs.com/package/bce-engine/v/0.3.1",
      "claim": "The released blueprint engine used as ground truth."
    },
    {
      "id": "jev-ai-pricing",
      "label": "Jev AI pricing page (independently operated reseller, not TypeSafe)",
      "url": "https://jev-ai.pro/pricing",
      "claim": "Annual plans list $0.124–$0.242 per million input tokens, output free (read 2026-09-24). The highest rate is the cost basis here."
    },
    {
      "id": "laya",
      "label": "Convai Innovations, Laya on Hugging Face, revision 55cf4c4",
      "url": "https://huggingface.co/convaiinnovations/laya/tree/55cf4c4ebb4ebe31b2550e8bdf3bd21b99753851/typed-decisions",
      "claim": "Apache-2.0 open-weight typed-decision model; the pinned revision EXP 004 used."
    },
    {
      "id": "nina",
      "label": "nina (github.com/xhulz/nina), release 0.28.10",
      "url": "https://github.com/xhulz/nina",
      "claim": "The composed reviewer agent used as the LLM baseline, used with the permission of its author, as confirmed by Odin Labs."
    },
    {
      "id": "exp-004",
      "label": "EXP 004, System-1 decisions without lock-in: Jev vs open-weight Laya",
      "url": "https://odin-labs-ai.github.io/odin-rnd/journal/system-1-decisions-without-lock-in.html",
      "claim": "The earlier Odin R&D measurement whose Jev call, Laya port and pins this experiment reuses."
    },
    {
      "id": "wilson-1927",
      "label": "E. B. Wilson, “Probable inference, the law of succession, and statistical inference”, Journal of the American Statistical Association 22 (1927)",
      "url": "https://doi.org/10.1080/01621459.1927.10502953",
      "claim": "The score interval used for single rates."
    },
    {
      "id": "newcombe-1998",
      "label": "R. G. Newcombe, “Improved confidence intervals for the difference between binomial proportions based on paired data”, Statistics in Medicine 17 (1998)",
      "url": "https://doi.org/10.1002/(SICI)1097-0258(19981030)17:20%3C2635::AID-SIM954%3E3.0.CO;2-C",
      "claim": "Method 10, the interval used for the paired cascade-versus-reviewer difference."
    }
  ]
}
