/**
* src/gates/output-contracts.ts — output contract data + validation gates,
* ported verbatim from the ZOB harness delegation output contracts.
*
* Contract data and validation logic are PURE. `readReferencedArtifacts`
* performs fs reads (STATEFUL) but only when a `repoRoot` is supplied, keeping
* the pure validation path fs-free and testable. `applyChildGates` mutates the
* ChildResult in place, matching the harness exactly. Zero @earendil-works/*.
*/
import { existsSync, readFileSync, statSync } from "node:fs";
import { extname, resolve, sep } from "node:path";
import type { ChildResult, OutputGateIssue, OutputGateIssueCode } from "../core/types.js";
import type { OutputContract, OutputRequirement } from "./types.js";
export const OUTPUT_GATE_ISSUE_CODES = [
"empty_output",
"unknown_contract",
"invalid_requirement_pattern",
"missing_final_marker",
"missing_requirement",
"deliverable_rejected",
"mismatched_todo_id",
"stale_child_goal_binding",
"mismatched_delegation_attempt",
] as const satisfies readonly OutputGateIssueCode[];
const COMMON_OUTPUT_REQUIREMENTS: OutputRequirement[] = [
{
name: "deliverable_delivered",
pattern: "deliverable_delivered\\s*:\\s*(yes|no)|\\s*(yes|no)\\s*",
message: "Missing required final marker: deliverable_delivered: yes/no",
},
{
name: "evidence",
pattern: "|\\bevidence\\b|proof",
message: "Missing evidence/proof section",
},
{
name: "risks_blockers",
pattern: "|risks?/blockers?|risks?|blockers?",
message: "Missing risks/blockers section",
},
{
name: "compliance",
pattern: "|\\bcompliance\\b|must not",
message: "Missing compliance line",
},
];
const OUTPUT_CONTRACTS: OutputContract[] = [
{
id: "base.v1",
description: "Base ZOB delegated output: deliverable marker, evidence, risks/blockers, compliance.",
agentNames: [],
required: COMMON_OUTPUT_REQUIREMENTS,
},
{
id: "explore.v1",
description: "Read-only exploration with reframing, files, answer, gaps, and next steps.",
agentNames: ["explore", "refactor-cartographer"],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "literal_request", pattern: "|literal request|literal_request", message: "Explore output missing literal_request" },
{ name: "actual_need", pattern: "|actual need|actual_need", message: "Explore output missing actual_need" },
{ name: "success_looks_like", pattern: "|success looks like|success_looks_like", message: "Explore output missing success_looks_like" },
{ name: "files", pattern: "|\\bfiles\\b", message: "Explore output missing files section" },
{ name: "answer", pattern: "|\\banswer\\b", message: "Explore output missing answer section" },
{ name: "gaps", pattern: "|\\bgaps\\b", message: "Explore output missing gaps section" },
{ name: "next_steps", pattern: "|next steps|next_steps", message: "Explore output missing next_steps section" },
],
},
{
id: "plan.v1",
description: "Planning output with scope, assumptions, validation ladder, stop conditions, and handoff contract.",
agentNames: ["planner", "plan", "chief-vision"],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "scope", pattern: "scope|in-scope|out-of-scope|forbidden", message: "Plan output missing scope table" },
{ name: "assumptions", pattern: "assumptions?", message: "Plan output missing assumptions" },
{ name: "implementation_slices", pattern: "implementation (steps|slices)|slices?", message: "Plan output missing implementation slices/steps" },
{ name: "validation_ladder", pattern: "validation ladder|validation", message: "Plan output missing validation ladder" },
{ name: "stop_conditions", pattern: "stop conditions?", message: "Plan output missing stop conditions" },
{ name: "handoff_contract", pattern: "handoff|TASK:|EXPECTED OUTCOME:|MUST NOT", message: "Plan output missing implementer handoff contract" },
],
},
{
id: "implement.v1",
description: "Bounded implementation report with gap verdict, changed files/no-change evidence, commands, and results.",
agentNames: ["implementer", "implement", "refactor-mover"],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "gap_verdict", pattern: "gap verdict|SUFFICIENT|GAP|no change|no-change", message: "Implement output missing gap/no-change verdict" },
{ name: "changed_files", pattern: "changed files|no change|no-change", message: "Implement output missing changed files or no-change evidence" },
{ name: "verification_commands", pattern: "verification|commands?", message: "Implement output missing verification commands" },
{ name: "results", pattern: "results?|exit code|passed|failed", message: "Implement output missing command/results evidence" },
],
},
{
id: "oracle.v1",
description: "Skeptical review with verdict, confidence, blockers, evidence, and no_ship decision.",
agentNames: ["oracle", "refactor-oracle"],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "verdict", pattern: "\\s*(PASS|FAIL|WARN)\\s*|\\b(PASS|FAIL|WARN)\\b", message: "Oracle output missing PASS/FAIL/WARN verdict" },
{ name: "confidence", pattern: "|\\bconfidence\\b", message: "Oracle output missing confidence" },
{ name: "blocking_issues", pattern: "|blocking issues?|blockers?", message: "Oracle output missing blocking issues" },
{ name: "non_blocking_notes", pattern: "|non[-_ ]blocking", message: "Oracle output missing non-blocking notes" },
{ name: "no_ship", pattern: "|no_ship|no ship", message: "Oracle output missing no_ship decision" },
],
},
{
id: "qa.v1",
description: "QA verification with verdict, commands, exit/output evidence, and reproduction details.",
agentNames: ["qa"],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "verdict", pattern: "\\b(PASS|FAIL|WARN|INCONCLUSIVE)\\b|verdict", message: "QA output missing verification verdict" },
{ name: "command", pattern: "commands?|cwd", message: "QA output missing command/cwd evidence" },
{ name: "exit_or_output", pattern: "exit code|output|stdout|stderr|important output", message: "QA output missing exit/output evidence" },
{ name: "reproduction", pattern: "reproduction|reproduce|steps", message: "QA output missing reproduction steps" },
],
},
{
id: "synthesis.v1",
description: "Parallel-lane synthesis with consensus, conflicts, missing evidence, next action, and rerun tasks.",
agentNames: ["synthesis"],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "consensus", pattern: "|\\bconsensus\\b", message: "Synthesis output missing consensus section" },
{ name: "conflicts", pattern: "|conflicts?", message: "Synthesis output missing conflicts section" },
{ name: "missing_evidence", pattern: "|missing_evidence|missing evidence", message: "Synthesis output missing missing_evidence section" },
{ name: "recommended_next_action", pattern: "|recommended_next_action|recommended next action", message: "Synthesis output missing recommended_next_action section" },
{ name: "tasks_to_rerun", pattern: "|tasks_to_rerun|tasks to rerun|rerun", message: "Synthesis output missing tasks_to_rerun section" },
],
},
{
id: "oracle-merge.v1",
description: "Merged review verdict with confidence, no_ship, blockers, evidence, and lane summary.",
agentNames: ["oracle-merge"],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "verdict", pattern: "\\s*(PASS|FAIL|WARN)\\s*|\\b(PASS|FAIL|WARN)\\b", message: "Oracle merge output missing PASS/FAIL/WARN verdict" },
{ name: "confidence", pattern: "|\\bconfidence\\b", message: "Oracle merge output missing confidence" },
{ name: "no_ship", pattern: "|no_ship|no ship", message: "Oracle merge output missing no_ship decision" },
{ name: "blocking_issues", pattern: "|blocking issues?|blockers?", message: "Oracle merge output missing blocking issues" },
{ name: "merged_lanes", pattern: "|merged_lanes|merged lanes", message: "Oracle merge output missing merged_lanes" },
],
},
{
id: "factory.v1",
description: "Factory design with manifest, schema, validators, pilot, sentinel, and resume strategy.",
agentNames: ["factory"],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "manifest", pattern: "manifest", message: "Factory output missing manifest" },
{ name: "schema", pattern: "schema", message: "Factory output missing schema" },
{ name: "validators", pattern: "validators?|validation gates?", message: "Factory output missing validators" },
{ name: "pilot", pattern: "pilot|smoke", message: "Factory output missing pilot plan/result" },
{ name: "sentinel", pattern: "sentinel", message: "Factory output missing sentinel strategy" },
{ name: "resume", pattern: "resume|checkpoint", message: "Factory output missing resume/checkpoint strategy" },
],
},
{
id: "research.v1",
description: "Sourced research with sources, unknowns, recommendation, evidence, risks, and compliance.",
agentNames: ["librarian", "research"],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "sources", pattern: "sources?|sources_consulted|URLs?|paths?", message: "Research output missing sources" },
{ name: "unknowns", pattern: "unknowns?|uncertainties", message: "Research output missing unknowns/uncertainties" },
{ name: "recommendation", pattern: "recommendation", message: "Research output missing recommendation" },
],
},
{
id: "brain-lookup.v1",
description: "Context/GBrain P0 lookup result with explicit scope, brain/source ids, citations, freshness, confidence, and no body storage.",
agentNames: [],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "context_scope", pattern: "context_scope|context scope", message: "Brain lookup output missing context_scope" },
{ name: "brain_id", pattern: "brain_id|brainId|brain id", message: "Brain lookup output missing brain_id" },
{ name: "source_id", pattern: "source_id|sourceId|source id", message: "Brain lookup output missing source_id" },
{ name: "citations", pattern: "citations?|source refs?", message: "Brain lookup output missing citations" },
{ name: "facts_or_patterns", pattern: "facts_or_patterns|facts|patterns", message: "Brain lookup output missing facts_or_patterns" },
{ name: "gaps", pattern: "gaps|source gaps", message: "Brain lookup output missing gaps" },
{ name: "freshness", pattern: "freshness|stale", message: "Brain lookup output missing freshness" },
{ name: "confidence", pattern: "confidence", message: "Brain lookup output missing confidence" },
{ name: "no_body_storage", pattern: "no_body_storage|bodyStored\\s*[:=]\\s*false|no body storage", message: "Brain lookup output missing no_body_storage" },
],
},
{
id: "context-pack.v1",
description: "Bounded Context/GBrain P0 context pack with source locks, citations, loading rules, agent profile map, and no body storage.",
agentNames: [],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "pack_id", pattern: "pack_id|packId|pack id", message: "Context pack output missing pack_id" },
{ name: "context_scope", pattern: "context_scope|context scope", message: "Context pack output missing context_scope" },
{ name: "source_locks", pattern: "source_locks|sourceLocks|source locks", message: "Context pack output missing source_locks" },
{ name: "loading_rules", pattern: "loading_rules|loadingRules|loading rules", message: "Context pack output missing loading_rules" },
{ name: "agent_profile_map", pattern: "agent_profile_map|agentProfileMap|agent profile map", message: "Context pack output missing agent_profile_map" },
{ name: "citations", pattern: "citations?|source refs?", message: "Context pack output missing citations" },
{ name: "budget_limits", pattern: "budget_limits|budgetLimits|context limits", message: "Context pack output missing budget_limits" },
{ name: "no_body_storage", pattern: "no_body_storage|bodyStored\\s*[:=]\\s*false|no body storage", message: "Context pack output missing no_body_storage" },
],
},
{
id: "context-writeback-proposal.v1",
description: "Context/GBrain P0 learning writeback proposal. Proposal only; no auto-promotion or GBrain write.",
agentNames: [],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "observed_problem", pattern: "observed_problem|observed problem", message: "Context writeback proposal missing observed_problem" },
{ name: "new_pattern", pattern: "new_pattern|new pattern", message: "Context writeback proposal missing new_pattern" },
{ name: "evidence_refs", pattern: "evidence_refs|evidence refs|evidence", message: "Context writeback proposal missing evidence_refs" },
{ name: "recommended_artifact", pattern: "recommended_artifact|recommended artifact", message: "Context writeback proposal missing recommended_artifact" },
{ name: "promotion_requires", pattern: "promotion_requires|promotion requires|oracle_PASS|human_approval|smoke_proof", message: "Context writeback proposal missing promotion_requires" },
{ name: "auto_promote", pattern: "auto_promote\\s*[:=]\\s*false|autoPromote\\s*[:=]\\s*false", message: "Context writeback proposal missing auto_promote=false" },
],
},
{
id: "context-steward.v1",
description: "Context Steward P0 output with cited context hints, source gaps, stale/missing context warnings, and writeback candidates only.",
agentNames: ["context-steward"],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "context_scope", pattern: "context_scope|context scope", message: "Context steward output missing context_scope" },
{ name: "context_hints", pattern: "context_hints|context hints", message: "Context steward output missing context_hints" },
{ name: "citations", pattern: "citations?|source refs?", message: "Context steward output missing citations" },
{ name: "source_gaps", pattern: "source_gaps|source gaps", message: "Context steward output missing source_gaps" },
{ name: "writeback_candidates", pattern: "writeback_candidates|writeback candidates", message: "Context steward output missing writeback_candidates" },
{ name: "parent_owned", pattern: "parent_owned|parent-owned", message: "Context steward output missing parent_owned" },
{ name: "no_plan_mutation", pattern: "no_plan_mutation|no plan mutation", message: "Context steward output missing no_plan_mutation" },
],
},
{
id: "spec.v1",
description: "Product/factory specification with scope, constraints, acceptance criteria, and planner handoff.",
agentNames: ["specifier", "spec"],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "problem", pattern: "|\\bproblem\\b", message: "Spec output missing problem" },
{ name: "context", pattern: "|\\bcontext\\b", message: "Spec output missing context" },
{ name: "objectives", pattern: "|objectives?", message: "Spec output missing objectives" },
{ name: "non_goals", pattern: "|non[-_ ]goals?", message: "Spec output missing non_goals" },
{ name: "in_scope", pattern: "|in[-_ ]scope", message: "Spec output missing in_scope" },
{ name: "out_of_scope", pattern: "|out[-_ ]of[-_ ]scope", message: "Spec output missing out_of_scope" },
{ name: "constraints", pattern: "|constraints?", message: "Spec output missing constraints" },
{ name: "acceptance_criteria", pattern: "|acceptance criteria|acceptance_criteria", message: "Spec output missing acceptance_criteria" },
{ name: "open_questions", pattern: "|open questions|open_questions", message: "Spec output missing open_questions" },
{ name: "handoff_to_planner", pattern: "|handoff to planner|handoff_to_planner|planner", message: "Spec output missing handoff_to_planner" },
],
},
{
id: "clarification.v1",
description: "Clarification gate with clarity score, verdict, allow_plan, guided questions, and minimum-to-plan.",
agentNames: ["clarifier"],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "clarity_score", pattern: "|clarity_score|clarity score", message: "Clarification output missing clarity_score" },
{ name: "verdict", pattern: "\\s*(CLEAR|NEEDS_CLARIFICATION|BLOCKED)\\s*|\\b(CLEAR|NEEDS_CLARIFICATION|BLOCKED)\\b", message: "Clarification output missing CLEAR/NEEDS_CLARIFICATION/BLOCKED verdict" },
{ name: "allow_plan", pattern: "\\s*(yes|no)\\s*|allow_plan\\s*[:=]\\s*(yes|no)", message: "Clarification output missing allow_plan yes/no" },
{ name: "ambiguities", pattern: "|ambiguities", message: "Clarification output missing ambiguities" },
{ name: "questions", pattern: "|questions?", message: "Clarification output missing questions" },
{ name: "assumptions", pattern: "|assumptions?", message: "Clarification output missing assumptions" },
{ name: "refined_spec", pattern: "|refined spec|refined_spec", message: "Clarification output missing refined_spec" },
{ name: "minimum_to_plan", pattern: "|minimum to plan|minimum_to_plan", message: "Clarification output missing minimum_to_plan" },
{ name: "acceptance_criteria", pattern: "|acceptance criteria|acceptance_criteria", message: "Clarification output missing acceptance_criteria" }
],
},
{
id: "launch-authorization.v1",
description: "Launch authorization envelope: spec lock plus explicit user launch bounds in-scope autonomous actions without storing raw bodies.",
agentNames: [],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "schema", pattern: "zob\\.launch-authorization\\.v1|launch-authorization\\.v1", message: "Launch authorization output missing schema" },
{ name: "spec_locked", pattern: "specLocked\\s*[:=]\\s*true|spec_locked\\s*[:=]\\s*true", message: "Launch authorization output missing specLocked=true" },
{ name: "user_launch_confirmed", pattern: "userLaunchConfirmed|user_launch_confirmed", message: "Launch authorization output missing user launch confirmation field" },
{ name: "allowed_actions", pattern: "allowedActions|allowed_actions|allowed actions", message: "Launch authorization output missing allowed actions" },
{ name: "allowed_paths", pattern: "allowedPaths|allowed_paths|allowed paths", message: "Launch authorization output missing allowed paths" },
{ name: "forbidden_paths", pattern: "forbiddenPaths|forbidden_paths|forbidden paths", message: "Launch authorization output missing forbidden paths" },
{ name: "apply_policy", pattern: "applyPolicy|apply_policy|apply policy", message: "Launch authorization output missing apply policy" },
{ name: "stop_conditions", pattern: "stopConditions|stop_conditions|stop conditions", message: "Launch authorization output missing stop conditions" },
{ name: "body_free", pattern: "bodyStored\\s*[:=]\\s*false|body-free|hash-only", message: "Launch authorization output missing body-free posture" },
],
},
{
id: "mission-readiness.v1",
description: "Interactive autonomy mission-readiness verdict with score, decision, safety gates, launch posture, and hash-only input metadata.",
agentNames: [],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "schema", pattern: "zob\\.mission-readiness\\.v1|mission-readiness\\.v1", message: "Mission readiness output missing schema" },
{ name: "mode", pattern: "mode|open|controlled|adaptive", message: "Mission readiness output missing mode" },
{ name: "decision", pattern: "auto_launch|clarify|block|stopped|decision", message: "Mission readiness output missing decision" },
{ name: "score", pattern: "score|readiness", message: "Mission readiness output missing score" },
{ name: "safety_gates", pattern: "safetyGates|safety_gates|no[- ]secrets|no[- ]destructive", message: "Mission readiness output missing safety gates" },
{ name: "manual_per_action_approval", pattern: "manualPerActionApproval|manual_per_action|per-action", message: "Mission readiness output missing manual per-action approval posture" },
{ name: "launch_authorization", pattern: "launchAuthorization|launch_authorization|launch authorized|launch=", message: "Mission readiness output missing launch authorization posture" },
{ name: "body_free", pattern: "rawInputStored\\s*[:=]\\s*false|bodyStored\\s*[:=]\\s*false|body-free|hash-only", message: "Mission readiness output missing body-free posture" },
],
},
{
id: "agent-event.v1",
description: "Typed parent-visible agent event with hash-only body/output refs, evidence, and no direct TODO mutation.",
agentNames: [],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "schema", pattern: "zob\\.agent-event\\.v1|agent-event\\.v1|zob\\.goal-room-message\\.v1", message: "Agent event output missing schema" },
{ name: "kind", pattern: "STATUS_UPDATE|FINDING|ACTION_TAKEN|ARTIFACT_READY|TODO_CLAIM|BLOCKER|RISK|CONTEXT_REQUEST|SPLIT_REQUEST|DELEGATION_REQUEST|ORACLE_REQUEST|HANDOFF|DECISION", message: "Agent event output missing typed kind" },
{ name: "body_hash", pattern: "bodyHash|body_hash", message: "Agent event output missing body hash" },
{ name: "parent_visible", pattern: "parentVisible\\s*[:=]\\s*true|parent_visible\\s*[:=]\\s*true", message: "Agent event output missing parent-visible posture" },
{ name: "parent_owned_actions", pattern: "parentOwnedActions\\s*[:=]\\s*true|parent_owned_actions\\s*[:=]\\s*true", message: "Agent event output missing parent-owned action posture" },
{ name: "no_direct_todo_mutation", pattern: "reducerRequiredForTodoMutation|no direct TODO mutation|no_direct_todo_mutation", message: "Agent event output missing reducer/no-direct-TODO-mutation guard" },
{ name: "body_free", pattern: "bodyStored\\s*[:=]\\s*false|body-free|hash-only", message: "Agent event output missing body-free posture" },
],
},
{
id: "prompt-pack.v1",
description: "Role-scoped prompt/context pack with bounded docs, skill refs, output contract, event policy, and body-free posture.",
agentNames: [],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "schema", pattern: "zob\\.prompt-pack\\.v1|prompt-pack\\.v1", message: "Prompt pack output missing schema" },
{ name: "role_id", pattern: "roleId|role_id|role id", message: "Prompt pack output missing role id" },
{ name: "prompt_refs", pattern: "promptRefs|prompt_refs|prompt refs", message: "Prompt pack output missing prompt refs" },
{ name: "skill_refs", pattern: "skillRefs|skill_refs|skill refs", message: "Prompt pack output missing skill refs" },
{ name: "doc_refs", pattern: "docRefs|doc_refs|doc refs", message: "Prompt pack output missing doc refs" },
{ name: "output_contract", pattern: "outputContract|output_contract|output contract", message: "Prompt pack output missing output contract" },
{ name: "event_policy", pattern: "eventPolicy|event_policy|agent-event", message: "Prompt pack output missing event policy" },
{ name: "body_free", pattern: "bodyStored\\s*[:=]\\s*false|body-free|hash-only", message: "Prompt pack output missing body-free posture" },
],
},
{
id: "lead-plan.v1",
description: "Lead plan with worker contracts, risks, evidence needs, no-ship criteria, and model classes.",
agentNames: [],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "lead_id", pattern: "lead_id|leadId|lead id", message: "Lead plan output missing lead_id" },
{ name: "phase", pattern: "phase", message: "Lead plan output missing phase" },
{ name: "worker_contracts", pattern: "worker_contracts|workerContracts|worker contracts", message: "Lead plan output missing worker_contracts" },
{ name: "required_tools", pattern: "required_tools|requiredTools|required tools", message: "Lead plan output missing required_tools" },
{ name: "allowed_paths", pattern: "allowed_paths|allowedPaths|allowed paths", message: "Lead plan output missing allowed_paths" },
{ name: "forbidden_paths", pattern: "forbidden_paths|forbiddenPaths|forbidden paths", message: "Lead plan output missing forbidden_paths" },
{ name: "output_contract", pattern: "output_contract|outputContract|output contract", message: "Lead plan output missing output_contract" },
{ name: "model_class", pattern: "model_class|modelClass|model class", message: "Lead plan output missing model_class" },
{ name: "evidence_needed", pattern: "evidence_needed|evidenceNeeded|evidence needed", message: "Lead plan output missing evidence_needed" },
{ name: "no_ship_criteria", pattern: "no_ship_criteria|noShipCriteria|no ship criteria", message: "Lead plan output missing no_ship_criteria" },
],
},
{
id: "todo-child-result.v1",
description: "Parent-owned delegated TODO claim with child goal status, evidence, validation commands, risks, no_ship, and final marker.",
agentNames: [],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "todo_id", pattern: "todo_id|TODO_ID|todo id", message: "TODO child result missing todo_id" },
{ name: "child_goal_status", pattern: "child_goal_status\\s*[:=]\\s*(ready_for_oracle|incomplete|blocked)|child goal status", message: "TODO child result missing child_goal_status" },
{ name: "status_claim", pattern: "status_claim\\s*[:=]\\s*(done|incomplete|blocked)|status claim", message: "TODO child result missing status_claim" },
{ name: "evidence_refs", pattern: "evidence_refs|evidence refs|evidence", message: "TODO child result missing evidence_refs" },
{ name: "validation_commands", pattern: "validation_commands|validation commands|commands", message: "TODO child result missing validation_commands" },
{ name: "subtodo_delta_proposals", pattern: "subtodo_delta_proposals|subtodo delta proposals", message: "TODO child result missing subtodo_delta_proposals" },
{ name: "no_ship", pattern: "no_ship|no ship", message: "TODO child result missing no_ship" },
{ name: "final_marker", pattern: "FINAL_MARKER\\s*:\\s*TODO_CHILD_RESULT_END|TODO_CHILD_RESULT_END", message: "TODO child result missing FINAL_MARKER: TODO_CHILD_RESULT_END" },
],
},
{
id: "todo-child-result.v2",
description: "Parent-owned delegated TODO claim v2; no_ship is advisory/readiness evidence and does not make a delivered child result a runtime failure.",
agentNames: [],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "todo_id", pattern: "todo_id|TODO_ID|todo id", message: "TODO child result missing todo_id" },
{ name: "child_goal_status", pattern: "child_goal_status\\s*[:=]\\s*(ready_for_oracle|incomplete|blocked)|child goal status", message: "TODO child result missing child_goal_status" },
{ name: "status_claim", pattern: "status_claim\\s*[:=]\\s*(done|incomplete|blocked)|status claim", message: "TODO child result missing status_claim" },
{ name: "evidence_refs", pattern: "evidence_refs|evidence refs|evidence", message: "TODO child result missing evidence_refs" },
{ name: "validation_commands", pattern: "validation_commands|validation commands|commands", message: "TODO child result missing validation_commands" },
{ name: "acceptance_blockers", pattern: "acceptance_blockers|acceptance blockers", message: "TODO child result v2 missing acceptance_blockers" },
{ name: "target_readiness", pattern: "target_readiness\\s*[:=]\\s*(ready_for_parent_acceptance|needs_parent_review|blocked)|target readiness", message: "TODO child result v2 missing target_readiness" },
{ name: "subtodo_delta_proposals", pattern: "subtodo_delta_proposals|subtodo delta proposals", message: "TODO child result missing subtodo_delta_proposals" },
{ name: "no_ship", pattern: "no_ship|no ship", message: "TODO child result missing no_ship" },
{ name: "final_marker", pattern: "FINAL_MARKER\\s*:\\s*(TODO_CHILD_RESULT_V2_END|TODO_CHILD_RESULT_END)|TODO_CHILD_RESULT_V2_END|TODO_CHILD_RESULT_END", message: "TODO child result missing FINAL_MARKER: TODO_CHILD_RESULT_V2_END" },
],
},
{
id: "todo-claim-validation.v1",
description: "Oracle validation of a returned delegated TODO claim; parent runtime may auto-accept only on PASS and no_ship=false.",
agentNames: [],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "todo_id", pattern: "todo_id|TODO_ID|todo id", message: "TODO claim validation missing todo_id" },
{ name: "claim_hash", pattern: "claim_hash\\s*[:=]\\s*[a-fA-F0-9]{64}|claim hash", message: "TODO claim validation missing claim_hash" },
{ name: "verdict", pattern: "verdict\\s*[:=]\\s*(PASS|WARN|FAIL)|\\b(PASS|WARN|FAIL)\\b", message: "TODO claim validation missing PASS/WARN/FAIL verdict" },
{ name: "recommended_action", pattern: "recommended_action\\s*[:=]\\s*(accept_claim|needs_review|reject_claim|block)|recommended action", message: "TODO claim validation missing recommended_action" },
{ name: "evidence_refs", pattern: "evidence_refs|evidence refs|evidence", message: "TODO claim validation missing evidence_refs" },
{ name: "validation_commands", pattern: "validation_commands|validation commands|commands", message: "TODO claim validation missing validation_commands" },
{ name: "blocking_issues", pattern: "blocking_issues|blocking issues|blockers", message: "TODO claim validation missing blocking_issues" },
{ name: "no_ship", pattern: "no_ship|no ship", message: "TODO claim validation missing no_ship" },
{ name: "confidence", pattern: "confidence\\s*[:=]\\s*(LOW|MEDIUM|HIGH)|confidence", message: "TODO claim validation missing confidence" },
{ name: "final_marker", pattern: "FINAL_MARKER\\s*:\\s*TODO_CLAIM_VALIDATION_END|TODO_CLAIM_VALIDATION_END", message: "TODO claim validation missing FINAL_MARKER: TODO_CLAIM_VALIDATION_END" },
],
},
{
id: "todo-split-request.v1",
description: "Parent-owned TODO replan signal: a delegated child requests that its parent split an oversized TODO instead of forcing poor completion.",
agentNames: [],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "todo_id", pattern: "todo_id|TODO_ID|todo id", message: "TODO split request missing todo_id" },
{ name: "reason", pattern: "reason\\s*[:=]|reason|rationale", message: "TODO split request missing reason" },
{ name: "recommended_action", pattern: "recommended_action\\s*[:=]\\s*(split|replan|factory|needs_user|blocked)|recommended action", message: "TODO split request missing recommended_action" },
{ name: "proposed_subtodos", pattern: "proposed_subtodos|proposed subtodos|subtasks", message: "TODO split request missing proposed_subtodos" },
{ name: "risk_level", pattern: "risk_level\\s*[:=]\\s*(low|medium|high)|risk level", message: "TODO split request missing risk_level" },
{ name: "validation_plan", pattern: "validation_plan|validation plan", message: "TODO split request missing validation_plan" },
{ name: "no_ship", pattern: "no_ship|no ship", message: "TODO split request missing no_ship" },
{ name: "final_marker", pattern: "FINAL_MARKER\\s*:\\s*TODO_SPLIT_REQUEST_END|TODO_SPLIT_REQUEST_END", message: "TODO split request missing FINAL_MARKER: TODO_SPLIT_REQUEST_END" },
],
},
{
id: "delegation-request.v1",
description: "Governed parent-owned request for a future delegation. It is a request only: no child direct dispatch or parent TODO mutation.",
agentNames: [],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "request_type", pattern: "request_type\\s*[:=]\\s*DELEGATION_REQUEST\\.v1|DELEGATION_REQUEST\\.v1", message: "Delegation request missing request_type: DELEGATION_REQUEST.v1" },
{ name: "request_id", pattern: "request_id|request id", message: "Delegation request missing request_id" },
{ name: "requested_by", pattern: "requested_by|requested by", message: "Delegation request missing requested_by" },
{ name: "requested_action", pattern: "requested_action|requested action", message: "Delegation request missing requested_action" },
{ name: "body_hash", pattern: "body_hash\\s*[:=]\\s*[a-fA-F0-9]{64}|body hash", message: "Delegation request missing body_hash" },
{ name: "agent", pattern: "agent\\s*[:=]", message: "Delegation request missing agent" },
{ name: "risk_level", pattern: "risk_level\\s*[:=]\\s*(low|medium|high)|risk level", message: "Delegation request missing risk_level" },
{ name: "no_ship", pattern: "no_ship|no ship", message: "Delegation request missing no_ship" },
{ name: "final_marker", pattern: "FINAL_MARKER\\s*:\\s*DELEGATION_REQUEST_END|DELEGATION_REQUEST_END", message: "Delegation request missing FINAL_MARKER: DELEGATION_REQUEST_END" },
],
},
{
id: "oracle-request.v1",
description: "Governed parent-owned request for oracle review. It is visible metadata only and executes no oracle dispatch by itself.",
agentNames: [],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "request_type", pattern: "request_type\\s*[:=]\\s*ORACLE_REQUEST\\.v1|ORACLE_REQUEST\\.v1", message: "Oracle request missing request_type: ORACLE_REQUEST.v1" },
{ name: "request_id", pattern: "request_id|request id", message: "Oracle request missing request_id" },
{ name: "requested_by", pattern: "requested_by|requested by", message: "Oracle request missing requested_by" },
{ name: "requested_action", pattern: "requested_action|requested action", message: "Oracle request missing requested_action" },
{ name: "body_hash", pattern: "body_hash\\s*[:=]\\s*[a-fA-F0-9]{64}|body hash", message: "Oracle request missing body_hash" },
{ name: "risk_level", pattern: "risk_level\\s*[:=]\\s*(low|medium|high)|risk level", message: "Oracle request missing risk_level" },
{ name: "no_ship", pattern: "no_ship|no ship", message: "Oracle request missing no_ship" },
{ name: "final_marker", pattern: "FINAL_MARKER\\s*:\\s*ORACLE_REQUEST_END|ORACLE_REQUEST_END", message: "Oracle request missing FINAL_MARKER: ORACLE_REQUEST_END" },
],
},
{
id: "context-request.v1",
description: "Governed parent-owned request for bounded context. It is visible metadata only and performs no corpus/backend write or child dispatch.",
agentNames: [],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "request_type", pattern: "request_type\\s*[:=]\\s*CONTEXT_REQUEST\\.v1|CONTEXT_REQUEST\\.v1", message: "Context request missing request_type: CONTEXT_REQUEST.v1" },
{ name: "request_id", pattern: "request_id|request id", message: "Context request missing request_id" },
{ name: "requested_by", pattern: "requested_by|requested by", message: "Context request missing requested_by" },
{ name: "requested_action", pattern: "requested_action|requested action", message: "Context request missing requested_action" },
{ name: "body_hash", pattern: "body_hash\\s*[:=]\\s*[a-fA-F0-9]{64}|body hash", message: "Context request missing body_hash" },
{ name: "context_scope_id", pattern: "context_scope_id|context scope", message: "Context request missing context_scope_id" },
{ name: "risk_level", pattern: "risk_level\\s*[:=]\\s*(low|medium|high)|risk level", message: "Context request missing risk_level" },
{ name: "no_ship", pattern: "no_ship|no ship", message: "Context request missing no_ship" },
{ name: "final_marker", pattern: "FINAL_MARKER\\s*:\\s*CONTEXT_REQUEST_END|CONTEXT_REQUEST_END", message: "Context request missing FINAL_MARKER: CONTEXT_REQUEST_END" },
],
},
{
id: "guidance-steward.v1",
description: "Documentation/guidance steward output with layer docs, role doc packs, rule gaps, and proposal-only writebacks.",
agentNames: ["doc-steward", "guidance-steward"],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "documentation_policy", pattern: "documentation_policy|documentationPolicy|documentation policy", message: "Guidance steward output missing documentation_policy" },
{ name: "guidance_index", pattern: "guidance_index|guidanceIndex|guidance index", message: "Guidance steward output missing guidance_index" },
{ name: "layer_docs", pattern: "layer_docs|layer docs|AGENTS\\.md", message: "Guidance steward output missing layer_docs" },
{ name: "role_doc_packs", pattern: "role_doc_packs|role doc packs|roleDocs", message: "Guidance steward output missing role_doc_packs" },
{ name: "writeback_proposals", pattern: "writeback_proposals|writeback proposals|proposal_only", message: "Guidance steward output missing writeback_proposals" },
{ name: "body_free", pattern: "bodyStored\\s*[:=]\\s*false|body-free|hash-only", message: "Guidance steward output missing body-free/hash-only posture" },
],
},
{
id: "temp-agent-card.v1",
description: "Run-scoped temporary agent card proposal. Proposal only; parent/governor validates before any temp agent joins a run.",
agentNames: ["temp-agent-creator", "agent-creator"],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "request_type", pattern: "AGENT_CREATE_REQUEST\\.v1|request_type", message: "Temp agent card output missing AGENT_CREATE_REQUEST.v1" },
{ name: "temp_agent_card", pattern: "temp_agent_card|temp-agent-card|TempAgentCard", message: "Temp agent card output missing temp_agent_card" },
{ name: "run_id", pattern: "run_id|runId", message: "Temp agent card output missing run_id" },
{ name: "role", pattern: "role", message: "Temp agent card output missing role" },
{ name: "allowed_tools", pattern: "allowed_tools|allowedTools|allowed tools", message: "Temp agent card output missing allowed_tools" },
{ name: "forbidden_paths", pattern: "forbidden_paths|forbiddenPaths|forbidden paths", message: "Temp agent card output missing forbidden_paths" },
{ name: "output_contract", pattern: "output_contract|outputContract|output contract", message: "Temp agent card output missing output_contract" },
{ name: "model_class", pattern: "model_class|modelClass|model class", message: "Temp agent card output missing model_class" },
{ name: "expires_at", pattern: "expires_at|expiresAt", message: "Temp agent card output missing expires_at" },
{ name: "promotion_policy", pattern: "promotion_policy|promotion requires|promotionEligible", message: "Temp agent card output missing promotion_policy" },
{ name: "body_free", pattern: "bodyStored\\s*[:=]\\s*false|promptBodiesStored\\s*[:=]\\s*false|body-free|hash-only", message: "Temp agent card output missing body-free posture" },
],
},
{
id: "orchestration-profile.v1",
description: "Declarative orchestration profile with roles, edges, phases, model policy, and completion gates.",
agentNames: [],
required: [
...COMMON_OUTPUT_REQUIREMENTS,
{ name: "profile", pattern: "profile", message: "Orchestration profile output missing profile" },
{ name: "roles", pattern: "roles", message: "Orchestration profile output missing roles" },
{ name: "edges", pattern: "edges", message: "Orchestration profile output missing edges" },
{ name: "phases", pattern: "phases", message: "Orchestration profile output missing phases" },
{ name: "model_policy", pattern: "model_policy|modelPolicy|model policy", message: "Orchestration profile output missing model_policy" },
{ name: "output_contracts", pattern: "output_contracts|outputContracts|output contracts", message: "Orchestration profile output missing output_contracts" },
{ name: "tools", pattern: "tools", message: "Orchestration profile output missing tools" },
{ name: "paths", pattern: "paths", message: "Orchestration profile output missing paths" },
{ name: "final_report", pattern: "final_report|finalReport|final report", message: "Orchestration profile output missing final_report" },
{ name: "refusal_rules", pattern: "refusal_rules|refusalRules|refusal rules", message: "Orchestration profile output missing refusal_rules" },
],
},
];
const OUTPUT_CONTRACT_BY_ID = new Map(OUTPUT_CONTRACTS.map((contract) => [contract.id, contract]));
const OUTPUT_CONTRACT_BY_AGENT = new Map(
OUTPUT_CONTRACTS.flatMap((contract) => contract.agentNames.map((agentName) => [agentName.toLowerCase(), contract.id] as const)),
);
const OUTPUT_CONTRACT_FINAL_MARKERS = new Map([
["todo-child-result.v1", "FINAL_MARKER: TODO_CHILD_RESULT_END"],
["todo-child-result.v2", "FINAL_MARKER: TODO_CHILD_RESULT_V2_END"],
["todo-claim-validation.v1", "FINAL_MARKER: TODO_CLAIM_VALIDATION_END"],
["todo-split-request.v1", "FINAL_MARKER: TODO_SPLIT_REQUEST_END"],
["delegation-request.v1", "FINAL_MARKER: DELEGATION_REQUEST_END"],
["oracle-request.v1", "FINAL_MARKER: ORACLE_REQUEST_END"],
["context-request.v1", "FINAL_MARKER: CONTEXT_REQUEST_END"],
]);
interface OutputContractValidationOptions {
repoRoot?: string;
maxArtifactBytes?: number;
/** Exact canonical TODO id bound by parent preflight for TODO-linked child output. */
expectedTodoId?: string;
}
const ARTIFACT_REF_REGEX = /(?:^|[\s("'`])((?:\.\/)?(?:reports|\.pi\/tmp|docs)\/[A-Za-z0-9._\/-]+\.(?:md|txt|json|jsonl))(?:$|[\s)"'`,.;:])/g;
const SAFE_ARTIFACT_EXTENSIONS = new Set([".md", ".txt", ".json", ".jsonl"]);
const DEFAULT_ARTIFACT_MAX_BYTES = 96 * 1024;
function escapeRegExp(value: string): string {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function requirementAliasPattern(name: string): RegExp {
const snake = escapeRegExp(name);
const hyphen = escapeRegExp(name.replace(/_/g, "-"));
const spaced = escapeRegExp(name.replace(/_/g, " "));
return new RegExp(`(?:<${snake}>|<${hyphen}>|\\b${snake}\\b|\\b${hyphen}\\b|\\b${spaced}\\b)`, "i");
}
function isInside(root: string, candidate: string): boolean {
const normalizedRoot = resolve(root);
const normalizedCandidate = resolve(candidate);
return normalizedCandidate === normalizedRoot || normalizedCandidate.startsWith(`${normalizedRoot}${sep}`);
}
function isSafeArtifactPath(repoRoot: string, candidate: string): boolean {
if (!isInside(repoRoot, candidate)) return false;
const relative = candidate.slice(resolve(repoRoot).length + 1).replace(/\\/g, "/");
if (!/^(reports|\.pi\/tmp|docs)\//.test(relative)) return false;
if (/(^|\/)(node_modules|dist|build)(\/|$)/.test(relative)) return false;
if (/(^|\/)(\.env|.*\.env|.*secret.*|.*credential.*|.*key.*)$/i.test(relative)) return false;
return SAFE_ARTIFACT_EXTENSIONS.has(extname(relative).toLowerCase());
}
function extractArtifactRefs(output: string): string[] {
const refs = new Set();
for (const match of output.matchAll(ARTIFACT_REF_REGEX)) {
const ref = match[1]?.replace(/^\.\//, "");
if (ref) refs.add(ref);
}
return [...refs];
}
function readReferencedArtifacts(output: string, options: OutputContractValidationOptions): string[] {
if (!options.repoRoot) return [];
const artifacts: string[] = [];
const maxBytes = options.maxArtifactBytes ?? DEFAULT_ARTIFACT_MAX_BYTES;
for (const ref of extractArtifactRefs(output)) {
const candidate = resolve(options.repoRoot, ref);
if (!isSafeArtifactPath(options.repoRoot, candidate) || !existsSync(candidate)) continue;
const stat = statSync(candidate);
if (!stat.isFile() || stat.size > maxBytes) continue;
artifacts.push(readFileSync(candidate, "utf8"));
}
return artifacts;
}
export function listOutputContracts(): string[] {
return OUTPUT_CONTRACTS.map((contract) => contract.id);
}
export function getOutputContractDefinitions(): Array<{ id: string; required: string[] }> {
return OUTPUT_CONTRACTS.map((contract) => ({ id: contract.id, required: contract.required.map((requirement) => requirement.name) }));
}
export function getOutputContractFinalMarker(contractId: string): string | undefined {
if (!OUTPUT_CONTRACT_BY_ID.has(contractId)) return undefined;
return OUTPUT_CONTRACT_FINAL_MARKERS.get(contractId) ?? "deliverable_delivered: yes";
}
export function inferOutputContract(agentName: string): string {
return OUTPUT_CONTRACT_BY_AGENT.get(agentName.toLowerCase()) ?? "base.v1";
}
export function validateOutputContractId(contractId: string | undefined): string[] {
if (!contractId) return [];
return OUTPUT_CONTRACT_BY_ID.has(contractId) ? [] : [`Unknown output contract '${contractId}'. Available: ${listOutputContracts().join(", ")}`];
}
function extractDeliverableMarker(output: string): "yes" | "no" | undefined {
const finalLine = output.split(/\r?\n/).filter((line) => line.trim()).at(-1);
if (!finalLine) return undefined;
const normalized = finalLine.trim().replace(/^[-*]\s+/, "").replace(/^\d+\.\s+/, "");
const colonMarker = /^deliverable_delivered\s*:\s*(yes|no)\s*\.?$/i.exec(normalized);
const xmlMarker = /^\s*(yes|no)\s*<\/deliverable_delivered>\s*$/i.exec(normalized);
const value = colonMarker?.[1] ?? xmlMarker?.[1];
if (value?.toLowerCase() === "yes" || value?.toLowerCase() === "no") return value.toLowerCase() as "yes" | "no";
return undefined;
}
function extractAnyDeliverableMarker(output: string): "yes" | "no" | undefined {
const marker = /(?:^|\n)\s*(?:[-*]\s*)?(?:deliverable_delivered\s*:\s*(yes|no)|\s*(yes|no)\s*<\/deliverable_delivered>)\s*(?:\n|$)/i.exec(output);
const value = marker?.[1] ?? marker?.[2];
if (value?.toLowerCase() === "yes" || value?.toLowerCase() === "no") return value.toLowerCase() as "yes" | "no";
return undefined;
}
function outputGateIssue(
contractId: string,
code: OutputGateIssueCode,
classification: OutputGateIssue["classification"],
message: string,
requirement?: string,
): OutputGateIssue {
return { code, classification, failureKind: "output_gate", contractId, requirement, message };
}
function validateOutputContractText(output: string, contract: OutputContract): OutputGateIssue[] {
const trimmed = output.trim();
if (!trimmed) return [outputGateIssue(contract.id, "empty_output", "output_missing", "Child produced no assistant output")];
const deliverableMarker = extractDeliverableMarker(trimmed);
const allowsEmbeddedDeliverable = ["todo-child-result.v1", "todo-child-result.v2", "todo-split-request.v1", "todo-claim-validation.v1", "delegation-request.v1", "oracle-request.v1", "context-request.v1"].includes(contract.id);
const embeddedDeliverableMarker = allowsEmbeddedDeliverable ? extractAnyDeliverableMarker(trimmed) : undefined;
const effectiveDeliverableMarker = embeddedDeliverableMarker ?? deliverableMarker;
const issues: OutputGateIssue[] = [];
for (const requirement of contract.required) {
if (requirement.name === "deliverable_delivered") {
if (allowsEmbeddedDeliverable ? !embeddedDeliverableMarker : !deliverableMarker) {
issues.push(outputGateIssue(contract.id, "missing_final_marker", "contract_format", requirement.message, requirement.name));
}
continue;
}
let pattern: RegExp;
try {
pattern = new RegExp(requirement.pattern, "i");
} catch (error) {
const message = `Invalid output contract '${contract.id}' requirement '${requirement.name}': ${error instanceof Error ? error.message : String(error)}`;
issues.push(outputGateIssue(contract.id, "invalid_requirement_pattern", "contract_configuration", message, requirement.name));
continue;
}
const aliasPattern = requirementAliasPattern(requirement.name);
if (!pattern.test(trimmed) && !aliasPattern.test(trimmed)) {
issues.push(outputGateIssue(contract.id, "missing_requirement", "contract_format", requirement.message, requirement.name));
}
}
if (effectiveDeliverableMarker === "no") {
issues.push(outputGateIssue(contract.id, "deliverable_rejected", "deliverable_rejected", "Child reported deliverable_delivered: no", "deliverable_delivered"));
}
return issues;
}
function validateExpectedTodoId(output: string, contractId: string, expectedTodoId: string | undefined): OutputGateIssue[] {
if (!expectedTodoId) return [];
const todoIds = [...output.matchAll(/^\s*(?:[-*]\s*)?todo_id\s*[:=]\s*([^\s#]+)/gim)]
.map((match) => match[1]?.trim())
.filter((value): value is string => Boolean(value));
if (todoIds.length === 0) {
return [outputGateIssue(contractId, "missing_requirement", "contract_format", `Bound TODO child result missing exact todo_id: expected ${expectedTodoId}`, "todo_id")];
}
const mismatches = [...new Set(todoIds.filter((todoId) => todoId !== expectedTodoId))];
if (mismatches.length === 0) return [];
return [outputGateIssue(
contractId,
"mismatched_todo_id",
"output_gate_semantic",
`Bound TODO child result todo_id mismatch: expected ${expectedTodoId}; received ${mismatches.join(", ")}`,
"todo_id",
)];
}
export function validateOutputContractIssues(output: string, contractId: string, options: OutputContractValidationOptions = {}): OutputGateIssue[] {
const trimmed = output.trim();
const contract = OUTPUT_CONTRACT_BY_ID.get(contractId);
if (!contract) {
const message = validateOutputContractId(contractId)[0] ?? `Unknown output contract '${contractId}'`;
return [outputGateIssue(contractId, "unknown_contract", "contract_configuration", message)];
}
let contractIssues = validateOutputContractText(trimmed, contract);
if (contractIssues.length > 0) {
for (const artifact of readReferencedArtifacts(trimmed, options)) {
if (validateOutputContractText(artifact, contract).length === 0 || validateOutputContractText(`${artifact}\n\n${trimmed}`, contract).length === 0) {
contractIssues = [];
break;
}
}
}
return [...contractIssues, ...validateExpectedTodoId(trimmed, contractId, options.expectedTodoId)];
}
export function validateOutputContract(output: string, contractId: string, options: OutputContractValidationOptions = {}): string[] {
return validateOutputContractIssues(output, contractId, options).map((issue) => issue.message);
}
export function validateChildOutputIssues(result: ChildResult, contractId = result.outputContract ?? inferOutputContract(result.agent), options: OutputContractValidationOptions = {}): OutputGateIssue[] {
return validateOutputContractIssues(result.output, contractId, options);
}
export function validateChildOutput(result: ChildResult, contractId = result.outputContract ?? inferOutputContract(result.agent), options: OutputContractValidationOptions = {}): string[] {
return validateChildOutputIssues(result, contractId, options).map((issue) => issue.message);
}
export function applyChildGates(result: ChildResult, options: OutputContractValidationOptions = {}): ChildResult {
result.gateIssues = validateChildOutputIssues(result, result.outputContract ?? inferOutputContract(result.agent), options);
result.gateErrors = result.gateIssues.map((issue) => issue.message);
result.gatePassed = result.gateIssues.length === 0;
return result;
}