sortie-dogs 0.13.13 → 0.13.15

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
@@ -19,8 +19,8 @@ evidence-backed result, not more agents for their own sake.
19
19
  [![Node.js](https://img.shields.io/node/v/sortie-dogs)](https://www.npmjs.com/package/sortie-dogs)
20
20
  [![MIT License](https://img.shields.io/npm/l/sortie-dogs)](LICENSE)
21
21
 
22
- **Current release: [v0.13.13](https://github.com/zufall-upon/Sortie-dogs/releases/tag/v0.13.13)**
23
- · [Release notes](docs/release-v0.13.13.md)
22
+ **Current release: [v0.13.15](https://github.com/zufall-upon/Sortie-dogs/releases/tag/v0.13.15)**
23
+ · [Release notes](docs/release-v0.13.15.md)
24
24
 
25
25
  [Quick start](#quick-start) · [Workflow](#mission-workflow) · [Models](#default-models) ·
26
26
  [Measured results](#measured-results) · [Configuration](#configuration) · [Documentation](#documentation)
@@ -57,7 +57,7 @@ Use an existing Codex CLI signed in with **ChatGPT**. On Windows, PowerShell **7
57
57
  available; POSIX uses bash. Sortie does not install Codex, start login or use a metered API fallback.
58
58
 
59
59
  ```sh
60
- npm install --save-dev sortie-dogs@0.13.13
60
+ npm install --save-dev sortie-dogs@0.13.15
61
61
  npx --no-install sortie-dogs codex init .
62
62
  ```
63
63
 
@@ -86,7 +86,7 @@ bridge. [Codex guide: SDK, permissions, progress, resume and manifest route](doc
86
86
  Use **OpenCode V2**. In the target project:
87
87
 
88
88
  ```sh
89
- npm install --save-dev sortie-dogs@0.13.13
89
+ npm install --save-dev sortie-dogs@0.13.15
90
90
  npx --no-install sortie-dogs init .
91
91
  ```
92
92
 
@@ -188,7 +188,7 @@ known expense. Official local evaluation and leaderboard acceptance are separate
188
188
  **8/23 (34.8%)**, versus v0.12.25's 7/23; all seven prior resolutions retained, no empty patches or
189
189
  official evaluation errors. [Fixed conditions, stopped-run accounting and costs](docs/benchmarks/swebench-lite-history.md#v0131-dev23-2026-09-30).
190
190
 
191
- All scores belong to their fixed candidates, **not v0.13.13**. SWE-bench remains an optional
191
+ All scores belong to their fixed candidates, **not v0.13.15**. SWE-bench remains an optional
192
192
  measurement, not a release gate. Different budgets, models and conditions are not controlled comparisons.
193
193
 
194
194
  ## Configuration
@@ -211,7 +211,7 @@ Install the desired package version and rerun **your host's** initializer:
211
211
 
212
212
  An OpenCode config-local bridge may resolve a separate package; npm-global update alone does not
213
213
  update it. [Global/update instructions](docs/configuration.md#global-availability).
214
- The current Mission asset marker is `0.13.13-mission-context-v1`; the unchanged Codex skill marker is
214
+ The current Mission asset marker is `0.13.15-native-identity-v1`; the unchanged Codex skill marker is
215
215
  `0.13.11-codex-skill-v2`. Installed markers alone do not prove a running host reloaded the new version.
216
216
 
217
217
  There is no supported uninstall command. Remove the dependency and only known Sortie-owned paths;
@@ -227,7 +227,7 @@ follow the [manual removal guide](docs/uninstall.md), never delete the entire `.
227
227
  [SWE-bench history](docs/benchmarks/swebench-lite-history.md) · [Historical local case study](docs/benchmark-reference.md).
228
228
  - **Development:** [Testing](docs/testing.md) · [CLI testing](docs/cli-testing.md) ·
229
229
  [Windows tests](docs/windows-tests.md) · [Release routine](docs/release-batch.md).
230
- - **Releases:** [v0.13.13](docs/release-v0.13.13.md) · [v0.13.12](docs/release-v0.13.12.md) · [v0.13.11](docs/release-v0.13.11.md) · [v0.13.10](docs/release-v0.13.10.md) ·
230
+ - **Releases:** [v0.13.15](docs/release-v0.13.15.md) · [v0.13.14](docs/release-v0.13.14.md) · [v0.13.13](docs/release-v0.13.13.md) · [v0.13.12](docs/release-v0.13.12.md) · [v0.13.11](docs/release-v0.13.11.md) · [v0.13.10](docs/release-v0.13.10.md) ·
231
231
  [v0.13.9](docs/release-v0.13.9.md) · [All GitHub releases](https://github.com/zufall-upon/Sortie-dogs/releases).
232
232
 
233
233
  ## Community
@@ -3,6 +3,6 @@
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.13.13-mission-context-v1";
6
+ export declare const V010_RUNTIME_ASSET_VERSION = "0.13.15-native-identity-v1";
7
7
  export declare const CODEX_SKILL_ASSET_VERSION = "0.13.11-codex-skill-v2";
8
8
  export type RuntimeAssetVersion = typeof RUNTIME_ASSET_VERSION | typeof V010_RUNTIME_ASSET_VERSION | typeof CODEX_SKILL_ASSET_VERSION;
@@ -3,5 +3,5 @@
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.13.13-mission-context-v1";
6
+ export const V010_RUNTIME_ASSET_VERSION = "0.13.15-native-identity-v1";
7
7
  export const CODEX_SKILL_ASSET_VERSION = "0.13.11-codex-skill-v2";
@@ -62,7 +62,7 @@ export declare function bootstrapWritePaths(tool: string, args: unknown): readon
62
62
  export declare function isGitMutation(tool: string, args: unknown): boolean;
63
63
  export declare function isRemoteMutation(tool: string, args: unknown): boolean;
64
64
  /**
65
- * Quoted segments keep their contents; every unquoted whitespace run collapses to one space so the
65
+ * Quoted segments and backslash escapes keep their contents; unquoted whitespace runs collapse so the
66
66
  * same command written with different spacing compares equal.
67
67
  */
68
68
  export declare function normalizeCommand(command: string): string;
@@ -858,14 +858,22 @@ export function isRemoteMutation(tool, args) {
858
858
  return extractWritePaths(tool, args).remoteMutation === true;
859
859
  }
860
860
  /**
861
- * Quoted segments keep their contents; every unquoted whitespace run collapses to one space so the
861
+ * Quoted segments and backslash escapes keep their contents; unquoted whitespace runs collapse so the
862
862
  * same command written with different spacing compares equal.
863
863
  */
864
864
  export function normalizeCommand(command) {
865
865
  let quote;
866
+ let escaped = false;
866
867
  let whitespace = false;
867
868
  let normalized = "";
868
869
  for (const character of command.trim()) {
870
+ if (escaped) {
871
+ normalized += character;
872
+ escaped = false;
873
+ continue;
874
+ }
875
+ if (character === "\\" && quote !== "'")
876
+ escaped = true;
869
877
  if (quote !== undefined) {
870
878
  normalized += character;
871
879
  if (character === quote)
@@ -8306,6 +8306,8 @@ export const SortieDogsPlugin = async (input, options) => {
8306
8306
  const direct = await input.runtimeBridge?.directMissionExecution?.(actor);
8307
8307
  if (!direct || direct.root !== root)
8308
8308
  throw new Error("mission-direct-grant-stale");
8309
+ if (activeSessions.get(actor)?.inFlightCalls.size)
8310
+ throw new Error("mission-direct-tools-running");
8309
8311
  // Cold terminal recovery can happen before the actor's next tool/context hook.
8310
8312
  if (!goalReservations.has(direct.callID)) {
8311
8313
  const snapshot = await (await goalLedger(root)).readGoal();
@@ -8317,9 +8319,13 @@ export const SortieDogsPlugin = async (input, options) => {
8317
8319
  else
8318
8320
  throw new Error("mission-direct-reservation-unavailable");
8319
8321
  }
8320
- await settleGoalDispatch(direct.callID, { status: "error",
8321
- output: "Actual native Reviewer ended before its direct correction was validated.",
8322
- metadata: { sessionId: actor } }, undefined, []);
8322
+ // Empty successful evidence forces a failed disposition. Reuse the saved
8323
+ // validation observation (including a cold recovered exit) without claiming
8324
+ // a failed native child terminal for this still-active controller.
8325
+ await settleGoalDispatch(direct.callID, {
8326
+ output: "Direct unit stopped incomplete; no successful validation or Mission acceptance.",
8327
+ metadata: { sessionId: actor }
8328
+ }, undefined, []);
8323
8329
  await releaseWriteGate(actor);
8324
8330
  releaseSessionEnforcement(actor);
8325
8331
  },
@@ -73,7 +73,8 @@ export function observedMissionValidation(validation, childSessionID, history, n
73
73
  !record(part.state) || !["completed", "error"].includes(String(part.state.status)) || !record(part.state.input) ||
74
74
  typeof part.state.input.command !== "string")
75
75
  return [];
76
- const command = normalizeCommand(part.state.input.command);
76
+ // Match raw input first: whitespace normalization destroys Python heredoc boundaries.
77
+ const command = part.state.input.command;
77
78
  const exit = record(part.state.metadata) ? part.state.metadata.exit : undefined;
78
79
  const started = record(part.time) ? time(part.time.ran) : record(part.state.time) ? time(part.state.time.start) : null;
79
80
  const completed = record(part.time) ? time(part.time.completed) : record(part.state.time) ? time(part.state.time.end) : null;
@@ -99,7 +100,7 @@ export function observedMissionValidation(validation, childSessionID, history, n
99
100
  occurrence = (occurrence + 1) % declared.length;
100
101
  }
101
102
  const { directory: actual, ...visible } = attempt;
102
- return [{ ...visible, ...(directory && resolve(directory, actual ?? ".") !== resolve(directory)
103
+ return [{ ...visible, command: members.join(" && "), ...(directory && resolve(directory, actual ?? ".") !== resolve(directory)
103
104
  ? { directory: resolve(directory, actual ?? ".") } : {}), ...(members.length > 1 ? { observed_members: members } : {}) }];
104
105
  });
105
106
  // Retain early attempts and the latest checks if a Worker retried many times.
@@ -266,17 +266,21 @@ export function createProfiledPlugin(profile, assetVersion) {
266
266
  async function identity(id) {
267
267
  const info = payload(await session("get", { path: { id }, query: { directory: input.directory } }));
268
268
  const parent = record(info) && typeof info.parentID === "string" ? info.parentID : undefined;
269
- let agent = selected.get(id);
269
+ // Native session identity survives reload; a completion's metadata.agent is its
270
+ // child author, not a selection of the receiving session's agent.
271
+ let agent = selected.get(id) ?? (record(info) && typeof info.agent === "string" ? info.agent : undefined);
270
272
  if (agent === undefined) {
271
273
  const history = await messages(id);
272
274
  for (const message of [...history].reverse()) {
275
+ if (Array.isArray(message.parts) && message.parts.length > 0 &&
276
+ message.parts.every(part => record(part) && part.synthetic === true))
277
+ continue;
273
278
  const metadata = record(message.info) ? message.info : message;
274
279
  if (metadata.role === "user" && typeof metadata.agent === "string") {
275
280
  agent = metadata.agent;
276
281
  break;
277
282
  }
278
283
  }
279
- agent ??= record(info) && typeof info.agent === "string" ? info.agent : undefined;
280
284
  }
281
285
  return { role: canonicalAgent(profile, agent), ...(parent === undefined ? {} : { parent }) };
282
286
  }
@@ -1001,6 +1005,14 @@ export function createProfiledPlugin(profile, assetVersion) {
1001
1005
  const detail = "Operation result: execution-failed (result accepted; process did not succeed).";
1002
1006
  return text.includes(detail) ? text : text.replace(/^((?:[ \t]*\r?\n)*[^\r\n]+)/u, `$1 ${detail}`);
1003
1007
  }
1008
+ function compactMissionEvidence(packet, run, root) {
1009
+ if (run && run.runID === packet.run_id && Array.isArray(packet.units)) {
1010
+ const validation = missionReviewValidation(run, operators.statePath(root));
1011
+ packet.units = packet.units.map((unit, index) => ({ ...unit, evidence: validation[index].evidence,
1012
+ evidence_details_ref: validation[index].details_ref }));
1013
+ }
1014
+ return packet;
1015
+ }
1004
1016
  function missionDispatchPacket(mission, run) {
1005
1017
  const packet = { ...missionPacket(mission, run), project_root: input.directory,
1006
1018
  coordinator_dispatch: mission.dispatchOpen ? "active" : ["completed", "cancelled"].includes(mission.phase) ? "terminal" : "resumable" };
@@ -1512,12 +1524,7 @@ export function createProfiledPlugin(profile, assetVersion) {
1512
1524
  }
1513
1525
  if (view === "progress")
1514
1526
  return JSON.stringify(missionProgress(mission, run, budget, packet));
1515
- if (view !== "full" && run?.runID === mission.runID && Array.isArray(packet.units)) {
1516
- const validation = missionReviewValidation(run, operators.statePath(root));
1517
- packet.units = packet.units.map((unit, index) => ({ ...unit, evidence: validation[index].evidence,
1518
- evidence_details_ref: validation[index].details_ref }));
1519
- }
1520
- return JSON.stringify(packet);
1527
+ return JSON.stringify(view === "full" ? packet : compactMissionEvidence(packet, run, root));
1521
1528
  }
1522
1529
  if (root && context.sessionID !== root) {
1523
1530
  const owned = await operators.read(root);
@@ -2026,8 +2033,12 @@ export function createProfiledPlugin(profile, assetVersion) {
2026
2033
  return JSON.stringify({ status: "correction-validated",
2027
2034
  execution: "same-native-task", validation: direct.unit.unit.validation,
2028
2035
  next_action: 'Compare the original requirements, retained findings and affected public behavior in this same context, then finish THIS native Task with SELF_RECHECKED and self_recheck: {"candidate":"current-validated","unresolved_findings":[],"residual_major":null}. No handoff reread, second Task or Operator round trip. This is author self-recheck; only the actual successful native terminal can bind it, never this tool result.' });
2029
- return JSON.stringify({ ...missionPacket(await missions.required(root), await operators.required(root)),
2030
- next_action: `Continue remaining units, or call ${reviewMission} with actual risk tags. Independent Review and Operator final acceptance remain required.` });
2036
+ const run = await operators.required(root);
2037
+ // Full protected path arrays can make V2 truncate this one-line JSON to zero lines,
2038
+ // hiding both the successful transition and the next action. Keep measured evidence
2039
+ // and durable detail references, as default status already does; never rewrite proof.
2040
+ return JSON.stringify({ ...compactMissionEvidence(missionPacket(await missions.required(root), run), run, root),
2041
+ next_action: `This direct unit is validated; do not repeat passed checks or finish this settled unit again. Continue remaining units with ${startDirectUnit}, or call ${reviewMission} with actual risk tags. Independent Review and Operator final acceptance remain required.` });
2031
2042
  });
2032
2043
  } };
2033
2044
  const stringList = { type: "array", items: { type: "string" } };
@@ -2664,13 +2675,26 @@ export function createProfiledPlugin(profile, assetVersion) {
2664
2675
  }
2665
2676
  const newFindings = review?.runID === run.runID && review.verdict === "findings" &&
2666
2677
  existing?.reviewIdentity !== missionReviewIdentity(mission);
2667
- const recovery = existing?.status === "failed" && run.phase === "awaiting-decision";
2678
+ let validationRecovery = false;
2679
+ if (existing?.status === "ready" && run.phase === "awaiting-acceptance") {
2680
+ try {
2681
+ await assertCorrectionValidation(run);
2682
+ }
2683
+ catch (error) {
2684
+ if (!(error instanceof Error) || !error.message.startsWith("mission-review-correction-validation-stale:"))
2685
+ throw error;
2686
+ // Read-only self-recheck cannot repair stale checks. Reuse the existing
2687
+ // same-author correction admission, without blessing historical evidence.
2688
+ validationRecovery = true;
2689
+ }
2690
+ }
2691
+ const recovery = existing !== undefined && ((existing.status === "failed" && run.phase === "awaiting-decision") || validationRecovery);
2668
2692
  if (existing && ((!newFindings && !recovery) || ["prepared", "running"].includes(existing.status)))
2669
2693
  return JSON.stringify({ status: `correction-${existing.status}`,
2670
2694
  ...(run.phase === "prepared" ? { task: operators.nextWorkerTask(run) } : {}),
2671
2695
  next_action: "Continue only the existing correction Task. Ready requires explicit SAME-author native self-recheck; only residual concrete Major risk requires a different Reviewer. Failure retains the candidate and cumulative spend." });
2672
2696
  if (!run.units.some(unit => unit.unit.write.length) ||
2673
- (!recovery && (run.phase !== "awaiting-acceptance" || review?.runID !== run.runID)) || review?.verdict !== "findings" ||
2697
+ (!recovery && (run.phase !== "awaiting-acceptance" || review?.runID !== run.runID)) || !review || (!validationRecovery && review.verdict !== "findings") ||
2674
2698
  !review.child || !review.initialPrompt || !review.result)
2675
2699
  throw new Error("mission-review-correction-unavailable");
2676
2700
  const original = mission.corrections?.at(-1);
@@ -2696,7 +2720,7 @@ export function createProfiledPlugin(profile, assetVersion) {
2696
2720
  throw new Error("mission-review-correction-budget-unavailable");
2697
2721
  const reviewIdentity = missionReviewIdentity(mission);
2698
2722
  const plan = missionPlan(mission, [{ title: `Correct ${run.units[0].unit.title}`,
2699
- objective: `Correct all retained concrete Major/Medium findings in this SAME native conversation; preserve every original requirement. Exact findings are at correction_context.findings in the handoff; retained_findings_ref is optional lineage, not an instruction to reread/print the entire Mission or previous review prompt. Relevant search and focused diagnostics may widen normally; do not rediscover unchanged work. ${REVIEWER_VALIDATION_WORKFLOW} Retain the requested commit/clean boundary. THEN explicitly self-recheck the original requirements, ALL retained findings, correction and relevant impact in this Task. Correct known defects and revalidate affected checks before finishing. First line SELF_RECHECKED. Next line self_recheck: {"candidate":"current-validated","unresolved_findings":[],"residual_major":null}. The HOST binds current-validated to actual current source and fresh successful native checks after this exact prompt; never copy a pre-edit hash. Explain the actual comparison below. List unresolved concrete Major/Medium defects in unresolved_findings (no acceptance). Only concrete reachable residual Major risk uses residual_major:{"reachable_path":"...","consequence":"serious consequence"} and requires a different Reviewer. Tags/hashes/Medium/prose gaps alone never trigger it. This is author self-recheck, NOT independent PASS. Legacy CORRECTION_READY without self-recheck only permits a same-author read-only fallback Task, never acceptance.`,
2723
+ objective: `${validationRecovery ? "The prior correction completed, but its inherited validation is now stale. Preserve completed implementation, reproduction, original findings and historical checks; refresh only affected checks in this SAME author context. If the check recipe needs repair, use repair_review here as its existing author. Do not redo unchanged implementation/discovery or request another independent Review. " : ""}Correct all retained concrete Major/Medium findings in this SAME native conversation; preserve every original requirement. Exact findings are at correction_context.findings in the handoff; retained_findings_ref is optional lineage, not an instruction to reread/print the entire Mission or previous review prompt. Relevant search and focused diagnostics may widen normally; do not rediscover unchanged work. ${REVIEWER_VALIDATION_WORKFLOW} Retain the requested commit/clean boundary. THEN explicitly self-recheck the original requirements, ALL retained findings, correction and relevant impact in this Task. Correct known defects and revalidate affected checks before finishing. First line SELF_RECHECKED. Next line self_recheck: {"candidate":"current-validated","unresolved_findings":[],"residual_major":null}. The HOST binds current-validated to actual current source and fresh successful native checks after this exact prompt; never copy a pre-edit hash. Explain the actual comparison below. List unresolved concrete Major/Medium defects in unresolved_findings (no acceptance). Only concrete reachable residual Major risk uses residual_major:{"reachable_path":"...","consequence":"serious consequence"} and requires a different Reviewer. Tags/hashes/Medium/prose gaps alone never trigger it. This is author self-recheck, NOT independent PASS. Legacy CORRECTION_READY without self-recheck only permits a same-author read-only fallback Task, never acceptance.`,
2700
2724
  read: [...new Set([...(mission.reviewScope?.read ?? []), ...run.units.flatMap(unit => unit.unit.read)])],
2701
2725
  write: [...new Set([...(mission.reviewScope?.write ?? []), ...run.units.flatMap(unit => unit.unit.write)])],
2702
2726
  validation: run.units.flatMap(unit => unit.unit.validation),
@@ -2881,15 +2905,20 @@ export function createProfiledPlugin(profile, assetVersion) {
2881
2905
  : { status: "skipped-low-risk" });
2882
2906
  });
2883
2907
  } };
2884
- async function assertCorrectionValidation(run) {
2908
+ async function correctionValidationFailure(run) {
2885
2909
  for (const unit of run.units) {
2886
2910
  if (!unit.reviewerCorrection || !unit.childSessionID)
2887
2911
  continue;
2888
2912
  const checked = await reviewerCorrectionValidationFresh(unit.unit.validation, unit.childSessionID, await messages(unit.childSessionID), Date.parse(unit.reviewerCorrection.admittedAt ?? run.createdAt), unit.reviewerCorrection.checks ?? [], input.directory, unit.directExecution?.validationCwd ?? unit.unit.validation_cwd, unit.reviewerCorrection.validationRegisteredAt);
2889
2913
  if (!checked.ready)
2890
- throw new Error(checked.reason);
2914
+ return checked.reason;
2891
2915
  }
2892
2916
  }
2917
+ async function assertCorrectionValidation(run) {
2918
+ const reason = await correctionValidationFailure(run);
2919
+ if (reason)
2920
+ throw new Error(reason);
2921
+ }
2893
2922
  /** Shared inline/fallback proof: the exact admitted native prompt's actual successful terminal. */
2894
2923
  async function observeNativeSelfRecheck(root, mission, run, author, callID, promptID, admittedAt, expectedSource, suppliedHistory) {
2895
2924
  if (!promptID || !Number.isFinite(admittedAt) || mission.runID !== run.runID || run.phase !== "awaiting-acceptance")
@@ -2915,7 +2944,11 @@ export function createProfiledPlugin(profile, assetVersion) {
2915
2944
  const checked = missionSelfRecheckReport(text, expectedSource ?? source.fingerprint, true);
2916
2945
  if (!checked)
2917
2946
  return undefined;
2918
- await assertCorrectionValidation(run);
2947
+ // A stale/missing check is an incomplete self-recheck, not a native lifecycle
2948
+ // accounting error. Settle the real terminal so later prompts can repair it.
2949
+ // Explicit acceptance still requires assertCorrectionValidation.
2950
+ if (await correctionValidationFailure(run))
2951
+ return undefined;
2919
2952
  if (expectedSource !== undefined && source.fingerprint !== expectedSource)
2920
2953
  return undefined;
2921
2954
  return { runID: run.runID, source: source.fingerprint, candidateSource: source.candidateFingerprint,
@@ -2933,27 +2966,39 @@ export function createProfiledPlugin(profile, assetVersion) {
2933
2966
  }
2934
2967
  tools[submitMission] = { description: "Coordinator: return only a completion candidate, a user-only decision, or a proven external/scope/budget blocker. Continue ordinary investigation, scope extensions and corrections yourself. Unit progress is published automatically without waking Operator.",
2935
2968
  args: { status: { type: "string", enum: ["ready", "needs-decision", "blocked"] }, summary: stringSchema }, execute: async (args, context) => {
2936
- const { root, mission } = await missionAuthority(context.sessionID);
2937
- if (args.status === "ready") {
2938
- if (mission.kind === "operation" && !missionExecutionComplete(mission)) {
2939
- return JSON.stringify({ ...missionPacket(mission, await operators.read(root)), status: "operation-incomplete",
2940
- next_action: missionExecutionStatus(mission) === "running"
2941
- ? "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."
2942
- : "Continue the requested operation or submit its actual blocker. A successful setup/NO_START check does not authorize ready." });
2969
+ const { root } = await missionAuthority(context.sessionID);
2970
+ return serializeDispatchTransition(root, async () => {
2971
+ const mission = await missions.required(root);
2972
+ if (args.status === "ready") {
2973
+ if (mission.kind === "operation" && !missionExecutionComplete(mission)) {
2974
+ return JSON.stringify({ ...missionPacket(mission, await operators.read(root)), status: "operation-incomplete",
2975
+ next_action: missionExecutionStatus(mission) === "running"
2976
+ ? "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."
2977
+ : "Continue the requested operation or submit its actual blocker. A successful setup/NO_START check does not authorize ready." });
2978
+ }
2979
+ const run = await operators.required(root);
2980
+ if (run.phase !== "awaiting-acceptance")
2981
+ throw new Error("mission-units-incomplete");
2982
+ await assertMissionReview(root, mission);
2943
2983
  }
2944
- const run = await operators.required(root);
2945
- if (run.phase !== "awaiting-acceptance")
2946
- throw new Error("mission-units-incomplete");
2947
- await assertMissionReview(root, mission);
2948
- }
2949
- const updated = await missions.update(root, state => {
2950
- state.phase = "submitted";
2951
- state.submission = { status: args.status, summary: args.summary };
2984
+ else if (args.status === "blocked" || args.status === "needs-decision") {
2985
+ // This owner is stopping its direct work, not validating it. Keep the
2986
+ // failed checks/candidate and settle its existing reservation once.
2987
+ const direct = await activateDirect(context.sessionID);
2988
+ if (direct)
2989
+ await control.failDirectUnit(root, context.sessionID);
2990
+ }
2991
+ const updated = await missions.update(root, state => {
2992
+ state.phase = "submitted";
2993
+ state.submission = { status: args.status, summary: args.summary };
2994
+ });
2995
+ const submittedRun = await operators.read(root);
2996
+ return JSON.stringify({ acceptance_summary: await missionAcceptanceSummary(updated, submittedRun, operators, acceptanceValidationObservation),
2997
+ ...compactMissionEvidence(missionPacket(updated, submittedRun), submittedRun, root),
2998
+ next_action: args.status === "ready"
2999
+ ? "Return this result and acceptance_summary to Operator now. Compare the original requests with the candidate and actual evidence; inspect concrete gaps only, then complete_mission if satisfied. No routine archive search or full source reread. Completion still requires its final comparison and complete_mission receipt."
3000
+ : "Return the saved blocker or user-only decision to Operator now. This is incomplete delivery, not acceptance; retain the candidate, failed checks and cumulative spend. Do not repeat unchanged work or dispatch a replacement merely to finalize this result." });
2952
3001
  });
2953
- const submittedRun = await operators.read(root);
2954
- return JSON.stringify({ acceptance_summary: await missionAcceptanceSummary(updated, submittedRun, operators, acceptanceValidationObservation),
2955
- ...missionPacket(updated, submittedRun),
2956
- next_action: "Return this result and acceptance_summary to Operator now. Compare the original requests with the candidate and actual evidence; inspect concrete gaps only, then complete_mission if satisfied. No routine archive search or full source reread. Completion still requires its final comparison and complete_mission receipt." });
2957
3002
  } };
2958
3003
  tools[completeMission] = { description: "Operator only: after comparing the original request, source, actual checks and current review disposition (explicit native author self-recheck is not independent PASS), explicitly accept the whole mission. A collected terminal failure can satisfy a run-once/report request, not a request for successful execution. Returns operation outcome separately from the measured 🐾 Mission receipt; never treat Worker start, CORRECTION_READY or one passing check as completion.",
2959
3004
  args: {}, execute: async (_args, context) => {
@@ -3720,14 +3765,19 @@ export function createProfiledPlugin(profile, assetVersion) {
3720
3765
  admittedAt: inline.inlineReview.admittedAt, result: report.result, selfRecheck: report };
3721
3766
  });
3722
3767
  const latest = await missions.required(ownership.root);
3768
+ const validationFailure = !report && inline.status === "ready" && settled.phase === "awaiting-acceptance"
3769
+ ? await correctionValidationFailure(settled) : undefined;
3723
3770
  output.output = JSON.stringify({ status: report ? "review-correction-recorded" : "review-correction-incomplete",
3724
3771
  reviewer_report: raw, independent: false,
3772
+ ...(validationFailure ? { reason: validationFailure } : {}),
3725
3773
  acceptance_summary: await missionAcceptanceSummary(latest, settled, operators, acceptanceValidationObservation),
3726
3774
  next_action: report && !report.unresolvedFindings.length && !report.residualMajor
3727
3775
  ? `Host already recorded source, formal checks, delivery and actual native author self-recheck. Compare the original request with this candidate, then ${current.coordinator ? submitMission : completeMission}; no routine operator_status, new review or validation Worker.`
3728
- : !report && inline.status === "ready" && settled.phase === "awaiting-acceptance"
3729
- ? `Formal correction validation remains current, but the actual native author self-recheck is missing/failed. No acceptance. Call ${reviewMission} only for the missing read-only self-recheck in the SAME author/context, without rerunning successful checks or requesting another Reviewer.`
3730
- : `No acceptance. Continue the SAME author through ${repairReview} for actual defects; only a concrete residual Major risk after self-recheck calls for a different Reviewer.` });
3776
+ : validationFailure
3777
+ ? `No acceptance: ${validationFailure}. Call ${repairReview} to refresh affected checks in the SAME author/context. Preserve completed work and historical evidence; do not redo unchanged implementation/discovery or request another independent Reviewer.`
3778
+ : !report && inline.status === "ready" && settled.phase === "awaiting-acceptance"
3779
+ ? `Formal correction validation remains current, but the actual native author self-recheck is missing/failed. No acceptance. Call ${reviewMission} only for the missing read-only self-recheck in the SAME author/context, without rerunning successful checks or requesting another Reviewer.`
3780
+ : `No acceptance. Continue the SAME author through ${repairReview} for actual defects; only a concrete residual Major risk after self-recheck calls for a different Reviewer.` });
3731
3781
  taskOwners.delete(request.callID);
3732
3782
  return;
3733
3783
  }
package/dist/plugin/v2.js CHANGED
@@ -147,7 +147,9 @@ function legacyClient(context) {
147
147
  active: async () => ({ data: await (context.session.active ? context.session.active() : active()) }),
148
148
  get: async (request) => {
149
149
  const id = sessionID(request);
150
- return { data: id === undefined ? undefined : await context.session.get({ sessionID: id }) };
150
+ const info = id === undefined ? undefined : await context.session.get({ sessionID: id });
151
+ // The native correction alias is still this same Reviewer's logical role.
152
+ return { data: record(info) && correctionAgent(info.agent) ? { ...info, agent: "dog-reviewer-v010" } : info };
151
153
  },
152
154
  messages: async (request) => {
153
155
  const id = sessionID(request);
@@ -511,7 +513,10 @@ async function registerV2Hooks(context, hooks, background, liveState) {
511
513
  };
512
514
  });
513
515
  for (const [name, definition] of Object.entries(hooks.tool ?? {})) {
514
- editor.add({ name, description: definition.description, input: toolSchema(definition.args), options: { codemode: false },
516
+ // The approved increase is discovered through execute/search. Direct-only registrations
517
+ // are absent from that catalog; retain the other controls' existing direct entrypoints.
518
+ editor.add({ name, description: definition.description, input: toolSchema(definition.args),
519
+ options: { codemode: name === "sortie_v010_extend_mission_budget" },
515
520
  execute: async (input, execution) => {
516
521
  await background.reconcile(String(execution.sessionID ?? ""));
517
522
  let content;
@@ -693,7 +698,7 @@ async function registerV2Hooks(context, hooks, background, liveState) {
693
698
  await reconcileCorrectionPermissions(String(event.sessionID));
694
699
  if (record(event.tools)) {
695
700
  const visible = {
696
- "dog-operator": ["start_mission", "plan_units", "start_direct_unit", "finish_direct_unit", "retry_mission_unit", "operator_next", "operator_status", "expand_unit", "review_mission", "repair_review", "complete_mission", "cancel_operator", "reflection"],
701
+ "dog-operator": ["start_mission", "plan_units", "start_direct_unit", "finish_direct_unit", "retry_mission_unit", "operator_next", "operator_status", "extend_mission_budget", "expand_unit", "review_mission", "repair_review", "complete_mission", "cancel_operator", "reflection"],
697
702
  "dogs-coordinator": ["plan_units", "start_direct_unit", "finish_direct_unit", "operator_next", "operator_status", "expand_unit", "review_mission", "repair_review", "submit_mission", "skip_mission_consultation", "retry_mission_unit", "rescue_mission_unit"],
698
703
  "dog-worker-v010": ["bind_write_gate", "release_write_gate", "operator_status", "expand_unit"],
699
704
  "dog-luna-worker-v010": ["bind_write_gate", "release_write_gate", "operator_status", "expand_unit"],
@@ -1,9 +1,11 @@
1
1
  import { type RuntimeProfile } from "./core/runtime-profile.ts";
2
2
  /** Repository-local dependency environment shared by all units; excluded from reviewed and captured source. */
3
3
  export declare const TOOL_ENVIRONMENT = ".sortie-env";
4
+ /** Task-specific environment limits qualify general dependency setup guidance. */
5
+ export declare const MISSION_DEPENDENCY_WORKFLOW = "Honor original dependency limits in setup and unit objectives. If the user permits installs only for\nproven-missing packages, an installed but incompatible package is not missing: do not replace it or\ndescribe it as missing compatible tooling. General registry/compatible-version guidance does not widen\nthat task-specific limit. Reuse a supplied environment; create one only when the original request permits.\nOtherwise carry out authorized dependency repair normally. If required behavior remains unverified under\nthose limits, retain in-scope work and report the limitation, not successful verification.";
4
6
  /** Ordinary requested Git delivery uses source scope, not repository control-storage scope. */
5
7
  export declare const MISSION_GIT_SCOPE = "Requested git add <paths> (with optional --) and git commit -m ... are normal source-scope Git operations;\nthey do not require .git/** scope. Preserve explicit user ordering and host Git lifecycle; attempt supported\noperations and report actual denials, not inferred gaps. Stage actual changed deliverables only: permission\nfor an unchanged file or deleted untracked scratch does not require staging it. Repair a missing in-request\noutput via expand_unit, then continue the SAME Task; covered paths need no new grant or dispatch.";
6
- export declare const VALIDATION_WORKFLOW = "## Time-aware validation workflow\n\nOperator/Coordinator: put the known meaningful formal check from the user, project or task context in validation,\nnot another objective copy. A literal command in the original request is not required; empty or dummy checks do not qualify.\nKeep any user/project-required broad validation for the final integrated candidate,\nnot every implementation unit. Do not add a full suite merely as a precaution or create a testing agent.\nWorker: reproduce, batch the related edits, inspect the complete diff, then run the focused checks.\nFix failures with the smallest exercising check before starting a costly suite. Do not execute the entire\nformal validation list after each patch; run it in declared order when the unit candidate is stable.\nIf broad validation is already declared, defer it until focused checks pass and known edits are finished;\ndo not remove it, substitute a tiny check for it, or claim an unrun requirement passed.\nAfter a late fix, select checks for the affected inputs; repeat broad validation when the requested\ncontract or host evidence freshness requires it, not solely because any file or Worker changed.\nDocumentation-only changes do not automatically invalidate unrelated runtime/test results; retain the\nchecked candidate and show why reuse is valid under project and host rules. Never promote stale proof.\nBefore a costly check, state its purpose and known duration (unknown if unmeasured). In the concise return,\nreport command, scope, actual exit/elapsed time and any rerun reason; never invent savings or timings.\nReview evidence gaps need original excerpts, not another patch or full test. Keep autonomous correction,\nrequired review, accepted criteria and cumulative budget intact; this workflow adds no approval or denial.\n";
8
+ export declare const VALIDATION_WORKFLOW = "## Time-aware validation workflow\n\nOperator/Coordinator: put the known meaningful formal check from the user, project or task context in validation,\nnot another objective copy. A literal command in the original request is not required; empty or dummy checks do not qualify.\nFor a concrete public reproduction, carry its entrypoint and relevant input into the unit objective and formal reproduction check.\nPreserve its fixture layout and working directory. A standalone example project belongs in its own fixture root within allowed paths;\ninvoke from there with the active environment, not the source checkout root where import paths or configuration may mask the failure.\nDerive the shell recipe without substituting lower-level library calls; those checks may supplement, never replace, the public entrypoint.\nReuse existing checks that already exercise it. If authorized setup cannot run it, retain useful work and report the verification gap, not a passing reproduction.\nFor direct work with an unknown reproduction recipe, resolve its flags, configuration and cwd with ordinary native diagnostics before formal registration.\nReuse an already observed failing baseline; do not rerun it merely to plan. A command that never exercises the reported path is not a passing reproduction.\nUse existing project configuration within the original path constraints. Known recipes and Worker-owned investigation need no extra preparation or unit.\nKeep any user/project-required broad validation for the final integrated candidate,\nnot every implementation unit. Do not add a full suite merely as a precaution or create a testing agent.\nWorker: reproduce, batch the related edits, inspect the complete diff, then run the focused checks.\nFix failures with the smallest exercising check before starting a costly suite. Do not execute the entire\nformal validation list after each patch; run it in declared order when the unit candidate is stable.\nIf broad validation is already declared, defer it until focused checks pass and known edits are finished;\ndo not remove it, substitute a tiny check for it, or claim an unrun requirement passed.\nAfter a late fix, select checks for the affected inputs; repeat broad validation when the requested\ncontract or host evidence freshness requires it, not solely because any file or Worker changed.\nDocumentation-only changes do not automatically invalidate unrelated runtime/test results; retain the\nchecked candidate and show why reuse is valid under project and host rules. Never promote stale proof.\nBefore a costly check, state its purpose and known duration (unknown if unmeasured). In the concise return,\nreport command, scope, actual exit/elapsed time and any rerun reason; never invent savings or timings.\nReview evidence gaps need original excerpts, not another patch or full test. Keep autonomous correction,\nrequired review, accepted criteria and cumulative budget intact; this workflow adds no approval or denial.\n";
7
9
  /** Worker-only projection; planning/review authorities retain the complete shared workflow. */
8
10
  export declare const WORKER_VALIDATION_WORKFLOW = "Batch edits, reconcile requirements/diff, run focused tests, then every registered formal check exactly in order:\nseparate foreground native shell calls, no extra tee, redirect or wrapper. Diagnostics are not formal evidence.\nRerun affected checks and required broad checks when contract/freshness requires; never drop required validation or claim stale/unrun proof.\n";
9
11
  /** Correction uses the inherited command identities; diagnostics do not acquire formal evidence. */
@@ -11,6 +13,8 @@ export declare const REVIEWER_VALIDATION_WORKFLOW = "Run inherited formal comman
11
13
  export declare function missionOperatorContent(profile: RuntimeProfile, version: string): string;
12
14
  /** Keep representation oracles consistent in direct execution, Worker and Review. */
13
15
  export declare const MISSION_REPRESENTATION_ORACLE = "Same-type representation, not name/visitor analogy; resolve conflicts.";
16
+ /** Coordinator authors and plans regressions without adding another validation/review lane. */
17
+ export declare const MISSION_REGRESSION_PLACEMENT = "Prefer an existing relevant test suite/subtest or fixture when it can express the regression assertion.\nIf a new test module is needed, prefer a distinctive behavior-specific name over automatically mirroring the implementation module.\nPreserve established assertions and project test discovery. This is placement guidance, not another test, unit or review requirement.";
14
18
  /** Shared by the installed Reviewer and its host-generated mission prompt. */
15
19
  export declare const MISSION_BEHAVIOR_REVIEW = "Establish the test oracle independently of the patch: tests added with the implementation are claims to\nreview, not established API behavior. For a changed failure path, compare a pre-existing analogous public\ntest and its shared assertion helper, including default expected outputs, with the new case. A new test\nthat asserts the implementation's current result can encode the defect rather than catch it. Preserve\nthe pre-change contract unless the request changes it; do not invent a universal failure-result convention.\nSame-type representation, not name/visitor analogy; resolve conflicts.\nTrace a concrete rejected input through the changed code back to the public caller, checking result,\nerror and observable state together, including earlier results that can survive failure. Missing assertions\nalone are not defects; a concrete contradiction with the established contract is a finding even if tests pass.\nDuring correction, reuse the established public test harness where applicable and exercise the smallest\nregression exposing the defect before the fix and passing afterward. Do not change expected values or\nhelper defaults merely to agree with the implementation; derive them from the original contract.\nPrefer existing suites/subtests for regressions. New test entry/helper names should be distinctive\nand compose with other same-package test files, not create generic package-level collisions.\n\nFor a requested per-call mode/option, trace where the setting is chosen and where changed code uses\nretained objects or closures. Distinguish the current caller's setting from a captured creation-time\nsetting; use the requested contract to decide which governs. If actual source crosses that boundary\nand existing tests do not cover it, exercise its smallest public case, including the disabled behavior.\nDo not invent a lifecycle matrix or infer that an opt-in feature is correct from only its enabled path.\n\nFor a changed failure handler, inspect the operation it calls and the public inputs reaching it,\nincluding failures not listed in the new handler. Use the supplied source/tests and established API behavior\nto identify a concrete input that could still violate the requested contract. A passing normal input does\nnot settle a different failure outcome of that same operation. A demonstrable material defect is FINDINGS; ask for\nan excerpt or result only when a specific material outcome cannot be settled. Do not invent new behavior,\nrequire an exhaustive exception inventory, or recommend catching every exception.\n\nReport FINDINGS only for concrete major or medium defects with a material impact on the original requirements,\npublic behavior, correctness or required validation. Name the consequence and smallest necessary fix.\nDo not turn minor style, wording, optional improvements or speculative edge cases into FINDINGS or EVIDENCE_GAPS.\nUse read/search to settle missing context in the current review. If a consequential uncertainty remains,\nrecord EVIDENCE_GAPS as an advisory limitation, not a demand for another evidence packet. Missing narration,\nrequirement mappings, hashes or clipped excerpts alone never justify a blocking finding.\n\nAssess behavioral requirements against actual source, tests and observed results. A required check known to\nhave failed or not run is a concrete finding; missing documentation of a check is not proof it did not run.\nFor incidental workflow constraints such as cache settings or command-path spelling, use existing host observations;\nabsence of a separate settings dump or historical log is not itself an evidence gap. Flag observed material\ncontradictions. The Operator owns final comparison with the original request. If a missing check genuinely\naffects correctness or a requested deliverable, name that consequence and the smallest useful next check.\nOn verification, compare the correction and its affected public outcomes with the original contract, not\nonly the wording of previous findings. Reuse unchanged evidence; do not expand into optional improvements\nor an exhaustive input matrix. PASS means no material finding, not exhaustive proof of every path.";
16
20
  /** Mission Reviewer uses ordinary host read/search permissions, not a supplied-packet-only protocol. */
@@ -3,6 +3,13 @@ import { STRATEGY_TRIGGERS, SOURCE_REVIEW_RISK_TAGS } from "./core/consultation.
3
3
  import { SCOUT_EVIDENCE_CODES } from "./core/scout-contract.js";
4
4
  /** Repository-local dependency environment shared by all units; excluded from reviewed and captured source. */
5
5
  export const TOOL_ENVIRONMENT = ".sortie-env";
6
+ /** Task-specific environment limits qualify general dependency setup guidance. */
7
+ export const MISSION_DEPENDENCY_WORKFLOW = `Honor original dependency limits in setup and unit objectives. If the user permits installs only for
8
+ proven-missing packages, an installed but incompatible package is not missing: do not replace it or
9
+ describe it as missing compatible tooling. General registry/compatible-version guidance does not widen
10
+ that task-specific limit. Reuse a supplied environment; create one only when the original request permits.
11
+ Otherwise carry out authorized dependency repair normally. If required behavior remains unverified under
12
+ those limits, retain in-scope work and report the limitation, not successful verification.`;
6
13
  /** Ordinary requested Git delivery uses source scope, not repository control-storage scope. */
7
14
  export const MISSION_GIT_SCOPE = `Requested git add <paths> (with optional --) and git commit -m ... are normal source-scope Git operations;
8
15
  they do not require .git/** scope. Preserve explicit user ordering and host Git lifecycle; attempt supported
@@ -13,6 +20,14 @@ export const VALIDATION_WORKFLOW = `## Time-aware validation workflow
13
20
 
14
21
  Operator/Coordinator: put the known meaningful formal check from the user, project or task context in validation,
15
22
  not another objective copy. A literal command in the original request is not required; empty or dummy checks do not qualify.
23
+ For a concrete public reproduction, carry its entrypoint and relevant input into the unit objective and formal reproduction check.
24
+ Preserve its fixture layout and working directory. A standalone example project belongs in its own fixture root within allowed paths;
25
+ invoke from there with the active environment, not the source checkout root where import paths or configuration may mask the failure.
26
+ Derive the shell recipe without substituting lower-level library calls; those checks may supplement, never replace, the public entrypoint.
27
+ Reuse existing checks that already exercise it. If authorized setup cannot run it, retain useful work and report the verification gap, not a passing reproduction.
28
+ For direct work with an unknown reproduction recipe, resolve its flags, configuration and cwd with ordinary native diagnostics before formal registration.
29
+ Reuse an already observed failing baseline; do not rerun it merely to plan. A command that never exercises the reported path is not a passing reproduction.
30
+ Use existing project configuration within the original path constraints. Known recipes and Worker-owned investigation need no extra preparation or unit.
16
31
  Keep any user/project-required broad validation for the final integrated candidate,
17
32
  not every implementation unit. Do not add a full suite merely as a precaution or create a testing agent.
18
33
  Worker: reproduce, batch the related edits, inspect the complete diff, then run the focused checks.
@@ -89,6 +104,8 @@ ${controls(profile, ["start_mission", "plan_units", "start_direct_unit", "finish
89
104
  You are Operator, the user-facing strategic authority. Preserve every current requirement, prohibition,
90
105
  quality threshold and explicit model/budget choice. Follow AGENTS.md and use the user's language.
91
106
 
107
+ ${MISSION_DEPENDENCY_WORKFLOW}
108
+
92
109
  1. For an implementation or operation request, give at most three short lines, then call ${profile.toolPrefix}start_mission
93
110
  with a few concise one-line requirements including negative constraints. The host saves the original
94
111
  user message verbatim and recent public context and generates IDs; do not copy it or author contracts, hashes or a proposal.
@@ -233,6 +250,10 @@ existing user authorization and project gates; npm publication remains manual.
233
250
  }
234
251
  /** Keep representation oracles consistent in direct execution, Worker and Review. */
235
252
  export const MISSION_REPRESENTATION_ORACLE = `Same-type representation, not name/visitor analogy; resolve conflicts.`;
253
+ /** Coordinator authors and plans regressions without adding another validation/review lane. */
254
+ export const MISSION_REGRESSION_PLACEMENT = `Prefer an existing relevant test suite/subtest or fixture when it can express the regression assertion.
255
+ If a new test module is needed, prefer a distinctive behavior-specific name over automatically mirroring the implementation module.
256
+ Preserve established assertions and project test discovery. This is placement guidance, not another test, unit or review requirement.`;
236
257
  /** Shared by the installed Reviewer and its host-generated mission prompt. */
237
258
  export const MISSION_BEHAVIOR_REVIEW = `Establish the test oracle independently of the patch: tests added with the implementation are claims to
238
259
  review, not established API behavior. For a changed failure path, compare a pre-existing analogous public
@@ -445,7 +466,14 @@ existing analogous source/tests for the expected contract when available. Avoid
445
466
  units just to restate the issue. A test of a neighboring name is not an adjacent check unless it runs
446
467
  the changed branch on a relevant different input; keep validation focused and do not require an extra
447
468
  test when the existing checks already exercise that boundary.
469
+ When changing an error fallback, distinguish already handled errors from errors the called operation
470
+ can still raise. Already handled failures and successful neighbors do not establish that new boundary.
471
+ Repeating the reported error with another input does not cover still-unhandled related errors.
472
+ If peer source/tests expose a plausible related still-unhandled error, probe its expected fallback
473
+ before choosing the fix; reuse adequate checks. Include this in the current author's objective/check
474
+ plan, not another unit or review round. Do not invent an exhaustive error matrix.
448
475
  ${MISSION_REPRESENTATION_ORACLE}
476
+ ${MISSION_REGRESSION_PLACEMENT}
449
477
  For read-only verification, use write: []; do not invent an output file or request write access to inputs.
450
478
  If declared build or tests create known generated paths, include those outputs in the initial write scope;
451
479
  do not add a separate setup unit just to prepare them.
@@ -458,8 +486,9 @@ Use absolute native paths for requested global installations or other external o
458
486
  a directory including a not-yet-created tree. These are execution/evidence scopes, not an additional
459
487
  permission grant: the host's native permissions still apply. Include the actual external input/output
460
488
  paths in read/write so validation and review observe them; do not substitute a repository symlink.
489
+ ${MISSION_DEPENDENCY_WORKFLOW}
461
490
  Keep all original requirements covered. Last validation command checks
462
- that unit. If your quick check shows repository-declared dependencies or the test runner are missing, keep
491
+ that unit. If your quick check shows repository-declared dependencies or the test runner are missing and setup is allowed, keep
463
492
  setup inside the first unit: declare its checks through the repository-local tool environment ${TOOL_ENVIRONMENT}/
464
493
  (for Python, ${TOOL_ENVIRONMENT}/bin/python -m pytest ...) and let that Worker create it. Never plan a separate setup
465
494
  unit or put ${TOOL_ENVIRONMENT}/ in a write scope. Host generates IDs, proof mapping, handoff, manifest and Task references. Dispatch the returned
@@ -558,17 +587,18 @@ Reuse supplied AGENTS.md; for gaps prefer exact ancestor files/affected subtrees
558
587
  Read handoff_path in full first: task.objective, verbatim original_requests/unit_instruction, constraints/continuity,
559
588
  coverage. Preserve user scope and ordering; prove assigned criteria, not Mission completion.
560
589
  Denied: reason/remedy. No routine manifest/goal/status/bind.
561
- Recovery: ${profile.toolPrefix}bind_write_gate with exact project_root and manifest_path=operation_manifest.
562
- Treat cwd/project_root and paths as opaque; never shorten or normalize segments.
563
- After compaction recover handoff; inspect diff/results before repeats.
590
+ ${profile.toolPrefix}bind_write_gate with exact project_root and manifest_path=operation_manifest.
591
+ cwd/project_root/paths: opaque; never shorten or normalize segments.
592
+ After compaction recover handoff; inspect diff/results.
564
593
  Read/search: existing permissions. Fix unit.write/outputs/checks
565
- here via ${profile.toolPrefix}expand_unit/contract updates and host repair diagnostics;
594
+ here via ${profile.toolPrefix}expand_unit/contract updates; host repair diagnostics;
566
595
  no extra approval, restart or delegation. Keep prohibitions, host Git lifecycle, cumulative budget.
567
596
  Requested add/commit needs source paths, not .git/** scope.
568
- Use pre-change test helpers as oracles, not new implementation/tests. Check
569
- result/error/state together, without a hypothetical exhaustive matrix. Keep reproduction entrypoint/input/layout; rerun or report why unverified.
597
+ Pre-change oracles, not new tests; check result/error/state. Fallback: probe other unhandled errors from peer source/tests.
598
+ No exhaustive matrix. Keep reproduction entrypoint/input/layout; rerun or report why unverified.
570
599
  ${MISSION_REPRESENTATION_ORACLE}
571
- Missing tooling: one documented bounded setup in ${TOOL_ENVIRONMENT}/; reuse, never delete.
600
+ Use bounded documented setup in ${TOOL_ENVIRONMENT}/; reuse, never delete.
601
+ User missing-only: installed/incompatible is not missing.
572
602
  Per-call modes: check retained creation/use for captured vs current settings.
573
603
  Tests: existing suites/subtests or distinctive regression entry names.
574
604
  ${WORKER_VALIDATION_WORKFLOW}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sortie-dogs",
3
- "version": "0.13.13",
3
+ "version": "0.13.15",
4
4
  "description": "Bounded agent harness and validated orchestration loop plugin for OpenCode",
5
5
  "keywords": [
6
6
  "opencode",