sortie-dogs 0.11.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/asset-version.d.ts +1 -1
- package/dist/asset-version.js +1 -1
- package/dist/core/work-loop.d.ts +7 -0
- package/dist/core/work-loop.js +52 -0
- package/dist/core/work-progress.d.ts +63 -0
- package/dist/core/work-progress.js +55 -0
- package/dist/plugin/v2.d.ts +3 -0
- package/dist/plugin/work-loop-v2.js +97 -5
- package/dist/runtime-assets-v011.d.ts +2 -2
- package/dist/runtime-assets-v011.js +18 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -91,6 +91,22 @@ execution plan, exact-file manifest, or model-authored proof mapping.
|
|
|
91
91
|
Technical scope and useful verification commands are discovered while working.
|
|
92
92
|
Project `AGENTS.md`, user instructions and native OpenCode permissions remain authoritative.
|
|
93
93
|
|
|
94
|
+
### Execution progress and bounded discovery
|
|
95
|
+
|
|
96
|
+
The native child call publishes host-observed progress every five seconds: elapsed
|
|
97
|
+
time, current inspection/command, last exit, command starts and edits. The same
|
|
98
|
+
information is available through `sortie_v011_work_status`. Activity is never
|
|
99
|
+
treated as completed work or an inferred benchmark score. Long jobs should reuse
|
|
100
|
+
their existing controller and ledger, rather than rebuilding one before launch.
|
|
101
|
+
|
|
102
|
+
By default, 12 inspection calls or three minutes without an executable step stop
|
|
103
|
+
the child with a **blocked** reason. A running native shell/check keeps its own
|
|
104
|
+
timeout. Resuming a discovery stall requires concrete next-step guidance; the same
|
|
105
|
+
child, user instructions, failed checks and attempt budget are retained. Plugin
|
|
106
|
+
options `maxDiscoveryCalls`, `maxPlanningMs` and `progressIntervalMs` configure these
|
|
107
|
+
bounds. Interrupted outgoing tool calls receive an explicitly unverified error
|
|
108
|
+
result when native history omitted one; stored history and check evidence are preserved.
|
|
109
|
+
|
|
94
110
|
### Roles and models
|
|
95
111
|
|
|
96
112
|
| Role | Model | Responsibility |
|
package/dist/asset-version.d.ts
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export declare const RUNTIME_ASSET_VERSION = "0.3.89-completion-proof-v1";
|
|
6
6
|
export declare const V010_RUNTIME_ASSET_VERSION = "0.10.0-v0912-language4-cost-rpt10-compaction-ref2-proposal1-review-remediation1-surface4-proposal-recovery2-quality1-terminal2-route1-path1-permission2-prerequisite1-v2-bridge1-intent1-reflection1-cancel1-proposal-budget1-goal-recovery1-delegate-execute1-v2-depth1-process-replacement1-unstarted-v2-1-cold-turn1-return-report-v2-2-root-revision1";
|
|
7
|
-
export declare const V011_RUNTIME_ASSET_VERSION = "0.11.
|
|
7
|
+
export declare const V011_RUNTIME_ASSET_VERSION = "0.11.1-user-proxy-native-v4-progress-recovery";
|
|
8
8
|
export type RuntimeAssetVersion = typeof RUNTIME_ASSET_VERSION | typeof V010_RUNTIME_ASSET_VERSION | typeof V011_RUNTIME_ASSET_VERSION;
|
package/dist/asset-version.js
CHANGED
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export const RUNTIME_ASSET_VERSION = "0.3.89-completion-proof-v1";
|
|
6
6
|
export const V010_RUNTIME_ASSET_VERSION = "0.10.0-v0912-language4-cost-rpt10-compaction-ref2-proposal1-review-remediation1-surface4-proposal-recovery2-quality1-terminal2-route1-path1-permission2-prerequisite1-v2-bridge1-intent1-reflection1-cancel1-proposal-budget1-goal-recovery1-delegate-execute1-v2-depth1-process-replacement1-unstarted-v2-1-cold-turn1-return-report-v2-2-root-revision1";
|
|
7
|
-
export const V011_RUNTIME_ASSET_VERSION = "0.11.
|
|
7
|
+
export const V011_RUNTIME_ASSET_VERSION = "0.11.1-user-proxy-native-v4-progress-recovery";
|
package/dist/core/work-loop.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type WorkActivity, type WorkProgress } from "./work-progress.js";
|
|
1
2
|
export declare const WORK_REFERENCE = "SORTIE_V011_WORK_REF ";
|
|
2
3
|
export declare const WORK_LIMITS: {
|
|
3
4
|
readonly attempts: 6;
|
|
@@ -67,6 +68,7 @@ export interface WorkState {
|
|
|
67
68
|
evidence?: string;
|
|
68
69
|
};
|
|
69
70
|
checkReplacements?: WorkCheckReplacement[];
|
|
71
|
+
progress?: WorkProgress;
|
|
70
72
|
}
|
|
71
73
|
export interface WorkStore {
|
|
72
74
|
get(key: string): Promise<unknown>;
|
|
@@ -108,6 +110,11 @@ export declare class WorkLoop {
|
|
|
108
110
|
admit(root: string, callID: string, input: Record<string, unknown>): Promise<void>;
|
|
109
111
|
claim(root: string, child: string, prompt: string): Promise<string>;
|
|
110
112
|
assertWorker(root: string, child: string): Promise<WorkState>;
|
|
113
|
+
activity(root: string, child: string, activity: WorkActivity, result?: {
|
|
114
|
+
status: string;
|
|
115
|
+
exit: number | null;
|
|
116
|
+
}): Promise<void>;
|
|
117
|
+
stall(root: string, callID: string, reason: string): Promise<boolean>;
|
|
111
118
|
check(root: string, actor: string, command: string, timeout: number, signal?: AbortSignal, execute?: typeof runWorkCheck): Promise<WorkCheck>;
|
|
112
119
|
settled(root: string, callID: string, succeeded: boolean, report: string): Promise<void>;
|
|
113
120
|
interrupt(root: string, cancelled?: boolean): Promise<void>;
|
package/dist/core/work-loop.js
CHANGED
|
@@ -4,6 +4,7 @@ import { lstat, readdir, readlink } from "node:fs/promises";
|
|
|
4
4
|
import { createReadStream } from "node:fs";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
|
+
import { isWorkAction, newWorkProgress } from "./work-progress.js";
|
|
7
8
|
const exec = promisify(execFile);
|
|
8
9
|
const hash = (value) => createHash("sha256").update(value).digest("hex");
|
|
9
10
|
export const WORK_REFERENCE = "SORTIE_V011_WORK_REF ";
|
|
@@ -201,6 +202,8 @@ export class WorkLoop {
|
|
|
201
202
|
return this.update(root, async (state) => {
|
|
202
203
|
if (state.work && !["completed", "cancelled"].includes(state.work.phase)) {
|
|
203
204
|
if (["interrupted", "blocked"].includes(state.work.phase)) {
|
|
205
|
+
if (state.work.progress?.stopped && !instructions.trim())
|
|
206
|
+
throw new Error("work-progress-blocked: provide a concrete next executable step before resuming; do not repeat the same exploratory dispatch");
|
|
204
207
|
if (state.work.attempts >= this.maxAttempts)
|
|
205
208
|
throw new Error("work-attempt-budget-exhausted");
|
|
206
209
|
state.work.phase = "ready";
|
|
@@ -243,6 +246,7 @@ export class WorkLoop {
|
|
|
243
246
|
work.callID = callID;
|
|
244
247
|
work.attempts++;
|
|
245
248
|
work.updatedAt = Date.now();
|
|
249
|
+
work.progress = newWorkProgress(work.updatedAt);
|
|
246
250
|
});
|
|
247
251
|
}
|
|
248
252
|
async claim(root, child, prompt) {
|
|
@@ -257,6 +261,7 @@ export class WorkLoop {
|
|
|
257
261
|
"## Original user instructions (all remain authoritative)", ...work.requests.map(item => item.text),
|
|
258
262
|
"## Operator notes", work.instructions, ...work.feedback,
|
|
259
263
|
"Use native read/glob/grep/patch/shell tools freely within the user's instructions and project AGENTS.md. Discover dependencies and affected files as you work.",
|
|
264
|
+
"Act early: use supplied runner/artifact/manifest paths and existing documented commands. Do not recreate a controller or repeat broad preflight for an execution request. After at most 12 inspection tools or 3 minutes without an executable step, the host yields this job to the operator. Long commands retain their native timeout. Start the smallest relevant reproduction/command, or report a concrete blocker. Tool activity does not prove task completion.",
|
|
260
265
|
"Run final meaningful checks with sortie_v011_check. Each remains an obligation until it passes on final source; use shell for exploration. Fix ordinary setup and test failures in this invocation. Verify the actual affected behavior and adjacent valid/invalid cases, not just syntax or one literal reproducer.",
|
|
261
266
|
"Return a concise account of changes, check IDs, requirements covered and anything genuinely unresolved. Never accept your own work or ask the user to fix a protocol field."].join("\n\n");
|
|
262
267
|
});
|
|
@@ -267,6 +272,53 @@ export class WorkLoop {
|
|
|
267
272
|
throw new Error("work-worker-inactive");
|
|
268
273
|
return work;
|
|
269
274
|
}
|
|
275
|
+
async activity(root, child, activity, result) {
|
|
276
|
+
await this.update(root, async (state) => {
|
|
277
|
+
const work = state.work;
|
|
278
|
+
if (!work || work.phase !== "running" || work.child !== child)
|
|
279
|
+
return;
|
|
280
|
+
const progress = work.progress ??= newWorkProgress();
|
|
281
|
+
if (result) {
|
|
282
|
+
const active = progress.active.find(item => item.id === activity.id);
|
|
283
|
+
if (!active)
|
|
284
|
+
return;
|
|
285
|
+
progress.active = progress.active.filter(item => item.id !== activity.id);
|
|
286
|
+
progress.last = { ...active, ...result };
|
|
287
|
+
if (isWorkAction(activity.tool))
|
|
288
|
+
progress.lastActionAt = Date.now();
|
|
289
|
+
if (result.status === "completed" && activity.tool === "patch")
|
|
290
|
+
progress.edits++;
|
|
291
|
+
}
|
|
292
|
+
else if (!progress.active.some(item => item.id === activity.id)) {
|
|
293
|
+
if (progress.active.length >= 128)
|
|
294
|
+
throw new Error("work-active-tool-limit");
|
|
295
|
+
progress.active.push(activity);
|
|
296
|
+
progress.tools++;
|
|
297
|
+
if (isWorkAction(activity.tool)) {
|
|
298
|
+
progress.inspections = 0;
|
|
299
|
+
progress.lastActionAt = Date.now();
|
|
300
|
+
if (activity.tool !== "patch")
|
|
301
|
+
progress.commands++;
|
|
302
|
+
}
|
|
303
|
+
else
|
|
304
|
+
progress.inspections++;
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
async stall(root, callID, reason) {
|
|
309
|
+
return this.update(root, async (state) => {
|
|
310
|
+
const work = state.work;
|
|
311
|
+
if (!work || work.phase !== "running" || work.callID !== callID)
|
|
312
|
+
return false;
|
|
313
|
+
const progress = work.progress ??= newWorkProgress();
|
|
314
|
+
progress.stopped = { at: Date.now(), reason };
|
|
315
|
+
work.phase = "blocked";
|
|
316
|
+
work.assessment = reason;
|
|
317
|
+
work.updatedAt = Date.now();
|
|
318
|
+
work.feedback.push(`Host stopped unproductive discovery: ${reason}. Resume only with a concrete executable next step; preserve existing processes and results.`);
|
|
319
|
+
return true;
|
|
320
|
+
});
|
|
321
|
+
}
|
|
270
322
|
async check(root, actor, command, timeout, signal, execute = runWorkCheck) {
|
|
271
323
|
if (this.activeChecks.has(root))
|
|
272
324
|
throw new Error("work-validation-already-running");
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** Host observations, not model-authored completion claims. */
|
|
2
|
+
export interface WorkActivity {
|
|
3
|
+
id: string;
|
|
4
|
+
tool: string;
|
|
5
|
+
detail: string;
|
|
6
|
+
startedAt: number;
|
|
7
|
+
}
|
|
8
|
+
export interface WorkProgress {
|
|
9
|
+
startedAt: number;
|
|
10
|
+
lastActionAt: number;
|
|
11
|
+
inspections: number;
|
|
12
|
+
tools: number;
|
|
13
|
+
commands: number;
|
|
14
|
+
edits: number;
|
|
15
|
+
active: WorkActivity[];
|
|
16
|
+
last?: WorkActivity & {
|
|
17
|
+
status: string;
|
|
18
|
+
exit: number | null;
|
|
19
|
+
};
|
|
20
|
+
stopped?: {
|
|
21
|
+
at: number;
|
|
22
|
+
reason: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export declare const WORK_PROGRESS_LIMITS: {
|
|
26
|
+
readonly idleMs: 180000;
|
|
27
|
+
readonly inspections: 12;
|
|
28
|
+
readonly intervalMs: 5000;
|
|
29
|
+
};
|
|
30
|
+
export declare const isWorkAction: (tool: string) => boolean;
|
|
31
|
+
export declare function newWorkProgress(now?: number): WorkProgress;
|
|
32
|
+
export declare function progressLimit(progress: WorkProgress, now: number, idleMs: number, inspections: number): string | null;
|
|
33
|
+
export declare function progressView(progress: WorkProgress, now?: number): {
|
|
34
|
+
phase: string;
|
|
35
|
+
elapsed_ms: number;
|
|
36
|
+
idle_ms: number;
|
|
37
|
+
summary: string;
|
|
38
|
+
note: string;
|
|
39
|
+
startedAt: number;
|
|
40
|
+
lastActionAt: number;
|
|
41
|
+
inspections: number;
|
|
42
|
+
tools: number;
|
|
43
|
+
commands: number;
|
|
44
|
+
edits: number;
|
|
45
|
+
active: WorkActivity[];
|
|
46
|
+
last?: WorkActivity & {
|
|
47
|
+
status: string;
|
|
48
|
+
exit: number | null;
|
|
49
|
+
};
|
|
50
|
+
stopped?: {
|
|
51
|
+
at: number;
|
|
52
|
+
reason: string;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
type RecordValue = Record<string, any>;
|
|
56
|
+
/** Repair only the outgoing context, including the trailing interrupted turn that native normalization misses.
|
|
57
|
+
* Persisted history/check receipts are never edited and a missing result is always an error, never success.
|
|
58
|
+
*/
|
|
59
|
+
export declare function repairWorkToolHistory(messages: RecordValue[]): {
|
|
60
|
+
messages: RecordValue[];
|
|
61
|
+
repaired: string[];
|
|
62
|
+
};
|
|
63
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export const WORK_PROGRESS_LIMITS = { idleMs: 180_000, inspections: 12, intervalMs: 5_000 };
|
|
2
|
+
export const isWorkAction = (tool) => ["shell", "patch", "sortie_v011_check"].includes(tool);
|
|
3
|
+
export function newWorkProgress(now = Date.now()) {
|
|
4
|
+
return { startedAt: now, lastActionAt: now, inspections: 0, tools: 0, commands: 0, edits: 0, active: [] };
|
|
5
|
+
}
|
|
6
|
+
export function progressLimit(progress, now, idleMs, inspections) {
|
|
7
|
+
// A running command/test has its native deadline; it is not model planning time.
|
|
8
|
+
if (progress.active.some(item => isWorkAction(item.tool)))
|
|
9
|
+
return null;
|
|
10
|
+
if (progress.inspections >= inspections)
|
|
11
|
+
return `discovery-limit: ${progress.inspections} inspection calls without an executable step`;
|
|
12
|
+
if (now - progress.lastActionAt >= idleMs)
|
|
13
|
+
return `planning-timeout: no executable step for ${Math.floor((now - progress.lastActionAt) / 1000)}s`;
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
export function progressView(progress, now = Date.now()) {
|
|
17
|
+
const active = progress.active.at(-1);
|
|
18
|
+
const phase = progress.stopped ? "blocked" : active ? isWorkAction(active.tool) ? "executing" : "inspecting" : "thinking";
|
|
19
|
+
return { ...progress, phase, elapsed_ms: Math.max(0, now - progress.startedAt),
|
|
20
|
+
idle_ms: Math.max(0, now - progress.lastActionAt),
|
|
21
|
+
summary: progress.stopped?.reason ?? (active ? `${active.tool}: ${active.detail}` :
|
|
22
|
+
progress.last ? `Last ${progress.last.tool}: ${progress.last.status}${progress.last.exit === null ? "" : ` (exit ${progress.last.exit})`}; awaiting next action` : "Waiting for first executable step"),
|
|
23
|
+
note: "Tool activity is not task completion. Command starts, edits and checks are observed separately; no benchmark score is inferred." };
|
|
24
|
+
}
|
|
25
|
+
/** Repair only the outgoing context, including the trailing interrupted turn that native normalization misses.
|
|
26
|
+
* Persisted history/check receipts are never edited and a missing result is always an error, never success.
|
|
27
|
+
*/
|
|
28
|
+
export function repairWorkToolHistory(messages) {
|
|
29
|
+
const result = [], pending = new Map(), repaired = [];
|
|
30
|
+
const flush = () => {
|
|
31
|
+
if (!pending.size)
|
|
32
|
+
return;
|
|
33
|
+
result.push({ role: "tool", content: [...pending.values()].map(call => {
|
|
34
|
+
repaired.push(call.id);
|
|
35
|
+
return { type: "tool-result", id: call.id, name: call.name, ...(call.namespace ? { namespace: call.namespace } : {}),
|
|
36
|
+
result: { type: "error", value: "Native tool result unavailable after interruption. Execution/success is unverified. Inspect existing files and owned processes before retrying; do not duplicate a running job." } };
|
|
37
|
+
}) });
|
|
38
|
+
pending.clear();
|
|
39
|
+
};
|
|
40
|
+
for (const message of messages) {
|
|
41
|
+
if (["assistant", "user"].includes(message.role))
|
|
42
|
+
flush();
|
|
43
|
+
result.push(message);
|
|
44
|
+
for (const part of Array.isArray(message.content) ? message.content : []) {
|
|
45
|
+
if (part.providerExecuted === true)
|
|
46
|
+
continue;
|
|
47
|
+
if (message.role === "assistant" && part.type === "tool-call" && typeof part.id === "string")
|
|
48
|
+
pending.set(part.id, part);
|
|
49
|
+
if (message.role === "tool" && part.type === "tool-result")
|
|
50
|
+
pending.delete(part.id);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
flush();
|
|
54
|
+
return { messages: repaired.length ? result : messages, repaired };
|
|
55
|
+
}
|
package/dist/plugin/v2.d.ts
CHANGED
|
@@ -4,6 +4,9 @@ type Registration = {
|
|
|
4
4
|
dispose(): Promise<void> | void;
|
|
5
5
|
};
|
|
6
6
|
interface V2ToolEditor {
|
|
7
|
+
update?(id: string, update: (tool: {
|
|
8
|
+
execute(input: unknown, context: JsonObject): Promise<JsonObject>;
|
|
9
|
+
}) => void): void;
|
|
7
10
|
add(tool: {
|
|
8
11
|
name: string;
|
|
9
12
|
description: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { WorkLoop, WORK_LIMITS, isRecord, workChanged, workCheckCurrent, workUnresolvedChecks } from "../core/work-loop.js";
|
|
2
2
|
import { estimateModelUsageCost } from "./model-cost.js";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
|
+
import { WORK_PROGRESS_LIMITS, progressLimit, progressView, repairWorkToolHistory } from "../core/work-progress.js";
|
|
4
5
|
export const V011_ROUTES = Object.freeze({
|
|
5
6
|
operator: { providerID: "openai", id: "gpt-6-sol", variant: "xhigh" },
|
|
6
7
|
worker: { providerID: "openai", id: "gpt-6-luna-fast", variant: "max" },
|
|
@@ -21,7 +22,17 @@ export function createUserProxyPlugin() {
|
|
|
21
22
|
if (!Number.isInteger(configured) || Number(configured) < 1 || Number(configured) > 32)
|
|
22
23
|
throw new Error("work-max-attempts-invalid");
|
|
23
24
|
const loop = new WorkLoop(context.location.directory, context.storage, Number(configured));
|
|
25
|
+
const option = (key, fallback, min, max) => {
|
|
26
|
+
const value = context.options?.[key] ?? fallback;
|
|
27
|
+
if (!Number.isInteger(value) || Number(value) < min || Number(value) > max)
|
|
28
|
+
throw new Error(`work-${key}-invalid`);
|
|
29
|
+
return Number(value);
|
|
30
|
+
};
|
|
31
|
+
const planningMs = option("maxPlanningMs", WORK_PROGRESS_LIMITS.idleMs, 50, 3_600_000);
|
|
32
|
+
const discoveryCalls = option("maxDiscoveryCalls", WORK_PROGRESS_LIMITS.inspections, 1, 128);
|
|
33
|
+
const progressInterval = option("progressIntervalMs", WORK_PROGRESS_LIMITS.intervalMs, 10, 60_000);
|
|
24
34
|
const controllers = new Map();
|
|
35
|
+
const monitors = new Set();
|
|
25
36
|
const stopping = new Map();
|
|
26
37
|
const lifetime = new AbortController();
|
|
27
38
|
const info = async (id) => {
|
|
@@ -136,6 +147,8 @@ export function createUserProxyPlugin() {
|
|
|
136
147
|
unresolved_checks: unresolved.map(check => check.id),
|
|
137
148
|
check_results: work.checks.filter(check => checkIDs.includes(check.id)).map(check => ({ ...check })),
|
|
138
149
|
check_replacements: work.checkReplacements ?? [],
|
|
150
|
+
child_session_id: work.child,
|
|
151
|
+
progress: work.progress ? progressView(work.progress) : null,
|
|
139
152
|
...(work.phase === "completed" ? { accepted_source_current: work.acceptedSource === source.fingerprint } : {}),
|
|
140
153
|
...(work.phase === "ready" ? { task: loop.task(work), next: "Call the native subagent tool with this task unchanged, in the foreground." }
|
|
141
154
|
: { next: work.phase === "review" ? "Compare all original requests with actual changes and check evidence, then call review_work. Revise on any omission."
|
|
@@ -151,7 +164,7 @@ export function createUserProxyPlugin() {
|
|
|
151
164
|
for (const controller of controllers.get(id) ?? [])
|
|
152
165
|
controller.abort();
|
|
153
166
|
if (work?.child && !["completed", "cancelled"].includes(work.phase)) {
|
|
154
|
-
await context.session.interrupt({ sessionID: work.child,
|
|
167
|
+
await context.session.interrupt({ sessionID: work.child, continue: false });
|
|
155
168
|
}
|
|
156
169
|
})();
|
|
157
170
|
stopping.set(id, operation);
|
|
@@ -169,6 +182,63 @@ export function createUserProxyPlugin() {
|
|
|
169
182
|
editor.update(worker, agent => { agent.model = { ...V011_ROUTES.worker }; });
|
|
170
183
|
});
|
|
171
184
|
await context.tool.transform(editor => {
|
|
185
|
+
// Keep the real native child card and executor. Publish host observations on that outstanding card,
|
|
186
|
+
// without synthetic prompts, a second model loop, or a hidden shell implementation.
|
|
187
|
+
editor.update?.("subagent", tool => {
|
|
188
|
+
const execute = tool.execute;
|
|
189
|
+
tool.execute = async (input, execution) => {
|
|
190
|
+
if (!isRecord(input) || input.agent !== worker)
|
|
191
|
+
return execute(input, execution);
|
|
192
|
+
const id = String(execution.sessionID), session = await info(id);
|
|
193
|
+
if (session.agent !== primary || session.parentID)
|
|
194
|
+
return execute(input, execution);
|
|
195
|
+
const work = await loop.current(id);
|
|
196
|
+
if (!work || work.phase !== "running" || work.callID !== execution.id)
|
|
197
|
+
throw new Error("work-dispatch-not-admitted");
|
|
198
|
+
const report = typeof execution.progress === "function" ? execution.progress : async () => { };
|
|
199
|
+
let metadata = {}, ended = false, pending;
|
|
200
|
+
const publish = async () => {
|
|
201
|
+
const current = await loop.current(id);
|
|
202
|
+
if (!current || current.id !== work.id || current.callID !== work.callID || !current.progress)
|
|
203
|
+
return;
|
|
204
|
+
const progress = progressView(current.progress);
|
|
205
|
+
await report({ ...metadata, ...(current.child ? { sessionID: current.child } : {}),
|
|
206
|
+
description: `${progress.phase} · ${Math.floor(progress.elapsed_ms / 1000)}s · ${progress.summary}`,
|
|
207
|
+
sortie_progress: progress });
|
|
208
|
+
const reason = current.phase === "running" ? progressLimit(current.progress, Date.now(), planningMs, discoveryCalls) : null;
|
|
209
|
+
if (reason && await loop.stall(id, work.callID, reason)) {
|
|
210
|
+
// Mark blocked first, so the native interrupted event cannot turn this into a resumable blind retry.
|
|
211
|
+
if (current.child)
|
|
212
|
+
await context.session.interrupt({ sessionID: current.child, continue: false });
|
|
213
|
+
await report({ ...metadata, ...(current.child ? { sessionID: current.child } : {}),
|
|
214
|
+
description: `BLOCKED · ${reason}`, sortie_progress: progressView((await loop.current(id)).progress) });
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
const tick = () => {
|
|
218
|
+
if (ended || pending || lifetime.signal.aborted)
|
|
219
|
+
return;
|
|
220
|
+
pending = publish().catch(error => { console.warn("[sortie-dogs-v011] progress", error instanceof Error ? error.message : "unknown"); })
|
|
221
|
+
.finally(() => { pending = undefined; });
|
|
222
|
+
};
|
|
223
|
+
const timer = setInterval(tick, progressInterval);
|
|
224
|
+
monitors.add(timer);
|
|
225
|
+
try {
|
|
226
|
+
await publish();
|
|
227
|
+
const result = await execute(input, { ...execution, progress: async (update) => {
|
|
228
|
+
metadata = { ...metadata, ...update };
|
|
229
|
+
await publish();
|
|
230
|
+
} });
|
|
231
|
+
return { ...result, metadata: { ...metadata, ...(isRecord(result.metadata) ? result.metadata : {}),
|
|
232
|
+
sortie_progress: (await loop.current(id))?.progress ? progressView((await loop.current(id)).progress) : null } };
|
|
233
|
+
}
|
|
234
|
+
finally {
|
|
235
|
+
ended = true;
|
|
236
|
+
clearInterval(timer);
|
|
237
|
+
monitors.delete(timer);
|
|
238
|
+
await pending;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
});
|
|
172
242
|
const add = (name, description, input, execute) => editor.add({ name: `sortie_v011_${name}`, description, input, options: { codemode: false },
|
|
173
243
|
execute: async (args, execution) => ({ content: JSON.stringify(await execute(isRecord(args) ? args : {}, execution)) }) });
|
|
174
244
|
add("start_work", "Operator: delegate the user's actual request to the low-cost implementer. The host retains original instructions; supply only concise extra guidance. Also resumes interrupted work without resetting attempts.", objectSchema({ instructions: { type: "string", maxLength: 8000 } }), async (args, execution) => {
|
|
@@ -282,6 +352,9 @@ export function createUserProxyPlugin() {
|
|
|
282
352
|
await loop.assertWorker(owner, id);
|
|
283
353
|
if (event.tool === "subagent")
|
|
284
354
|
throw new Error("work-worker-does-not-delegate");
|
|
355
|
+
const input = isRecord(event.input) ? event.input : {};
|
|
356
|
+
await loop.activity(owner, id, { id: String(event.id), tool: String(event.tool), startedAt: Date.now(),
|
|
357
|
+
detail: String(input.command ?? input.path ?? input.pattern ?? event.tool).replace(/\s+/gu, " ").slice(0, 240) });
|
|
285
358
|
}
|
|
286
359
|
else if (event.tool === "subagent" && isRecord(event.input)) {
|
|
287
360
|
if (event.input.agent === worker)
|
|
@@ -291,9 +364,15 @@ export function createUserProxyPlugin() {
|
|
|
291
364
|
}
|
|
292
365
|
});
|
|
293
366
|
await context.tool.hook("execute.after", async (event) => {
|
|
367
|
+
const id = String(event.sessionID), session = await info(id);
|
|
368
|
+
if (session.agent === worker) {
|
|
369
|
+
const result = isRecord(event.result) ? event.result : {};
|
|
370
|
+
const output = isRecord(result.output) ? result.output : result.metadata;
|
|
371
|
+
await loop.activity(await root(id), id, { id: String(event.id), tool: String(event.tool), detail: "", startedAt: 0 }, { status: output?.status === "running" ? "background-started" : String(event.status), exit: Number.isInteger(output?.exit) ? output.exit : null });
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
294
374
|
if (event.tool !== "subagent")
|
|
295
375
|
return;
|
|
296
|
-
const id = String(event.sessionID), session = await info(id);
|
|
297
376
|
if (session.agent !== primary || session.parentID)
|
|
298
377
|
return;
|
|
299
378
|
const result = isRecord(event.result) ? event.result : {};
|
|
@@ -307,6 +386,8 @@ export function createUserProxyPlugin() {
|
|
|
307
386
|
throw new Error("Sortie v0.11 requires SOL6 or Luna6. Set the selected session and auxiliary compaction/title agents to these models; reviewer/advisor settings are independent.");
|
|
308
387
|
}
|
|
309
388
|
const owner = await root(String(event.sessionID)), work = await loop.current(owner);
|
|
389
|
+
if (Array.isArray(event.messages))
|
|
390
|
+
event.messages = repairWorkToolHistory(event.messages).messages;
|
|
310
391
|
if (([V011_ROUTES.worker.id, "gpt-6-luna"].includes(event.model?.id) || (!event.model && session.agent === worker)) && isRecord(event.options)) {
|
|
311
392
|
// OpenAI documents priority as the backwards-compatible spelling of Fast mode.
|
|
312
393
|
event.options.serviceTier = "priority";
|
|
@@ -315,11 +396,16 @@ export function createUserProxyPlugin() {
|
|
|
315
396
|
event.system.push({ type: "text", text: `SORTIE v0.11 durable work ${work.id}; phase=${work.phase}. User intent survives compaction. ` +
|
|
316
397
|
(session.agent === primary ? "Your role is the user's proxy: delegate routine work, compare results with every original instruction, and accept only with review_work. " : "You own all routine investigation, edits, testing and corrections. ") +
|
|
317
398
|
"Use work_status after compaction for current evidence. Do not reconstruct a proposal or an execution manifest." });
|
|
399
|
+
if (session.agent === worker && work?.progress && Array.isArray(event.system)) {
|
|
400
|
+
event.system.push({ type: "text", text: `Execution pacing: ${work.progress.inspections}/${discoveryCalls} inspection calls since the last executable step; planning limit ${planningMs / 1000}s. ` +
|
|
401
|
+
"Reuse supplied paths and documented commands. Run the relevant reproduction or existing controller early. For long work, use the native shell/controller and its existing ledger; do not repeatedly poll or invent a replacement runner. If blocked, return the exact blocker promptly." });
|
|
402
|
+
}
|
|
318
403
|
if (session.agent === worker && work && Array.isArray(event.system) && work.requests.length > (work.deliveredRequests ?? 0)) {
|
|
319
404
|
event.system.push({ type: "text", text: "Additional original user instructions received during execution:\n" +
|
|
320
405
|
work.requests.slice(work.deliveredRequests ?? 0).map(request => request.text).join("\n\n") });
|
|
321
406
|
}
|
|
322
407
|
if (session.agent === worker && isRecord(event.tools)) {
|
|
408
|
+
delete event.tools.subagent;
|
|
323
409
|
for (const name of Object.keys(event.tools))
|
|
324
410
|
if (name.startsWith("sortie_v011_") && !["sortie_v011_check", "sortie_v011_work_status", "sortie_v011_compact"].includes(name))
|
|
325
411
|
delete event.tools[name];
|
|
@@ -382,9 +468,15 @@ export function createUserProxyPlugin() {
|
|
|
382
468
|
}
|
|
383
469
|
})().catch(error => { if (!lifetime.signal.aborted)
|
|
384
470
|
console.warn("[sortie-dogs-v011] event stream", error); });
|
|
385
|
-
return () => {
|
|
386
|
-
|
|
387
|
-
|
|
471
|
+
return () => {
|
|
472
|
+
lifetime.abort();
|
|
473
|
+
for (const timer of monitors)
|
|
474
|
+
clearInterval(timer);
|
|
475
|
+
monitors.clear();
|
|
476
|
+
for (const active of controllers.values())
|
|
477
|
+
for (const controller of active)
|
|
478
|
+
controller.abort();
|
|
479
|
+
};
|
|
388
480
|
},
|
|
389
481
|
};
|
|
390
482
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { RuntimeAsset } from "./runtime-assets.js";
|
|
2
|
-
export declare const OPERATOR_INSTRUCTIONS = "# Sortie-dogs v0.11 \u2014 the user's proxy\n\nYou are the only user-facing operator. Your job is to protect the user's actual intent and quality while a cheaper\nimplementer handles the work. Use the user's language. Preserve negative constraints, references and completion conditions.\n\nFor an action request:\n1.
|
|
3
|
-
export declare const IMPLEMENTER_INSTRUCTIONS = "# Sortie-dogs v0.11 \u2014 low-cost implementer\n\nYou handle the entire routine task for the user-facing operator: investigate, find files, edit, prepare dependencies,\nbuild, test, diagnose failures, correct them and report evidence. Use the user's language and follow project AGENTS.md.\nUse native glob/grep/read/patch/shell tools. Discover the correct implementation and verification commands as you work.\nThere is no proposal submission, pre-approved exact-file manifest, or milestone schema to fill in.\n\nTreat a reported example as an entry point to the affected behavior, not the entire specification. Inspect the surrounding\nimplementation, analogous paths and tests to identify nearby valid inputs, invalid inputs and boundary/error conditions.\nFor a bug fix, test that relevant family of behavior through the actual library/API, using repository-established semantics.\nDo not stop at the single reported exception or literal input, invent new behavior, or broaden exception handling blindly.\nWhen changing a fallback/exception boundary, first identify the guarded operation's ordinary failure modes from its public\ncontract, local implementation, analogous handlers or small runtime probes. Exercise distinct mechanisms such as missing\ninputs, invalid content and incompatible input types where applicable, then check whether each should take the same fallback\nor deliberately propagate. A syntactic variation of the original example does not test a different failure mechanism.\nReport the failure classes checked, their expected behavior and actual verification in a few lines, so the operator can\nchallenge omissions. Preserve successful behavior and avoid catching unrelated programmer/system errors indiscriminately.\nFor dependencies, follow the repository's declared build/runtime/test requirements and use permitted package registries.\nDiagnose the actual install failure before declaring a blocker; an unnecessary offline flag or conflicting local pin is\noften a repairable setup choice, not an external outage. Respect the user's setup time and authority limits.\n\nThe incoming host-expanded task includes every original user instruction and the operator's feedback. Preserve all of it,\nincluding negative constraints and quality requirements. Do not substitute easier deliverables or weaken tests to pass.\nDo not spawn other agents. Do not ask the user to repair protocol fields. Resolve ordinary technical problems yourself.\nReturn to the operator only for a real scope/authority decision, a genuine external blocker, or finished work.\n\nRun meaningful final verification through sortie_v011_check, which executes the command and records actual exit status\nand source identity. You may use shell for discovery and exploratory checks. Fix failing required checks and repeat affected\nchecks after the last edit. Generation/build may change source: run the final oracle after those outputs are stable.\nKeep a final check command stable when rerunning it. If it must change, explain which earlier check it replaces and why it\npreserves coverage. A collection/import failure is unverified behavior, not a passing test; syntax checks cannot replace it.\nNative compaction preserves the work; use work_status to recover it when needed. Keep working until the requested scope is\ncomplete. A returned correction task resumes this same session; apply the feedback and verify again.\n\nReturn a short report of changed files, requirement coverage, actual check IDs and unresolved items. Include relevant\ncommands/results, not a full tool transcript. Only the operator accepts work. Never say all requirements are met if any\nremain unverified. SOL6 and Luna6 Fast are the execution model families; this role uses Luna6 with Fast service tier.\n";
|
|
2
|
+
export declare const OPERATOR_INSTRUCTIONS = "# Sortie-dogs v0.11 \u2014 the user's proxy\n\nYou are the only user-facing operator. Your job is to protect the user's actual intent and quality while a cheaper\nimplementer handles the work. Use the user's language. Preserve negative constraints, references and completion conditions.\n\nFor an action request:\n1. State the immediate action in at most three lines. Call sortie_v011_start_work, adding only short useful guidance.\n Pass known runner, artifact, manifest and ledger paths from the conversation instead of making the child rediscover them.\n The host retains the original request automatically. Dispatch the returned task through subagent verbatim, foreground.\n2. Let dogs-coordinator investigate, implement, test and fix problems inside that one invocation. It has native search,\n editing and shell tools. Do not perform routine reconnaissance yourself or precompute a list of files and commands.\n3. After the child returns, call sortie_v011_work_status. Inspect the actual changed source/diff and validation receipts.\n Compare every original user instruction, including prohibitions and quality requirements, with the result.\n A worker's confident summary and a passing irrelevant test are insufficient. Read any relevant source the packet omits.\n Inspect stored check output with work_status(check_ids=[...]), especially failed/blocked checks and claimed behavior tests.\n Confirm the reproduction exercises the actual changed library path, and nearby valid/invalid behavior is covered.\n For an exception guard or fallback, challenge its failure boundary: can another ordinary failure of the guarded operation\n still escape? Require evidence for distinct failure mechanisms, or a source-grounded reason why they should propagate.\n Several variations that trigger the same failure are not evidence that the boundary is complete. Delegate missing probes.\n4. If anything is missing, call sortie_v011_review_work with decision=revise and precise correction feedback, then dispatch\n its returned task. This resumes the same cheap child. If complete, use decision=accept with relevant current successful\n check IDs and a substantive assessment of requirement coverage. Only its succeeded receipt means accepted completion.\n\nEvery formal check remains an obligation until it passes on the final source. Source edits and unrelated passing checks\ndo not erase failures. When a corrected or combined command truly covers an earlier check, use check_replacements with\nthe old check ID, the selected passing replacement ID, and the reason its behavioral coverage is equivalent or stronger.\nNever use that mechanism to excuse unavailable tests or substitute compilation/environment discovery for behavior tests.\nRepair ordinary dependency/setup/collection problems through the implementer within the user's limits. If required\nverification genuinely cannot run, report decision=blocked with the exact blocker; partial work is not succeeded completion.\nWhen the blocker is resolved, start_work resumes the same job and child. Do not restrict permitted dependency installation\nmerely because source browsing or solution retrieval is forbidden; preserve the user's actual permission boundaries.\n\nKeep this loop simple. You author neither a proposal nor a GoalDeclaration, manifest, milestone/proof mapping or speculative\nwhole-project plan. The implementer chooses technical steps as it learns. Keep all accepted scope; cost savings must never\njustify an easier objective, hidden omissions, changed oracles or unsupported success claims.\n\nAnswer ordinary questions directly. Request user decisions only for actual ambiguity, missing authority or external blockers.\nUse existing dog-reviewer-v010 / dog-advisor-v010 only when independent review or a material design question warrants it;\nprovide concrete source/evidence and preserve their existing role instructions. Do not create an extra mandatory review layer.\nKeep costs down by inspecting compact evidence rather than repeating the implementer's full investigation.\n\nOpenCode owns normal continuation and compaction. After compaction or restart, work_status restores authoritative state.\nAn interrupted job resumes with start_work without losing requests or resetting attempts. Respect cancellation; cancel_work\nstops the owned child. Never claim DONE on interruption or launch a second overlapping implementer.\n\nThe host publishes observed activity on the native child call and work_status: current command/inspection, elapsed time,\nlast exit, command starts and edits. These are activity, not completed work or benchmark scores. A detached launch is not\ncompletion; use its existing native controller/ledger and actual terminal evidence. Do not poll while a background tool\nhas promised a completion notification. For a benchmark campaign, report queued/running/scored/pass/fail only from its ledger.\nThe host returns blocked after 12 inspection calls or 3 minutes without an executable step (configurable). Running native\ncommands keep their own timeout. When this happens, inspect the concrete blocker and give a specific next command, not\n\"continue investigating\". Do not blindly retry the same stalled child. Preserve its session, checks and attempt budget.\n\nFinish concisely with \u2705 DONE, \u23F8 INTERRUPTED, \u26D4 BLOCKED or \u2753 NEED_DECISION, changes, verification and any next action.\nFor completed implementation cite the real receipt and checks. Include host-reported costs as estimates when available;\ndo not invent spend, scores or success. Follow project AGENTS.md.\n";
|
|
3
|
+
export declare const IMPLEMENTER_INSTRUCTIONS = "# Sortie-dogs v0.11 \u2014 low-cost implementer\n\nYou handle the entire routine task for the user-facing operator: investigate, find files, edit, prepare dependencies,\nbuild, test, diagnose failures, correct them and report evidence. Use the user's language and follow project AGENTS.md.\nUse native glob/grep/read/patch/shell tools. Discover the correct implementation and verification commands as you work.\nThere is no proposal submission, pre-approved exact-file manifest, or milestone schema to fill in.\n\nExecute early. Start with the supplied paths and the relevant documented entrypoint. In an execution-only task, reuse the\nexisting runner/controller and fixed inputs; do not build a replacement controller, redesign accounting or rerun unrelated\nfull suites before starting the requested work. For a fix, run a small reproduction early and interleave inspection with\nconcrete edits/checks. After 6 inspection tools, choose the next executable step; the host bounds discovery at 12 calls or\n3 minutes without one. If that step cannot run, report its exact prerequisite/blocker promptly. Do not use trivial shell\ncommands to reset the discovery counter. Long commands run through native shell/controller ownership with real progress\nand exit evidence, rather than repeated model turns. Do not poll when a background completion notification is pending.\n\nTreat a reported example as an entry point to the affected behavior, not the entire specification. Inspect the surrounding\nimplementation, analogous paths and tests to identify nearby valid inputs, invalid inputs and boundary/error conditions.\nFor a bug fix, test that relevant family of behavior through the actual library/API, using repository-established semantics.\nDo not stop at the single reported exception or literal input, invent new behavior, or broaden exception handling blindly.\nWhen changing a fallback/exception boundary, first identify the guarded operation's ordinary failure modes from its public\ncontract, local implementation, analogous handlers or small runtime probes. Exercise distinct mechanisms such as missing\ninputs, invalid content and incompatible input types where applicable, then check whether each should take the same fallback\nor deliberately propagate. A syntactic variation of the original example does not test a different failure mechanism.\nReport the failure classes checked, their expected behavior and actual verification in a few lines, so the operator can\nchallenge omissions. Preserve successful behavior and avoid catching unrelated programmer/system errors indiscriminately.\nFor dependencies, follow the repository's declared build/runtime/test requirements and use permitted package registries.\nDiagnose the actual install failure before declaring a blocker; an unnecessary offline flag or conflicting local pin is\noften a repairable setup choice, not an external outage. Respect the user's setup time and authority limits.\n\nThe incoming host-expanded task includes every original user instruction and the operator's feedback. Preserve all of it,\nincluding negative constraints and quality requirements. Do not substitute easier deliverables or weaken tests to pass.\nDo not spawn other agents. Do not ask the user to repair protocol fields. Resolve ordinary technical problems yourself.\nReturn to the operator only for a real scope/authority decision, a genuine external blocker, or finished work.\n\nRun meaningful final verification through sortie_v011_check, which executes the command and records actual exit status\nand source identity. You may use shell for discovery and exploratory checks. Fix failing required checks and repeat affected\nchecks after the last edit. Generation/build may change source: run the final oracle after those outputs are stable.\nKeep a final check command stable when rerunning it. If it must change, explain which earlier check it replaces and why it\npreserves coverage. A collection/import failure is unverified behavior, not a passing test; syntax checks cannot replace it.\nNative compaction preserves the work; use work_status to recover it when needed. Keep working until the requested scope is\ncomplete. A returned correction task resumes this same session; apply the feedback and verify again.\n\nReturn a short report of changed files, requirement coverage, actual check IDs and unresolved items. Include relevant\ncommands/results, not a full tool transcript. Only the operator accepts work. Never say all requirements are met if any\nremain unverified. SOL6 and Luna6 Fast are the execution model families; this role uses Luna6 with Fast service tier.\n";
|
|
4
4
|
export declare const runtimeAssets: readonly RuntimeAsset[];
|
|
@@ -6,7 +6,8 @@ You are the only user-facing operator. Your job is to protect the user's actual
|
|
|
6
6
|
implementer handles the work. Use the user's language. Preserve negative constraints, references and completion conditions.
|
|
7
7
|
|
|
8
8
|
For an action request:
|
|
9
|
-
1.
|
|
9
|
+
1. State the immediate action in at most three lines. Call sortie_v011_start_work, adding only short useful guidance.
|
|
10
|
+
Pass known runner, artifact, manifest and ledger paths from the conversation instead of making the child rediscover them.
|
|
10
11
|
The host retains the original request automatically. Dispatch the returned task through subagent verbatim, foreground.
|
|
11
12
|
2. Let dogs-coordinator investigate, implement, test and fix problems inside that one invocation. It has native search,
|
|
12
13
|
editing and shell tools. Do not perform routine reconnaissance yourself or precompute a list of files and commands.
|
|
@@ -44,6 +45,14 @@ OpenCode owns normal continuation and compaction. After compaction or restart, w
|
|
|
44
45
|
An interrupted job resumes with start_work without losing requests or resetting attempts. Respect cancellation; cancel_work
|
|
45
46
|
stops the owned child. Never claim DONE on interruption or launch a second overlapping implementer.
|
|
46
47
|
|
|
48
|
+
The host publishes observed activity on the native child call and work_status: current command/inspection, elapsed time,
|
|
49
|
+
last exit, command starts and edits. These are activity, not completed work or benchmark scores. A detached launch is not
|
|
50
|
+
completion; use its existing native controller/ledger and actual terminal evidence. Do not poll while a background tool
|
|
51
|
+
has promised a completion notification. For a benchmark campaign, report queued/running/scored/pass/fail only from its ledger.
|
|
52
|
+
The host returns blocked after 12 inspection calls or 3 minutes without an executable step (configurable). Running native
|
|
53
|
+
commands keep their own timeout. When this happens, inspect the concrete blocker and give a specific next command, not
|
|
54
|
+
"continue investigating". Do not blindly retry the same stalled child. Preserve its session, checks and attempt budget.
|
|
55
|
+
|
|
47
56
|
Finish concisely with ✅ DONE, ⏸ INTERRUPTED, ⛔ BLOCKED or ❓ NEED_DECISION, changes, verification and any next action.
|
|
48
57
|
For completed implementation cite the real receipt and checks. Include host-reported costs as estimates when available;
|
|
49
58
|
do not invent spend, scores or success. Follow project AGENTS.md.
|
|
@@ -55,6 +64,14 @@ build, test, diagnose failures, correct them and report evidence. Use the user's
|
|
|
55
64
|
Use native glob/grep/read/patch/shell tools. Discover the correct implementation and verification commands as you work.
|
|
56
65
|
There is no proposal submission, pre-approved exact-file manifest, or milestone schema to fill in.
|
|
57
66
|
|
|
67
|
+
Execute early. Start with the supplied paths and the relevant documented entrypoint. In an execution-only task, reuse the
|
|
68
|
+
existing runner/controller and fixed inputs; do not build a replacement controller, redesign accounting or rerun unrelated
|
|
69
|
+
full suites before starting the requested work. For a fix, run a small reproduction early and interleave inspection with
|
|
70
|
+
concrete edits/checks. After 6 inspection tools, choose the next executable step; the host bounds discovery at 12 calls or
|
|
71
|
+
3 minutes without one. If that step cannot run, report its exact prerequisite/blocker promptly. Do not use trivial shell
|
|
72
|
+
commands to reset the discovery counter. Long commands run through native shell/controller ownership with real progress
|
|
73
|
+
and exit evidence, rather than repeated model turns. Do not poll when a background completion notification is pending.
|
|
74
|
+
|
|
58
75
|
Treat a reported example as an entry point to the affected behavior, not the entire specification. Inspect the surrounding
|
|
59
76
|
implementation, analogous paths and tests to identify nearby valid inputs, invalid inputs and boundary/error conditions.
|
|
60
77
|
For a bug fix, test that relevant family of behavior through the actual library/API, using repository-established semantics.
|