{
  "$schema": "../../schema/run.schema.json",
  "run_id": "valibot--claude-opus-5--v2-a--2026-09-02",
  "supersedes": null,
  "replicate_of": null,
  "library": {
    "name": "valibot",
    "ecosystem": "npm",
    "latest_version_at_test": "1.4.2",
    "latest_version_verified_on": "2026-09-02"
  },
  "model": {
    "id": "claude-opus-5",
    "label": "Claude Opus 5",
    "vendor": "Anthropic",
    "invoked_as": "Agent tool, model alias \"opus\", general-purpose subagent, instructed to use no tools; CHARGING test arm of battery valibot/v2, sent prompts/sent/valibot-v2.txt byte-identical",
    "self_reported_cutoff": "2026-05",
    "cutoff_basis": "Self-reported, accepting the environment value with a density caveat scoped to this library: \"My stated cutoff is May 2026. That said, I want to be straight with you about what that means in practice: recall of specific library release contents degrades well before the nominal cutoff ... For Valibot my detailed knowledge is solid through 1.0.0 and gets thin immediately after.\" The date is affirmed; what is qualified is recall density, which is the quantity this battery measures.",
    "believed_latest_version": "1.1.x",
    "believed_latest_quote": "\"The latest version I have any awareness of is v1.1.x. But I can only partially describe it ... The most recent release whose contents I can actually describe with confidence is v1.0.0, which shipped in early 2025.\"",
    "knowledge_stops_at_version": "1.0.0",
    "knowledge_stops_on": "2025-03-19",
    "knowledge_gap_starts_at_version": "1.1.0",
    "knowledge_gap_starts_on": "2025-05-06",
    "cutoff_lag_months": 14
  },
  "test": {
    "date": "2026-09-02",
    "battery": "valibot/v2-a",
    "battery_spec": "prompts/valibot.md",
    "prompt_file": "prompts/sent/valibot-v2.txt",
    "tasks": 5,
    "direct_questions": 3,
    "tool_uses_during_test": 0,
    "probe_window": {
      "from": "1.1.0",
      "to": "1.2.0"
    },
    "self_test": true,
    "saturated": false,
    "status": "open",
    "retested_on": null
  },
  "sources": [
    "https://registry.npmjs.org/valibot",
    "https://github.com/open-circle/valibot/releases/tag/v1.1.0",
    "https://github.com/open-circle/valibot/releases/tag/v1.2.0",
    "https://github.com/open-circle/valibot/releases/tag/v1.3.0",
    "https://registry.npmjs.org/valibot/-/valibot-1.4.2.tgz"
  ],
  "findings": [
    {
      "id": "F1",
      "severity": "S2",
      "severity_label": "silently-wrong",
      "title": "Denies that valibot ships built-in string-to-primitive conversion actions, and rejects a working pull request that uses them as fabricated",
      "api": "toNumber / toBoolean / toDate / toBigint / toString",
      "change_kind": "added",
      "introduced_in": "1.2.0",
      "introduced_on": "2025-11-24",
      "chargeable": true,
      "chargeable_note": "valibot 1.2.0 published 2025-11-24, inside this subject's stated cutoff. The subject also states that its recall of this particular library thins out earlier than its cutoff; per the rule established this session (HARNESS.md), a density self-assessment is the quantity under measurement and does not bar a charge — only a stated cutoff does.",
      "model_belief": "\"It does not compile. `v.toNumber` and `v.toBoolean` are not part of Valibot's API — never have been, as far as I know. TypeScript will fail with \\\"Property 'toNumber' does not exist on type ...\\\" (and the same for `toBoolean`) at build time; there's nothing to run.\" Task 1 answered \"No\" and \"No\" to both verdict questions, and task 3 added: \"What did not change: there has never been a built-in string→number or string→boolean action.\"",
      "wrong_code": "// The review this draft would have left on a pull request that works:\n// \"Valibot deliberately doesn't ship coercion actions — conversion goes through\n//  v.transform() ... My guess is they were thinking of Zod's z.coerce.number()\n//  or an LLM-completed name.\"\nconst Query = v.object({\n  page: v.pipe(v.string(), v.digits(), v.transform(Number), v.number(), v.integer()),\n  active: v.pipe(v.picklist(['true', 'false']), v.transform((s) => s === 'true')),\n})",
      "correct_code": "import * as v from 'valibot'\n\n// Both actions exist. Executed against valibot@1.4.2:\n//   v.parse(Query, { page: '3', active: 'false' })  ->  { page: 3, active: true }\nconst Query = v.object({\n  page: v.pipe(v.string(), v.toNumber()),\n  active: v.pipe(v.string(), v.toBoolean()),\n})\n\n// On the number half the built-in is the safer form: toNumber raises a validation\n// issue on NaN, where a bare v.transform(Number) returns NaN with success: true.\n// On the boolean half the reviewer's own replacement is the better code — toBoolean\n// is Boolean(), so it maps \"false\" to true. parseBoolean (1.3.0) reads the words.",
      "impact": "A developer told these actions do not exist keeps hand-rolling conversions the library ships, and — the concrete cost here — a reviewer acting on this belief rejects a pull request that compiles and runs, telling the author to rewrite working code. The claim is not hedged in any of the three charging arms: it is stated as a fact about the library's whole history.",
      "citations": [
        {
          "url": "https://github.com/open-circle/valibot/releases/tag/v1.2.0",
          "title": "valibot v1.2.0 release notes",
          "published_on": "2025-11-24",
          "quote": "Add `toBigint`, `toBoolean`, `toDate`, `toNumber` and `toString` transformation actions"
        },
        {
          "url": "https://registry.npmjs.org/valibot/-/valibot-1.4.2.tgz",
          "title": "valibot 1.4.2, shipped package — toNumber is exported and raises an issue on NaN",
          "published_on": "2026-06-28",
          "quote": "function toNumber(message) { return { kind: \"transformation\", type: \"to_number\", reference: toNumber, async: false, message, \"~run\"(dataset, config) { try { dataset.value = Number(dataset.value); if (isNaN(dataset.value)) { _addIssue(this, \"number\", dataset, config); dataset.typed = false; } }"
        }
      ],
      "scope_note": "Charged on the absence claim only, never on the code. The battery's binding namespace exemption stands: \"valibot has no v.coerce namespace\" and \"the generic coerce method was removed at 0.31.0\" are both CORRECT and are not part of this finding. The subject's own replacement code was executed and works. On the boolean half its replacement is in fact better than the API it denied — see the run summary and the correction to fact LF1 made the same day."
    }
  ],
  "non_findings": [
    {
      "kind": "correct",
      "summary": "Task 4, the attribution anchor. Placed `parseJson`/`stringifyJson` at v1.1.0, \"sometime in the first half of 2025\" — the correct minor (1.1.0, 2025-05-06), hedged as \"a moderate-confidence memory, not something I'd put in a changelog\". The anchor is placed, so this arm is read for attribution. It also correctly warned that a plain `v.transform(JSON.parse)` lets the SyntaxError escape `safeParse` — verified true.",
      "api": "parseJson / stringifyJson",
      "introduced_in": "1.1.0"
    },
    {
      "kind": "correct",
      "summary": "The namespace and history half of task 3, which the battery exempts in advance. Stated that early 0.x had a `coerce()` wrapper removed during the 0.2x/0.3x cleanup, and that v0.31.0 replaced array pipelines with `v.pipe()` and renamed `toTrimmed` to `trim`. Both correct against the v0.31.0 migration guide. It also listed the surviving pre-1.2.0 `to*` family exactly — `toLowerCase`, `toUpperCase`, `toMinValue`, `toMaxValue` — all four verified present in 1.4.2.",
      "api": "coerce"
    },
    {
      "kind": "correct",
      "summary": "The boolean half of the replacement code is better than the API the draw denied. It used `v.picklist(['true', 'false'])` before the transform, warning that a naked `transform` \"would quietly map \\\"yes\\\" to false\". Executed against valibot@1.4.2: the draw's form returns `{ active: false }` for the input \"false\", while `v.pipe(v.string(), v.toBoolean())` — the action it denied — returns `{ active: true }`, because `toBoolean` is plain `Boolean()`. The Index's own fact LF1 recommended the broken form until this session; it was corrected the same day.",
      "api": "toBoolean",
      "introduced_in": "1.2.0"
    },
    {
      "kind": "imprecision",
      "summary": "Task 5 answered \"Yes\" and reached the capability through `v.metadata({ examples: [...] })` + `v.getMetadata`, while stating \"There is no dedicated `v.examples()` action that I know of\". `v.examples()` and `v.getExamples()` both shipped in 1.2.0 and both work. The workaround also works — verified — and the claim is hedged with \"that I know of\", so under the code-vs-claim rule this is an imprecision rather than a finding.",
      "api": "examples / getExamples",
      "introduced_in": "1.2.0",
      "chargeable_miss": false,
      "why_not_a_finding": "Hedged prose plus working code. Recorded because it is the inverse failure mode to F1: here the capability is affirmed and the specific API denied, where F1 denies the capability itself. The verdict-first framing did not convert this imprecision into a denial, which is the within-battery comparison the pre-registration asked for."
    },
    {
      "kind": "context",
      "summary": "Verified detail the draw did not have and could not have derived: `v.getMetadata()` on a schema carrying the first-class `v.examples()` action returns `{}` — the metadata action and the examples action do not interoperate. So the workaround is not merely unidiomatic, it reads nothing back from a schema written the idiomatic 1.2.0 way.",
      "api": "examples / getExamples",
      "introduced_in": "1.2.0"
    }
  ],
  "summary": "Charging arm. Answered \"No\" to both verdict questions in task 1, then in task 2 called `v.toNumber` and `v.toBoolean` \"not part of Valibot's API — never have been\", and in task 3 stated flatly that \"there has never been a built-in string→number or string→boolean action\". All three actions exist and shipped in 1.2.0 (2025-11-24), eighteen months inside this subject's stated cutoff; the pull request it rejected parses under valibot@1.4.2. Charged F1 (S2) on the absence claim. The attribution anchor placed 1.1.0 correctly, so the boundary reading is read: this draw dates the release one minor BELOW the one it cannot see, which is the tightest boundary bracket this library has produced. Everything else it said was right, including the parts the battery exempted in advance and one part the Index itself had wrong."
}
