pub import {
COMPLETION_JUDGE_DEFAULT_CAP,
CompletionReview,
agent_completion_review,
agent_completion_review_should_skip_llm,
agent_done_judge_cap,
agent_verify_completion_judge_cap,
} from "std/agent/completion_review"
import { agent_feedback_emit } from "std/agent/feedback"
import {
COMPLETION_JUDGE_GAP_CLASSES,
CompletionJudgeAdmission,
CompletionJudgeGapClass,
CompletionVerificationState,
__completion_arbitration_converts,
__completion_judge_gap_class,
__completion_verification_evidence_index,
__completion_verification_state_read,
__judge_emit_decision,
__judge_emit_started,
__judge_run_checkpoint,
} from "std/agent/judge_internals"
import { __normalize_judge_config } from "std/agent/options_formats"
import { JudgeConfig } from "std/agent/options_types"
import { __with_prompt_fragment } from "std/agent/preflight"
import { completion_judge_user_prompt } from "std/agent/prompts"
import { agent_emit_event, agent_session_messages } from "std/agent/state"
import { agent_tool_annotations, agent_tool_is_read_only } from "std/agent/tool_annotations"
import {
AgentTranscriptIssue,
AgentTranscriptToolLifecycle,
AgentTranscriptToolLifecycleReport,
AgentTranscriptToolLifecycleStatus,
agent_transcript_tool_lifecycle_report,
} from "std/agent/transcript"
// The completion judge is a terminal checkpoint, not a second agent loop. Give
// it the latest effect-bearing actions and their observations without replaying
// an unbounded session or the full callable schemas it cannot use.
const __JUDGE_EVIDENCE_FIELD_CHAR_LIMIT = 4500
const __JUDGE_EVIDENCE_LABEL_CHAR_LIMIT = 160
/** Action the completion-judge model is allowed to return. */
pub type CompletionJudgeAction = "accept" | "continue"
/** Runtime completion decision after deadline and policy enforcement. */
pub type CompletionDirectiveAction = CompletionJudgeAction | "stop_unverified"
pub type CompletionEvidenceRole = "observation" | "mutation" | "verification" | "other"
pub type CompletionToolSemantics = {
name: string,
kind: string,
side_effect_level: string,
read_only: bool,
evidence_role: CompletionEvidenceRole,
}
pub type CompletionEvidenceAction = {
ordinal: int,
evidence_index: int,
tool_call_id: string,
name: string,
semantics: CompletionToolSemantics,
lifecycle_status: AgentTranscriptToolLifecycleStatus,
arguments: string,
observation: string,
result_facts: string,
has_error_result: bool,
}
pub type CompletionEvidenceIssue = {
code: string,
record_index: int,
message: string,
details: string,
}
pub type CompletionEvidencePacket = {
schema: "harn.completion_judge_evidence.v2",
raw_message_count: int,
raw_transcript_digest: string,
relevant_action_count: int,
omitted_read_only_call_count: int,
omitted_older_action_count: int,
lifecycle_issue_count: int,
lifecycle_issues: list<CompletionEvidenceIssue>,
actions: list<CompletionEvidenceAction>,
fallback_observations: list<CompletionEvidenceAction>,
}
/**
* One selected action, named on the projection receipt. Recovering WHICH action
* a packet dropped used to take arithmetic on the counts; a host that declared
* no verification role anywhere is visible here at a glance instead.
*/
pub type CompletionEvidenceSelection = {
name: string,
evidence_role: CompletionEvidenceRole,
evidence_index: int,
}
pub type CompletionEvidenceProjectionStats = {
schema: "harn.completion_judge_evidence_projection.v1",
raw_message_count: int,
relevant_call_count: int,
included_action_count: int,
included_fallback_observation_count: int,
omitted_read_only_call_count: int,
omitted_older_action_count: int,
lifecycle_issue_count: int,
serialized_chars: int,
raw_transcript_digest: string,
selected_actions: list<CompletionEvidenceSelection>,
}
pub type CompletionEvidenceProjection = {
packet: CompletionEvidencePacket,
serialized: string,
stats: CompletionEvidenceProjectionStats,
}
pub type CompletionEvidenceSnapshot = {
schema: "harn.completion_evidence_snapshot.v1",
evidence_id: string,
session_id: string,
task: string,
stop_reason: string,
text: string,
visible_text: string,
last_text: string,
// `text`, `visible_text`, and `last_text` are all the same PROJECTION of the
// candidate turn: completion markers and structural tags are already gone.
// `raw_text` is the unprojected emission for that same turn. A policy asking
// "did the model declare completion?" must read `raw_text`; the projected
// fields answer "what should a reader be shown?" and cannot answer the first
// question at all.
raw_text: string,
transcript: string,
judge_evidence: string,
judge_evidence_projection: CompletionEvidenceProjectionStats,
all_tools_used: string,
successful_tools_used: string,
iteration: int,
knowledge_state: string,
// Absent until a deterministic gate has actually run and reported. A judge
// stage reads it; nothing else may synthesize one.
verification?: CompletionVerificationState?,
}
pub type CompletionJudgeInvocations = {
deterministic: bool,
verify_completion_judge: bool,
done_judge: bool,
}
pub type CompletionFeedbackDelivery = "none" | "deferred" | "delivered"
pub type CompletionDirectiveReceipt = {
schema: "harn.completion_directive_receipt.v1",
evidence_id: string,
action: CompletionDirectiveAction,
trigger: string,
source: string,
outcome: string,
reason: string,
invoked: CompletionJudgeInvocations,
feedback_delivery: CompletionFeedbackDelivery,
projection: CompletionEvidenceProjectionStats,
// The gap the model judge named, and — when an adjudication was overruled —
// WHAT overruled it. `trigger` names whichever adjudicator ANSWERED, not the
// boundary that asked, so a reader counting conversions must count
// `converted_from` and never filter by `trigger`. The completion gate also
// writes `converted_from`, but only ever a bare ladder reason
// (`failed_verification`), never an arbitration name, so the two never alias.
converted_from?: string?,
gap_class?: string?,
verification?: CompletionVerificationState?,
admission?: CompletionJudgeAdmission?,
checkpoint?: dict?,
}
pub type CompletionDirective = {
action: CompletionDirectiveAction,
evidence_id: string,
feedback?: string?,
repair?: string?,
receipt: CompletionDirectiveReceipt,
feedback_history: dict,
}
/**
* Decoded completion decision. The schema admits `done` and `continue` only;
* an out-of-contract value is preserved verbatim so telemetry can show what
* the judge actually said, and is treated as a non-veto by the caller.
*/
pub type CompletionJudgeVerdict = {
verdict: string,
detail: string,
gap_class: CompletionJudgeGapClass,
}
/** Normalized result from either a deterministic or model completion check. */
type CompletionStageVerdict = {
vetoed: bool,
confirm?: bool,
verdict?: string,
feedback?: string?,
reasoning?: string,
next_step?: string,
source?: string,
trigger?: string?,
reason?: string?,
gap_class?: CompletionJudgeGapClass?,
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?,
}
/** Stable identity for the two model-judge positions in the completion plan. */
enum CompletionJudgeSlot {
Verification
Completion
}
/** Ordered checks evaluated at one completion boundary. */
enum CompletionPolicyStage {
Deterministic(check: any)
ModelJudge(
slot: CompletionJudgeSlot,
config: JudgeConfig,
trigger: string?,
prior_invocations: int,
max_invocations: int?,
)
}
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,
}
fn __unique_names(names: any) -> list<string> {
let unique = []
for name in names {
if name != "" && !contains(unique, name) {
unique = unique.appending(name)
}
}
return unique
}
fn __session_tool_names(lifecycle: AgentTranscriptToolLifecycleReport) -> list<string> {
let names = []
for call in lifecycle.calls {
names = names.appending(call.name)
}
return __unique_names(names)
}
fn __session_successful_tool_names(lifecycle: AgentTranscriptToolLifecycleReport) -> list<string> {
let names: list<string> = []
for call in lifecycle.calls {
if call.status == "completed" && call.result?.outcome == "ok" {
names = names + [call.name]
}
}
return __unique_names(names)
}
fn __judge_tool_entries(opts: dict) {
const registry = opts?.tools
return if type_of(registry) == "dict" {
registry?.tools ?? []
} else if type_of(registry) == "list" {
registry
} else {
[]
}
}
fn __judge_tool_name(entry: dict) -> string {
return to_string(entry?.name ?? entry?.function?.name ?? "")
}
fn __judge_tool_entry(opts: dict, name: any) {
for entry in __judge_tool_entries(opts) {
if __judge_tool_name(entry) == name {
return entry
}
}
return nil
}
fn __judge_declared_evidence_role(value: string?) -> CompletionEvidenceRole? {
const role = lowercase(to_string(value ?? ""))
if role == "observation" || role == "mutation" || role == "verification" {
return role
}
return nil
}
fn __judge_evidence_role(
declared: CompletionEvidenceRole?,
kind: string,
effect: string,
read_only: bool,
) -> CompletionEvidenceRole {
if declared != nil {
return declared
}
if kind == "edit" || effect == "workspace_write" {
return "mutation"
}
if read_only {
return "observation"
}
return "other"
}
fn __judge_tool_descriptor(opts: dict, name: any) -> CompletionToolSemantics {
const entry = __judge_tool_entry(opts, name)
const annotations = agent_tool_annotations(entry, opts?.policy, name)
const read_only = entry != nil && agent_tool_is_read_only(entry, opts?.policy, name)
const kind = lowercase(to_string(annotations?.kind ?? ""))
const effect = lowercase(to_string(annotations?.side_effect_level ?? ""))
const declared_role = __judge_declared_evidence_role(annotations?.completion_evidence_role)
const evidence_role = __judge_evidence_role(declared_role, kind, effect, read_only)
return {
name: __judge_clip_evidence(name, __JUDGE_EVIDENCE_LABEL_CHAR_LIMIT),
kind: __judge_clip_evidence(
annotations?.kind ?? annotations?.tool_kind ?? annotations?.toolKind ?? "",
__JUDGE_EVIDENCE_LABEL_CHAR_LIMIT,
),
side_effect_level: __judge_clip_evidence(
annotations?.side_effect_level ?? annotations?.sideEffectLevel ?? annotations?.side_effect
?? annotations?.sideEffect
?? "",
__JUDGE_EVIDENCE_LABEL_CHAR_LIMIT,
),
read_only: read_only,
evidence_role: evidence_role,
}
}
fn __judge_clip_evidence(value: any, limit: int) -> string {
const text = if type_of(value) == "string" {
value
} else {
json_stringify(value ?? "")
}
if len(text) <= limit {
return text
}
const tail_chars = limit / 3
const head_chars = limit - tail_chars
const omitted = len(text) - limit
return substring(text, 0, head_chars)
+ "\n…["
+ to_string(omitted)
+ " chars elided — middle of evidence]…\n"
+ substring(text, len(text) - tail_chars, len(text))
}
fn __judge_descriptor_is_verifier(descriptor: dict) -> bool {
return descriptor?.evidence_role == "verification"
}
fn __judge_descriptor_is_mutation(descriptor: dict) -> bool {
return descriptor?.evidence_role == "mutation"
}
fn __judge_descriptor_is_observation(descriptor: dict) -> bool {
return descriptor?.evidence_role == "observation"
}
fn __judge_lifecycle_action(
lifecycle: AgentTranscriptToolLifecycle,
opts: dict,
) -> CompletionEvidenceAction {
const descriptor = __judge_tool_descriptor(opts, lifecycle.name)
const result = lifecycle.result
let observations = []
for matched in lifecycle.results {
observations = observations + [matched.text]
}
return {
ordinal: lifecycle.ordinal,
evidence_index: lifecycle.evidence_index,
tool_call_id: __judge_clip_evidence(lifecycle.tool_call_id, __JUDGE_EVIDENCE_LABEL_CHAR_LIMIT),
name: descriptor.name,
semantics: descriptor,
lifecycle_status: lifecycle.status,
arguments: __judge_clip_evidence(lifecycle.args, __JUDGE_EVIDENCE_FIELD_CHAR_LIMIT),
observation: __judge_clip_evidence(join(observations, "\n"), __JUDGE_EVIDENCE_FIELD_CHAR_LIMIT),
result_facts: __judge_clip_evidence(result?.data ?? {}, __JUDGE_EVIDENCE_FIELD_CHAR_LIMIT),
has_error_result: lifecycle.results.any({ matched -> matched.outcome == "error" }),
}
}
fn __judge_latest(
current: CompletionEvidenceAction?,
candidate: CompletionEvidenceAction?,
) -> CompletionEvidenceAction? {
if candidate == nil {
return current
}
if current == nil || candidate.evidence_index >= current.evidence_index {
return candidate
}
return current
}
fn __judge_unique_slot_actions(
candidates: list<CompletionEvidenceAction?>,
) -> list<CompletionEvidenceAction> {
let selected: list<CompletionEvidenceAction> = []
let seen = {}
for candidate in candidates {
if candidate == nil {
continue
}
const key = to_string(candidate.ordinal) + ":" + to_string(candidate.evidence_index)
if seen[key] ?? false {
continue
}
seen = seen + {[key]: true}
selected = selected + [candidate]
}
return selected
}
fn __judge_issue_evidence(issue: AgentTranscriptIssue) -> CompletionEvidenceIssue {
return {
code: __judge_clip_evidence(issue.code, __JUDGE_EVIDENCE_LABEL_CHAR_LIMIT),
record_index: issue.record_index,
message: __judge_clip_evidence(issue.message, __JUDGE_EVIDENCE_LABEL_CHAR_LIMIT),
details: __judge_clip_evidence(issue.details, __JUDGE_EVIDENCE_FIELD_CHAR_LIMIT),
}
}
/**
* Build the bounded evidence packet sent to the completion-judge LLM. The raw
* transcript remains on the canonical payload for deterministic gates, replay,
* and audit. Selection is structural: explicitly read-only calls are omitted;
* explicit mutation and verification roles are retained. Canonical edit and
* workspace-write annotations imply mutation; execute tools must declare a
* completion evidence role instead of being guessed from provider prose or
* process capability.
*
* @effects: []
* @errors: []
* @api_stability: internal
*/
fn __judge_evidence_projection_from_lifecycle(
messages: string | bytes | list | dict | set | range | nil,
lifecycle: AgentTranscriptToolLifecycleReport,
opts: dict,
raw_transcript_digest: any = nil,
) -> CompletionEvidenceProjection {
let relevant_call_count = 0
let omitted_read_only_call_count = 0
let latest_mutation = nil
let latest_verification = nil
let latest_problem = nil
let latest_observation = nil
for call in lifecycle.calls {
const action = __judge_lifecycle_action(call, opts)
if action.semantics.read_only {
omitted_read_only_call_count = omitted_read_only_call_count + 1
latest_observation = __judge_latest(latest_observation, action)
continue
}
relevant_call_count = relevant_call_count + 1
if __judge_descriptor_is_observation(action.semantics) {
latest_observation = __judge_latest(latest_observation, action)
}
if __judge_descriptor_is_mutation(action.semantics) {
latest_mutation = __judge_latest(latest_mutation, action)
}
if __judge_descriptor_is_verifier(action.semantics) {
latest_verification = __judge_latest(latest_verification, action)
}
if action.lifecycle_status != "completed" || action.has_error_result {
latest_problem = __judge_latest(latest_problem, action)
}
}
let issue_tail = lifecycle.issues
if len(issue_tail) > 3 {
issue_tail = issue_tail.slice(len(issue_tail) - 3, len(issue_tail))
}
let latest_issues = []
for issue in issue_tail {
latest_issues = latest_issues + [__judge_issue_evidence(issue)]
}
const selected = __judge_unique_slot_actions(
[latest_mutation, latest_verification, latest_problem],
)
const observation_fallback = if len(selected) == 0 && latest_observation != nil {
[latest_observation]
} else {
[]
}
const raw_digest = raw_transcript_digest
?? ("sha256:" + sha256(json_stringify(messages)))
const packet = {
schema: "harn.completion_judge_evidence.v2",
raw_message_count: len(messages),
raw_transcript_digest: raw_digest,
relevant_action_count: relevant_call_count,
omitted_read_only_call_count: omitted_read_only_call_count,
omitted_older_action_count: max(relevant_call_count - len(selected), 0),
lifecycle_issue_count: lifecycle.invalid_count,
lifecycle_issues: latest_issues,
actions: selected,
fallback_observations: observation_fallback,
}
let selection: list<CompletionEvidenceSelection> = []
for action in selected {
selection = selection
+ [
{
name: action.name,
evidence_role: action.semantics.evidence_role,
evidence_index: action.evidence_index,
},
]
}
const serialized = json_stringify(packet)
return {
packet: packet,
serialized: serialized,
stats: {
schema: "harn.completion_judge_evidence_projection.v1",
raw_message_count: len(messages),
relevant_call_count: relevant_call_count,
included_action_count: len(selected),
included_fallback_observation_count: len(observation_fallback),
omitted_read_only_call_count: omitted_read_only_call_count,
omitted_older_action_count: max(relevant_call_count - len(selected), 0),
lifecycle_issue_count: lifecycle.invalid_count,
serialized_chars: len(serialized),
raw_transcript_digest: packet.raw_transcript_digest,
selected_actions: selection,
},
}
}
pub fn __judge_evidence_projection(
messages: string | bytes | list | dict | set | range | nil,
opts: dict,
raw_transcript_digest: any = nil,
) -> CompletionEvidenceProjection {
return __judge_evidence_projection_from_lifecycle(
messages,
agent_transcript_tool_lifecycle_report(messages),
opts,
raw_transcript_digest,
)
}
fn __judge_knowledge_state(lifecycle: AgentTranscriptToolLifecycleReport, text: string) -> string {
let tool_evidence = []
for call in lifecycle.calls {
tool_evidence = tool_evidence.appending(
{
name: call.name,
tool_call_id: call.tool_call_id,
status: call.status,
results: call.results.map(
{ result -> {outcome: result.outcome, text: result.text, data: result.data} },
)
.to_list(),
},
)
}
return "sha256:" + sha256(json_stringify({latest_text: text, tool_evidence: tool_evidence}))
}
fn __judge_payload(
agent: HarnessAgent,
session: dict,
opts: dict,
stop_reason: string,
text: string,
iteration: int,
raw_text: string = "",
) -> CompletionEvidenceSnapshot {
const messages = agent_session_messages(agent, session.session_id)
const lifecycle = agent_transcript_tool_lifecycle_report(messages)
const tool_names = __session_tool_names(lifecycle)
const successful_tool_names = __session_successful_tool_names(lifecycle)
const transcript = json_stringify(messages)
const evidence = __judge_evidence_projection_from_lifecycle(
messages,
lifecycle,
opts,
"sha256:" + sha256(transcript),
)
const evidence_id = "sha256:"
+ sha256(
json_stringify(
{
task: session?.task ?? "",
trigger: stop_reason,
candidate_text: text,
actions: evidence.packet.actions,
fallback_observations: evidence.packet.fallback_observations,
lifecycle_issues: evidence.packet.lifecycle_issues,
},
),
)
return {
schema: "harn.completion_evidence_snapshot.v1",
evidence_id: evidence_id,
session_id: session.session_id,
task: session?.task ?? "",
stop_reason: stop_reason,
text: text,
visible_text: text,
last_text: text,
raw_text: raw_text == "" ? text : raw_text,
transcript: transcript,
judge_evidence: evidence.serialized,
judge_evidence_projection: evidence.stats,
all_tools_used: join(tool_names, ", "),
successful_tools_used: join(successful_tool_names, ", "),
iteration: iteration,
knowledge_state: __judge_knowledge_state(lifecycle, text),
verification: nil,
}
}
/** Bind a gate reading onto the snapshot the remaining stages will be shown. */
fn __judge_payload_with_verification(
payload: CompletionEvidenceSnapshot,
state: CompletionVerificationState?,
) -> CompletionEvidenceSnapshot {
if state == nil {
return payload
}
return payload
+ {
verification: state
+ {
observed_at_evidence_index: __completion_verification_evidence_index(
payload.judge_evidence_projection.selected_actions,
),
},
}
}
const __COMPLETION_JUDGE_DETAIL_CHAR_LIMIT: int = 240
/**
* __judge_completion_verdict_schema.
*
* One discriminant and one dual-purpose detail keep completion decisions
* inside small-model output budgets. `detail` is the evidence basis for
* `done`, or the single concrete gap and next action for `continue`.
*
* `gap_class` names WHAT a `continue` is refusing on. It is deliberately NOT
* required: the checkpoint validates this schema strictly, and cached, replayed,
* and older-pin judge responses carry no such field. An absent value decodes to
* `other`, which is the reading that grants no authority — so the compatible
* direction and the safe direction are the same direction.
*
* @effects: []
* @errors: []
* @api_stability: experimental
* @example: __judge_completion_verdict_schema()
*/
pub fn __judge_completion_verdict_schema() -> dict {
return {
type: "object",
properties: {
verdict: {type: "string", enum: ["done", "continue"]},
detail: {type: "string", minLength: 1, maxLength: __COMPLETION_JUDGE_DETAIL_CHAR_LIMIT},
gap_class: {
type: "string",
enum: COMPLETION_JUDGE_GAP_CLASSES,
description: "On `continue`, the kind of gap being named. On `done`, use `other`.",
},
},
required: ["verdict", "detail"],
additionalProperties: false,
}
}
/**
* __judge_completion_verdict_read.
*
* Decode a judge response into the compact verdict. The superseded shape
* `{action, reason, repair, ...}` still decodes, because cached and replayed
* judge responses outlive the schema that produced them and a decision read
* as absent would silently become an approval.
*
* @effects: []
* @errors: []
* @api_stability: experimental
* @example: __judge_completion_verdict_read({verdict: "done", detail: "tests pass"})
*/
pub fn __judge_completion_verdict_read(result: dict) -> CompletionJudgeVerdict {
const raw = trim(to_string(result?.verdict ?? result?.action ?? ""))
const verdict = if raw == "accept" {
"done"
} else {
raw
}
// First non-blank wins rather than first non-nil: the superseded shape
// carried an empty `repair` on approval, which would otherwise shadow the
// `reason` that holds the audit basis.
let detail = ""
for candidate in [result?.detail, result?.repair, result?.reason] {
if detail == "" {
detail = trim(to_string(candidate ?? ""))
}
}
const clamped = if len(detail) > __COMPLETION_JUDGE_DETAIL_CHAR_LIMIT {
substring(detail, 0, __COMPLETION_JUDGE_DETAIL_CHAR_LIMIT)
} else {
detail
}
return {
verdict: verdict,
detail: clamped,
gap_class: __completion_judge_gap_class(result?.gap_class),
}
}
fn __judge_stable_prefix(
judge_cfg: any,
opts: dict,
payload: CompletionEvidenceSnapshot,
system: string,
schema: any,
) -> string {
const rubric = trim(to_string(judge_cfg?.rubric ?? opts?.rubric ?? ""))
const rubric_block = if rubric == "" {
""
} else {
"\n\nCompletion rubric:\n" + rubric
}
return system
+ "\n\nStable completion goal:\n"
+ payload.task
+ rubric_block
+ "\n\nRequired verdict schema:\n"
+ json_stringify(schema)
}
fn __judge_invoke_closure(
verify_completion: unknown,
payload: CompletionEvidenceSnapshot,
) -> CompletionStageVerdict {
const result = verify_completion(payload)
if result == nil || result == "" {
return {vetoed: false, confirm: true, source: "deterministic", trigger: "verify_completion"}
}
if type_of(result) == "bool" {
return {vetoed: !result, confirm: result, source: "deterministic", trigger: "verify_completion"}
}
if type_of(result) == "string" {
return {
vetoed: true,
feedback: result,
confirm: false,
source: "deterministic",
trigger: "verify_completion",
}
}
if type_of(result) == "dict" {
const confirm = result?.confirm == true
const message = to_string(result?.message ?? result?.feedback ?? "")
return {
vetoed: !confirm,
feedback: message,
confirm: confirm,
verification: __completion_verification_state_read(result?.verification),
source: "deterministic",
reason: if result?.reason == nil {
nil
} else {
to_string(result.reason)
},
converted_from: if result?.converted_from == nil {
nil
} else {
to_string(result.converted_from)
},
escalation_recommended: if result?.escalation_recommended == nil {
nil
} else {
result.escalation_recommended
},
escalation_target: if result?.escalation_target == nil {
nil
} else {
to_string(result.escalation_target)
},
trigger: to_string(result?.trigger ?? "verify_completion"),
}
}
return {
vetoed: false,
confirm: false,
source: "deterministic",
trigger: "verify_completion",
reason: "completion_verifier_invalid_result",
terminal_judge_reason: "completion_verifier_invalid_result",
terminal_evidence_preserved: true,
}
}
fn __judge_invoke_structured(
harness: Harness,
judge_cfg: JudgeConfig,
opts: dict,
payload: CompletionEvidenceSnapshot,
) -> CompletionStageVerdict {
const system = judge_cfg.system ?? ""
const schema = __judge_completion_verdict_schema()
agent_emit_event(
harness.agent,
payload.session_id,
"typed_checkpoint",
payload.judge_evidence_projection,
)
const stable_prefix = __judge_stable_prefix(judge_cfg, opts, payload, system, schema)
const user = completion_judge_user_prompt(harness.fs, payload)
const ran = __judge_run_checkpoint(
harness,
{
judge_cfg: judge_cfg,
opts: opts,
payload: payload,
checkpoint_name: "agent.completion_judge",
system: stable_prefix,
user: user,
schema: schema,
},
)
const checkpoint = ran.checkpoint
const duration_ms = ran.duration_ms
if !checkpoint.ok {
const unavailable = checkpoint?.status == "admission_refused"
|| checkpoint?.status == "timeout"
|| checkpoint?.error_category == "timeout"
|| checkpoint?.error?.status == "timeout"
if unavailable {
const judge_outcome = if checkpoint?.status == "admission_refused" {
"admission_refused"
} else {
"timeout"
}
const terminal_reason = if judge_outcome == "admission_refused" {
"completion_judge_deadline_admission_refused"
} else {
"completion_judge_deadline_timeout"
}
return {
vetoed: false,
confirm: false,
verdict: "unavailable",
reasoning: to_string(checkpoint.error),
next_step: "",
judge_duration_ms: duration_ms,
source: "llm",
judge_outcome: judge_outcome,
reason: terminal_reason,
terminal_judge_reason: terminal_reason,
terminal_evidence_preserved: true,
admission: ran.admission,
typed_checkpoint: checkpoint,
}
}
if judge_cfg.fail_open_on_error ?? false {
return {
vetoed: false,
verdict: "done",
reasoning: to_string(checkpoint.error),
next_step: "",
judge_duration_ms: duration_ms,
source: "llm",
admission: ran.admission,
typed_checkpoint: checkpoint,
}
}
return {
vetoed: true,
feedback: to_string(checkpoint.error),
verdict: "continue",
reasoning: to_string(checkpoint.error),
next_step: to_string(checkpoint.error),
judge_duration_ms: duration_ms,
source: "llm",
admission: ran.admission,
typed_checkpoint: checkpoint,
}
}
const decoded = __judge_completion_verdict_read(checkpoint.data)
const verdict = decoded.verdict
const detail = decoded.detail
const feedback_default = judge_cfg.feedback_fallback ?? ""
const feedback = if detail == "" {
feedback_default
} else {
detail
}
return {
vetoed: verdict == "continue",
verdict: verdict,
gap_class: decoded.gap_class,
feedback: if verdict == "continue" {
feedback
} else {
nil
},
reasoning: detail,
next_step: if verdict == "continue" {
detail
} else {
""
},
judge_duration_ms: duration_ms,
source: "llm",
admission: ran.admission,
typed_checkpoint: checkpoint,
}
}
/**
* agent_judge_config.
*
* Normalize a `done_judge` or `verify_completion_judge` option. Returns nil when
* no judge can run — absent, `false`, or a value of a type the judge seam
* cannot invoke — and the configuration dict otherwise. `true` normalizes to an
* empty dict, meaning "a judge with default settings".
*
* @effects: []
* @errors: []
* @api_stability: experimental
* @example: agent_judge_config(opts?.done_judge)
*/
pub fn agent_judge_config(judge_cfg: JudgeConfig?) -> JudgeConfig? {
return __normalize_judge_config(judge_cfg)
}
/**
* agent_has_done_judge.
*
* Does this run have a completion judge that can actually run? Every consumer
* asking that question must ask here rather than testing `done_judge != nil`
* itself: the raw test answers `true` for `done_judge: false`, which is a
* different question, and two notions of judge presence in one loop is how a
* judge ends up governing behavior for a run that disabled it.
*
* @effects: []
* @errors: []
* @api_stability: experimental
* @example: agent_has_done_judge(opts?.done_judge)
*/
pub fn agent_has_done_judge(judge_cfg: JudgeConfig?) -> bool {
return agent_judge_config(judge_cfg) != nil
}
fn __done_contract_primary_prompt(opts: dict) {
const marker = to_string(opts?.done_sentinel ?? "the configured done sentinel")
return join(
[
"Before yielding as done, state a concise Done claim immediately before " + marker + ".",
"The claim must cover: requested work, evidence observed, files or artifacts changed, and anything deliberately not done.",
"Do not claim completion from a green self-authored check alone; connect the evidence back to the user's visible request.",
],
"\n",
)
}
pub fn __done_contract_judge_system(opts: dict) -> string {
const extra = trim(to_string(opts?.rubric ?? ""))
let lines = [
"You are a strict done-contract judge for an autonomous agent.",
"Decide whether the latest completion claim is justified by the task, transcript, tool results, and changed artifacts.",
"Return `done` only when the claim covers the visible request and names adequate evidence.",
"Return `continue` when evidence is missing, self-authored tests are too weak, or visible requirements are unaddressed.",
"Judge the substance of the claim, never its wire format. Response markers, sentinels, and layout are enforced elsewhere, and their absence is never a reason to continue.",
"Return only `verdict`, one concise `detail`, and the `gap_class` the gap belongs to.",
"On `done`, use `detail` for the strongest evidence that every requirement is met.",
"On `continue`, use `detail` for the single most important gap and the substantive next action that closes it, never a restatement or formatting instruction.",
]
if extra != "" {
lines = lines.appending("Additional rubric:\n" + extra)
}
return join(lines, "\n")
}
/**
* agent_done_contract.
*
* Build an agent_loop option bundle for a bidirectional done contract:
* the primary agent must state a concise completion claim before yielding, and
* the done judge must return one bounded evidence detail on approval or one
* bounded gap-and-action detail on veto. The helper rides the existing `done_judge`
* seam and context-profile prompt-fragment channel; it does not add a new loop
* hook. Compose it with base options:
*
* ```harn,ignore
* agent_loop(harness, task, nil, base_opts + agent_done_contract({max_invocations: 3}))
* ```
*
* @effects: []
* @errors: [runtime]
* @api_stability: experimental
*/
pub fn agent_done_contract(options: dict = {}) -> dict {
const opts = if type_of(options) == "dict" {
options
} else {
{}
}
const judge = if type_of(opts?.judge) == "dict" {
opts.judge
} else {
{}
}
const max_invocations = opts?.max_invocations ?? judge?.max_invocations ?? 3
const bundle = {
done_judge: judge
+ {
fail_open_on_error: opts?.fail_open_on_error ?? judge?.fail_open_on_error ?? false,
feedback_fallback: opts?.feedback_fallback
?? judge?.feedback_fallback
?? "The done claim is not yet justified; address the specific gaps before finishing.",
max_invocations: max_invocations,
model: opts?.model ?? judge?.model,
provider: opts?.provider ?? judge?.provider,
system: opts?.system ?? judge?.system ?? __done_contract_judge_system(opts),
},
loop_until_done: opts?.loop_until_done ?? true,
}
return __with_prompt_fragment(
bundle,
{body: __done_contract_primary_prompt(opts), id: "done_contract", source: "std/agent/judge"},
)
}
fn __judge_directive(
verdict: dict,
payload: dict,
invoked: any,
completion_judge_cap_reached: any,
done_judge_cap_reached: any,
feedback_composition: any,
feedback_history: dict,
defer_feedback: bool,
) -> CompletionDirective {
const terminal_reason = trim(to_string(verdict?.terminal_judge_reason ?? ""))
const reason = if terminal_reason != "" {
terminal_reason
} else if completion_judge_cap_reached {
"completion_judge_cap_reached"
} else if done_judge_cap_reached {
"done_judge_cap_reached"
} else {
to_string(verdict?.reason ?? verdict?.reasoning ?? "")
}
const action = if terminal_reason != ""
|| completion_judge_cap_reached
|| done_judge_cap_reached {
"stop_unverified"
} else if verdict.vetoed {
"continue"
} else {
"accept"
}
const has_feedback = verdict.vetoed
&& trim(to_string(verdict?.feedback ?? "")) != ""
const feedback_delivery = if len(feedback_composition?.messages ?? []) > 0 {
"delivered"
} else if has_feedback && defer_feedback {
"deferred"
} else {
"none"
}
const receipt = {
schema: "harn.completion_directive_receipt.v1",
evidence_id: payload.evidence_id,
action: action,
trigger: verdict?.trigger ?? payload.stop_reason,
source: verdict?.source ?? "policy",
outcome: verdict?.judge_outcome ?? verdict?.verdict ?? action,
reason: reason,
invoked: invoked,
feedback_delivery: feedback_delivery,
projection: payload.judge_evidence_projection,
converted_from: verdict?.converted_from,
gap_class: verdict?.gap_class,
verification: payload.verification,
admission: verdict?.admission,
checkpoint: verdict?.typed_checkpoint,
}
return {
action: action,
evidence_id: payload.evidence_id,
feedback: verdict?.feedback,
repair: verdict?.next_step,
receipt: receipt,
feedback_history: feedback_history,
}
}
fn __completion_judge_applies(stop_reason: string, due: bool) -> bool {
if !due {
return false
}
return contains(["sentinel", "natural", "stalled"], stop_reason)
}
/** Build the one ordered plan for a completion boundary. */
fn __completion_policy_plan(
opts: dict,
stop_reason: string,
review: CompletionReview = {scrutiny: "standard"},
) -> list<CompletionPolicyStage> {
let stages: list<CompletionPolicyStage> = []
if opts?.verify_completion != nil {
stages = stages + [CompletionPolicyStage.Deterministic(opts.verify_completion)]
}
const verification_config = agent_judge_config(opts?.verify_completion_judge)
if verification_config != nil {
stages = stages
+ [
CompletionPolicyStage.ModelJudge(
CompletionJudgeSlot.Verification(),
verification_config,
opts?._verify_completion_judge_trigger,
opts?._verify_completion_judge_invocations ?? 0,
agent_verify_completion_judge_cap(verification_config, review),
),
]
}
const completion_config = agent_judge_config(opts?.done_judge)
if completion_config != nil
&& __completion_judge_applies(stop_reason, opts?._done_judge_due ?? true) {
stages = stages
+ [
CompletionPolicyStage.ModelJudge(
CompletionJudgeSlot.Completion(),
completion_config,
opts?._done_judge_trigger,
opts?._done_judge_invocations ?? 0,
agent_done_judge_cap(completion_config),
),
]
}
return stages
}
/**
* Whether this run declares anything that could adjudicate a completion at
* `stop_reason`.
*
* A caller that is about to end the run and wants the turn adjudicated first
* needs to know whether asking is worth a provider call — and, more importantly,
* whether an answer it gets back means anything. An evaluation with no stages is
* not vetoed, so the directive seals `accept`: silence read as approval. Ask
* this before evaluating, and treat a false as "nobody was asked".
*
* The plan is the single owner of what counts as an adjudicator; this only
* reports whether it produced any stages.
*
* @effects: []
* @errors: []
* @api_stability: experimental
* @example: agent_completion_adjudicator_declared(opts, "stalled")
*/
pub fn agent_completion_adjudicator_declared(opts: dict, stop_reason: string) -> bool {
return len(__completion_policy_plan(opts, stop_reason)) > 0
}
fn __completion_policy_initial_evaluation(
payload: CompletionEvidenceSnapshot,
) -> CompletionPolicyEvaluation {
return {
verdict: {vetoed: false},
invoked: {deterministic: false, verify_completion_judge: false, done_judge: false},
verification_judge_cap_reached: false,
completion_judge_cap_reached: false,
payload: payload,
deterministic_reason: "",
}
}
fn __completion_policy_can_advance(evaluation: CompletionPolicyEvaluation) -> bool {
return !evaluation.verdict.vetoed
&& trim(to_string(evaluation.verdict.terminal_judge_reason ?? ""))
== ""
&& !evaluation.verification_judge_cap_reached
&& !evaluation.completion_judge_cap_reached
}
fn __completion_policy_mark_cap(
evaluation: CompletionPolicyEvaluation,
slot: CompletionJudgeSlot,
) -> CompletionPolicyEvaluation {
match slot {
Verification() -> { return evaluation + {verification_judge_cap_reached: true} }
Completion() -> { return evaluation + {completion_judge_cap_reached: true} }
}
}
fn __completion_policy_mark_model_invoked(
evaluation: CompletionPolicyEvaluation,
slot: CompletionJudgeSlot,
invoked: bool,
) -> CompletionPolicyEvaluation {
const prior = evaluation.invoked
match slot {
Verification() -> { return evaluation
+ {invoked: prior + {verify_completion_judge: invoked}} }
Completion() -> { return evaluation + {invoked: prior + {done_judge: invoked}} }
}
}
const __COMPLETION_VERIFICATION_CONVERSION: string = "failed_verification_contradicted_by_gate"
/**
* 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.
*/
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,
}
}
fn __completion_policy_run_stage(
harness: Harness,
opts: dict,
iteration: int,
evaluation: CompletionPolicyEvaluation,
stage: CompletionPolicyStage,
) -> CompletionPolicyEvaluation {
const payload = evaluation.payload
match stage {
Deterministic(check) -> {
const verdict = __judge_invoke_closure(check, payload)
__judge_emit_decision(harness.agent, payload.session_id, iteration, verdict)
return evaluation
+ {
verdict: verdict,
invoked: evaluation.invoked + {deterministic: true},
payload: __judge_payload_with_verification(payload, verdict.verification),
deterministic_reason: to_string(verdict.reason ?? ""),
}
}
ModelJudge(slot, config, trigger, prior_invocations, max_invocations) -> {
if max_invocations != nil && prior_invocations >= max_invocations {
return __completion_policy_mark_cap(evaluation, slot)
}
// Decide whether the LLM confirmation is needed BEFORE announcing that a
// judge started. A skipped judge never ran, so emitting a started event
// for it would put a call in the timeline that no decision ever answers.
match slot {
Verification() -> {
if agent_completion_review_should_skip_llm(
agent_completion_review(harness.llm, opts),
evaluation,
payload,
) {
const skipped = {
vetoed: false,
verdict: "done",
source: "catalog",
judge_outcome: "skipped_light_scrutiny",
judge_duration_ms: 0,
trigger: trigger,
}
__judge_emit_decision(harness.agent, payload.session_id, iteration, skipped)
return evaluation + {verdict: skipped}
}
}
Completion() -> {
}
}
__judge_emit_started(harness.agent, payload.session_id, iteration, trigger)
let verdict = __judge_invoke_structured(harness, config, opts, payload)
verdict = __completion_arbitrate_verification(verdict, evaluation) + {trigger: trigger}
__judge_emit_decision(harness.agent, payload.session_id, iteration, verdict)
const next = evaluation + {verdict: verdict}
return __completion_policy_mark_model_invoked(
next,
slot,
verdict.judge_outcome != "admission_refused",
)
}
}
}
fn __completion_policy_evaluate(
harness: Harness,
opts: dict,
payload: CompletionEvidenceSnapshot,
iteration: int,
stages: list<CompletionPolicyStage>,
) -> CompletionPolicyEvaluation {
let evaluation = __completion_policy_initial_evaluation(payload)
for stage in stages {
if !__completion_policy_can_advance(evaluation) {
break
}
evaluation = __completion_policy_run_stage(harness, opts, iteration, evaluation, stage)
}
return evaluation
}
/**
* Evaluate one verification/completion boundary and return one typed directive.
*
* `text` is the projected candidate turn — what a reader should be shown.
* `raw_text` is that same turn before projection, and is the field a policy
* must read to decide whether the model declared completion: the parser has
* already stripped completion markers out of `text`. Callers with the
* unprojected turn in hand should pass it; omitting it leaves `raw_text`
* equal to `text`, which preserves the pre-existing behavior.
*
* @effects: [agent, host, llm.call]
* @errors: []
*/
pub fn agent_evaluate_completion(
harness: Harness,
session: dict,
opts: dict,
stop_reason: string,
text: string,
iteration: int = 0,
raw_text: string = "",
) -> CompletionDirective {
const payload = __judge_payload(
harness.agent,
session,
opts,
stop_reason,
text,
iteration,
raw_text,
)
const stages = __completion_policy_plan(
opts,
stop_reason,
agent_completion_review(harness.llm, opts),
)
const evaluation = __completion_policy_evaluate(harness, opts, payload, iteration, stages)
const verdict = evaluation.verdict
// The evaluated snapshot, not the one built before any stage ran: it is the
// one carrying whatever the deterministic gate observed.
const evaluated = evaluation.payload
let feedback_composition = nil
if verdict.vetoed
&& verdict.feedback != nil
&& verdict.feedback != ""
&& !(opts?._defer_judge_feedback ?? false) {
feedback_composition = agent_feedback_emit(
harness.agent,
session.session_id,
[
{
kind: "verify_completion",
content: verdict.feedback,
source: verdict.trigger ?? "completion_judge",
reason: "judge_veto",
action: "continue",
next_step: verdict.next_step ?? "",
},
],
{history: opts?._feedback_history ?? {}, knowledge_state: evaluated.knowledge_state},
)
}
const directive = __judge_directive(
verdict,
evaluated,
evaluation.invoked,
evaluation.verification_judge_cap_reached,
evaluation.completion_judge_cap_reached,
feedback_composition,
feedback_composition?.history ?? opts?._feedback_history ?? {},
opts?._defer_judge_feedback ?? false,
)
agent_emit_event(harness.agent, session.session_id, "typed_checkpoint", directive.receipt)
return directive
}