sortie-dogs 0.9.10 → 0.9.11

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
@@ -20,9 +20,42 @@ bounded implementation, canonical validation, and evidence-backed completion.
20
20
 
21
21
  Requirements: Node.js 22.6 or newer, npm, and OpenCode.
22
22
 
23
- Guides: [日本語](docs/guide-ja.md) · [简体中文](docs/guide-zh-CN.md) · [CLI testing](docs/cli-testing.md)
23
+ Guides: [日本語](docs/guide-ja.md) · [简体中文](docs/guide-zh-CN.md) · [テスト実行](docs/testing.md) · [CLI testing](docs/cli-testing.md)
24
24
 
25
- Release: [v0.9.10](https://github.com/zufall-upon/Sortie-dogs/releases/tag/v0.9.10)
25
+ Release: [v0.9.11](https://github.com/zufall-upon/Sortie-dogs/releases/tag/v0.9.11)
26
+
27
+ ## Provisional quality–cost position
28
+
29
+ **Reference values, not a successful benchmark claim.** Quality and end-to-end completion
30
+ problems remain. The latest completed qualification attempt ended at `IN_PROGRESS`, so its official
31
+ verifier was not run. Further benchmarks are frozen while completion defects are repaired.
32
+
33
+ The last complete measured **Bare OpenCode vs Sortie** pair below used one frozen task,
34
+ `datacurve/anko-typed-variable-bindings`, on 2026-09-10. It used Sortie **v0.9.5**, not the
35
+ current release. Both candidates failed the official verifier.
36
+
37
+ | Metric · one task, one trial per arm | Bare OpenCode | Sortie v0.9.5 |
38
+ | --- | ---: | ---: |
39
+ | Verified PASS | 0/1 | 0/1 |
40
+ | Task-check completion · F2P | 55.6% · 5/9 | 88.9% · 8/9 |
41
+ | Retained checks · P2P | 94/94 | 93/94 |
42
+ | Estimated API-equivalent total cost | $5.42 | $1.46 |
43
+ | Median agent wall · n=1 | 28.7 min | 10.0 min |
44
+ | Premium-model token share · Sol | 100% | 20.1% |
45
+
46
+ Later Sortie-only evidence is weaker: the v0.9.9 recovery candidate passed **5/9** task
47
+ checks with **0/1 Verified PASS**; `0.9.11-bench.2` did not reach a gradeable completion.
48
+ Those attempts are not pooled into the historical pair above.
49
+
50
+ ![Historical quality–cost reference: Bare at $5.42 and 55.6% task-check completion; Sortie v0.9.5 at $1.46 and 88.9%. Neither achieved Verified PASS.](docs/assets/quality-cost-reference.svg)
51
+
52
+ The goal is **higher OpenCode task success with selective use of premium models**.
53
+ These reference observations do not yet establish that success-rate claim: Sortie missed
54
+ one task check and regressed one retained check. Codex, Pi, and Oh My OpenCode belong to
55
+ separate methodologies and are not assigned comparable positions on this chart.
56
+
57
+ [Definitions, frozen inputs, current failure status, and limitations](docs/benchmark-reference.md)
58
+ · [Machine-readable reference values](docs/benchmarks/provisional-reference.json)
26
59
 
27
60
  ## Why Sortie-dogs?
28
61
 
@@ -2,5 +2,5 @@
2
2
  * Version of the installable runtime assets. Kept in its own module so the plugin can compare an
3
3
  * installed project marker without importing every asset body.
4
4
  */
5
- export declare const RUNTIME_ASSET_VERSION = "0.3.86-codegen-proof-v1";
5
+ export declare const RUNTIME_ASSET_VERSION = "0.3.89-completion-proof-v1";
6
6
  export type RuntimeAssetVersion = typeof RUNTIME_ASSET_VERSION;
@@ -2,4 +2,4 @@
2
2
  * Version of the installable runtime assets. Kept in its own module so the plugin can compare an
3
3
  * installed project marker without importing every asset body.
4
4
  */
5
- export const RUNTIME_ASSET_VERSION = "0.3.86-codegen-proof-v1";
5
+ export const RUNTIME_ASSET_VERSION = "0.3.89-completion-proof-v1";
@@ -978,11 +978,11 @@ async function stagedPaths(context) {
978
978
  return new Set(fields);
979
979
  }
980
980
  async function assertNoSubmodules(context, entries) {
981
- for (const entry of entries) {
982
- const index = await context.git(["ls-files", "--stage", "-z", "--", entry.path]);
983
- if (index.toString("utf8").startsWith("160000 ")) {
984
- throw new WorktreeCommitArtifactError("invalid-state", "Submodule changes are forbidden.");
985
- }
981
+ if (entries.length === 0)
982
+ return;
983
+ const index = await context.git(["ls-files", "--stage", "-z", "--", ...entries.map(({ path }) => path)]);
984
+ if (index.toString("utf8").split("\0").some((entry) => entry.startsWith("160000 "))) {
985
+ throw new WorktreeCommitArtifactError("invalid-state", "Submodule changes are forbidden.");
986
986
  }
987
987
  }
988
988
  function decodeGitOutput(source, message) {
@@ -1261,7 +1261,7 @@ export async function produceWorktreeCommitArtifact(request, control) {
1261
1261
  throw new WorktreeCommitArtifactError("validation-failed", "Validation was cancelled.");
1262
1262
  control?.enterProtectedPhase();
1263
1263
  await context.git(["add", "--", ...before.map(({ path }) => path)]);
1264
- await assertValidatedIndex(context, before, beforeFingerprint);
1264
+ // Keep one fresh index check after staging, immediately before the protected commit.
1265
1265
  await assertValidatedIndex(context, before, beforeFingerprint);
1266
1266
  await context.git([
1267
1267
  "-c", "user.name=Sortie Fabric",
@@ -2138,57 +2138,79 @@ export class ParallelDispatchCoordinator {
2138
2138
  };
2139
2139
  }
2140
2140
  async buildFabricCandidate(runID, base, tasks) {
2141
+ if (tasks.length === 0)
2142
+ return base;
2141
2143
  let head = base;
2142
- for (const task of tasks) {
2143
- const artifact = task.artifact;
2144
- const nonce = randomUUID();
2145
- const indexPath = join(this.stateRoot, `.fabric-index-${nonce}`);
2146
- const patchPath = join(this.stateRoot, `.fabric-patch-${nonce}`);
2147
- const messagePath = join(this.stateRoot, `.fabric-message-${nonce}`);
2148
- const indexEnvironment = this.cleanGitEnvironment({ GIT_INDEX_FILE: indexPath });
2149
- try {
2150
- await this.gitBuffer(["read-tree", head], indexEnvironment);
2151
- const patch = await this.gitBuffer([
2152
- "diff-tree", "-p", "--binary", "--full-index", "--no-ext-diff", "--no-renames",
2153
- artifact.base_sha, artifact.commit_sha, "--",
2154
- ]);
2155
- if (patch.byteLength > 8 * 1024 * 1024) {
2156
- throw new ParallelDispatchError("wave-integration-failed", "Fabric artifact patch exceeds the practical bound.");
2157
- }
2158
- await writeFile(patchPath, patch, { flag: "wx", mode: 0o600 });
2159
- await this.gitBuffer(["apply", "--cached", "--3way", "--binary", patchPath], indexEnvironment);
2160
- const tree = (await this.gitBuffer(["write-tree"], indexEnvironment)).toString("utf8").trim();
2161
- const timestamp = (await this.gitBuffer(["show", "-s", "--format=%ct", artifact.commit_sha])).toString("utf8").trim();
2162
- if (!SHA.test(tree) || !/^\d{1,12}$/u.test(timestamp))
2144
+ const indexPath = join(this.stateRoot, `.fabric-index-${randomUUID()}`);
2145
+ const indexEnvironment = this.cleanGitEnvironment({ GIT_INDEX_FILE: indexPath });
2146
+ try {
2147
+ // Commit objects are immutable; collect their timestamps once for this build only.
2148
+ const commits = [...new Set(tasks.map((task) => task.artifact.commit_sha))];
2149
+ const metadata = (await this.gitBuffer([
2150
+ "show", "-s", "--no-walk=unsorted", "--format=%H%x00%ct", ...commits, "--",
2151
+ ])).toString("utf8").trimEnd();
2152
+ const timestamps = new Map();
2153
+ for (const line of metadata.split(/\r?\n/u)) {
2154
+ const fields = line.split("\0");
2155
+ if (fields.length !== 2 || !SHA.test(fields[0]) || !/^\d{1,12}$/u.test(fields[1]) || timestamps.has(fields[0])) {
2163
2156
  throw new Error("identity");
2164
- const message = `Sortie fabric integration: ${task.descriptor.task_id}\n\n` +
2165
- `Sortie-Run: ${runID}\nSortie-Task: ${task.descriptor.task_id}\n` +
2166
- `Sortie-Artifact: ${artifact.commit_sha}\nSortie-Base: ${artifact.base_sha}\n`;
2167
- await writeFile(messagePath, message, { flag: "wx", mode: 0o600 });
2168
- const commitEnvironment = this.cleanGitEnvironment({
2169
- GIT_AUTHOR_NAME: "Sortie Fabric",
2170
- GIT_AUTHOR_EMAIL: "sortie@example.invalid",
2171
- GIT_COMMITTER_NAME: "Sortie Fabric",
2172
- GIT_COMMITTER_EMAIL: "sortie@example.invalid",
2173
- GIT_AUTHOR_DATE: `${timestamp} +0000`,
2174
- GIT_COMMITTER_DATE: `${timestamp} +0000`,
2175
- });
2176
- const commit = (await this.gitBuffer(["commit-tree", tree, "-p", head, "-F", messagePath], commitEnvironment))
2177
- .toString("utf8").trim();
2178
- if (!SHA.test(commit))
2179
- throw new Error("commit");
2180
- head = commit;
2181
- }
2182
- catch (error) {
2183
- if (error instanceof ParallelDispatchError)
2184
- throw error;
2185
- throw new ParallelDispatchError("wave-integration-failed", "Fabric artifact could not be applied to the hidden candidate.");
2157
+ }
2158
+ timestamps.set(fields[0], fields[1]);
2186
2159
  }
2187
- finally {
2188
- await Promise.all([indexPath, `${indexPath}.lock`, patchPath, messagePath].map((path) => rm(path, { force: true }).catch(() => undefined)));
2160
+ if (timestamps.size !== commits.length || commits.some((commit) => !timestamps.has(commit)))
2161
+ throw new Error("identity");
2162
+ await this.gitBuffer(["read-tree", base], indexEnvironment);
2163
+ for (const task of tasks) {
2164
+ const artifact = task.artifact;
2165
+ const nonce = randomUUID();
2166
+ const patchPath = join(this.stateRoot, `.fabric-patch-${nonce}`);
2167
+ const messagePath = join(this.stateRoot, `.fabric-message-${nonce}`);
2168
+ try {
2169
+ const patch = await this.gitBuffer([
2170
+ "diff-tree", "-p", "--binary", "--full-index", "--no-ext-diff", "--no-renames",
2171
+ artifact.base_sha, artifact.commit_sha, "--",
2172
+ ]);
2173
+ if (patch.byteLength > 8 * 1024 * 1024) {
2174
+ throw new ParallelDispatchError("wave-integration-failed", "Fabric artifact patch exceeds the practical bound.");
2175
+ }
2176
+ await writeFile(patchPath, patch, { flag: "wx", mode: 0o600 });
2177
+ await this.gitBuffer(["apply", "--cached", "--3way", "--binary", patchPath], indexEnvironment);
2178
+ const tree = (await this.gitBuffer(["write-tree"], indexEnvironment)).toString("utf8").trim();
2179
+ const timestamp = timestamps.get(artifact.commit_sha);
2180
+ if (!SHA.test(tree) || timestamp === undefined)
2181
+ throw new Error("identity");
2182
+ const message = `Sortie fabric integration: ${task.descriptor.task_id}\n\n` +
2183
+ `Sortie-Run: ${runID}\nSortie-Task: ${task.descriptor.task_id}\n` +
2184
+ `Sortie-Artifact: ${artifact.commit_sha}\nSortie-Base: ${artifact.base_sha}\n`;
2185
+ await writeFile(messagePath, message, { flag: "wx", mode: 0o600 });
2186
+ const commitEnvironment = this.cleanGitEnvironment({
2187
+ GIT_AUTHOR_NAME: "Sortie Fabric",
2188
+ GIT_AUTHOR_EMAIL: "sortie@example.invalid",
2189
+ GIT_COMMITTER_NAME: "Sortie Fabric",
2190
+ GIT_COMMITTER_EMAIL: "sortie@example.invalid",
2191
+ GIT_AUTHOR_DATE: `${timestamp} +0000`,
2192
+ GIT_COMMITTER_DATE: `${timestamp} +0000`,
2193
+ });
2194
+ const commit = (await this.gitBuffer(["commit-tree", tree, "-p", head, "-F", messagePath], commitEnvironment))
2195
+ .toString("utf8").trim();
2196
+ if (!SHA.test(commit))
2197
+ throw new Error("commit");
2198
+ head = commit;
2199
+ }
2200
+ finally {
2201
+ await Promise.all([patchPath, messagePath].map((path) => rm(path, { force: true }).catch(() => undefined)));
2202
+ }
2189
2203
  }
2204
+ return head;
2205
+ }
2206
+ catch (error) {
2207
+ if (error instanceof ParallelDispatchError)
2208
+ throw error;
2209
+ throw new ParallelDispatchError("wave-integration-failed", "Fabric artifact could not be applied to the hidden candidate.");
2210
+ }
2211
+ finally {
2212
+ await Promise.all([indexPath, `${indexPath}.lock`].map((path) => rm(path, { force: true }).catch(() => undefined)));
2190
2213
  }
2191
- return head;
2192
2214
  }
2193
2215
  cleanGitEnvironment(extra = {}) {
2194
2216
  const environment = { ...process.env };
@@ -2054,6 +2054,9 @@ export const SortieDogsPlugin = async (input, options) => {
2054
2054
  entry.execution.units.includes(reservation.unitID));
2055
2055
  const newEvidence = acceptedEvidence.filter((entry) => entry.measurement.criterion_ids.some((criterionID) => !state.satisfied_criteria.includes(criterionID)));
2056
2056
  const progress = newEvidence.length > 0;
2057
+ // Revalidation can succeed for an already-satisfied criterion after a candidate change.
2058
+ // New criterion coverage controls progress accounting, not the validation disposition.
2059
+ const validated = acceptedEvidence.length > 0;
2057
2060
  const metadata = isRecord(output.metadata) ? output.metadata : undefined;
2058
2061
  const interrupted = metadata?.status === "cancel" || metadata?.status === "cancelled" ||
2059
2062
  output.status === "cancel" || output.status === "cancelled";
@@ -2063,12 +2066,12 @@ export const SortieDogsPlugin = async (input, options) => {
2063
2066
  execution.endedAt !== undefined && Date.parse(execution.startedAt) >= reservation.started - 1000 &&
2064
2067
  execution.outcome === "fail");
2065
2068
  const processDefect = !failedAcceptanceExecution && (childSessionID === undefined || hostBindingDefect ||
2066
- goalValidationDefects.has(childSessionID));
2067
- const resultClass = progress ? "acceptance" : interrupted ? "interrupted" : processDefect ? "process-defect" : "acceptance";
2069
+ goalValidationDefects.has(childSessionID) || !validated);
2070
+ const resultClass = validated ? "acceptance" : interrupted ? "interrupted" : processDefect ? "process-defect" : "acceptance";
2068
2071
  await ledger.appendGoal({ kind: "unit.settled", at: new Date().toISOString(),
2069
2072
  reservation_id: reservation.reservationID, receipt_id: goalFingerprint({ call_id: callID, output: outputText.slice(0, 2048) }),
2070
2073
  goal_id: state.goal_id, unit_id: reservation.unitID,
2071
- disposition: progress ? "succeeded" : interrupted ? "cancelled" : "failed", result_class: resultClass,
2074
+ disposition: validated ? "succeeded" : interrupted ? "cancelled" : "failed", result_class: resultClass,
2072
2075
  progress_fingerprint: progress ? goalFingerprint(acceptedEvidence) : null,
2073
2076
  evidence: acceptedEvidence, elapsed_ms: Math.max(0, Date.now() - reservation.started), cost_usd: null });
2074
2077
  if (childSessionID !== undefined) {
@@ -7,7 +7,7 @@ export interface RuntimeAsset {
7
7
  }
8
8
  export declare const runtimeAssets: readonly [{
9
9
  readonly name: "dog-coordinator";
10
- readonly version: "0.3.86-codegen-proof-v1";
10
+ readonly version: "0.3.89-completion-proof-v1";
11
11
  readonly installPath: "agent/dog-coordinator.md";
12
12
  readonly content: `---
13
13
  description: Canonical MkII coordinator packaged by Sortie-dogs
@@ -1391,6 +1391,9 @@ criterion -> changed or inspected implementation path -> concrete exercising tes
1391
1391
  An aggregate validation command without that mapping is insufficient. Multi-form criteria must cover their
1392
1392
  materially distinct syntax, value-shape, scope, and error paths; any missing path remains UNPROVEN and requires
1393
1393
  continued implementation or validation. Include the complete trace in every high-risk SourceReview artifact.
1394
+ For accepted failure behavior, require result/error/state evidence and a valid case; for accepted composite
1395
+ or wrapped-value behavior, require public-entry-point evidence. Accept justified N/A dimensions and existing
1396
+ sufficient evidence without extra validation or review. Do not accept expectations copied from the candidate's output.
1394
1397
  Any generated input/output pair in the changed manifest is high risk and requires SourceReview. DONE requires
1395
1398
  generator command evidence, post-generation candidate identity, generated-output stability, and canonical
1396
1399
  validation after generation. Reject evidence produced only before regeneration.
@@ -1468,10 +1471,10 @@ TERMINAL_STATUS_SEMANTICS_FIXTURE
1468
1471
  END_TERMINAL_STATUS_SEMANTICS_FIXTURE
1469
1472
 
1470
1473
  RUNTIME_ASSET_VERSION_SYNC_FIXTURE
1471
- runtime_version: 0.3.86-codegen-proof-v1
1474
+ runtime_version: 0.3.89-completion-proof-v1
1472
1475
  shared_marker: src/asset-version.ts
1473
- packaged_expectation: test/plugin-loader.test.ts uses 0.3.86-codegen-proof-v1
1474
- initialize_expectation: test/initialize.test.ts uses 0.3.86-codegen-proof-v1
1476
+ packaged_expectation: test/plugin-loader.test.ts uses 0.3.89-completion-proof-v1
1477
+ initialize_expectation: test/initialize.test.ts uses 0.3.89-completion-proof-v1
1475
1478
  rule: runtime asset versions, shared marker, packaged expectation, and initialize expectation change together
1476
1479
  END_RUNTIME_ASSET_VERSION_SYNC_FIXTURE
1477
1480
 
@@ -1494,32 +1497,32 @@ END_INTERNAL_TERMINAL_PROOF_FIXTURE
1494
1497
  `;
1495
1498
  }, {
1496
1499
  readonly name: "dog-worker";
1497
- readonly version: "0.3.86-codegen-proof-v1";
1500
+ readonly version: "0.3.89-completion-proof-v1";
1498
1501
  readonly installPath: "agent/dog-worker.md";
1499
1502
  readonly content: string;
1500
1503
  }, {
1501
1504
  readonly name: "dog-luna-worker";
1502
- readonly version: "0.3.86-codegen-proof-v1";
1505
+ readonly version: "0.3.89-completion-proof-v1";
1503
1506
  readonly installPath: "agent/dog-luna-worker.md";
1504
1507
  readonly content: string;
1505
1508
  }, {
1506
1509
  readonly name: "dog-scout";
1507
- readonly version: "0.3.86-codegen-proof-v1";
1510
+ readonly version: "0.3.89-completion-proof-v1";
1508
1511
  readonly installPath: "agent/dog-scout.md";
1509
1512
  readonly content: "---\ndescription: Bounded evidence scout for dog-coordinator\nmode: subagent\nsteps: 8\npermission:\n bash: deny\n webfetch: deny\n task: deny\n question: deny\n glob: deny\n grep: deny\n edit: deny\n list: deny\n write: deny\n patch: deny\ntools:\n bash: false\n webfetch: false\n task: false\n question: false\n glob: false\n grep: false\n edit: false\n list: false\n write: false\n patch: false\n---\n# dog-scout\n\nAccept one concrete missing_evidence_code: manifest, validation, or owner-risk. Accept only an\nexplicit absolute project_root and a known_paths list of at most four paths from dog-coordinator.\nResolve only that evidence key from those paths under project_root; never resolve a path against the\nsession directory. Use Read only, with at most 120 lines and no more than one read per supplied path.\nDo not resolve a second key, explore, invoke another tool, retry, edit, stage, commit, or become user-facing.\n\nWhen project_root is missing, or a supplied path does not resolve under it, or a resolved path is\nunreadable, report that dispatch defect as the facts for the requested key and name the exact paths.\nDo not retry, guess another root, or answer from an unread path.\n\nReturn exactly one concise JSON object of at most 800 characters with exactly these keys:\nmissing_evidence_code, facts, evidence_paths, risks. Use no Markdown, code fence, commentary, or raw log. Return it only\nto dog-coordinator. Write the facts and risks prose in the language the dispatch uses for its own\nprose; keep the keys, paths, commands, and identifiers verbatim.\n";
1510
1513
  }, {
1511
1514
  readonly name: "dog-reviewer";
1512
- readonly version: "0.3.86-codegen-proof-v1";
1515
+ readonly version: "0.3.89-completion-proof-v1";
1513
1516
  readonly installPath: "agent/dog-reviewer.md";
1514
- readonly content: "---\ndescription: Independent source reviewer for dog-coordinator\nmode: subagent\npermission:\n bash: deny\n webfetch: deny\n task: deny\n question: deny\n glob: deny\n grep: deny\n edit: deny\n list: deny\n write: deny\n patch: deny\n read: deny\ntools:\n bash: false\n webfetch: false\n task: false\n question: false\n glob: false\n grep: false\n edit: false\n list: false\n write: false\n patch: false\n read: false\n---\n# dog-reviewer\n\nAccept only one bounded SourceReview request from dog-coordinator after canonical\nvalidation for one high-risk candidate. Review only the supplied acceptance criteria, exact\nmanifest, changedLogicSummary, supplied changed-code excerpts, and validation evidence. Confirm every acceptance item explicitly\nmaps to at least one changedLogicSummary entry and assess that changed logic against the mapped\nacceptance item. Missing or incomplete coverage is a concrete finding, never PASS.\nRequire one indexed acceptance[i] -> changedLogicSummary[j] mapping line per acceptance item and\nreject a missing index or unequal mapping count before assessing the changed logic.\nAlso require each acceptance item to map to a concrete exercising test/input/branch and result. A broad\nsuite PASS without criterion-level exercise evidence is insufficient. When one item contains materially\ndifferent syntax forms, value shapes, scopes, or error paths, reject PASS unless representative traces cover\neach path or the artifact proves they share one implementation path.\nWhen changed files include generator inputs or checked-in generated outputs, require the canonical generator\ncommand, a stable post-generation diff, and validation executed after generation. Reject PASS if helper logic\nexists only in a generated output, regeneration removes behavior, or validation predates the generated candidate.\nDo not request raw logs or full source files, review low-risk candidates, expand scope, or dispatch\nanother agent.\nTreat those supplied fields as the complete bounded SourceReview artifact; use only that artifact and invoke no tools.\nDo not infer that a branch or exemption is absent from source because a prose summary omits it.\nIf the supplied excerpts do not establish a claim, report an evidence gap and request the exact\nbranch/helper excerpt in the next artifact; do not prescribe a source fix for an unproven defect.\n\nReturn one concise PASS or concrete-finding response only to dog-coordinator before the\ncoordinator commit. Write every finding, evidence, and required-fix sentence in the language the\nsupplied artifact uses for its own prose, one statement per line, and keep verdict values,\nidentifiers, paths, and commands verbatim. Do not implement, remediate, resolve blockers, edit,\nstage, commit, or become user-facing. Remain host-routed: do not require or identify a provider, vendor, model, variant,\nor transport.\n";
1517
+ readonly content: "---\ndescription: Independent source reviewer for dog-coordinator\nmode: subagent\npermission:\n bash: deny\n webfetch: deny\n task: deny\n question: deny\n glob: deny\n grep: deny\n edit: deny\n list: deny\n write: deny\n patch: deny\n read: deny\ntools:\n bash: false\n webfetch: false\n task: false\n question: false\n glob: false\n grep: false\n edit: false\n list: false\n write: false\n patch: false\n read: false\n---\n# dog-reviewer\n\nAccept only one bounded SourceReview request from dog-coordinator after canonical\nvalidation for one high-risk candidate. Review only the supplied acceptance criteria, exact\nmanifest, changedLogicSummary, supplied changed-code excerpts, and validation evidence. Confirm every acceptance item explicitly\nmaps to at least one changedLogicSummary entry and assess that changed logic against the mapped\nacceptance item. Missing or incomplete coverage is a concrete finding, never PASS.\nRequire one indexed acceptance[i] -> changedLogicSummary[j] mapping line per acceptance item and\nreject a missing index or unequal mapping count before assessing the changed logic.\nAlso require each acceptance item to map to a concrete exercising test/input/branch and result. A broad\nsuite PASS without criterion-level exercise evidence is insufficient. When one item contains materially\ndifferent syntax forms, value shapes, scopes, or error paths, reject PASS unless representative traces cover\neach path or the artifact proves they share one implementation path.\nCheck contract-derived expectations only for accepted behavior. For an applicable failure or composite-value\ncriterion, error-only or helper-only assertions can leave public result/state behavior unproved. Accept justified\nN/A dimensions; do not demand new behavior, new review rounds, or redundant checks outside accepted scope.\nWhen changed files include generator inputs or checked-in generated outputs, require the canonical generator\ncommand, a stable post-generation diff, and validation executed after generation. Reject PASS if helper logic\nexists only in a generated output, regeneration removes behavior, or validation predates the generated candidate.\nDo not request raw logs or full source files, review low-risk candidates, expand scope, or dispatch\nanother agent.\nTreat those supplied fields as the complete bounded SourceReview artifact; use only that artifact and invoke no tools.\nDo not infer that a branch or exemption is absent from source because a prose summary omits it.\nIf the supplied excerpts do not establish a claim, report an evidence gap and request the exact\nbranch/helper excerpt in the next artifact; do not prescribe a source fix for an unproven defect.\n\nReturn one concise PASS or concrete-finding response only to dog-coordinator before the\ncoordinator commit. Write every finding, evidence, and required-fix sentence in the language the\nsupplied artifact uses for its own prose, one statement per line, and keep verdict values,\nidentifiers, paths, and commands verbatim. Do not implement, remediate, resolve blockers, edit,\nstage, commit, or become user-facing. Remain host-routed: do not require or identify a provider, vendor, model, variant,\nor transport.\n";
1515
1518
  }, {
1516
1519
  readonly name: "dog-advisor";
1517
- readonly version: "0.3.86-codegen-proof-v1";
1520
+ readonly version: "0.3.89-completion-proof-v1";
1518
1521
  readonly installPath: "agent/dog-advisor.md";
1519
1522
  readonly content: "---\ndescription: Focused technical advisor for dog-coordinator\nmode: subagent\npermission:\n bash: deny\n webfetch: deny\n task: deny\n question: deny\n glob: deny\n grep: deny\n edit: deny\n list: deny\n write: deny\n patch: deny\n read: deny\ntools:\n bash: false\n webfetch: false\n task: false\n question: false\n glob: false\n grep: false\n edit: false\n list: false\n write: false\n patch: false\n read: false\n---\n# dog-advisor\n\nAccept only one bounded Strategy request from dog-coordinator for one candidate and one focused\nquestion. Use only the supplied acceptance criteria, exact manifest, constraints, and concise\nevidence. Do not request raw logs or full source files, expand scope, or dispatch another agent.\nTreat those supplied fields as the complete bounded Strategy artifact; use only that artifact and invoke no tools.\nReject every SourceReview request and return the rejection only to dog-coordinator; SourceReview is\ndog-reviewer-only work.\n\nReturn concise options and one recommendation only to dog-coordinator. Write every option,\nrecommendation, and consideration in the language the supplied request uses for its own prose, one\nstatement per line, and keep identifiers, paths, and commands verbatim. Do not perform\nSourceReview, implement, remediate, resolve blockers, edit, stage, commit, or become user-facing.\nImplementation remains dog-worker work. Remain host-routed: do not require or identify a\nprovider, vendor, model, variant, or transport.\n";
1520
1523
  }, {
1521
1524
  readonly name: "sortie";
1522
- readonly version: "0.3.86-codegen-proof-v1";
1525
+ readonly version: "0.3.89-completion-proof-v1";
1523
1526
  readonly installPath: "command/sortie.md";
1524
1527
  readonly content: "---\ndescription: Start the canonical Sortie-dogs MkII workflow\nagent: dog-coordinator\n---\nRequest: $ARGUMENTS\n\n1. If $ARGUMENTS is empty, request task context and stop; give project init guidance first.\n2. Do not preflight installed runtime assets. The plugin reports version skew without adding model\n turns; proceed from task evidence and project instructions.\n3. On restart or re-entry, reconstruct context from project-local durable artifacts and the\n latest bounded handoff or checkpoint. Preserve both manifests and ordered validation history;\n resume the same task through dog-coordinator with only the required delta.\n4. Otherwise transfer request and project context to dog-coordinator. Frontmatter is the single coordinator\n transfer; never route a worker to the user.\n";
1525
1528
  }];
@@ -1,5 +1,5 @@
1
1
  import { GOAL_DECLARATION_FORMAT } from "./core/goal-declaration-format.js";
2
- const ASSET_VERSION = "0.3.86-codegen-proof-v1";
2
+ const ASSET_VERSION = "0.3.89-completion-proof-v1";
3
3
  // Kept local so source-mode CLI execution does not load the plugin graph.
4
4
  const BACKLOG_DRAIN_CAPABILITY = "sortie_enable_backlog_drain";
5
5
  const PARALLEL_PREPARE_CAPABILITY = "sortie_prepare_parallel_dispatch";
@@ -101,6 +101,8 @@ Validation budget exhaustion, host counters, local routing, and unavailable host
101
101
  defects, never TRUE_BLOCKER: external and never a reason to ask the user for an internal route. Return the
102
102
  typed defect to dog-coordinator for autonomous repair. A changed candidate may run the next declared
103
103
  validation; an unchanged duplicate remains forbidden.
104
+ Run the exact declared canonical validation string in its own tool call. Do not prepend or append
105
+ formatting, generation, or cleanup commands: host evidence must match the declared command boundary.
104
106
 
105
107
  Before returning canonical PASS, build a criterion-level trace for every accepted criterion. Each trace
106
108
  must name the criterion, the changed implementation path or inspected existing path, the concrete test
@@ -108,6 +110,11 @@ case/input form or static branch that exercises it, and PASS or UNPROVEN. A broa
108
110
  not prove every criterion. Split criteria that cover multiple syntax forms, value shapes, scopes, or error
109
111
  paths into representative paths. If any accepted edge remains UNPROVEN, add a manifest-authorized check or
110
112
  return the evidence gap; never report completion from aggregate validation alone.
113
+ Derive expected behavior from the accepted contract and existing public semantics, not the candidate.
114
+ Only when an accepted criterion covers failure behavior, check the public return/result, error, and
115
+ observable state together, paired with a valid case. For an accepted composite or wrapped-value feature,
116
+ exercise its public entry point as well as its helper. Mark inapplicable dimensions N/A with a short reason;
117
+ do not invent failure behavior, widen acceptance, or repeat a proved check to satisfy this guidance.
111
118
  Treat generated-source boundaries as high risk. If a manifest changes a generator input, grammar, schema,
112
119
  template, or a checked-in generated output, identify the repository's canonical generator and run it before
113
120
  the final validation. Prove the regenerated output is stable and that canonical validation ran against that
@@ -1613,6 +1620,9 @@ criterion -> changed or inspected implementation path -> concrete exercising tes
1613
1620
  An aggregate validation command without that mapping is insufficient. Multi-form criteria must cover their
1614
1621
  materially distinct syntax, value-shape, scope, and error paths; any missing path remains UNPROVEN and requires
1615
1622
  continued implementation or validation. Include the complete trace in every high-risk SourceReview artifact.
1623
+ For accepted failure behavior, require result/error/state evidence and a valid case; for accepted composite
1624
+ or wrapped-value behavior, require public-entry-point evidence. Accept justified N/A dimensions and existing
1625
+ sufficient evidence without extra validation or review. Do not accept expectations copied from the candidate's output.
1616
1626
  Any generated input/output pair in the changed manifest is high risk and requires SourceReview. DONE requires
1617
1627
  generator command evidence, post-generation candidate identity, generated-output stability, and canonical
1618
1628
  validation after generation. Reject evidence produced only before regeneration.
@@ -1821,6 +1831,9 @@ Also require each acceptance item to map to a concrete exercising test/input/bra
1821
1831
  suite PASS without criterion-level exercise evidence is insufficient. When one item contains materially
1822
1832
  different syntax forms, value shapes, scopes, or error paths, reject PASS unless representative traces cover
1823
1833
  each path or the artifact proves they share one implementation path.
1834
+ Check contract-derived expectations only for accepted behavior. For an applicable failure or composite-value
1835
+ criterion, error-only or helper-only assertions can leave public result/state behavior unproved. Accept justified
1836
+ N/A dimensions; do not demand new behavior, new review rounds, or redundant checks outside accepted scope.
1824
1837
  When changed files include generator inputs or checked-in generated outputs, require the canonical generator
1825
1838
  command, a stable post-generation diff, and validation executed after generation. Reject PASS if helper logic
1826
1839
  exists only in a generated output, regeneration removes behavior, or validation predates the generated candidate.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sortie-dogs",
3
- "version": "0.9.10",
3
+ "version": "0.9.11",
4
4
  "description": "Bounded agent harness and validated orchestration loop plugin for OpenCode",
5
5
  "keywords": [
6
6
  "opencode",