sortie-dogs 0.16.2 → 0.16.3
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/dist/asset-version.d.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
* installed project marker without importing every asset body.
|
|
4
4
|
*/
|
|
5
5
|
export declare const RUNTIME_ASSET_VERSION = "0.3.89-completion-proof-v1";
|
|
6
|
-
export declare const V010_RUNTIME_ASSET_VERSION = "0.16.
|
|
6
|
+
export declare const V010_RUNTIME_ASSET_VERSION = "0.16.3-strategy-admission-recovery-v1";
|
|
7
7
|
export declare const CODEX_SKILL_ASSET_VERSION = "0.14.6-codex-skill-v1";
|
|
8
8
|
export type RuntimeAssetVersion = typeof RUNTIME_ASSET_VERSION | typeof V010_RUNTIME_ASSET_VERSION | typeof CODEX_SKILL_ASSET_VERSION;
|
package/dist/asset-version.js
CHANGED
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
* installed project marker without importing every asset body.
|
|
4
4
|
*/
|
|
5
5
|
export const RUNTIME_ASSET_VERSION = "0.3.89-completion-proof-v1";
|
|
6
|
-
export const V010_RUNTIME_ASSET_VERSION = "0.16.
|
|
6
|
+
export const V010_RUNTIME_ASSET_VERSION = "0.16.3-strategy-admission-recovery-v1";
|
|
7
7
|
export const CODEX_SKILL_ASSET_VERSION = "0.14.6-codex-skill-v1";
|
|
@@ -344,7 +344,10 @@ export declare class OperatorRuntime {
|
|
|
344
344
|
prepareMission(root: string, raw: unknown, dispatcher?: {
|
|
345
345
|
sessionID: string;
|
|
346
346
|
callID: string;
|
|
347
|
-
}, supersededRunID?: string, terminalChildren?: readonly string[], replaceRequirements?: boolean, context?: Record<string, unknown
|
|
347
|
+
}, supersededRunID?: string, terminalChildren?: readonly string[], replaceRequirements?: boolean, context?: Record<string, unknown>, terminalTaskCalls?: {
|
|
348
|
+
runID: string;
|
|
349
|
+
callIDs: readonly string[];
|
|
350
|
+
}): Promise<OperatorState>;
|
|
348
351
|
/** Stage a settled mission's replacement; rejected preparation never cancels the usable run. */
|
|
349
352
|
replanMission(root: string, runID: string, raw: unknown, dispatcher?: {
|
|
350
353
|
sessionID: string;
|
|
@@ -380,6 +383,8 @@ export declare class OperatorRuntime {
|
|
|
380
383
|
}>;
|
|
381
384
|
private matchesWorkerTask;
|
|
382
385
|
matchesRecordedWorkerTask(state: OperatorState, unitID: string, args: unknown): boolean;
|
|
386
|
+
/** Historical comparison only: cancellation revoked the admission generation. */
|
|
387
|
+
matchesCancelledWorkerTask(state: OperatorState, unitID: string, args: unknown): boolean;
|
|
383
388
|
admitOperator(root: string, callID: string, args: unknown): Promise<OperatorTask>;
|
|
384
389
|
private admitOperatorOnce;
|
|
385
390
|
bindOperator(root: string, sessionID: string, prompt: string): Promise<void>;
|
|
@@ -766,8 +766,8 @@ export class OperatorRuntime {
|
|
|
766
766
|
}
|
|
767
767
|
}
|
|
768
768
|
/** Mission authority is supplied only by the owning profile, never by a model-authored plan. */
|
|
769
|
-
prepareMission(root, raw, dispatcher, supersededRunID, terminalChildren = [], replaceRequirements = false, context) {
|
|
770
|
-
return this.serial(root, () => this.prepareOnce(root, raw, undefined, { dispatcher, supersededRunID, terminalChildren, replaceRequirements, context }));
|
|
769
|
+
prepareMission(root, raw, dispatcher, supersededRunID, terminalChildren = [], replaceRequirements = false, context, terminalTaskCalls) {
|
|
770
|
+
return this.serial(root, () => this.prepareOnce(root, raw, undefined, { dispatcher, supersededRunID, terminalChildren, replaceRequirements, context, terminalTaskCalls }));
|
|
771
771
|
}
|
|
772
772
|
/** Stage a settled mission's replacement; rejected preparation never cancels the usable run. */
|
|
773
773
|
replanMission(root, runID, raw, dispatcher, context) {
|
|
@@ -946,6 +946,17 @@ export class OperatorRuntime {
|
|
|
946
946
|
if (mission?.supersededRunID !== undefined && !superseding)
|
|
947
947
|
throw new Error("mission-superseded-run-mismatch");
|
|
948
948
|
const terminalChildren = mission?.terminalChildren ?? [];
|
|
949
|
+
const unclaimedCalls = previous?.units.flatMap(unit => !unit.directExecution && unit.status === "cancelled" &&
|
|
950
|
+
unit.childSessionID === null && unit.callID !== null ? [unit.callID] : []) ?? [];
|
|
951
|
+
// A cancelled admission can precede its child's first prompt. Only the owning
|
|
952
|
+
// profile's exact native Task proof, not null or cancellation alone, closes it.
|
|
953
|
+
if (mission && previous?.phase === "cancelled" && unclaimedCalls.length &&
|
|
954
|
+
(mission?.terminalTaskCalls?.runID !== previous.runID ||
|
|
955
|
+
new Set(mission.terminalTaskCalls.callIDs).size !== unclaimedCalls.length ||
|
|
956
|
+
mission.terminalTaskCalls.callIDs.length !== unclaimedCalls.length ||
|
|
957
|
+
unclaimedCalls.some(call => !mission.terminalTaskCalls.callIDs.includes(call)))) {
|
|
958
|
+
throw new Error("mission-cancelled-run-task-not-terminal");
|
|
959
|
+
}
|
|
949
960
|
const retainAcceptance = previous !== undefined && cancelledMissionRetainsAcceptance(previous);
|
|
950
961
|
const predecessorChildren = previous?.units.flatMap(unit => !unit.directExecution && unit.childSessionID !== null
|
|
951
962
|
? [unit.childSessionID] : []) ?? [];
|
|
@@ -957,7 +968,7 @@ export class OperatorRuntime {
|
|
|
957
968
|
unit.repairValidation !== null || (unit.status === "pending" && (unit.callID !== null || unit.childSessionID !== null ||
|
|
958
969
|
unit.evidence.length > 0 || unit.resultClass !== null)) ||
|
|
959
970
|
(unit.status === "succeeded" && (unit.childSessionID === null || unit.callID === null || unit.evidence.length === 0 || unit.resultClass !== "acceptance")) ||
|
|
960
|
-
(unit.status === "cancelled" &&
|
|
971
|
+
(unit.status === "cancelled" && unit.callID === null)))) {
|
|
961
972
|
throw new Error("mission-superseded-run-has-work: reconcile prior workers and evidence before changing acceptance");
|
|
962
973
|
}
|
|
963
974
|
if (plan.git_lifecycle !== undefined) {
|
|
@@ -1197,9 +1208,11 @@ export class OperatorRuntime {
|
|
|
1197
1208
|
await handle.close();
|
|
1198
1209
|
}
|
|
1199
1210
|
}
|
|
1200
|
-
if ((superseding || replanning || previous?.phase === "completed"
|
|
1211
|
+
if ((superseding || replanning || previous?.phase === "completed" ||
|
|
1212
|
+
previous?.phase === "cancelled" && unclaimedCalls.length > 0 && mission?.terminalTaskCalls !== undefined) && previous) {
|
|
1201
1213
|
// A crash before the state switch may leave a checkpoint while the old run later advances.
|
|
1202
1214
|
// Sequence-qualified replan snapshots keep that history without blocking a subsequent correction.
|
|
1215
|
+
// Same-request recovery also retains the original unclaimed cancellation, not just its costs.
|
|
1203
1216
|
const archive = replanning || previous.phase === "completed" ? this.replanArchivePath(previous)
|
|
1204
1217
|
: `${this.file(root)}.${previous.runID}.archive`;
|
|
1205
1218
|
try {
|
|
@@ -1370,6 +1383,28 @@ export class OperatorRuntime {
|
|
|
1370
1383
|
: args;
|
|
1371
1384
|
return unit !== undefined && this.matchesWorkerTask(state, unit, normalized);
|
|
1372
1385
|
}
|
|
1386
|
+
/** Historical comparison only: cancellation revoked the admission generation. */
|
|
1387
|
+
matchesCancelledWorkerTask(state, unitID, args) {
|
|
1388
|
+
const unit = state.units.find(item => item.unit.id === unitID);
|
|
1389
|
+
if (state.phase !== "cancelled" || !unit || unit.status !== "cancelled" || unit.childSessionID !== null ||
|
|
1390
|
+
!record(args) || typeof args.prompt !== "string")
|
|
1391
|
+
return false;
|
|
1392
|
+
if (args.prompt === unit.task.prompt)
|
|
1393
|
+
return this.matchesRecordedWorkerTask(state, unitID, args);
|
|
1394
|
+
if (!args.prompt.startsWith(`${OPERATOR_TASK_REFERENCE} `))
|
|
1395
|
+
return false;
|
|
1396
|
+
let reference;
|
|
1397
|
+
try {
|
|
1398
|
+
reference = JSON.parse(args.prompt.slice(OPERATOR_TASK_REFERENCE.length + 1));
|
|
1399
|
+
}
|
|
1400
|
+
catch {
|
|
1401
|
+
return false;
|
|
1402
|
+
}
|
|
1403
|
+
if (!record(reference) || !Number.isSafeInteger(reference.g) || reference.g < 1 ||
|
|
1404
|
+
reference.g >= state.generation)
|
|
1405
|
+
return false;
|
|
1406
|
+
return this.matchesRecordedWorkerTask({ ...state, generation: reference.g }, unitID, args);
|
|
1407
|
+
}
|
|
1373
1408
|
admitOperator(root, callID, args) {
|
|
1374
1409
|
return this.serial(root, () => this.admitOperatorOnce(root, callID, args));
|
|
1375
1410
|
}
|
package/dist/plugin/profiled.js
CHANGED
|
@@ -1251,6 +1251,9 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
1251
1251
|
return packet;
|
|
1252
1252
|
}
|
|
1253
1253
|
function missionDispatchPacket(mission, run) {
|
|
1254
|
+
const strategyRecovery = " If a method stalled or proposed avoidable extra work, Operator: compare the original goal, actual result and concrete unmet work before choosing to continue, change or stop that method. " +
|
|
1255
|
+
`For a concrete adjustment, use ${profile.toolPrefix}plan_units with proposed units, reason and handoff_context, then dispatch its returned same-Coordinator continuation. ` +
|
|
1256
|
+
"Preserve required checks through lawful registration/replanning and reuse current evidence. Otherwise continue normally; no extra approval or logging-only step.";
|
|
1254
1257
|
const packet = { ...missionPacket(mission, run), project_root: input.directory,
|
|
1255
1258
|
coordinator_dispatch: mission.dispatchOpen ? "active" : ["completed", "cancelled"].includes(mission.phase) ? "terminal" : "resumable" };
|
|
1256
1259
|
if (mission.phase === "submitted" && mission.submission?.status === "blocked" &&
|
|
@@ -1284,7 +1287,7 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
1284
1287
|
if (mission.runID === run?.runID && run?.units.some(unit => unit.status === "running")) {
|
|
1285
1288
|
if (mission.coordinator && !mission.dispatchOpen && !["completed", "cancelled"].includes(mission.phase))
|
|
1286
1289
|
return { ...packet, task: missions.task(mission),
|
|
1287
|
-
next_action: "Resume this existing Coordinator Task with its task_id to continue the retained unit. Do not dispatch a replacement or wait for background work that has already ended." };
|
|
1290
|
+
next_action: "Resume this existing Coordinator Task with its task_id to continue the retained unit. Do not dispatch a replacement or wait for background work that has already ended." + strategyRecovery };
|
|
1288
1291
|
return { ...packet, next_action: "The current unit is active. Continue its owning session; do not dispatch a replacement Worker or Coordinator. Native background completion will return to the parent." };
|
|
1289
1292
|
}
|
|
1290
1293
|
if (mission.coordinator === null && mission.runID === run?.runID && !mission.dispatchOpen &&
|
|
@@ -1330,7 +1333,7 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
1330
1333
|
next_action: (run?.units.some(unit => unit.dispatchDenial) ? `${packet.next_action}\n` : "") +
|
|
1331
1334
|
(mission.coordinator === null
|
|
1332
1335
|
? "Fast-lane needs correction or coordination: dispatch this exact Coordinator Task with the existing Worker changes, checks and concrete remaining work. Keep the same mission, requirements and cumulative budget; do not replace an active Worker."
|
|
1333
|
-
: "The previous Coordinator Task is finished. Dispatch this exact Task to continue the same mission and Coordinator session; keep the original requirements and cumulative budget.") };
|
|
1336
|
+
: "The previous Coordinator Task is finished. Dispatch this exact Task to continue the same mission and Coordinator session; keep the original requirements and cumulative budget." + strategyRecovery) };
|
|
1334
1337
|
}
|
|
1335
1338
|
return packet;
|
|
1336
1339
|
}
|
|
@@ -1559,6 +1562,58 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
1559
1562
|
await operators.recordStoppedChild(root, run.runID, child);
|
|
1560
1563
|
}
|
|
1561
1564
|
}
|
|
1565
|
+
async function cancelledUnclaimedTaskCalls(root, run) {
|
|
1566
|
+
const units = run.units.filter(unit => !unit.directExecution && unit.status === "cancelled" &&
|
|
1567
|
+
unit.childSessionID === null && unit.callID !== null);
|
|
1568
|
+
if (!units.length)
|
|
1569
|
+
return undefined;
|
|
1570
|
+
const owner = run.operatorSessionID ?? root;
|
|
1571
|
+
const deny = () => new Error("mission-cancelled-run-task-not-terminal");
|
|
1572
|
+
// The bounded chat context may omit the old Task. Use the existing native
|
|
1573
|
+
// history reader, and never substitute a model's stop report for a tool terminal.
|
|
1574
|
+
const history = await reviewMessages(owner, undefined, true);
|
|
1575
|
+
for (const unit of units) {
|
|
1576
|
+
const parts = history.flatMap(message => record(message.info) && message.info.role === "assistant" &&
|
|
1577
|
+
message.info.sessionID === owner && Array.isArray(message.parts) ? message.parts : []).filter(part => record(part) && part.type === "tool" && part.tool === "task" && part.callID === unit.callID);
|
|
1578
|
+
const part = parts.length === 1 && record(parts[0]) ? parts[0] : undefined;
|
|
1579
|
+
const state = part && record(part.state) ? part.state : undefined;
|
|
1580
|
+
if (!part || part.sessionID !== undefined && part.sessionID !== owner || !state ||
|
|
1581
|
+
!["completed", "error"].includes(String(state.status)) ||
|
|
1582
|
+
record(state.metadata) && ["running", "pending", "unknown"].includes(String(state.metadata.status)) ||
|
|
1583
|
+
!record(state.time) || typeof state.time.end !== "number" || !Number.isFinite(state.time.end) ||
|
|
1584
|
+
!operators.matchesCancelledWorkerTask(run, unit.unit.id, state.input) || taskChildSessionID(state) !== undefined)
|
|
1585
|
+
throw deny();
|
|
1586
|
+
}
|
|
1587
|
+
// A parent Task terminal does not prove that an unclaimed child never started.
|
|
1588
|
+
// Reuse the owning service's active snapshot and child lineage before allowing
|
|
1589
|
+
// this formerly unrepresentable cancellation into prepareMission.
|
|
1590
|
+
const active = payload(await session("active", {}));
|
|
1591
|
+
if (!record(active) || !Object.values(active).every(value => record(value) &&
|
|
1592
|
+
["running", "retry"].includes(String(value.type))) || owner !== root && Object.hasOwn(active, owner))
|
|
1593
|
+
throw deny();
|
|
1594
|
+
const children = payload(await session("children", { path: { id: owner }, query: { directory: input.directory } }));
|
|
1595
|
+
if (!Array.isArray(children) || children.some(child => !record(child) || typeof child.id !== "string") ||
|
|
1596
|
+
new Set(children.map(child => child.id)).size !== children.length)
|
|
1597
|
+
throw deny();
|
|
1598
|
+
for (const child of children) {
|
|
1599
|
+
const info = payload(await session("get", { path: { id: child.id }, query: { directory: input.directory } }));
|
|
1600
|
+
if (!record(info) || info.id !== child.id || info.parentID !== owner ||
|
|
1601
|
+
typeof info.agent !== "string" || !info.agent.trim())
|
|
1602
|
+
throw deny();
|
|
1603
|
+
const role = canonicalAgent(profile, info.agent);
|
|
1604
|
+
// Root Fast-lane Tasks must not claim unrelated profile or native agents.
|
|
1605
|
+
if (owner === root && role !== "dog-worker" && role !== "dog-luna-worker")
|
|
1606
|
+
continue;
|
|
1607
|
+
if (!role)
|
|
1608
|
+
throw deny();
|
|
1609
|
+
const descendants = payload(await session("children", { path: { id: child.id }, query: { directory: input.directory } }));
|
|
1610
|
+
if (Object.hasOwn(active, child.id) || !Array.isArray(descendants) || descendants.length ||
|
|
1611
|
+
!["succeeded", "failed", "interrupted"].includes(String(info.outcome)) &&
|
|
1612
|
+
!(info.outcome === undefined && run.stoppedChildren?.includes(child.id)))
|
|
1613
|
+
throw deny();
|
|
1614
|
+
}
|
|
1615
|
+
return { runID: run.runID, callIDs: units.map(unit => unit.callID) };
|
|
1616
|
+
}
|
|
1562
1617
|
async function stop(root, reason, retireRoot = true) {
|
|
1563
1618
|
const directActor = (await operators.read(root))?.units.find(unit => unit.status === "running" && unit.directExecution)?.directExecution?.actor;
|
|
1564
1619
|
// Stopping owned execution does not write its artifacts. Its existing reservation
|
|
@@ -3031,8 +3086,24 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
3031
3086
|
// packet can already contain one even while its Reviewer is still pending.
|
|
3032
3087
|
const { task: _dispatchTask, ...packet } = missionDispatchPacket(mission, previous);
|
|
3033
3088
|
const reviewPending = mission.runID === previous?.runID && mission.review?.verdict === "pending";
|
|
3034
|
-
const
|
|
3035
|
-
|
|
3089
|
+
const running = mission.runID === previous?.runID ? previous.units.filter(unit => unit.status === "running") : [];
|
|
3090
|
+
let retainedDirectOwnerEnded = false;
|
|
3091
|
+
if (!mission.dispatchOpen && !reviewPending && mission.coordinator && running.length &&
|
|
3092
|
+
running.every(unit => unit.directExecution?.actor === mission.coordinator && !unit.reviewerCorrection)) {
|
|
3093
|
+
// This only forwards a proposal to the SAME owner. Native session termination
|
|
3094
|
+
// does not settle its retained unit or authorize a new writer/contract.
|
|
3095
|
+
const native = payload(await session("get", { path: { id: mission.coordinator } }).catch(() => undefined));
|
|
3096
|
+
if (record(native) && native.id === mission.coordinator && native.parentID === root &&
|
|
3097
|
+
canonicalAgent(profile, native.agent) === "dog-operator" &&
|
|
3098
|
+
["succeeded", "failed", "interrupted"].includes(String(native.outcome))) {
|
|
3099
|
+
try {
|
|
3100
|
+
await assertDirectPlanningQuiescent(mission.coordinator);
|
|
3101
|
+
retainedDirectOwnerEnded = true;
|
|
3102
|
+
}
|
|
3103
|
+
catch { /* A live/unproven native tool still owns this work; do not offer another Task. */ }
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
const active = mission.dispatchOpen || reviewPending || running.length > 0 && !retainedDirectOwnerEnded;
|
|
3036
3107
|
const continuation = !active ? missions.task(mission) : undefined;
|
|
3037
3108
|
// Existing Coordinator resumes accept a concrete prompt. Do not use the opaque
|
|
3038
3109
|
// initial reference here: claim() would expand it from unchanged Mission state
|
|
@@ -3108,7 +3179,8 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
3108
3179
|
const cancelledPredecessor = previous?.phase === "cancelled" &&
|
|
3109
3180
|
(mission.supersededRunID === previous.runID ||
|
|
3110
3181
|
(mission.supersededRunID === undefined && ["explicit-cancellation", "agent-changed"].includes(previous.decision ?? "") &&
|
|
3111
|
-
previous.units.some(unit => (previous
|
|
3182
|
+
previous.units.some(unit => (previous?.decision === "agent-changed" || unit.status === "cancelled") &&
|
|
3183
|
+
(unit.childSessionID !== null || !unit.directExecution && unit.callID !== null))));
|
|
3112
3184
|
let budget = await control.currentBudget(root);
|
|
3113
3185
|
if (cancelledPredecessor && budget?.reserved_units) {
|
|
3114
3186
|
await control.reconcileAbortedOperatorOrphan(root);
|
|
@@ -3119,6 +3191,7 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
3119
3191
|
// Reconcile it even when reservation recovery has nothing to do. Never resume it,
|
|
3120
3192
|
// interrupt live work here, or manufacture its native terminal/validation records.
|
|
3121
3193
|
await stopCancelledChildren(root, previous, true);
|
|
3194
|
+
previous = await operators.required(root);
|
|
3122
3195
|
}
|
|
3123
3196
|
if (budget && budget.remaining_units < plan.units.length && !same)
|
|
3124
3197
|
throw new Error(`mission-budget-exhausted: plan needs ${plan.units.length} units; ${budget.remaining_units} remain. ` +
|
|
@@ -3156,8 +3229,9 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
3156
3229
|
...(previous ? { retained_validation: missionReviewValidation(previous, replanning || previous.phase === "completed"
|
|
3157
3230
|
? operators.replanArchivePath(previous) : cancelledPredecessor ? `${operators.statePath(root)}.${previous.runID}.archive` : operators.statePath(root)),
|
|
3158
3231
|
prior_run_id: previous.runID } : {}) };
|
|
3232
|
+
const terminalTaskCalls = cancelledPredecessor && previous ? await cancelledUnclaimedTaskCalls(root, previous) : undefined;
|
|
3159
3233
|
const state = replanning && previous ? await operators.replanMission(root, previous.runID, plan, dispatcher, context)
|
|
3160
|
-
: await operators.prepareMission(root, plan, dispatcher, mission.supersededRunID, terminalChildren, mission.requirementsReplaced, context);
|
|
3234
|
+
: await operators.prepareMission(root, plan, dispatcher, mission.supersededRunID, terminalChildren, mission.requirementsReplaced, context, terminalTaskCalls);
|
|
3161
3235
|
// prepareMission retains an identical prepared run after either registration stage
|
|
3162
3236
|
// fails. Verify its pinned controls, then idempotently register the same declaration;
|
|
3163
3237
|
// only the durable Mission binding below makes it eligible for admission.
|
|
@@ -163,6 +163,18 @@ ${controls(profile, ["start_mission", "plan_units", "start_direct_unit", "finish
|
|
|
163
163
|
You are Operator, the user-facing strategic authority. Preserve every current requirement, prohibition,
|
|
164
164
|
quality threshold and explicit model/budget choice. Follow AGENTS.md and use the user's language.
|
|
165
165
|
|
|
166
|
+
Own strategy, not just the relay of a stopped author's reply. At a method stall or proposed extra step,
|
|
167
|
+
compare the user's goal, actual result and concrete unmet work. Continue planned useful work without
|
|
168
|
+
intervention; suppress unnecessary expansion, and choose only the recovery needed for a real obstacle.
|
|
169
|
+
Separate a required outcome/check from an AI-selected method. Do not promote your own checker, evidence
|
|
170
|
+
format or procedure into an immutable user requirement. An AI-selected method already registered as
|
|
171
|
+
formal validation still needs lawful recipe correction or replanning; never silently omit it or invent PASS.
|
|
172
|
+
When a change of method is needed, give the existing author the concrete alternative, rationale and
|
|
173
|
+
retained obligations, not merely "resume" or the same failing instruction. Use plan_units with proposed
|
|
174
|
+
units, reason and handoff_context to obtain its concrete Coordinator continuation when available.
|
|
175
|
+
Do not restart a release, cancel live work or take over implementation just to exercise strategic authority.
|
|
176
|
+
Ordinary implementation decisions remain with the author; no per-Task approval or decision-record ritual.
|
|
177
|
+
|
|
166
178
|
${MISSION_DEPENDENCY_WORKFLOW}
|
|
167
179
|
|
|
168
180
|
1. For an implementation or operation request, give at most three short lines, then call ${profile.toolPrefix}start_mission
|