sortie-dogs 0.12.25 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Sortie-dogs
2
2
 
3
+ <p align="center">
4
+ <img src="docs/assets/sortie-dogs-logo.png" alt="Sortie-dogs logo" width="640">
5
+ </p>
6
+
3
7
  **A goal-preserving, adaptive execution harness for OpenCode that optimizes cost,
4
8
  time, and proof without taking your setup over.**
5
9
 
@@ -28,6 +32,14 @@ implementation, validation, review, and model routing.
28
32
  Guides: [日本語](docs/guide-ja.md) · [简体中文](docs/guide-zh-CN.md) ·
29
33
  [Testing](docs/testing.md) · [CLI testing](docs/cli-testing.md)
30
34
 
35
+ ## SWE-bench Lite: 170/300 (56.67%)
36
+
37
+ The fixed **Sortie-dogs v0.12.24** harness resolved **170 of 300 SWE-bench Lite test issues** in one pass@1 campaign, with 9 empty patches and no official evaluation errors. Every instance has a frozen prediction and an inference-time trajectory. The task Workers ran `openai/gpt-6-luna-fast#max`; operator, coordinator and review roles ran `openai/gpt-6-sol#xhigh`. This is a system result, **not** a Luna-only model comparison or a Verified/full SWE-bench score.
38
+
39
+ [Technical report and per-repository results](docs/benchmarks/swebench-lite-v01224-test300-2026-09-29.md) · [Public predictions, logs and trajectories](https://github.com/zufall-upon/sortie-dogs-swebench-lite-20260929)
40
+
41
+ The single official 300-instance report and frozen predictions are hash-bound in the report. Confirmed inference expense was **$162.99**; a separate **$34.60** of usage has unknown pricing and is held against the campaign cap, **not** counted as known expense. Leaderboard registration and maintainer acceptance are separate from this official local evaluation.
42
+
31
43
  > **Beta:** v0.12.2 builds on the v0.10.23 execution engine. Runtime behavior,
32
44
  > configuration, and generated assets may still change before 1.0.
33
45
 
@@ -202,7 +214,7 @@ not invent, probe, or translate variant names.
202
214
  - `freeTierFallbackModels`: ordered global last-resort model IDs. Default:
203
215
  `opencode/deepseek-v4-flash-free`; `[]` disables this fallback.
204
216
  - `dedicatedWorkerModel`: canonical stable serial target, default
205
- `openai/gpt-6-sol` / `medium`. The v0.10 profile also supplies its explicit
217
+ `openai/gpt-6.1-sol` / `medium`. The v0.10 profile also supplies its explicit
206
218
  role routes below; do not infer the v0.10 worker route from this stable setting.
207
219
  - `consultation.strategy`: fixed advisor identity, optional `required`, and
208
220
  positive `maxCallsPerCandidate`; default one call and not required.
@@ -239,14 +251,14 @@ or explicit risk. Workers own static, targeted, and related checks; the root own
239
251
  canonical and full-suite checks. An unchanged candidate, command, and environment
240
252
  reuse the same evidence instead of spending the validation budget again.
241
253
 
242
- ### Default v0.12 routes
254
+ ### Default routes
243
255
 
244
- - `dog-operator`: `openai/gpt-6-sol` / `xhigh`
245
- - `dogs-coordinator`: `openai/gpt-6-sol` / `xhigh`
256
+ - `dog-operator`: `openai/gpt-6.1-sol` / `xhigh`
257
+ - `dogs-coordinator`: `openai/gpt-6.1-sol` / `xhigh`
246
258
  - `dog-worker-v010`: `openai/gpt-6-luna-fast` / `max`
247
259
  - `dog-scout-v010`: `openai/gpt-6-luna-fast` / `max`
248
- - `dog-reviewer-v010`: `openai/gpt-6-sol` / `xhigh`
249
- - `dog-advisor-v010`: `openai/gpt-6-sol` / `xhigh`
260
+ - `dog-reviewer-v010`: `openai/gpt-6.1-sol` / `xhigh`
261
+ - `dog-advisor-v010`: `openai/gpt-6.1-sol` / `xhigh`
250
262
 
251
263
  An explicit model and variant selected in OpenCode remains authoritative for that
252
264
  session. Child role defaults fill absent native settings and may be overridden by
@@ -3,5 +3,5 @@
3
3
  * installed project marker without importing every asset body.
4
4
  */
5
5
  export declare const RUNTIME_ASSET_VERSION = "0.3.89-completion-proof-v1";
6
- export declare const V010_RUNTIME_ASSET_VERSION = "0.12.25-mission-continuity-v1";
6
+ export declare const V010_RUNTIME_ASSET_VERSION = "0.13.1-quality-first-review-v1";
7
7
  export type RuntimeAssetVersion = typeof RUNTIME_ASSET_VERSION | typeof V010_RUNTIME_ASSET_VERSION;
@@ -3,4 +3,4 @@
3
3
  * installed project marker without importing every asset body.
4
4
  */
5
5
  export const RUNTIME_ASSET_VERSION = "0.3.89-completion-proof-v1";
6
- export const V010_RUNTIME_ASSET_VERSION = "0.12.25-mission-continuity-v1";
6
+ export const V010_RUNTIME_ASSET_VERSION = "0.13.1-quality-first-review-v1";
@@ -11,7 +11,7 @@ export declare class ProjectInitializationError extends Error {
11
11
  readonly code: ProjectInitializationErrorCode;
12
12
  constructor(code: ProjectInitializationErrorCode, message: string, options?: ErrorOptions);
13
13
  }
14
- export declare const V010_COORDINATOR_MODEL = "openai/gpt-6-sol#xhigh";
14
+ export declare const V010_COORDINATOR_MODEL = "openai/gpt-6.1-sol#xhigh";
15
15
  /**
16
16
  * Report a user config that routes the v0.10 Coordinator away from its packaged model. Measured Luna
17
17
  * Coordinators repeated plans and review dispatches until timeout, so init surfaces it without editing it.
@@ -133,10 +133,10 @@ function classifyVersionTransition(installedValue, currentValue, v010Restore = f
133
133
  // cross-minor migration; skipped lines still fail closed instead of bypassing migration steps.
134
134
  const adjacentPreOneLine = installed.major === 0 && current.major === 0 &&
135
135
  current.minor === installed.minor + 1;
136
- // v0.11 used a different runtime. This explicit v010 asset migration restores
137
- // the 0.10 line directly into 0.12 without selecting any v0.11 execution path.
136
+ // v0.11 used a different runtime. The v010 asset migration restores the 0.10
137
+ // preview into the 0.12/0.13 Mission line without selecting a v0.11 execution path.
138
138
  const restoredPreview = v010Restore && installed.major === 0 && installed.minor === 10 &&
139
- current.major === 0 && current.minor === 12;
139
+ current.major === 0 && (current.minor === 12 || current.minor === 13);
140
140
  return sameLine || adjacentPreOneLine || restoredPreview ? "compatible-update" : "incompatible";
141
141
  }
142
142
  async function metadata(path) {
@@ -542,7 +542,7 @@ async function initializeRoot(requestedRoot, layout, installAssets = runtimeAsse
542
542
  preservedLegacyPaths,
543
543
  };
544
544
  }
545
- export const V010_COORDINATOR_MODEL = "openai/gpt-6-sol#xhigh";
545
+ export const V010_COORDINATOR_MODEL = "openai/gpt-6.1-sol#xhigh";
546
546
  /**
547
547
  * Report a user config that routes the v0.10 Coordinator away from its packaged model. Measured Luna
548
548
  * Coordinators repeated plans and review dispatches until timeout, so init surfaces it without editing it.
@@ -80,7 +80,8 @@ export interface MissionExecution {
80
80
  startedAt: string;
81
81
  completedAt?: string;
82
82
  exit?: number;
83
- status?: "completed" | "error";
83
+ status?: "running" | "completed" | "error";
84
+ shellID?: string;
84
85
  outcome?: "not-started" | "execution-failed" | "executed";
85
86
  result?: Record<string, unknown>;
86
87
  }[];
@@ -141,15 +142,10 @@ export interface OperatorMission {
141
142
  child?: string;
142
143
  /** Completed, independent initial review for this mission, not merely an inherited child ID. */
143
144
  initialPrompt?: string;
144
- /** Reviews on this mission that found only missing evidence; bounded by MISSION_EVIDENCE_GAP_REVIEW_LIMIT. */
145
+ /** Observed evidence-only reviews; reporting only, never an acceptance threshold. */
145
146
  evidenceGapReviews?: number;
146
147
  };
147
148
  }
148
- /**
149
- * Evidence-only findings do not establish a defect. Each extra round costs a full Reviewer pass and often a
150
- * Worker unit, so after this many evidence-only reviews the candidate may be submitted with the gaps listed.
151
- */
152
- export declare const MISSION_EVIDENCE_GAP_REVIEW_LIMIT = 2;
153
149
  export declare const MISSION_CONSULTATION_LIMIT = 32;
154
150
  /** Review coverage survives a narrower replan; it is not a Worker write grant. */
155
151
  export declare function missionReviewScope(previous: MissionReviewScope | undefined, ...runs: OperatorState[]): MissionReviewScope;
@@ -217,5 +213,5 @@ export declare function missionPlan(mission: OperatorMission, raw: unknown): Ope
217
213
  export declare function missionPacket(mission: OperatorMission, run?: OperatorState): Record<string, unknown>;
218
214
  /** Models forward a short capability, never recopy the host's evidence hashes and source packet. */
219
215
  export declare function missionReviewTask(mission: OperatorMission): OperatorTask;
220
- /** Project explicitly grouped R-ID traces into the legacy reviewer's ordered criterion mapping. */
221
- export declare function missionReviewTraces(mission: OperatorMission, raw: unknown): string[];
216
+ /** Optional implementation notes supplement host-observed source/checks; prose is not a coverage gate. */
217
+ export declare function missionReviewTraces(_mission: OperatorMission, raw: unknown): string[];
@@ -18,11 +18,6 @@ export function missionValidationCommand(command) {
18
18
  const expression = `exec(${JSON.stringify(heredoc[4])})`;
19
19
  return `${heredoc[1]} -c '${expression.replaceAll("'", "'\\''")}'`;
20
20
  }
21
- /**
22
- * Evidence-only findings do not establish a defect. Each extra round costs a full Reviewer pass and often a
23
- * Worker unit, so after this many evidence-only reviews the candidate may be submitted with the gaps listed.
24
- */
25
- export const MISSION_EVIDENCE_GAP_REVIEW_LIMIT = 2;
26
21
  export const MISSION_CONSULTATION_LIMIT = 32;
27
22
  /** Review coverage survives a narrower replan; it is not a Worker write grant. */
28
23
  export function missionReviewScope(previous, ...runs) {
@@ -38,7 +33,7 @@ export function missionReviewVerdict(text) {
38
33
  /** Whether the recorded review permits submission and acceptance of the current candidate. */
39
34
  export function missionReviewAccepted(review) {
40
35
  return review.verdict === "PASS" || review.verdict === "skipped-low-risk" ||
41
- (review.verdict === "evidence-gaps" && (review.evidenceGapReviews ?? 0) >= MISSION_EVIDENCE_GAP_REVIEW_LIMIT);
36
+ review.verdict === "evidence-gaps";
42
37
  }
43
38
  export function missionExecutionStatus(mission) {
44
39
  if (mission.kind !== "operation")
@@ -241,11 +236,11 @@ export class OperatorMissionRuntime {
241
236
  }
242
237
  if (previous)
243
238
  await this.save(this.file(root, `.${previous.id}`), previous);
244
- // Explicit replacement links the latest cancelled run even when a user narrows the
245
- // request in the same turn. Ordinary same-turn continuation still retains acceptance.
246
- // A cancelled standalone/legacy run has no mission-owned runID; only replacement links it.
247
- const predecessor = previous?.runID ?? previous?.supersededRunID ??
248
- (replaceRequirements ? options.cancelledRunID : undefined);
239
+ // The Operator runtime's current cancelled run is authoritative for an explicit
240
+ // replacement. A no-run mission can still carry an older ancestor's run ID.
241
+ // Ordinary continuation retains that predecessor when no current run is supplied.
242
+ const predecessor = (replaceRequirements ? options.cancelledRunID : undefined) ??
243
+ previous?.runID ?? previous?.supersededRunID;
249
244
  const state = { version: "0.12", id: `mission-${randomUUID()}`, root, requests: [request],
250
245
  kind: options.kind ?? "implementation",
251
246
  context: (options.context ?? []).filter(item => item.id !== request.id),
@@ -469,7 +464,7 @@ export function missionPacket(mission, run) {
469
464
  run_id: mission.review.runID, current_run: currentReview,
470
465
  source_fingerprint: mission.review.source, reviewer_session_id: mission.review.child ?? null,
471
466
  result: mission.review.result ?? null, evidence_gap_reviews: mission.review.evidenceGapReviews ?? 0,
472
- evidence_gap_review_limit: MISSION_EVIDENCE_GAP_REVIEW_LIMIT, accepted: reviewAccepted,
467
+ evidence_gaps_advisory: true, accepted: reviewAccepted,
473
468
  passed: currentReview && mission.review.verdict === "PASS", permits_submission: reviewAccepted && operationComplete } : null,
474
469
  ...(run ? { run_id: run.runID, status: run.phase, decision: run.decision,
475
470
  units: run.units.map(unit => ({ id: unit.unit.id, title: unit.unit.title, status: unit.status,
@@ -482,21 +477,21 @@ export function missionPacket(mission, run) {
482
477
  next_action: mission.phase === "completed" ? "Mission completed. Report the accepted result and retained review gaps; no further dispatch or completion call is needed."
483
478
  : mission.phase === "submitted" && mission.submission?.status === "ready"
484
479
  ? "Operator: compare the submitted candidate with the original requirements and actual evidence, then complete_mission if satisfied. Report remaining evidence gaps; they are not a review PASS."
485
- : run?.phase === "awaiting-decision" ? (run.units.some(unit => unit.dispatchDenial)
486
- ? "Coordinator: inspect units[].dispatch_denial before changing the plan. Correct only its diagnosed cause; do not repeat an unchanged refused Task or replan for a host-state mismatch. Report an unresolved runtime mismatch with the loaded runtime identity; preserve requirements and cumulative spend."
487
- : run.units.some(unit => unit.status === "failed" && unit.resultClass === "acceptance" && unit.failure?.outcome === "fail" && !unit.normalRemediationUsed)
488
- ? "Coordinator: one exact declared validation failed. Reconcile the returned Worker and cumulative budget, then call retry_mission_unit once for that unit before replanning. This preserves the same scope and acceptance."
489
- : run.units.some(unit => unit.status === "failed" && unit.resultClass === "acceptance" && unit.failure?.outcome === "fail" && unit.normalRemediationUsed && !unit.terminalRescue)
490
- ? "Coordinator: the one ordinary remediation failed the declared validation again. If its native terminal, writer release, exact candidate and cumulative budget permit, call rescue_mission_unit once; inspect and report a non_rescue reason, then continue ordinary correction within budget. Rescue does not bypass validation, review or root acceptance."
491
- : "Coordinator: correct the cause and call plan_units with the remaining work and all requirements; budget is cumulative.")
492
- : run?.phase === "awaiting-acceptance" && !operationComplete
493
- ? `Requested operation is ${operationStatus}. Continue the actual operation or report its blocker; auxiliary checks and review disposition cannot complete it.`
494
- : run?.phase === "awaiting-acceptance" ? (currentReview && mission.review?.verdict === "evidence-gaps" && !reviewAccepted
495
- ? "Coordinator: the Reviewer found only missing evidence. Supply focused original-file excerpts through review_mission evidence and traces; do not re-implement. At the evidence-gap limit, review ends with the gaps listed, not with execution proof."
496
- : reviewAccepted
497
- ? "Coordinator: review permits submission. Submit the candidate with any remaining gaps; do not repeat passed validation or review. Operator performs final acceptance."
480
+ : mission.kind === "operation" && operationStatus === "running"
481
+ ? "The declared operation is already running. Inspect its native shell/progress; do not start another Worker or run. Wait for a terminal result, or report the existing run as blocked if its completion cannot be observed."
482
+ : run?.phase === "awaiting-decision" ? (run.units.some(unit => unit.dispatchDenial)
483
+ ? "Coordinator: inspect units[].dispatch_denial before changing the plan. Correct only its diagnosed cause; do not repeat an unchanged refused Task or replan for a host-state mismatch. Report an unresolved runtime mismatch with the loaded runtime identity; preserve requirements and cumulative spend."
484
+ : run.units.some(unit => unit.status === "failed" && unit.resultClass === "acceptance" && unit.failure?.outcome === "fail" && !unit.normalRemediationUsed)
485
+ ? "Coordinator: one exact declared validation failed. Reconcile the returned Worker and cumulative budget, then call retry_mission_unit once for that unit before replanning. This preserves the same scope and acceptance."
486
+ : run.units.some(unit => unit.status === "failed" && unit.resultClass === "acceptance" && unit.failure?.outcome === "fail" && unit.normalRemediationUsed && !unit.terminalRescue)
487
+ ? "Coordinator: the one ordinary remediation failed the declared validation again. If its native terminal, writer release, exact candidate and cumulative budget permit, call rescue_mission_unit once; inspect and report a non_rescue reason, then continue ordinary correction within budget. Rescue does not bypass validation, review or root acceptance."
488
+ : "Coordinator: correct the cause and call plan_units with the remaining work and all requirements; budget is cumulative.")
489
+ : run?.phase === "awaiting-acceptance" && !operationComplete
490
+ ? `Requested operation is ${operationStatus}. Continue the actual operation or report its blocker; auxiliary checks and review disposition cannot complete it.`
491
+ : run?.phase === "awaiting-acceptance" ? (reviewAccepted
492
+ ? "Coordinator: review permits submission. Retain advisory review notes; do not repeat passed validation or review for evidence formatting. Operator performs final acceptance against the original requirements."
498
493
  : "Coordinator: address recorded findings or obtain the required independent review, then submit_mission. Operator compares all requirements with source/evidence before complete_mission.")
499
- : "Coordinator: continue the next useful unit within original requirements. Return only a completion candidate, user-only decision, or scope/budget extension." };
494
+ : "Coordinator: continue the next useful unit within original requirements. Return only a completion candidate, user-only decision, or scope/budget extension." };
500
495
  }
501
496
  /** Models forward a short capability, never recopy the host's evidence hashes and source packet. */
502
497
  export function missionReviewTask(mission) {
@@ -506,19 +501,12 @@ export function missionReviewTask(mission) {
506
501
  return { ...review.task, prompt: `${MISSION_REVIEW_REFERENCE}${JSON.stringify({ r: mission.root,
507
502
  m: mission.id, n: review.runID, h: digest(review.task.prompt) })}` };
508
503
  }
509
- /** Project explicitly grouped R-ID traces into the legacy reviewer's ordered criterion mapping. */
510
- export function missionReviewTraces(mission, raw) {
511
- if (!Array.isArray(raw) || raw.length === 0 || !raw.every(item => typeof item === "string" && item.trim())) {
512
- throw new Error("mission-review-traces: supply concise implementation/test traces");
504
+ /** Optional implementation notes supplement host-observed source/checks; prose is not a coverage gate. */
505
+ export function missionReviewTraces(_mission, raw) {
506
+ if (raw === undefined)
507
+ return [];
508
+ if (!Array.isArray(raw) || !raw.every(item => typeof item === "string")) {
509
+ throw new Error("mission-review-traces: optional notes must be strings");
513
510
  }
514
- const grouped = raw.map(text => ({ text,
515
- ids: [...text.matchAll(/(?:^|[.;]\s+|[\r\n])\s*((?:R\d+\s*[/,]?\s*)+):/gu)]
516
- .flatMap(match => match[1].match(/R\d+/gu) ?? []) }));
517
- if (grouped.every(item => item.ids.length === 0) && raw.length === mission.requirements.length)
518
- return raw;
519
- const unknown = grouped.flatMap(item => item.ids).filter(id => !mission.requirements.some(requirement => requirement.id === id));
520
- const missing = mission.requirements.filter(requirement => !grouped.some(item => item.ids.includes(requirement.id)));
521
- if (unknown.length || missing.length)
522
- throw new Error(`mission-review-traces: name the existing R IDs; missing ${missing.map(item => item.id).join(", ")}; unknown ${unknown.join(", ")}`);
523
- return mission.requirements.map(requirement => grouped.filter(item => item.ids.includes(requirement.id)).map(item => item.text).join("\n"));
511
+ return raw.map(text => text.trim()).filter(Boolean);
524
512
  }
@@ -227,6 +227,22 @@ export interface OperatorState {
227
227
  decision: string | null;
228
228
  receipt: GoalTerminalReceipt | null;
229
229
  }
230
+ export interface OperatorProgress {
231
+ readonly profile: string;
232
+ readonly view: "progress";
233
+ readonly run_id: string;
234
+ readonly stage: OperatorPhase;
235
+ readonly decision: string | null;
236
+ readonly current_unit: {
237
+ readonly id: string;
238
+ readonly title: string;
239
+ readonly status: UnitState["status"];
240
+ } | null;
241
+ readonly completed_units: number;
242
+ readonly total_units: number;
243
+ readonly budget_remaining_units: number | null;
244
+ readonly next_action: string | null;
245
+ }
230
246
  /** A later mission may replace an unexecuted cancellation, but must retain settled work's acceptance. */
231
247
  export declare function cancelledMissionRetainsAcceptance(state: OperatorState): boolean;
232
248
  export declare function operatorGitPathAuthorized(path: string, scopes: readonly string[], platform?: NodeJS.Platform): boolean;
@@ -371,10 +387,13 @@ export declare class OperatorRuntime {
371
387
  draftStatus(root: string): Promise<unknown | undefined>;
372
388
  completionGoalFingerprint(state: OperatorState): Promise<string>;
373
389
  verifyContinuityControls(state: OperatorState): Promise<void>;
390
+ /** Compact, read-only projection of the same durable run and decision as the complete packet. */
391
+ progress(state: OperatorState, budgetRemainingUnits: number | null, nextAction?: string | null): OperatorProgress;
374
392
  packet(state: OperatorState): unknown;
375
393
  /** Reconstruct the admitted child's instructions without trusting a lossy conversation summary. */
376
394
  workerContext(root: string, child: string): Promise<string | undefined>;
377
395
  continuationCheckpoint(root: string): Promise<string | undefined>;
396
+ private continuationNextAction;
378
397
  private controlReference;
379
398
  private verifyControls;
380
399
  /** Follow only the host's authorized parent-link repair; keep the durable control pin in sync. */
@@ -920,11 +920,11 @@ export class OperatorRuntime {
920
920
  `goal_declaration_path: ${declarationPath}`, "acceptance:", ...plan.acceptance.map(value => ` - ${value}`),
921
921
  "validation:", ...unit.validation.map(value => ` - ${value}`),
922
922
  ...(mission ? ["Read handoff_path and other repository files using their project-relative paths as supplied. The native working directory is project_root; do not prepend or reconstruct its absolute path for read/search/shell. Copy project_root only when binding the write gate. Preserve explicitly declared external paths."] : []),
923
- mission ? "Read-only investigation commands are unrestricted. Use shell to reproduce and diagnose without asking for command registration. Keep all writes, including generated/transient outputs and cleanup, inside unit.write. Run formal validation exactly as listed, in order and in separate calls, so the host records its real result. If a write scope or formal check must change, return the precise change to your Coordinator; it can extend/redeclare immediately within the original requirements. Diagnostic success is not formal acceptance evidence."
923
+ mission ? "Read-only investigation commands are unrestricted. Use shell to reproduce and diagnose without asking for command registration. Keep all writes, including generated/transient outputs and cleanup, inside unit.write. Run formal validation exactly as listed, in order and in separate calls, so the host records its real result. If a write scope or formal check must change, return the precise change to your parent Operator or Coordinator; it can extend/redeclare immediately within the original requirements. Diagnostic success is not formal acceptance evidence."
924
924
  : "Execute validation in its declared order. Earlier entries may be approved generator, build, formatter, or exact cleanup commands required before canonical criterion tests. Every persistent or transient generator output must be declared in unit.write. Cleanup may remove only declared unit.write outputs and must be an explicit ordered command after generation and before post-commit or canonical validation; never add an ignore rule or remove an undeclared path. If any necessary command, input, output, or cleanup is missing, do not run an undeclared command or variant and do not use resume evidence tooling to invent permission; return a contract-repair decision.",
925
925
  ...(mission ? [] : ["Preserve existing public API success and error return semantics unless acceptance explicitly changes them, and cover those compatibility boundaries in the declared validation."]),
926
926
  mission
927
- ? "Complete the assigned work and listed validation within this Task. For a known operation, proceed through setup, execution and result collection; preparation alone is not execution. Preserve existing public behavior when changing source. Do not spawn nested subagents. The Coordinator handles any applicable independent review after your return; review is not a prerequisite to execution. Return actual results, including failed or not-started operations, and any exact contract correction needed."
927
+ ? "Complete the assigned work and listed validation within this Task. For a known operation, proceed through setup, execution and result collection; preparation alone is not execution. Preserve existing public behavior when changing source. Do not spawn nested subagents. The parent Operator or Coordinator handles any applicable independent review after your return; review is not a prerequisite to execution. Return actual results, including failed or not-started operations, and any exact contract correction needed."
928
928
  : "Do not spawn nested subagents for consultation. Required consultations belong to the root before dispatch; use the confirmed decisions and evidence declared in the unit objective and inputs. If required consultation results or user decisions are missing, return the exact contract gap to the parent instead of attempting a deeper Task, inventing consent, or asking the user to repeat an already recorded decision.",
929
929
  ...(plan.git_lifecycle !== undefined && index === plan.units.length - 1 ? [
930
930
  `git_post_commit_validation: ${JSON.stringify(plan.git_lifecycle.post_commit_validation)}`,
@@ -1564,6 +1564,11 @@ export class OperatorRuntime {
1564
1564
  state.units.some(candidate => candidate.terminalRescue))
1565
1565
  throw new Error("operator-mission-normal-remediation-unavailable");
1566
1566
  await this.verifyControls(unit);
1567
+ // The ordinary retry keeps its exact scope and validation, but the next Worker needs the
1568
+ // host-observed failed command/exit rather than reconstructing it from an earlier session.
1569
+ unit.task = { ...unit.task, prompt: `${unit.task.prompt}\nnormal_remediation_prior_validation: ${JSON.stringify({
1570
+ command: unit.failure.command, exit_code: unit.failure.exitCode
1571
+ })}` };
1567
1572
  unit.normalRemediationUsed = true;
1568
1573
  unit.status = "pending";
1569
1574
  unit.callID = null;
@@ -1899,6 +1904,16 @@ export class OperatorRuntime {
1899
1904
  for (const unit of current.units)
1900
1905
  await this.verifyControls(unit);
1901
1906
  }
1907
+ /** Compact, read-only projection of the same durable run and decision as the complete packet. */
1908
+ progress(state, budgetRemainingUnits, nextAction) {
1909
+ const current = state.units.find(unit => unit.status !== "succeeded");
1910
+ const authoritativeAction = nextAction !== undefined ? nextAction
1911
+ : this.continuationNextAction(state);
1912
+ return { profile: state.profile, view: "progress", run_id: state.runID, stage: state.phase, decision: state.decision,
1913
+ current_unit: current === undefined ? null : { id: current.unit.id, title: current.unit.title, status: current.status },
1914
+ completed_units: state.units.filter(unit => unit.status === "succeeded").length, total_units: state.units.length,
1915
+ budget_remaining_units: budgetRemainingUnits, next_action: typeof authoritativeAction === "string" ? authoritativeAction : null };
1916
+ }
1902
1917
  packet(state) {
1903
1918
  const proved = new Set(state.units.flatMap(unit => unit.evidence.flatMap(item => item.measurement.criterion_ids)));
1904
1919
  const current = state.units.find(unit => unit.status !== "succeeded");
@@ -2030,9 +2045,10 @@ export class OperatorRuntime {
2030
2045
  }
2031
2046
  async continuationCheckpoint(root) {
2032
2047
  const state = await this.read(root);
2033
- const cancelledRemediation = state?.phase === "cancelled" &&
2034
- [ACCEPTANCE_REMEDIATION_DECISION, REVIEW_REMEDIATION_DECISION].includes(state.decision ?? "");
2035
- if (!state || state.phase === "completed" || (state.phase === "cancelled" && !cancelledRemediation))
2048
+ if (!state)
2049
+ return undefined;
2050
+ const nextAction = this.continuationNextAction(state);
2051
+ if (nextAction === null)
2036
2052
  return undefined;
2037
2053
  const current = state.units.find(unit => unit.status !== "succeeded");
2038
2054
  return JSON.stringify({ authority: "durable-operator-state", root_session_id: state.rootSessionID,
@@ -2041,14 +2057,21 @@ export class OperatorRuntime {
2041
2057
  next_task_ref: current?.status === "pending" ? (state.units.length > 1
2042
2058
  ? state.operatorSessionID === null ? this.dispatchTask(state).prompt : null
2043
2059
  : current.repairValidation === null ? this.workerTask(state, current).prompt : this.repairWorkerTask(state, current).prompt) : null,
2044
- next_action: state.decision === ACCEPTANCE_REMEDIATION_DECISION || state.decision === REVIEW_REMEDIATION_DECISION
2045
- ? `call ${this.profile.toolPrefix}operator_status and execute its cancel-then-replacement next_action in this turn; do not stop after reporting status or call resume_operator`
2046
- : state.phase === "awaiting-acceptance" ? `call ${this.profile.toolPrefix}operator_status and follow its independent-review next_action; do not complete before review disposition`
2047
- : state.phase === "prepared" ? "call operator_status, then operator_next; dispatch only the exact returned Task"
2048
- : state.phase === "running" ? "call operator_status; do not duplicate the running Task"
2049
- : state.phase === "cancelled"
2050
- ? `call ${this.profile.toolPrefix}operator_status and execute its prepare_operator next_action in this turn; do not stop after reporting status`
2051
- : `call ${this.profile.toolPrefix}operator_status and execute its exact next_action in this turn; do not stop after reporting status` });
2060
+ next_action: nextAction });
2061
+ }
2062
+ continuationNextAction(state) {
2063
+ const cancelledRemediation = state.phase === "cancelled" &&
2064
+ [ACCEPTANCE_REMEDIATION_DECISION, REVIEW_REMEDIATION_DECISION].includes(state.decision ?? "");
2065
+ if (state.phase === "completed" || (state.phase === "cancelled" && !cancelledRemediation))
2066
+ return null;
2067
+ return state.decision === ACCEPTANCE_REMEDIATION_DECISION || state.decision === REVIEW_REMEDIATION_DECISION
2068
+ ? `call ${this.profile.toolPrefix}operator_status and execute its cancel-then-replacement next_action in this turn; do not stop after reporting status or call resume_operator`
2069
+ : state.phase === "awaiting-acceptance" ? `call ${this.profile.toolPrefix}operator_status and follow its independent-review next_action; do not complete before review disposition`
2070
+ : state.phase === "prepared" ? "call operator_status, then operator_next; dispatch only the exact returned Task"
2071
+ : state.phase === "running" ? "call operator_status; do not duplicate the running Task"
2072
+ : state.phase === "cancelled"
2073
+ ? `call ${this.profile.toolPrefix}operator_status and execute its prepare_operator next_action in this turn; do not stop after reporting status`
2074
+ : `call ${this.profile.toolPrefix}operator_status and execute its exact next_action in this turn; do not stop after reporting status`;
2052
2075
  }
2053
2076
  controlReference(path) {
2054
2077
  const local = relative(this.projectRoot, path);
@@ -23,7 +23,7 @@ export interface AdaptiveReviewProvider {
23
23
  readonly fingerprint: string;
24
24
  }>;
25
25
  }
26
- export declare const DEFAULT_ADAPTIVE_REMEDIATION_MODEL = "openai/gpt-6-sol";
26
+ export declare const DEFAULT_ADAPTIVE_REMEDIATION_MODEL = "openai/gpt-6.1-sol";
27
27
  export interface AdaptiveRemediationSessionClient extends OpenCodeModelAvailabilityClient {
28
28
  session: {
29
29
  create(input: {
@@ -10,7 +10,7 @@ import { produceWorktreeCommitArtifact, runContainedValidation } from "../core/w
10
10
  import { openCodeModel } from "./model-routing-hook.js";
11
11
  const SHA = /^[a-f0-9]{40}(?:[a-f0-9]{24})?$/u;
12
12
  const HASH = /^[a-f0-9]{64}$/u;
13
- export const DEFAULT_ADAPTIVE_REMEDIATION_MODEL = "openai/gpt-6-sol";
13
+ export const DEFAULT_ADAPTIVE_REMEDIATION_MODEL = "openai/gpt-6.1-sol";
14
14
  const responseData = (value) => {
15
15
  if (value === null || typeof value !== "object")
16
16
  return {};
@@ -6060,8 +6060,12 @@ export const SortieDogsPlugin = async (input, options) => {
6060
6060
  appLogInfo("run-metrics.career-unavailable", textInput.sessionID, { outcome: runOutcome }, "warn");
6061
6061
  }
6062
6062
  }
6063
- if (sortieResult !== undefined)
6064
- textOutput.text = insertSortieResult(textOutput.text, sortieResult);
6063
+ if (sortieResult !== undefined) {
6064
+ const review = terminal?.receipt?.status === "succeeded"
6065
+ ? await input.runtimeBridge?.missionReviewPresentation?.(textInput.sessionID).catch(() => undefined)
6066
+ : undefined;
6067
+ textOutput.text = insertSortieResult(textOutput.text, sortieResult, review?.verdict, review?.evidenceGaps);
6068
+ }
6065
6069
  else if (metrics !== undefined && runOutcome === "DONE")
6066
6070
  textOutput.text = insertRunMetrics(textOutput.text, metrics);
6067
6071
  const { debrief: _debriefObservation, ...metricSummary } = metrics ?? {};
@@ -7445,7 +7449,7 @@ export const SortieDogsPlugin = async (input, options) => {
7445
7449
  },
7446
7450
  };
7447
7451
  input.runtimeBridge?.connected?.({
7448
- renderReturnReport: async (root, text, expectedReceipt) => {
7452
+ renderReturnReport: async (root, text, expectedReceipt, missionReview, reviewEvidenceGaps) => {
7449
7453
  let rendered;
7450
7454
  await serializeChatTransition(root, async () => {
7451
7455
  if (!isCoordinatorSession(root) && !await recoverCoordinatorRoot(root))
@@ -7473,7 +7477,7 @@ export const SortieDogsPlugin = async (input, options) => {
7473
7477
  catch {
7474
7478
  appLogInfo("run-metrics.career-unavailable", root, { profile: runtimeProfile.id }, "warn");
7475
7479
  }
7476
- rendered = insertSortieResult(receiptBoundTerminalText(text, receipt), result);
7480
+ rendered = insertSortieResult(receiptBoundTerminalText(text, receipt), result, missionReview, reviewEvidenceGaps);
7477
7481
  });
7478
7482
  return rendered;
7479
7483
  },
@@ -1,6 +1,17 @@
1
1
  import type { OperatorState } from "../core/operator-runtime.js";
2
2
  import { type MissionEvidenceExcerpt, type MissionReviewScope, type OperatorMission } from "../core/operator-mission.js";
3
3
  import { type RuntimeProfile } from "../core/runtime-profile.js";
4
+ /** Show native command outcomes to the Reviewer without turning non-criterion checks into acceptance evidence. */
5
+ export declare function observedMissionValidation(validation: readonly string[], childSessionID: string | null, history: readonly Record<string, unknown>[]): {
6
+ attempts: readonly {
7
+ command: string;
8
+ exit_code: number | null;
9
+ started_ms: number | null;
10
+ completed_ms: number | null;
11
+ }[];
12
+ not_observed: readonly string[];
13
+ omitted_attempts: number;
14
+ };
4
15
  export declare function missionReviewBaseline(directory: string): Promise<string | undefined>;
5
16
  /** A child ID alone cannot establish the initial phase after a restart or failed dispatch. */
6
17
  export declare function initialMissionReviewPrompt(mission: OperatorMission, prompts: readonly string[]): string | undefined;
@@ -14,4 +25,5 @@ export declare function missionReviewSource(directory: string, run: OperatorStat
14
25
  fingerprint: string;
15
26
  excerpt: string;
16
27
  truncatedEvidence: string[];
28
+ truncatedSource: string[];
17
29
  }>;