{
  "tool": "api_why",
  "args": {
    "sources": [
      {
        "fileName": "icetakes/app/judgment.bosatsu",
        "source": "package Icetakes/Judgment\n\nfrom Bosatsu/Predef import (\n  String, Bool, Int, List, Unit, True, False, LT, GT,\n  eq_String, cmp_Int\n)\nfrom Yichus/Json import (\n  Json, JsonNull, ParseResult, Parsed, InvalidJson,\n  DecodeResult, Decoded, DecodeError, decode_bind,\n  parse_json, required_field, as_string, as_bool, as_int, as_array, as_object\n)\nfrom Yichus/Organize import FamilySpec, ReadingOrder, ReadingStep\n\nexport (\n  EvidenceRef(),\n  JudgmentSchema(),\n  Judgment(),\n  LawyerJudgment(),\n  EventTimeEstimate(),\n  UrgencyAssessment(),\n  DevilsAdvocateJudgment(),\n  TaxonomyJudgment(),\n  decode_judgment,\n  role_value_decoders,\n  judgment_reading\n)\n\nexposes (Yichus/Json, Yichus/Organize)\n\n# Evidence IDs identify immutable inputs supplied to this judgment attempt.\n# The decoder accepts only IDs in the caller's frozen evidence set.\nstruct EvidenceRef(id: String)\n\nenum JudgmentSchema:\n  DevilsAdvocateV1\n  TaxonomyV1\n  LawyerV1\n\nenum EventTimeEstimate:\n  UnknownEventTime\n  EstimatedEventTime(epoch_millis: Int)\n\nenum UrgencyAssessment:\n  SupportedUrgency\n  OverstatedUrgency\n  UnderstatedUrgency\n  UnclearUrgency\n\nstruct LawyerJudgment(\n  congruence_basis_points: Int,\n  congruence_reasoning: String,\n  event_time: EventTimeEstimate,\n  urgency_assessment: UrgencyAssessment,\n  urgency_reasoning: String,\n  brief_bullets: List[String],\n  evidence_refs: List[EvidenceRef]\n)\n\nstruct DevilsAdvocateJudgment(\n  has_fatal_flaw: Bool,\n  attack_argument: String,\n  evidence_refs: List[EvidenceRef]\n)\n\n# Agora's examples use topic-relative stance strings such as \"Skeptical\" and\n# \"Pro-Tariff\". A charter may constrain that vocabulary later; this syntax\n# contract does not silently collapse it to Burrora's Pro/Anti/Neutral summary.\nstruct TaxonomyJudgment(\n  topic: String,\n  stance: String,\n  evidence_refs: List[EvidenceRef]\n)\n\nenum Judgment:\n  DevilsAdvocate(value: DevilsAdvocateJudgment)\n  Taxonomy(value: TaxonomyJudgment)\n  Lawyer(value: LawyerJudgment)\n\ndef member_string(items: List[String], target: String) -> Bool:\n  loop items:\n    case []: False\n    case [item, *rest]:\n      match eq_String(item, target):\n        case True: True\n        case False: member_string(rest, target)\n\ndef member_evidence(refs: List[EvidenceRef], target: String) -> Bool:\n  loop refs:\n    case []: False\n    case [EvidenceRef(id), *rest]:\n      match eq_String(id, target):\n        case True: True\n        case False: member_evidence(rest, target)\n\ndef expected_fields(\n  allowed: List[String],\n  fields: List[(String, Json)]\n) -> DecodeResult[Unit]:\n  loop fields:\n    case []: Decoded(())\n    case [(name, _), *rest]:\n      match member_string(allowed, name):\n        case True: expected_fields(allowed, rest)\n        case False: DecodeError(name, \"unexpected field\")\n\ndef nonempty_string(path: String, value: Json) -> DecodeResult[String]:\n  text <- decode_bind(as_string(path, value))\n  match eq_String(text, \"\"):\n    case True: DecodeError(path, \"empty string\")\n    case False: Decoded(text)\n\ndef required_nonempty_string(name: String, value: Json) -> DecodeResult[String]:\n  field <- decode_bind(required_field(name, value))\n  nonempty_string(name, field)\n\ndef decode_evidence_ref(\n  value: Json,\n  allowed: List[EvidenceRef],\n  seen: List[EvidenceRef]\n) -> DecodeResult[EvidenceRef]:\n  id <- decode_bind(as_string(\"evidence_refs\", value))\n  match eq_String(id, \"\"):\n    case True: DecodeError(\"evidence_refs\", \"empty evidence reference\")\n    case False:\n      match member_evidence(allowed, id):\n        case False: DecodeError(\"evidence_refs\", \"unknown evidence reference\")\n        case True:\n          match member_evidence(seen, id):\n            case True: DecodeError(\"evidence_refs\", \"duplicate evidence reference\")\n            case False: Decoded(EvidenceRef(id))\n\ndef decode_evidence_refs(\n  values: List[Json],\n  allowed: List[EvidenceRef],\n  seen: List[EvidenceRef]\n) -> DecodeResult[List[EvidenceRef]]:\n  recur values:\n    case []: Decoded([])\n    case [value, *rest]:\n      match decode_evidence_ref(value, allowed, seen):\n        case DecodeError(path, reason): DecodeError(path, reason)\n        case Decoded(evidence):\n          match decode_evidence_refs(rest, allowed, [evidence, *seen]):\n            case DecodeError(path, reason): DecodeError(path, reason)\n            case Decoded(decoded_rest): Decoded([evidence, *decoded_rest])\n\ndef required_evidence_refs(\n  value: Json,\n  allowed: List[EvidenceRef]\n) -> DecodeResult[List[EvidenceRef]]:\n  field <- decode_bind(required_field(\"evidence_refs\", value))\n  values <- decode_bind(as_array(\"evidence_refs\", field))\n  match values:\n    case []: DecodeError(\"evidence_refs\", \"at least one evidence reference required\")\n    case [_, *_]: decode_evidence_refs(values, allowed, [])\n\ndef required_basis_points(value: Json) -> DecodeResult[Int]:\n  field <- decode_bind(required_field(\"congruence_basis_points\", value))\n  score <- decode_bind(as_int(\"congruence_basis_points\", field))\n  match (cmp_Int(score, 0), cmp_Int(score, 10000)):\n    case (LT, _) | (_, GT):\n      DecodeError(\"congruence_basis_points\", \"must be between 0 and 10000\")\n    case _: Decoded(score)\n\ndef required_event_time(value: Json) -> DecodeResult[EventTimeEstimate]:\n  field <- decode_bind(required_field(\"event_time_epoch_millis\", value))\n  match field:\n    case JsonNull: Decoded(UnknownEventTime)\n    case _:\n      epoch_millis <- decode_bind(as_int(\"event_time_epoch_millis\", field))\n      Decoded(EstimatedEventTime(epoch_millis))\n\ndef required_urgency_assessment(value: Json) -> DecodeResult[UrgencyAssessment]:\n  text <- decode_bind(required_nonempty_string(\"urgency_assessment\", value))\n  match text:\n    case \"supported\": Decoded(SupportedUrgency)\n    case \"overstated\": Decoded(OverstatedUrgency)\n    case \"understated\": Decoded(UnderstatedUrgency)\n    case \"unclear\": Decoded(UnclearUrgency)\n    case _: DecodeError(\"urgency_assessment\", \"unknown urgency assessment\")\n\ndef required_brief_bullets(value: Json) -> DecodeResult[List[String]]:\n  field <- decode_bind(required_field(\"brief_bullets\", value))\n  bullets <- decode_bind(as_array(\"brief_bullets\", field))\n  match bullets:\n    case [first, second, third]:\n      first_text <- decode_bind(nonempty_string(\"brief_bullets[0]\", first))\n      second_text <- decode_bind(nonempty_string(\"brief_bullets[1]\", second))\n      third_text <- decode_bind(nonempty_string(\"brief_bullets[2]\", third))\n      Decoded([first_text, second_text, third_text])\n    case _: DecodeError(\"brief_bullets\", \"exactly three bullets required\")\n\ndef decode_lawyer_value(\n  value: Json,\n  allowed: List[EvidenceRef]\n) -> DecodeResult[LawyerJudgment]:\n  fields <- decode_bind(as_object(\"$\", value))\n  _ <- decode_bind(expected_fields([\n    \"congruence_basis_points\", \"congruence_reasoning\",\n    \"event_time_epoch_millis\", \"urgency_assessment\",\n    \"urgency_reasoning\", \"brief_bullets\", \"evidence_refs\"\n  ], fields))\n  score <- decode_bind(required_basis_points(value))\n  congruence_reasoning <- decode_bind(required_nonempty_string(\"congruence_reasoning\", value))\n  event_time <- decode_bind(required_event_time(value))\n  urgency_assessment <- decode_bind(required_urgency_assessment(value))\n  urgency_reasoning <- decode_bind(required_nonempty_string(\"urgency_reasoning\", value))\n  brief_bullets <- decode_bind(required_brief_bullets(value))\n  evidence_refs <- decode_bind(required_evidence_refs(value, allowed))\n  Decoded(LawyerJudgment(\n    score, congruence_reasoning, event_time, urgency_assessment,\n    urgency_reasoning, brief_bullets, evidence_refs\n  ))\n\ndef decode_devils_advocate_value(\n  value: Json,\n  allowed: List[EvidenceRef]\n) -> DecodeResult[DevilsAdvocateJudgment]:\n  fields <- decode_bind(as_object(\"$\", value))\n  _ <- decode_bind(expected_fields([\"has_fatal_flaw\", \"attack_argument\", \"evidence_refs\"], fields))\n  fatal_field <- decode_bind(required_field(\"has_fatal_flaw\", value))\n  has_fatal_flaw <- decode_bind(as_bool(\"has_fatal_flaw\", fatal_field))\n  attack_argument <- decode_bind(required_nonempty_string(\"attack_argument\", value))\n  evidence_refs <- decode_bind(required_evidence_refs(value, allowed))\n  Decoded(DevilsAdvocateJudgment(has_fatal_flaw, attack_argument, evidence_refs))\n\ndef decode_taxonomy_value(\n  value: Json,\n  allowed: List[EvidenceRef]\n) -> DecodeResult[TaxonomyJudgment]:\n  fields <- decode_bind(as_object(\"$\", value))\n  _ <- decode_bind(expected_fields([\"topic\", \"stance\", \"evidence_refs\"], fields))\n  topic <- decode_bind(required_nonempty_string(\"topic\", value))\n  stance <- decode_bind(required_nonempty_string(\"stance\", value))\n  evidence_refs <- decode_bind(required_evidence_refs(value, allowed))\n  Decoded(TaxonomyJudgment(topic, stance, evidence_refs))\n\n\n# The OpenRouter adapter returns untrusted text. The expected versioned schema\n# comes from the claimed job, not the model response, and selects exactly one\n# role decoder.\n# Parsing and every schema check happen before a completion transaction may\n# persist the resulting Judgment.\ndef decode_judgment(\n  schema: JudgmentSchema,\n  source: String,\n  allowed: List[EvidenceRef]\n) -> DecodeResult[Judgment]:\n  match parse_json(source):\n    case InvalidJson(reason): DecodeError(\"$\", reason)\n    case Parsed(value):\n      match schema:\n        case DevilsAdvocateV1:\n          judgment <- decode_bind(decode_devils_advocate_value(value, allowed))\n          Decoded(DevilsAdvocate(judgment))\n        case TaxonomyV1:\n          judgment <- decode_bind(decode_taxonomy_value(value, allowed))\n          Decoded(Taxonomy(judgment))\n        case LawyerV1:\n          judgment <- decode_bind(decode_lawyer_value(value, allowed))\n          Decoded(Lawyer(judgment))\n\n\n# These declarations make the intended decomposition inspectable. Yichus\n# checks them against the compiled program; they do not alter the decoder.\nrole_value_decoders = FamilySpec(\"role value decoders\", [\n  \"decode_devils_advocate_value\",\n  \"decode_taxonomy_value\",\n  \"decode_lawyer_value\"\n])\n\njudgment_reading = ReadingOrder([\n  ReadingStep(\"member_string\", \"checks whether a field name belongs to a complete schema\"),\n  ReadingStep(\"expected_fields\", \"rejects every field outside the role's complete schema\"),\n  ReadingStep(\"nonempty_string\", \"accepts a string value and rejects empty text\"),\n  ReadingStep(\"required_nonempty_string\", \"requires one string field exactly once and rejects empty text\"),\n  ReadingStep(\"member_evidence\", \"checks an evidence ID against a typed evidence set\"),\n  ReadingStep(\"decode_evidence_ref\", \"accepts one nonempty known evidence ID exactly once\"),\n  ReadingStep(\"decode_evidence_refs\", \"preserves the validated evidence order\"),\n  ReadingStep(\"required_evidence_refs\", \"requires a nonempty array of evidence IDs\"),\n  ReadingStep(\"required_basis_points\", \"accepts exact congruence basis points in the closed range\"),\n  ReadingStep(\"required_event_time\", \"distinguishes an unknown event time from an exact epoch-millisecond estimate\"),\n  ReadingStep(\"required_urgency_assessment\", \"accepts only the declared urgency-assessment vocabulary\"),\n  ReadingStep(\"required_brief_bullets\", \"requires exactly three nonempty brief points\"),\n  ReadingStep(\"decode_devils_advocate_value\", \"builds the typed judgment only after every field is valid\"),\n  ReadingStep(\"decode_taxonomy_value\", \"builds the typed taxonomy only after every field is valid\"),\n  ReadingStep(\"decode_lawyer_value\", \"builds the typed Lawyer judgment only after every field is valid\"),\n  ReadingStep(\"decode_judgment\", \"parses untrusted text under the job's versioned schema\")\n])\n"
      }
    ],
    "binding": "Icetakes/Judgment::decode_judgment",
    "inputs": {
      "schema": {
        "LawyerV1": {}
      },
      "source": "{\"congruence_basis_points\": 8500, \"congruence_reasoning\": \"Grounded\", \"event_time_epoch_millis\": null, \"urgency_assessment\": \"supported\", \"urgency_reasoning\": \"Uncertain\", \"brief_bullets\": [\"A\", \"B\", \"C\"], \"evidence_refs\": [\"article:1\"]}",
      "allowed": [
        {
          "id": "article:1"
        }
      ]
    },
    "observe": false
  }
}
