sortie-dogs 0.13.4 → 0.13.5
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 +24 -8
- package/dist/asset-version.d.ts +1 -1
- package/dist/asset-version.js +1 -1
- package/dist/core/goal-bound.d.ts +3 -0
- package/dist/core/goal-bound.js +1 -0
- package/dist/core/operator-mission.js +5 -4
- package/dist/plugin/index.js +13 -1
- package/dist/plugin/model-live-state.d.ts +15 -0
- package/dist/plugin/model-live-state.js +65 -0
- package/dist/plugin/profiled.js +7 -3
- package/dist/plugin/protected-snapshot.js +16 -11
- package/dist/plugin/v2.js +35 -7
- package/dist/runtime-mission-assets.d.ts +2 -0
- package/dist/runtime-mission-assets.js +20 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,10 +35,10 @@ implementation, validation, review, and model routing.
|
|
|
35
35
|
Guides: [日本語](docs/guide-ja.md) · [简体中文](docs/guide-zh-CN.md) ·
|
|
36
36
|
[Testing](docs/testing.md) · [CLI testing](docs/cli-testing.md)
|
|
37
37
|
|
|
38
|
-
**Current release: [v0.13.
|
|
39
|
-
([release notes](docs/release-v0.13.
|
|
38
|
+
**Current release: [v0.13.5](https://github.com/zufall-upon/Sortie-dogs/releases/tag/v0.13.5)**
|
|
39
|
+
([release notes](docs/release-v0.13.5.md)). The default Mission runtime retains the `v010`
|
|
40
40
|
profile, command and configuration names for compatibility; these names do not mean v0.10 is installed.
|
|
41
|
-
The current asset marker is `0.13.
|
|
41
|
+
The current asset marker is `0.13.5-live-state-v1`.
|
|
42
42
|
|
|
43
43
|
## SWE-bench Lite: 170/300 (56.67%)
|
|
44
44
|
|
|
@@ -48,7 +48,7 @@ The fixed **Sortie-dogs v0.12.24** harness resolved **170 of 300 SWE-bench Lite
|
|
|
48
48
|
|
|
49
49
|
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.
|
|
50
50
|
|
|
51
|
-
Historical scores below belong to their fixed candidates, not v0.13.
|
|
51
|
+
Historical scores below belong to their fixed candidates, not v0.13.5. SWE-bench is a separate,
|
|
52
52
|
optional measurement rather than a mandatory release gate.
|
|
53
53
|
|
|
54
54
|
> **Beta:** v0.13.x is still stabilizing. Runtime behavior,
|
|
@@ -93,7 +93,23 @@ existing local bridge loads enforcement and model routing. OpenCode can reload w
|
|
|
93
93
|
but replacing an installed dependency may require a full restart. A new chat session alone does not
|
|
94
94
|
prove the newly installed plugin is loaded.
|
|
95
95
|
|
|
96
|
-
## v0.13.
|
|
96
|
+
## v0.13.5 runtime updates
|
|
97
|
+
|
|
98
|
+
PR #154 preserves stable model instructions and appends changed host state at native history
|
|
99
|
+
boundaries, with complete current-state reconstruction after compaction. Genuine Reviewer tools
|
|
100
|
+
have deterministic read-only-first ordering; the real correction-permission transition still remains.
|
|
101
|
+
The final candidate restores v3 behavioral review and combined assignment/findings while retaining
|
|
102
|
+
exact instruction discovery, inherited Reviewer formal-command delivery, literal local shell-file
|
|
103
|
+
scope reconciliation and explicit repository-root read/write scope support. New whole-project
|
|
104
|
+
captures avoid bookkeeping-only invalidation; legacy evidence and real source/artifact freshness remain.
|
|
105
|
+
|
|
106
|
+
The final pre-release cycle 11 Anko sample passed official local score 1 and selected public probes 9/9
|
|
107
|
+
in 26m1.866s at estimated $1.41968316. It was faster but 15.762% costlier than the earlier v3 sample,
|
|
108
|
+
not combined cost-preserving optimization or a general quality/speed guarantee. Release receipts:
|
|
109
|
+
`_testenv/releases/0.13.5/`. See [release notes](docs/release-v0.13.5.md) and
|
|
110
|
+
[candidate tradeoffs/failures](docs/cache-prefix-loop-20261003.md). Native Worker startup is not completion.
|
|
111
|
+
|
|
112
|
+
## v0.13.4 runtime updates (retained)
|
|
97
113
|
|
|
98
114
|
PR #152 restores same-session Coordinator/Operator implementation and formal validation through
|
|
99
115
|
`plan_units(executor="self")`, `start_direct_unit` and `finish_direct_unit`. Known single-unit work can
|
|
@@ -397,7 +413,7 @@ register stable and `v010` from the same package installation path in one host.
|
|
|
397
413
|
Project-local installation is recommended. To expose the current Mission assets globally:
|
|
398
414
|
|
|
399
415
|
```sh
|
|
400
|
-
npm install --global sortie-dogs@0.13.
|
|
416
|
+
npm install --global sortie-dogs@0.13.5
|
|
401
417
|
sortie-dogs init --global --profile v010
|
|
402
418
|
```
|
|
403
419
|
|
|
@@ -409,7 +425,7 @@ can resolve a **separate dependency** under that config root. Updating npm-globa
|
|
|
409
425
|
it. For that layout, also install the same release at the actual config root, then rerun global init:
|
|
410
426
|
|
|
411
427
|
```sh
|
|
412
|
-
npm install --prefix "$HOME/.config/opencode" sortie-dogs@0.13.
|
|
428
|
+
npm install --prefix "$HOME/.config/opencode" sortie-dogs@0.13.5
|
|
413
429
|
sortie-dogs init --global --profile v010
|
|
414
430
|
```
|
|
415
431
|
|
|
@@ -432,7 +448,7 @@ version, preserves user configuration, and stops safely on unknown ownership or
|
|
|
432
448
|
conflicting files.
|
|
433
449
|
|
|
434
450
|
Align any exact version pin or separate bridge dependency with the intended release too. An installed
|
|
435
|
-
marker of `0.13.
|
|
451
|
+
marker of `0.13.5-live-state-v1` identifies the assets; it does not prove an already-running
|
|
436
452
|
OpenCode process has reloaded the plugin.
|
|
437
453
|
|
|
438
454
|
There is no supported uninstall command. Remove the npm dependency separately,
|
package/dist/asset-version.d.ts
CHANGED
|
@@ -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.13.
|
|
6
|
+
export declare const V010_RUNTIME_ASSET_VERSION = "0.13.5-live-state-v1";
|
|
7
7
|
export type RuntimeAssetVersion = typeof RUNTIME_ASSET_VERSION | typeof V010_RUNTIME_ASSET_VERSION;
|
package/dist/asset-version.js
CHANGED
|
@@ -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.13.
|
|
6
|
+
export const V010_RUNTIME_ASSET_VERSION = "0.13.5-live-state-v1";
|
|
@@ -39,6 +39,9 @@ export interface GoalEvidence {
|
|
|
39
39
|
readonly candidate_paths: readonly string[];
|
|
40
40
|
/** Missing on legacy evidence: retain its original all-paths snapshot recipe. */
|
|
41
41
|
readonly source_policy?: "project-files-v1" | "declared-paths-v1";
|
|
42
|
+
/** New captures distinguish a whole-project output grant from explicitly named control-like artifacts.
|
|
43
|
+
* Missing on old evidence: keep its original candidate recipe, including bookkeeping bytes. */
|
|
44
|
+
readonly candidate_policy?: "project-root-artifacts-v1";
|
|
42
45
|
/** Fixed when validation starts. Full manifest_hash still identifies the historical execution contract. */
|
|
43
46
|
readonly freshness?: {
|
|
44
47
|
readonly contract_hash: string;
|
package/dist/core/goal-bound.js
CHANGED
|
@@ -65,6 +65,7 @@ export function validGoalEvidence(value, state) {
|
|
|
65
65
|
protectedBinding.candidate_paths.every(text) &&
|
|
66
66
|
(protectedBinding.source_policy === undefined || protectedBinding.source_policy === "project-files-v1" ||
|
|
67
67
|
protectedBinding.source_policy === "declared-paths-v1") &&
|
|
68
|
+
(protectedBinding.candidate_policy === undefined || protectedBinding.candidate_policy === "project-root-artifacts-v1") &&
|
|
68
69
|
(protectedBinding.freshness === undefined || (protectedBinding.freshness !== null && typeof protectedBinding.freshness === "object" &&
|
|
69
70
|
HASH.test(protectedBinding.freshness.contract_hash) &&
|
|
70
71
|
Array.isArray(protectedBinding.freshness.scratch_paths) && protectedBinding.freshness.scratch_paths.every(text) &&
|
|
@@ -443,10 +443,11 @@ export function missionPlan(mission, raw, projectRoot) {
|
|
|
443
443
|
if (!Array.isArray(entries) || !entries.every(item => typeof item === "string"))
|
|
444
444
|
throw new Error(`mission-unit-${index + 1}: ${field} must be paths`);
|
|
445
445
|
return [...new Set(entries.map(item => {
|
|
446
|
-
//
|
|
447
|
-
// Resolve
|
|
448
|
-
|
|
449
|
-
|
|
446
|
+
// An explicit repository-root scope means this project for both reads and writes.
|
|
447
|
+
// Resolve this shorthand only at the host boundary; saved plans, prohibitions and
|
|
448
|
+
// native permissions still use the exact directory scope, never an inferred grant.
|
|
449
|
+
const rootScope = [".", "./", ".\\", "./**", ".\\**"].includes(item);
|
|
450
|
+
return normalizeExecutionScope(rootScope && projectRoot ? `${resolve(projectRoot).replaceAll("\\", "/")}/**` : item);
|
|
450
451
|
}))];
|
|
451
452
|
};
|
|
452
453
|
// A sole unit owns the whole request. This schedules work; it does not prove acceptance.
|
package/dist/plugin/index.js
CHANGED
|
@@ -7487,7 +7487,8 @@ export const SortieDogsPlugin = async (input, options) => {
|
|
|
7487
7487
|
if (extracted.paths.length)
|
|
7488
7488
|
await input.runtimeBridge?.assertMissionWrite?.(toolInput.sessionID, extracted.paths);
|
|
7489
7489
|
const nativeFile = /^(?:write|edit)(?:$|[_-])/iu.test(toolInput.tool) || /patch/iu.test(toolInput.tool);
|
|
7490
|
-
|
|
7490
|
+
const nativeShell = ["bash", "shell"].includes(toolInput.tool) && authorization.writeScopes.length > 0;
|
|
7491
|
+
if ((nativeFile && !extracted.ambiguous || nativeShell && !extracted.gitAmbiguous) && extracted.paths.length) {
|
|
7491
7492
|
const missing = [];
|
|
7492
7493
|
for (const path of extracted.paths) {
|
|
7493
7494
|
const actual = nativeFile ? resolve(input.directory, path) : path;
|
|
@@ -7498,6 +7499,17 @@ export const SortieDogsPlugin = async (input, options) => {
|
|
|
7498
7499
|
if (!(error instanceof WriteDeniedError) || !["manifest-scope", "project-boundary"].includes(error.reason))
|
|
7499
7500
|
throw error;
|
|
7500
7501
|
const local = relative(authorization.projectRoot, resolve(input.directory, actual)).replaceAll("\\", "/");
|
|
7502
|
+
if (nativeShell) {
|
|
7503
|
+
// Reconcile known local file destinations, not expansions, directory trees or
|
|
7504
|
+
// opaque program internals. Unknown executables may accompany a literal rm/redirect.
|
|
7505
|
+
if (/[$*?\[\]{}()~`]/u.test(path) || local === ".." || local.startsWith("../") || isAbsolute(local))
|
|
7506
|
+
continue;
|
|
7507
|
+
if (extracted.createdDirectories?.length || extracted.requiredDirectories?.some(directory => resolve(input.directory, isRecord(output.args) && typeof output.args.workdir === "string" ? output.args.workdir : ".", directory) ===
|
|
7508
|
+
resolve(input.directory, actual)))
|
|
7509
|
+
continue;
|
|
7510
|
+
if (await stat(resolve(input.directory, actual)).then(value => value.isDirectory()).catch(() => false))
|
|
7511
|
+
continue;
|
|
7512
|
+
}
|
|
7501
7513
|
missing.push(local === ".." || local.startsWith("../") || isAbsolute(local) ? resolve(input.directory, actual) : local);
|
|
7502
7514
|
}
|
|
7503
7515
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ModelLiveStateHistory {
|
|
2
|
+
version: 1;
|
|
3
|
+
native: string[];
|
|
4
|
+
blocks: Record<string, string[]>;
|
|
5
|
+
updates: {
|
|
6
|
+
after: number;
|
|
7
|
+
text: string[];
|
|
8
|
+
}[];
|
|
9
|
+
}
|
|
10
|
+
/** Reinsert already sent state at native boundaries; append changed blocks, never persist fake native messages. */
|
|
11
|
+
export declare function modelLiveState(system: readonly string[], messages?: readonly unknown[], saved?: unknown): {
|
|
12
|
+
system: string[];
|
|
13
|
+
messages?: unknown[];
|
|
14
|
+
history?: ModelLiveStateHistory;
|
|
15
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3
|
+
// Only Sortie's own generated elements are classified here, not the native system
|
|
4
|
+
// prompt or retrieved/user/tool text. Keep policy and role instructions in front;
|
|
5
|
+
// counters, findings and lifecycle changes should not invalidate all prior reads.
|
|
6
|
+
const headings = new Set([
|
|
7
|
+
"SORTIE_GOAL_BOUND_STATE", "SORTIE_ACCEPTANCE_CONTINUITY_STATE", "SORTIE_PARALLEL_DISPATCH_STATE",
|
|
8
|
+
// Recognize the former split findings block so restored histories can explicitly retire it.
|
|
9
|
+
"SORTIE_REVIEWER_CONTINUOUS_CONTEXT", "SORTIE_REVIEWER_RETAINED_FINDINGS", "SORTIE_WORKER_CONTEXT", "SORTIE_FAST_LANE_TERMINAL",
|
|
10
|
+
]);
|
|
11
|
+
const prefixes = ["Native tools actually available in this request: ",
|
|
12
|
+
"Confirmed launch conditions (fixed caps, not remaining Worker/campaign budget): "];
|
|
13
|
+
const blockName = (text) => {
|
|
14
|
+
const heading = text.split("\n", 1)[0];
|
|
15
|
+
return headings.has(heading) ? heading : prefixes.find(prefix => text.startsWith(prefix));
|
|
16
|
+
};
|
|
17
|
+
const projected = new WeakSet();
|
|
18
|
+
function retained(value) {
|
|
19
|
+
return object(value) && value.version === 1 && Array.isArray(value.native) && value.native.every(hash => typeof hash === "string") &&
|
|
20
|
+
object(value.blocks) && Object.values(value.blocks).every(texts => Array.isArray(texts) && texts.every(text => typeof text === "string")) &&
|
|
21
|
+
Array.isArray(value.updates) && value.updates.every((update, index, updates) => object(update) &&
|
|
22
|
+
Number.isSafeInteger(update.after) && Number(update.after) >= 0 && Number(update.after) <= value.native.length &&
|
|
23
|
+
(index === 0 || Number(update.after) >= Number(updates[index - 1].after)) &&
|
|
24
|
+
Array.isArray(update.text) && update.text.every(text => typeof text === "string"));
|
|
25
|
+
}
|
|
26
|
+
/** Reinsert already sent state at native boundaries; append changed blocks, never persist fake native messages. */
|
|
27
|
+
export function modelLiveState(system, messages, saved) {
|
|
28
|
+
// Compatibility callers with no transcript still receive every state field.
|
|
29
|
+
if (messages === undefined)
|
|
30
|
+
return { system: [...system] };
|
|
31
|
+
const stable = [], blocks = {};
|
|
32
|
+
for (const text of system) {
|
|
33
|
+
const name = blockName(text);
|
|
34
|
+
if (name === undefined)
|
|
35
|
+
stable.push(text);
|
|
36
|
+
else
|
|
37
|
+
(blocks[name] ??= []).push(text);
|
|
38
|
+
}
|
|
39
|
+
const native = messages.filter(message => !object(message) || !projected.has(message));
|
|
40
|
+
const hashes = native.map(message => createHash("sha256").update(JSON.stringify(message) ?? "undefined").digest("hex"));
|
|
41
|
+
// Compaction, edited native history or a missing/malformed ledger starts from
|
|
42
|
+
// complete CURRENT durable host state. Old projection is not review/test proof.
|
|
43
|
+
const prior = retained(saved) && saved.native.length <= hashes.length &&
|
|
44
|
+
saved.native.every((hash, index) => hashes[index] === hash) ? saved : { version: 1, native: [], blocks: {}, updates: [] };
|
|
45
|
+
const changed = Object.entries(blocks).filter(([name, text]) => JSON.stringify(prior.blocks[name]) !== JSON.stringify(text)).flatMap(([, text]) => text);
|
|
46
|
+
const withdrawn = Object.keys(prior.blocks).filter(name => !Object.hasOwn(blocks, name));
|
|
47
|
+
if (withdrawn.length)
|
|
48
|
+
changed.push(`SORTIE_LIVE_STATE_WITHDRAWN\n${JSON.stringify(withdrawn)}`);
|
|
49
|
+
const history = { version: 1, native: hashes, blocks,
|
|
50
|
+
updates: [...prior.updates, ...(changed.length ? [{ after: native.length, text: changed }] : [])] };
|
|
51
|
+
const output = [];
|
|
52
|
+
let cursor = 0;
|
|
53
|
+
for (let index = 0; index <= native.length; index++) {
|
|
54
|
+
while (history.updates[cursor]?.after === index) {
|
|
55
|
+
// V2 lowers role=system to chronological developer input, not absolute instructions.
|
|
56
|
+
const update = { role: "system", content: history.updates[cursor++].text.map(text => ({ type: "text", text })),
|
|
57
|
+
metadata: { "sortie-dogs/live-state": "request-only" } };
|
|
58
|
+
projected.add(update);
|
|
59
|
+
output.push(update);
|
|
60
|
+
}
|
|
61
|
+
if (index < native.length)
|
|
62
|
+
output.push(native[index]);
|
|
63
|
+
}
|
|
64
|
+
return { system: stable, messages: output, history };
|
|
65
|
+
}
|
package/dist/plugin/profiled.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { V010_RUNTIME_ASSET_VERSION } from "../asset-version.js";
|
|
2
|
-
import { MISSION_BEHAVIOR_REVIEW, MISSION_GIT_SCOPE } from "../runtime-mission-assets.js";
|
|
2
|
+
import { MISSION_BEHAVIOR_REVIEW, MISSION_GIT_SCOPE, REVIEWER_VALIDATION_WORKFLOW } from "../runtime-mission-assets.js";
|
|
3
3
|
import { cancelledMissionRetainsAcceptance, operatorGitPathAuthorized, OperatorContractError, OperatorRuntime } from "../core/operator-runtime.js";
|
|
4
4
|
import { DEFAULT_OPERATOR_PROPOSAL_BUDGET, OPERATOR_APPROVAL_CONTRACT, OPERATOR_PROPOSAL_BUDGET_CAPS, OPERATOR_PROPOSAL_REVISION_CONTRACT, OperatorProposalBudgetError, OperatorProposalRuntime } from "../core/operator-proposal.js";
|
|
5
5
|
import { CANONICAL_AGENT_ROLES, canonicalAgent, profileAgent, profileTool, V010_RUNTIME_PROFILE } from "../core/runtime-profile.js";
|
|
@@ -2450,7 +2450,7 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
2450
2450
|
await activateDirect(author);
|
|
2451
2451
|
return JSON.stringify({ status: "correction-running", execution: "same-native-task",
|
|
2452
2452
|
findings: correction.findings, validation: unit.unit.validation, write: unit.unit.write,
|
|
2453
|
-
next_action: `Correct all retained defects here using your existing context and public test harness; no handoff read or new Task.
|
|
2453
|
+
next_action: `Correct all retained defects here using your existing context and public test harness; no handoff read or new Task. ${REVIEWER_VALIDATION_WORKFLOW} Retain requested commit/clean delivery, then ${finishDirectUnit} and your explicit SELF_RECHECKED native terminal. Additional investigation may widen when useful; no reduced effort or acceptance of unresolved Medium.` });
|
|
2454
2454
|
}
|
|
2455
2455
|
tools[repairReview] = { description: "Reviewer: record your concrete Major/Medium findings and continue correction, inherited formal checks, commit and self-recheck HERE without ending this native Task. Controller: recover the ORIGINAL correction owner after a terminated review/correction. Host prepares/binds the existing authorized write scope and validation; no findings transcription, new approval or Worker rediscovery. Author self-recheck is not independent PASS. Operation and read-only reviews do not use this route.",
|
|
2456
2456
|
args: { findings: optionalStringSchema }, execute: async (args, context) => {
|
|
@@ -2526,7 +2526,7 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
2526
2526
|
throw new Error("mission-review-correction-budget-unavailable");
|
|
2527
2527
|
const reviewIdentity = missionReviewIdentity(mission);
|
|
2528
2528
|
const plan = missionPlan(mission, [{ title: `Correct ${run.units[0].unit.title}`,
|
|
2529
|
-
objective:
|
|
2529
|
+
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.`,
|
|
2530
2530
|
read: [...new Set([...(mission.reviewScope?.read ?? []), ...run.units.flatMap(unit => unit.unit.read)])],
|
|
2531
2531
|
write: [...new Set([...(mission.reviewScope?.write ?? []), ...run.units.flatMap(unit => unit.unit.write)])],
|
|
2532
2532
|
validation: run.units.flatMap(unit => unit.unit.validation) }], input.directory);
|
|
@@ -3783,6 +3783,10 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
3783
3783
|
(output.system ??= []).push(`SORTIE_RUNTIME_PROFILE ${profile.id}; marker ${assetVersion}. ` +
|
|
3784
3784
|
`Shared MkII protocol role names are logical: ${protocolMap}. Use only ${profile.toolPrefix} tools for this profile. ` +
|
|
3785
3785
|
"Never rewrite user acceptance or evidence to rename protocol roles. Final acceptance belongs only to the root coordinator.");
|
|
3786
|
+
output.system.push("SORTIE_LIVE_STATE_POLICY\nCurrent host state may follow the conversation as a request-only system update. " +
|
|
3787
|
+
"Use the latest update of each named state block for current findings, assignment, counters, acceptance continuity and receipt. " +
|
|
3788
|
+
"SORTIE_LIVE_STATE_WITHDRAWN retires the listed blocks; earlier scope, tool lists and receipts are historical, not current authority. " +
|
|
3789
|
+
"It does not replace original requirements, native tool outcomes or formal evidence; author self-recheck is not independent approval.");
|
|
3786
3790
|
const mission = await missions.read(root), run = await operators.read(root);
|
|
3787
3791
|
const inline = mission?.corrections?.find(item => item.runID === run?.runID && item.author === request.sessionID &&
|
|
3788
3792
|
item.inlineReview && ["running", "ready"].includes(item.status) && !item.selfRecheck);
|
|
@@ -88,11 +88,11 @@ export function isRuntimeControlPath(path) {
|
|
|
88
88
|
const first = path.replaceAll("\\", "/").split("/")[0];
|
|
89
89
|
return controlRoots.has(process.platform === "win32" ? first.toLowerCase() : first);
|
|
90
90
|
}
|
|
91
|
-
async function protectedScopeDigest(projectRoot, paths, manifestHash, sourcePolicy, excluded = [], binding) {
|
|
91
|
+
async function protectedScopeDigest(projectRoot, paths, manifestHash, sourcePolicy, excluded = [], binding, candidatePolicy) {
|
|
92
92
|
const entries = [];
|
|
93
93
|
const canonicalRoot = await realpath(projectRoot);
|
|
94
94
|
const scratchExcluded = binding ? snapshotScratchExclusion(binding) : () => false;
|
|
95
|
-
const visit = async (absolute, ancestors = new Set()) => {
|
|
95
|
+
const visit = async (absolute, ancestors = new Set(), projectArtifacts = false) => {
|
|
96
96
|
if (excluded.some(root => !outside(root, absolute)))
|
|
97
97
|
return true;
|
|
98
98
|
if (scratchExcluded(absolute))
|
|
@@ -100,7 +100,7 @@ async function protectedScopeDigest(projectRoot, paths, manifestHash, sourcePoli
|
|
|
100
100
|
const scoped = relative(projectRoot, absolute).replaceAll("\\", "/");
|
|
101
101
|
if (scoped === ".." || scoped.startsWith("../") || isAbsolute(scoped))
|
|
102
102
|
return false;
|
|
103
|
-
if (sourcePolicy === "project-files-v1" && isRuntimeControlPath(scoped))
|
|
103
|
+
if ((sourcePolicy === "project-files-v1" || projectArtifacts) && isRuntimeControlPath(scoped))
|
|
104
104
|
return true;
|
|
105
105
|
const metadata = await lstat(absolute).catch((error) => {
|
|
106
106
|
if (error.code === "ENOENT")
|
|
@@ -126,7 +126,7 @@ async function protectedScopeDigest(projectRoot, paths, manifestHash, sourcePoli
|
|
|
126
126
|
entries.push([scoped, `symlink:${link}`]);
|
|
127
127
|
const next = new Set([...ancestors, target]);
|
|
128
128
|
for (const child of (await readdir(absolute)).sort())
|
|
129
|
-
if (!await visit(join(absolute, child), next))
|
|
129
|
+
if (!await visit(join(absolute, child), next, projectArtifacts))
|
|
130
130
|
return false;
|
|
131
131
|
return true;
|
|
132
132
|
}
|
|
@@ -142,7 +142,7 @@ async function protectedScopeDigest(projectRoot, paths, manifestHash, sourcePoli
|
|
|
142
142
|
entries.push([scoped, "directory"]);
|
|
143
143
|
const next = new Set([...ancestors, real]);
|
|
144
144
|
for (const child of (await readdir(absolute)).sort())
|
|
145
|
-
if (!await visit(join(absolute, child), next))
|
|
145
|
+
if (!await visit(join(absolute, child), next, projectArtifacts))
|
|
146
146
|
return false;
|
|
147
147
|
return true;
|
|
148
148
|
}
|
|
@@ -151,19 +151,23 @@ async function protectedScopeDigest(projectRoot, paths, manifestHash, sourcePoli
|
|
|
151
151
|
entries.push([scoped, "file", createHash("sha256").update(await readFile(absolute)).digest("hex")]);
|
|
152
152
|
return true;
|
|
153
153
|
};
|
|
154
|
-
for (const path of [...new Set(paths)].sort())
|
|
155
|
-
|
|
154
|
+
for (const path of [...new Set(paths)].sort()) {
|
|
155
|
+
// Only a whole-project grant excludes incidental host bookkeeping. A separately
|
|
156
|
+
// declared control-like output still traverses and pins its exact bytes.
|
|
157
|
+
const projectArtifacts = candidatePolicy === "project-root-artifacts-v1" && resolve(path) === resolve(projectRoot);
|
|
158
|
+
if (!await visit(path, new Set(), projectArtifacts))
|
|
156
159
|
return undefined;
|
|
160
|
+
}
|
|
157
161
|
return goalFingerprint({ manifest_hash: `sha256:${manifestHash}`, entries });
|
|
158
162
|
}
|
|
159
163
|
function outside(projectRoot, path) {
|
|
160
164
|
const scoped = relative(projectRoot, path).replaceAll("\\", "/");
|
|
161
165
|
return scoped === ".." || scoped.startsWith("../") || isAbsolute(scoped);
|
|
162
166
|
}
|
|
163
|
-
async function declaredScopeDigest(projectRoot, paths, manifestHash, source, excluded = [], binding) {
|
|
167
|
+
async function declaredScopeDigest(projectRoot, paths, manifestHash, source, excluded = [], binding, candidatePolicy) {
|
|
164
168
|
const local = paths.filter(path => !outside(projectRoot, path));
|
|
165
169
|
const external = paths.filter(path => outside(projectRoot, path));
|
|
166
|
-
const project = await protectedScopeDigest(projectRoot, local, manifestHash, source ? "project-files-v1" : undefined, excluded, binding);
|
|
170
|
+
const project = await protectedScopeDigest(projectRoot, local, manifestHash, source ? "project-files-v1" : undefined, excluded, binding, candidatePolicy);
|
|
167
171
|
if (project === undefined)
|
|
168
172
|
return undefined;
|
|
169
173
|
const artifacts = await declaredArtifacts(external).catch(() => undefined);
|
|
@@ -226,6 +230,7 @@ export async function protectedSnapshot(authorization, options = {}) {
|
|
|
226
230
|
const tracked = await exec("git", ["ls-files", "-z"], { cwd: authorization.projectRoot, maxBuffer: 16 * 1024 * 1024 }).then(value => value.stdout.split("\0").filter(Boolean)).catch(() => []);
|
|
227
231
|
const binding = { manifest_hash: `sha256:${manifestHash}`, project_root: authorization.projectRoot,
|
|
228
232
|
manifest_path: relativePath, source_policy: external ? "declared-paths-v1" : "project-files-v1",
|
|
233
|
+
candidate_policy: "project-root-artifacts-v1",
|
|
229
234
|
source_paths: sourcePaths.map(evidencePath),
|
|
230
235
|
candidate_paths: candidatePaths.map(evidencePath),
|
|
231
236
|
freshness: { contract_hash: validationContractHash(manifest), scratch_paths: validationScratchPaths(authorization.projectRoot, manifest.validation, process.env),
|
|
@@ -270,13 +275,13 @@ export async function refreshProtectedSnapshot(projectRoot, binding) {
|
|
|
270
275
|
}
|
|
271
276
|
if (binding.source_policy === "declared-paths-v1") {
|
|
272
277
|
const source = await declaredScopeDigest(projectRoot, sourcePaths, manifestHash, true, [], binding);
|
|
273
|
-
const candidate = await declaredScopeDigest(projectRoot, candidatePaths, manifestHash, false, [], binding);
|
|
278
|
+
const candidate = await declaredScopeDigest(projectRoot, candidatePaths, manifestHash, false, [], binding, binding.candidate_policy);
|
|
274
279
|
return source === undefined || candidate === undefined ? undefined : { source, candidate };
|
|
275
280
|
}
|
|
276
281
|
// Legacy evidence retains its original recipe; a new policy cannot relabel stale proof as fresh.
|
|
277
282
|
if (binding.source_policy !== undefined && binding.source_policy !== "project-files-v1")
|
|
278
283
|
return undefined;
|
|
279
284
|
const source = await protectedScopeDigest(projectRoot, sourcePaths, manifestHash, binding.source_policy, [], binding);
|
|
280
|
-
const candidate = await protectedScopeDigest(projectRoot, candidatePaths, manifestHash, undefined, [], binding);
|
|
285
|
+
const candidate = await protectedScopeDigest(projectRoot, candidatePaths, manifestHash, undefined, [], binding, binding.candidate_policy);
|
|
281
286
|
return source === undefined || candidate === undefined ? undefined : { source, candidate };
|
|
282
287
|
}
|
package/dist/plugin/v2.js
CHANGED
|
@@ -8,6 +8,7 @@ import { V010_RUNTIME_ASSET_VERSION } from "../asset-version.js";
|
|
|
8
8
|
import { NativeBackgroundLifecycle } from "./native-background.js";
|
|
9
9
|
import { isSessionNotFoundError } from "@opencode/client";
|
|
10
10
|
import { completionReportModelMessages } from "./receipt-presentation.js";
|
|
11
|
+
import { modelLiveState } from "./model-live-state.js";
|
|
11
12
|
// Capture once when this module evaluates. A later package replacement must not make an old
|
|
12
13
|
// process report the replacement's bytes as its loaded adapter.
|
|
13
14
|
const loadedAdapter = Object.freeze({ adapter_url: import.meta.url,
|
|
@@ -16,10 +17,11 @@ const loadedAdapter = Object.freeze({ adapter_url: import.meta.url,
|
|
|
16
17
|
// Named module snapshots, not a claim to identify every transitive dependency. Retain these
|
|
17
18
|
// bytes across package replacement so an unchanged adapter cannot conceal an old implementation.
|
|
18
19
|
implementation_sha256: Object.freeze(Object.fromEntries([
|
|
19
|
-
"index", "profiled", "gate", "protected-snapshot", "declared-artifacts", "native-contract-read", "native-background",
|
|
20
|
+
"index", "profiled", "gate", "protected-snapshot", "declared-artifacts", "native-contract-read", "native-background", "model-live-state",
|
|
20
21
|
].map(name => [`${name}.js`, createHash("sha256").update(readFileSync(new URL(`./${name}.${import.meta.url.endsWith(".ts") ? "ts" : "js"}`, import.meta.url))).digest("hex")]))),
|
|
21
22
|
loaded_at: new Date().toISOString(), pid: process.pid });
|
|
22
23
|
const childSelectionKey = (id) => `v2-child-model-selection:${id}`;
|
|
24
|
+
const liveStateKey = (id) => `v2-model-live-state:${id}`;
|
|
23
25
|
function record(value) {
|
|
24
26
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
25
27
|
}
|
|
@@ -330,6 +332,13 @@ export function createV2ReturnReportFinalizer(context, hooks) {
|
|
|
330
332
|
};
|
|
331
333
|
}
|
|
332
334
|
const correctionAgent = (agent) => typeof agent === "string" && /^dog-reviewer-correction-v010-[a-f0-9]{16}$/u.test(agent);
|
|
335
|
+
const reviewerCorrectionOnlyTools = new Set(["edit", "write", "patch", "shell", "sortie_v010_bind_write_gate",
|
|
336
|
+
"sortie_v010_release_write_gate", "sortie_v010_operator_status", "sortie_v010_finish_direct_unit"]);
|
|
337
|
+
// Order only the genuine, already-filtered native definitions. Initial read-only
|
|
338
|
+
// tools remain a deterministic prefix when correction admits additional tools;
|
|
339
|
+
// object identities, schemas, executors and native authority are unchanged.
|
|
340
|
+
const reviewerModelToolOrder = (tools) => Object.fromEntries(Object.entries(tools).sort(([left], [right]) => Number(reviewerCorrectionOnlyTools.has(left)) - Number(reviewerCorrectionOnlyTools.has(right)) ||
|
|
341
|
+
(left < right ? -1 : left > right ? 1 : 0)));
|
|
333
342
|
const correctionAgentID = (sessionID) => `dog-reviewer-correction-v010-${createHash("sha256").update(sessionID).digest("hex").slice(0, 16)}`;
|
|
334
343
|
const correctionStoragePrefix = "v2-reviewer-correction-permissions:";
|
|
335
344
|
// OpenCode 2.0.18 util/wildcard.ts + permission.evaluate ordering.
|
|
@@ -473,7 +482,7 @@ async function syncReviewerCorrectionPermissionsOnce(context, hooks, sessionID,
|
|
|
473
482
|
if (info.agent !== selected)
|
|
474
483
|
await context.session.switchAgent({ sessionID, agent: selected });
|
|
475
484
|
}
|
|
476
|
-
async function registerV2Hooks(context, hooks, background) {
|
|
485
|
+
async function registerV2Hooks(context, hooks, background, liveState) {
|
|
477
486
|
const explicitlySelectedChildren = new Set();
|
|
478
487
|
const selectedChildModels = new Set();
|
|
479
488
|
const selectionKey = (parent, role, prompt) => `${parent}\0${role}\0${prompt}`;
|
|
@@ -736,8 +745,6 @@ async function registerV2Hooks(context, hooks, background) {
|
|
|
736
745
|
const output = { system: [] };
|
|
737
746
|
await hooks["experimental.chat.system.transform"]({ sessionID: String(event.sessionID ?? "") }, output);
|
|
738
747
|
await reconcileCorrectionPermissions(String(event.sessionID));
|
|
739
|
-
if (Array.isArray(event.system))
|
|
740
|
-
event.system.push(...output.system.map(text => ({ type: "text", text })));
|
|
741
748
|
if (record(event.tools)) {
|
|
742
749
|
const visible = {
|
|
743
750
|
"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"],
|
|
@@ -759,10 +766,28 @@ async function registerV2Hooks(context, hooks, background) {
|
|
|
759
766
|
if (!allowed || !key.startsWith("sortie_v010_") || !allowed.includes(key.slice("sortie_v010_".length)))
|
|
760
767
|
delete event.tools[key];
|
|
761
768
|
}
|
|
762
|
-
if (
|
|
763
|
-
|
|
769
|
+
if (event.agent === "dog-reviewer-v010" || correctionAgent(event.agent)) {
|
|
770
|
+
const tools = reviewerModelToolOrder(event.tools);
|
|
771
|
+
event.tools = tools;
|
|
772
|
+
output.system.push(`Native tools actually available in this request: ${Object.keys(tools).sort().join(", ")}. Use these tools directly; do not infer missing tools or ask the root to transcribe source.`);
|
|
764
773
|
}
|
|
765
774
|
}
|
|
775
|
+
const id = String(event.sessionID ?? "");
|
|
776
|
+
const previous = liveState.get(id) ?? await context.storage?.get(liveStateKey(id)).catch(() => {
|
|
777
|
+
console.warn("[sortie-dogs-v010] Live-state projection restore unavailable; rebuilding current host state");
|
|
778
|
+
return undefined;
|
|
779
|
+
});
|
|
780
|
+
const projection = modelLiveState(output.system, Array.isArray(event.messages) ? event.messages : undefined, previous);
|
|
781
|
+
if (projection.history) {
|
|
782
|
+
liveState.set(id, projection.history);
|
|
783
|
+
await context.storage?.set(liveStateKey(id), projection.history).catch(() => {
|
|
784
|
+
console.warn("[sortie-dogs-v010] Live-state projection persistence unavailable; current instance retains continuity");
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
if (Array.isArray(event.system))
|
|
788
|
+
event.system.push(...projection.system.map(text => ({ type: "text", text })));
|
|
789
|
+
if (projection.messages)
|
|
790
|
+
event.messages = projection.messages;
|
|
766
791
|
});
|
|
767
792
|
if (hooks["experimental.session.compacting"])
|
|
768
793
|
await context.session.hook("compaction", async (event) => {
|
|
@@ -796,6 +821,7 @@ export function createSortieDogsV2Plugin(legacyFactory = SortieDogsV010Plugin) {
|
|
|
796
821
|
id: "sortie-dogs.v010",
|
|
797
822
|
async setup(context) {
|
|
798
823
|
let hooks;
|
|
824
|
+
const liveState = new Map();
|
|
799
825
|
const background = new NativeBackgroundLifecycle(context, async (dispatch, text) => {
|
|
800
826
|
if (dispatch.owner)
|
|
801
827
|
hooks.backgroundOwner?.(dispatch.callID, dispatch.owner);
|
|
@@ -808,7 +834,7 @@ export function createSortieDogsV2Plugin(legacyFactory = SortieDogsV010Plugin) {
|
|
|
808
834
|
hooks = await legacyFactory({ directory: context.location.directory, client: legacyClient(context),
|
|
809
835
|
nativeBackground: background, returnReportTransport: "tool-result",
|
|
810
836
|
reviewerCorrectionPermissions: !!context.agent && !!context.storage && !!context.session.switchAgent }, context.options ?? {});
|
|
811
|
-
await registerV2Hooks(context, hooks, background);
|
|
837
|
+
await registerV2Hooks(context, hooks, background, liveState);
|
|
812
838
|
// Recreate exact durable correction profiles at plugin setup, before resumed native Steps
|
|
813
839
|
// resolve their agent/tool snapshots. Storage.scan is part of Plugin 2.0.18's public domain.
|
|
814
840
|
if (context.storage?.scan) {
|
|
@@ -836,6 +862,8 @@ export function createSortieDogsV2Plugin(legacyFactory = SortieDogsV010Plugin) {
|
|
|
836
862
|
await background.event(event);
|
|
837
863
|
if (event.type === "session.deleted") {
|
|
838
864
|
await context.storage?.remove?.(childSelectionKey(id));
|
|
865
|
+
liveState.delete(id);
|
|
866
|
+
await context.storage?.remove?.(liveStateKey(id));
|
|
839
867
|
// This native event is definite deletion, unlike a transient session.get failure.
|
|
840
868
|
await disposeCorrectionPermissions(context, id);
|
|
841
869
|
}
|
|
@@ -6,6 +6,8 @@ export declare const MISSION_GIT_SCOPE = "Requested git add <paths> (with option
|
|
|
6
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";
|
|
7
7
|
/** Worker-only projection; planning/review authorities retain the complete shared workflow. */
|
|
8
8
|
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
|
+
/** Correction uses the inherited command identities; diagnostics do not acquire formal evidence. */
|
|
10
|
+
export declare const REVIEWER_VALIDATION_WORKFLOW = "Run inherited formal commands in order as exact separate foreground native shell calls.\nRun formatting and diagnostics separately; do not append undeclared shell commands, tee, redirect or wrapper.";
|
|
9
11
|
export declare function missionOperatorContent(profile: RuntimeProfile, version: string): string;
|
|
10
12
|
/** Shared by the installed Reviewer and its host-generated mission prompt. */
|
|
11
13
|
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.\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.";
|
|
@@ -34,6 +34,9 @@ export const WORKER_VALIDATION_WORKFLOW = `Batch edits, reconcile requirements/d
|
|
|
34
34
|
separate foreground native shell calls, no extra tee, redirect or wrapper. Diagnostics are not formal evidence.
|
|
35
35
|
Rerun affected checks and required broad checks when contract/freshness requires; never drop required validation or claim stale/unrun proof.
|
|
36
36
|
`;
|
|
37
|
+
/** Correction uses the inherited command identities; diagnostics do not acquire formal evidence. */
|
|
38
|
+
export const REVIEWER_VALIDATION_WORKFLOW = `Run inherited formal commands in order as exact separate foreground native shell calls.
|
|
39
|
+
Run formatting and diagnostics separately; do not append undeclared shell commands, tee, redirect or wrapper.`;
|
|
37
40
|
const OPERATION_GUIDE = `## Practical operation guide
|
|
38
41
|
|
|
39
42
|
Use file paths for exact outputs and dir/** for a directory tree, including a directory that does not
|
|
@@ -310,7 +313,8 @@ binds the existing scope/checks and lets you correct HERE, without another promp
|
|
|
310
313
|
round trip. Reuse the known project test harness and the reasoning/source already in this conversation.
|
|
311
314
|
If correction exposes another concrete Major/Medium defect, retain it through ${profile.toolPrefix}repair_review
|
|
312
315
|
here before fixing it; the host accumulates known findings without another Task, scope or check contract.
|
|
313
|
-
|
|
316
|
+
${REVIEWER_VALIDATION_WORKFLOW}
|
|
317
|
+
After current formal checks, retain the requested commit/clean boundary, then
|
|
314
318
|
${profile.toolPrefix}finish_direct_unit. Explicitly self-recheck and end this same native Task with
|
|
315
319
|
SELF_RECHECKED; only its actual successful terminal binds the current validated source. This is author
|
|
316
320
|
self-recheck, never independent approval of your own edits. No unresolved Medium may pass.
|
|
@@ -525,31 +529,30 @@ mode: subagent
|
|
|
525
529
|
---
|
|
526
530
|
# ${profileAgent(profile, "dog-worker")}
|
|
527
531
|
|
|
528
|
-
Implement, test and requested commit in this Task.
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
532
|
+
Implement, test and requested commit in this Task.
|
|
533
|
+
Reuse supplied AGENTS.md; for gaps prefer exact ancestor files/affected subtrees over parent globs.
|
|
534
|
+
Read handoff_path in full first: task.objective, verbatim original_requests/unit_instruction, Mission constraints,
|
|
535
|
+
coverage indices. Preserve user scope and ordering; prove assigned criteria, not Mission completion.
|
|
536
|
+
Ready: implement. Denied: reason/remedy. No routine manifest/goal/status/bind.
|
|
537
|
+
Recovery: ${profile.toolPrefix}bind_write_gate with exact project_root and manifest_path=operation_manifest.
|
|
533
538
|
Treat cwd/project_root and paths as opaque; never shorten or normalize segments.
|
|
534
|
-
After compaction recover handoff; inspect diff/results before
|
|
535
|
-
|
|
536
|
-
Read/search: existing permissions. Fix unit.write estimates/outputs/checks
|
|
539
|
+
After compaction recover handoff; inspect diff/results before repeats.
|
|
540
|
+
Read/search: existing permissions. Fix unit.write/outputs/checks
|
|
537
541
|
here via ${profile.toolPrefix}expand_unit/contract updates and host repair diagnostics;
|
|
538
|
-
no extra approval, restart or delegation. Keep prohibitions, host Git lifecycle
|
|
542
|
+
no extra approval, restart or delegation. Keep prohibitions, host Git lifecycle, cumulative budget.
|
|
539
543
|
Requested add/commit needs source paths, not .git/** scope.
|
|
540
|
-
|
|
541
544
|
Use pre-change test helpers as oracles, not new implementation/tests. Check
|
|
542
545
|
result/error/state together, without a hypothetical exhaustive matrix. Keep reproduction entrypoint/input/layout; rerun or report why unverified.
|
|
543
546
|
Missing tooling: one documented bounded setup in ${TOOL_ENVIRONMENT}/; reuse, never delete.
|
|
544
547
|
Per-call modes: check retained creation/use for captured vs current settings.
|
|
545
548
|
Tests: existing suites/subtests or distinctive regression entry names.
|
|
546
549
|
${WORKER_VALIDATION_WORKFLOW}
|
|
547
|
-
Done: behavior
|
|
550
|
+
Done: behavior/ordered checks/commit; never fabricate completion.
|
|
548
551
|
Parent: independent Review after return, not before execution; no review-before-commit gate.
|
|
549
|
-
Do not spawn nested subagents, amend, push
|
|
550
|
-
rerun reasons, unresolved/untested behavior; no proof
|
|
551
|
-
No unchanged denial
|
|
552
|
-
TRUE_BLOCKER: external: / TRUE_BLOCKER: user-decision
|
|
553
|
-
|
|
552
|
+
Do not spawn nested subagents, amend, push/publish. Return changes, command/exit/elapsed,
|
|
553
|
+
rerun reasons, unresolved/untested behavior; no proof doc.
|
|
554
|
+
No unchanged denial retry. Early: unrecoverable PROCESS_DEFECT: local: + diagnostic or proven
|
|
555
|
+
TRUE_BLOCKER: external: / TRUE_BLOCKER: user-decision:.
|
|
556
|
+
Prose: user's latest instruction language (previous if unclear); protocol/code/quotes verbatim.
|
|
554
557
|
`;
|
|
555
558
|
}
|