harn-stdlib 0.10.130

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, and stamps `converted_from` so a reader can count
// overrules without inferring them.
import { CompletionEvidenceSnapshot } from "std/agent/completion_evidence"
import { 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,
  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,
}

const __COMPLETION_VERIFICATION_CONVERSION: string = "failed_verification_contradicted_by_gate"

const __COMPLETION_CONTRADICTION_CONVERSION: string =
  "gap_contradicts_done_survived_reask_over_passing_verifier"

/**
 * 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 {
  if !verdict.vetoed || verdict.source != "llm" {
    return verdict
  }
  if !__completion_arbitration_converts(
    to_string(verdict.gap_class ?? "other"),
    evaluation.invoked.deterministic,
    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: "",
      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 {
  if !verdict.vetoed || verdict.source != "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.deterministic_reason,
    to_string(evaluation.payload.verification?.observed ?? "not_run"),
  ) {
    return verdict
  }
  return verdict
    + {
      vetoed: false,
      verdict: "done",
      feedback: nil,
      next_step: "",
      converted_from: __COMPLETION_CONTRADICTION_CONVERSION,
      reason: __COMPLETION_CONTRADICTION_CONVERSION,
    }
}