harn-stdlib 0.10.133

Embedded Harn standard library source catalog
Documentation
// std/agent/judge_arbitration — the completion arbitration layer.
//
// A model judge decides whether a run is finished, and two of its refusals can
// be overruled by evidence the runtime already holds. Both overrules live here,
// beside the verdict shapes they read, so the rule that narrows a refusal sits
// next to the rule that narrows the other one and neither can drift from the
// receipt it writes.
//
// `__completion_arbitration_converts` and
// `__completion_contradiction_arbitration_converts` in `std/agent/judge_internals`
// own the CONDITIONS and the reasoning for each clause. This module owns what a
// conversion does to the verdict: it flips the acceptance, clears the feedback
// that no turn could answer, names the party whose answer the verdict now IS,
// and stamps both the typed override and `converted_from` so a reader can count
// overrules without inferring them.
//
// Both conversions overrule a judge, so both write the attribution pair harn#8229
// defined: `source` names the author of the verdict of record, `judge_source`
// stays with the party that produced the judge's own answer, and `override`
// carries the overruling party and the rule in a typed object rather than in
// prose a consumer has to pattern-match.
//
// They did not, and the gap was one-directional in a way that hid it. harn#8229
// instrumented the gate forcing a `continue` over a judge's `done`, which is the
// direction the completion seam takes. These two take the other direction, a
// `done` of record over a judge's `continue`, and stamped only `converted_from`
// while leaving `source` at `llm`. Measured across an eval archive: every one of
// the eight conversions published the gate's answer under the model's name,
// which is the exact reading harn#8229 exists to prevent, surviving on the half
// of the ladder it did not reach.
import { CompletionEvidenceSnapshot } from "std/agent/completion_evidence"
import {
  CompletionRequirementBoundary,
  CompletionRequirementReport,
} from "std/agent/completion_requirements"
import {
  CompletionJudgeAdmission,
  CompletionJudgeGapClass,
  CompletionVerificationState,
  __completion_arbitration_converts,
  __completion_contradiction_arbitration_converts,
} from "std/agent/judge_internals"

/** Which adjudicators actually ran at one completion boundary. */
pub type CompletionJudgeInvocations = {
  deterministic: bool,
  verify_completion_judge: bool,
  turn_end_condition: bool,
}

/** Normalized result from either a deterministic or model completion check. */
pub type CompletionStageVerdict = {
  vetoed: bool,
  confirm?: bool,
  verdict?: string,
  feedback?: string?,
  reasoning?: string,
  next_step?: string,
  source?: string,
  // The judge's own answer, and the party that produced it. `verdict` and
  // `source` above describe the answer of RECORD, which an override may have
  // authored; these two stay the model's, so a reader can tell the two apart
  // and arbitration can still find a model refusal after a conversion.
  judge_verdict?: string?,
  judge_source?: string?,
  // Typed conversion record: the overriding party and the rule it applied.
  // Absent when the verdict of record is the judge's own.
  override?: dict?,
  trigger?: string?,
  reason?: string?,
  gap_class?: CompletionJudgeGapClass?,
  // Set when a `done` reply that named a gap was re-asked once with the
  // contradiction spelled out. Present so the contradiction arbitration can
  // tell a first self-contradiction from one that survived being pointed out.
  contradiction_reasked?: bool?,
  verification?: CompletionVerificationState?,
  converted_from?: string?,
  escalation_recommended?: bool?,
  escalation_target?: string?,
  judge_duration_ms?: int | float,
  judge_outcome?: string?,
  terminal_judge_reason?: string?,
  terminal_evidence_preserved?: bool,
  admission?: CompletionJudgeAdmission?,
  typed_checkpoint?: dict?,
  requirement_report?: CompletionRequirementReport?,
}

pub type CompletionPolicyEvaluation = {
  verdict: CompletionStageVerdict,
  invoked: CompletionJudgeInvocations,
  verification_judge_cap_reached: bool,
  completion_judge_cap_reached: bool,
  // The snapshot as of this stage. Later stages see what earlier stages
  // established; a second model judge must not read the gate's facts through the
  // first judge's overwritten verdict.
  payload: CompletionEvidenceSnapshot,
  // The deterministic gate's own reason, held separately because
  // `evaluation.verdict` is overwritten by every stage that follows it.
  deterministic_reason: string,
  // Each stage replaces only its own assessment. The ledger is the projection
  // across all stages, including declarations that have not yet been assessed.
  requirement_boundary: CompletionRequirementBoundary,
  ledger: CompletionRequirementReport,
}

const __COMPLETION_VERIFICATION_CONVERSION: string = "failed_verification_contradicted_by_gate"

const __COMPLETION_CONTRADICTION_CONVERSION: string =
  "gap_contradicts_done_survived_reask_over_passing_verifier"

/**
 * The typed override both conversions stamp, in the shape harn#8229 defined.
 *
 * Shared so the two arbitrations cannot describe the same kind of event
 * differently, which is how the prose field they used to write alone drifted
 * from the decision it was describing.
 *
 * `judge_verdict` prefers the field harn#8229 sets upstream and falls back to
 * the verdict being converted. The fallback is not decoration: a replayed or
 * older-pin verdict reaches here without the pair, and recording the overruled
 * answer as absent would leave exactly the hole this closes. The verdict on
 * this path is a refusal by construction, so the fallback is the judge's own
 * word either way.
 *
 * @effects: []
 * @errors: []
 * @api_stability: internal
 * @example: __completion_conversion_override(verdict, "failed_verification_contradicted_by_gate")
 */
fn __completion_conversion_override(verdict: CompletionStageVerdict, rule: string) -> dict {
  return {
    party: "gate",
    rule: rule,
    judge_verdict: to_string(verdict?.judge_verdict ?? verdict.verdict ?? ""),
  }
}

/**
 * Refuse a model `continue` that names a failed verification the deterministic
 * gate observed passing, and convert it to `done` with a receipt saying so.
 *
 * This narrows WHAT EVIDENCE a refusal may rest on; it does not weaken refusal.
 * Every other `gap_class` vetoes exactly as before, so artifact, manner and
 * negative-clause, and authorization gaps keep the judge's full authority — and
 * those are where its demonstrated discriminating power lives.
 *
 * `__completion_arbitration_converts` owns the conditions and why each is
 * required. The invocation map is read here rather than re-derived: whether an
 * adjudicator actually ran is the invocation map's question, not this
 * function's.
 *
 * @effects: []
 * @errors: []
 * @api_stability: internal
 * @example: __completion_arbitrate_verification(verdict, evaluation)
 */
pub fn __completion_arbitrate_verification(
  verdict: CompletionStageVerdict,
  evaluation: CompletionPolicyEvaluation,
) -> CompletionStageVerdict {
  // Reads `judge_source`, the party that produced the judge's own answer, not
  // `source`, which names the author of the verdict of record and therefore
  // reads "gate" on exactly the overridden decisions this arbitration exists
  // to reconsider. Falls back to `source` for a verdict built before the pair
  // existed, so an older shape keeps its previous behaviour rather than
  // silently ceasing to arbitrate.
  const judged_by = to_string(verdict?.judge_source ?? verdict.source ?? "")
  if !verdict.vetoed || judged_by != "llm" {
    return verdict
  }
  if !__completion_arbitration_converts(
    to_string(verdict.gap_class ?? "other"),
    evaluation.invoked.deterministic,
    evaluation.payload.verification?.oracle_expected ?? false,
    evaluation.deterministic_reason,
    to_string(evaluation.payload.verification?.observed ?? "not_run"),
  ) {
    return verdict
  }
  // `reasoning` keeps the judge's own words. The record must show that a real
  // refusal was overruled, not that no refusal happened.
  return verdict
    + {
      vetoed: false,
      verdict: "done",
      feedback: nil,
      next_step: "",
      // `source` is the author of the verdict of record, and after this line
      // that author is the gate. `judge_source` is deliberately untouched: it
      // names who produced the judge's own answer, stays `llm` across an
      // override, and is what the guard above reads so a later arbitration can
      // still recognise a model refusal.
      // `source` is the author of the verdict of record, and after this line
      // that author is the gate. `judge_source` is deliberately untouched: it
      // names who produced the judge's own answer, stays `llm` across an
      // override, and is what the guard above reads so a later arbitration can
      // still recognise a model refusal.
      source: "gate",
      override: __completion_conversion_override(verdict, __COMPLETION_VERIFICATION_CONVERSION),
      converted_from: __COMPLETION_VERIFICATION_CONVERSION,
      reason: __COMPLETION_VERIFICATION_CONVERSION,
    }
}

/**
 * Refuse a self-contradiction that survived its one re-ask and names nothing,
 * on a run the deterministic gate observed verifying green.
 *
 * `__completion_contradiction_arbitration_converts` owns the conditions and why
 * each is required. This is the second half of the harn#8060 fix: the re-ask
 * gives the judge one chance to name a real gap or drop the field, and this
 * stops an unnamed contradiction that took neither exit from spending the
 * invocation budget on feedback no turn can answer.
 *
 * As with the verification arbitration, `reasoning` keeps the judge's own words,
 * so the record shows a refusal that was overruled rather than one that never
 * happened.
 *
 * @effects: []
 * @errors: []
 * @api_stability: internal
 * @example: __completion_arbitrate_contradiction(verdict, evaluation)
 */
pub fn __completion_arbitrate_contradiction(
  verdict: CompletionStageVerdict,
  evaluation: CompletionPolicyEvaluation,
) -> CompletionStageVerdict {
  // Reads `judge_source`, the party that produced the judge's own answer, not
  // `source`, which names the author of the verdict of record and therefore
  // reads "gate" on exactly the overridden decisions this arbitration exists
  // to reconsider. Falls back to `source` for a verdict built before the pair
  // existed, so an older shape keeps its previous behaviour rather than
  // silently ceasing to arbitrate.
  const judged_by = to_string(verdict?.judge_source ?? verdict.source ?? "")
  if !verdict.vetoed || judged_by != "llm" {
    return verdict
  }
  if to_string(verdict.reason ?? "") != "completion_judge_gap_contradicts_done" {
    return verdict
  }
  if !__completion_contradiction_arbitration_converts(
    to_string(verdict.gap_class ?? "other"),
    verdict.contradiction_reasked ?? false,
    evaluation.invoked.deterministic,
    evaluation.payload.verification?.oracle_expected ?? false,
    evaluation.deterministic_reason,
    to_string(evaluation.payload.verification?.observed ?? "not_run"),
  ) {
    return verdict
  }
  return verdict
    + {
      vetoed: false,
      verdict: "done",
      feedback: nil,
      next_step: "",
      source: "gate",
      override: __completion_conversion_override(verdict, __COMPLETION_CONTRADICTION_CONVERSION),
      converted_from: __COMPLETION_CONTRADICTION_CONVERSION,
      reason: __COMPLETION_CONTRADICTION_CONVERSION,
    }
}