sortie-dogs 0.10.0 → 0.10.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 +27 -20
- package/dist/asset-version.d.ts +1 -1
- package/dist/asset-version.js +1 -1
- package/dist/core/operator-proposal.d.ts +10 -0
- package/dist/core/operator-proposal.js +135 -23
- package/dist/core/operator-runtime.js +16 -0
- package/dist/plugin/profiled.js +63 -8
- package/dist/runtime-assets-v010.js +22 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,7 +70,7 @@ bounded implementation, canonical validation, and evidence-backed completion.
|
|
|
70
70
|
|
|
71
71
|
Guides: [日本語](docs/guide-ja.md) · [简体中文](docs/guide-zh-CN.md) · [テスト実行](docs/testing.md) · [CLI testing](docs/cli-testing.md)
|
|
72
72
|
|
|
73
|
-
Release: [v0.10.
|
|
73
|
+
Release: [v0.10.1](https://github.com/zufall-upon/Sortie-dogs/releases/tag/v0.10.1)
|
|
74
74
|
|
|
75
75
|
## Latest local benchmark case study
|
|
76
76
|
|
|
@@ -88,31 +88,38 @@ Run configuration was fixed per product configuration:
|
|
|
88
88
|
children on `openai/gpt-5.6-sol` / `medium`, with the pinned Sortie package and runtime assets.
|
|
89
89
|
No Luna, Astra, or Opus messages were observed in these trials.
|
|
90
90
|
|
|
91
|
-
| Metric · one frozen task | Bare OpenCode | Sortie v0.9.12 | Sortie v0.10.
|
|
92
|
-
| --- | ---: | ---: | ---: |
|
|
93
|
-
| Attempts needed | 3 | 5 | 1 |
|
|
94
|
-
| Completed runs compared | 3 | 3 | 1 |
|
|
95
|
-
| Verified PASS | 0/3 | 0/3 | 0/1 |
|
|
96
|
-
| Task checks · F2P | 11.1% · 3/27 | 85.2% · 23/27 | 88.9% · 8/9 |
|
|
97
|
-
| Retained checks · P2P | 282/282 | 282/282 | 94/94 |
|
|
98
|
-
| Median agent wall | 24.5 min | 25.7 min |
|
|
99
|
-
| Median model steps | 43 | 39 |
|
|
100
|
-
| Implementation child sessions · total | 0 | 13 |
|
|
101
|
-
| Estimated API-equivalent cost · median completed run | $3.53 | $2.85 | **$
|
|
102
|
-
| Estimated cost · completed runs | $10.69 | $9.74 | **$
|
|
103
|
-
| Additional interrupted-attempt cost | $0 | $6.20 | $0 |
|
|
104
|
-
| Total cost to acquire completed runs | $10.69 | $15.94 | **$
|
|
91
|
+
| Metric · one frozen task | Bare OpenCode | Sortie v0.9.12 | Sortie v0.10.1 RC qualification |
|
|
92
|
+
| --- | ---: | ---: | ---: |
|
|
93
|
+
| Attempts needed | 3 | 5 | 1 |
|
|
94
|
+
| Completed runs compared | 3 | 3 | 1 |
|
|
95
|
+
| Verified PASS | 0/3 | 0/3 | 0/1 |
|
|
96
|
+
| Task checks · F2P | 11.1% · 3/27 | 85.2% · 23/27 | 88.9% · 8/9 |
|
|
97
|
+
| Retained checks · P2P | 282/282 | 282/282 | 94/94 |
|
|
98
|
+
| Median agent wall | 24.5 min | 25.7 min | 23.2 min · n=1 |
|
|
99
|
+
| Median model steps | 43 | 39 | 41 · CLI stream only |
|
|
100
|
+
| Implementation child sessions · total | 0 | 13 | 3 |
|
|
101
|
+
| Estimated API-equivalent cost · median completed run | $3.53 | $2.85 | **$4.48** · n=1 |
|
|
102
|
+
| Estimated cost · completed runs | $10.69 | $9.74 | **$4.48** |
|
|
103
|
+
| Additional interrupted-attempt cost · same fixed snapshot | $0 | $6.20 | $0 |
|
|
104
|
+
| Total cost to acquire completed runs · same fixed snapshot | $10.69 | $15.94 | **$4.48** |
|
|
105
105
|
|
|
106
106
|
All three Bare runs passed 1/9 task checks. The three completed Sortie runs passed 7/9, 8/9,
|
|
107
107
|
and 8/9. Every compared candidate retained 94/94 prior checks, but every official verifier still
|
|
108
108
|
returned reward 0. The two interrupted Sortie attempts are excluded from completed-run quality,
|
|
109
109
|
time, and cost aggregates; their attempt count and estimated cost remain visible above.
|
|
110
110
|
|
|
111
|
-
The v0.10 value is a standalone qualification-only reference, not a fourth matched run or a
|
|
112
|
-
FrontierHarness leaderboard result. It used no fresh Bare control and a localized Docker-free
|
|
113
|
-
verifier.
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
The v0.10.1 RC value is a standalone qualification-only reference, not a fourth matched run or a
|
|
112
|
+
FrontierHarness leaderboard result. It used no fresh Bare control and a localized Docker-free
|
|
113
|
+
verifier. The RC label identifies an unreleased working-tree snapshot; its packed metadata remained
|
|
114
|
+
`0.10.0`, fixed by package SHA-256 `67582ee023c3bd57813262e292c067a3f90f30d61c01d8caf6f91f09ae78f61d`.
|
|
115
|
+
|
|
116
|
+
Its estimated API-equivalent cost is **$4.478613**: $3.23 Sol and $1.25 Terra when rounded to cents.
|
|
117
|
+
The audit prices all root and descendant assistant messages: 84 priced requests, 4,215,698 tokens,
|
|
118
|
+
100% pricing coverage, and no unpriced requests. It uses the product's 2026-09-14 Standard schedule
|
|
119
|
+
per request, including cached input, reasoning as output, and the long-context rate band. The earlier
|
|
120
|
+
delivery-incomplete run used a different packed source snapshot, so it is not folded into this RC's
|
|
121
|
+
same-snapshot acquisition cost. Historical Bare and v0.9.12 costs retain their 2026-07-30 schedule;
|
|
122
|
+
their cost cells are not same-rate comparisons with this RC estimate.
|
|
116
123
|
|
|
117
124
|

|
|
118
125
|
|
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.10.0-v0912-language4-cost-rpt10-compaction-ref1-proposal1-review-remediation1-surface3";
|
|
6
|
+
export declare const V010_RUNTIME_ASSET_VERSION = "0.10.0-v0912-language4-cost-rpt10-compaction-ref1-proposal1-review-remediation1-surface3-proposal-recovery2-quality1";
|
|
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.10.0-v0912-language4-cost-rpt10-compaction-ref1-proposal1-review-remediation1-surface3";
|
|
6
|
+
export const V010_RUNTIME_ASSET_VERSION = "0.10.0-v0912-language4-cost-rpt10-compaction-ref1-proposal1-review-remediation1-surface3-proposal-recovery2-quality1";
|
|
@@ -95,6 +95,9 @@ export declare class OperatorProposalRuntime {
|
|
|
95
95
|
readonly projectRoot: string;
|
|
96
96
|
constructor(projectRoot: string, profile: RuntimeProfile);
|
|
97
97
|
private file;
|
|
98
|
+
private spendFile;
|
|
99
|
+
private readSpend;
|
|
100
|
+
private saveSpend;
|
|
98
101
|
read(root: string): Promise<OperatorProposalState | undefined>;
|
|
99
102
|
private readUnlocked;
|
|
100
103
|
private save;
|
|
@@ -117,9 +120,16 @@ export declare class OperatorProposalRuntime {
|
|
|
117
120
|
accountRead(root: string, actor: string, path?: string): Promise<OperatorProposalState>;
|
|
118
121
|
private accountReadUnlocked;
|
|
119
122
|
submit(root: string, actor: string, raw: unknown): Promise<OperatorProposalState>;
|
|
123
|
+
submitJSON(root: string, actor: string, source: string): Promise<OperatorProposalState>;
|
|
120
124
|
private submitUnlocked;
|
|
121
125
|
approve(root: string, raw: unknown, commit?: boolean): Promise<OperatorProposalState>;
|
|
122
126
|
private approveUnlocked;
|
|
127
|
+
/**
|
|
128
|
+
* Release a pre-approval proposal grant on explicit root cancellation. Without this the root holds an
|
|
129
|
+
* immutable investigating intent whose admitted child can no longer bind, so no retry path exists at all.
|
|
130
|
+
* An approved proposal is already connected to the execution lane and stays durable.
|
|
131
|
+
*/
|
|
132
|
+
discardPreApproval(root: string): Promise<OperatorProposalState | undefined>;
|
|
123
133
|
required(root: string): Promise<OperatorProposalState>;
|
|
124
134
|
assertExecutionApproved(root: string, planHash: string): Promise<void>;
|
|
125
135
|
private requiredUnlocked;
|
|
@@ -120,23 +120,63 @@ function parseIntent(value) {
|
|
|
120
120
|
function parsePacket(value, state) {
|
|
121
121
|
if (Buffer.byteLength(JSON.stringify(value)) > 128 * 1024)
|
|
122
122
|
throw new Error("operator-proposal-too-large");
|
|
123
|
-
if (!record(value)
|
|
124
|
-
return proposalError("/", "operator-proposal-invalid", "
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
123
|
+
if (!record(value))
|
|
124
|
+
return proposalError("/", "operator-proposal-invalid", "object", { expected: "object", actual_type: jsonType(value) });
|
|
125
|
+
const diagnostics = [];
|
|
126
|
+
const invalid = (pointer, rule, expected, actual, code = "operator-proposal-invalid") => {
|
|
127
|
+
diagnostics.push({ document: "proposal", pointer, code, rule, repair_kind: "repair-field",
|
|
128
|
+
repair_paths: [pointer], expected, actual_type: jsonType(actual) });
|
|
129
|
+
};
|
|
130
|
+
const fields = ["schema_version", "revision", "coverage", "existing_surface", "uncovered", "negative_handling", "read_scope", "write_scope", "budget_estimate", "plan"];
|
|
131
|
+
for (const key of Object.keys(value).filter(key => !fields.includes(key)).sort()) {
|
|
132
|
+
invalid(`/${pointerToken(key)}`, "unknown-field", "remove-unknown-field", value[key]);
|
|
133
|
+
}
|
|
134
|
+
for (const key of fields)
|
|
135
|
+
if (!Object.hasOwn(value, key))
|
|
136
|
+
invalid(`/${key}`, "required", "required-field", undefined);
|
|
137
|
+
if (Object.hasOwn(value, "schema_version") && value.schema_version !== "0.1") {
|
|
138
|
+
invalid("/schema_version", "schema-version", 'literal-"0.1"', value.schema_version);
|
|
139
|
+
}
|
|
140
|
+
if (Object.hasOwn(value, "revision") && (!Number.isSafeInteger(value.revision) || value.revision < 1)) {
|
|
141
|
+
invalid("/revision", "positive-integer", "positive-integer", value.revision, "operator-proposal-revision-invalid");
|
|
142
|
+
}
|
|
143
|
+
for (const key of ["coverage", "existing_surface", "uncovered", "negative_handling", "read_scope", "write_scope"]) {
|
|
144
|
+
if (!Object.hasOwn(value, key))
|
|
145
|
+
continue;
|
|
146
|
+
const items = value[key];
|
|
147
|
+
if (!Array.isArray(items)) {
|
|
148
|
+
invalid(`/${key}`, "array", "array", items);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (key === "read_scope" || key === "write_scope")
|
|
152
|
+
items.forEach((item, index) => {
|
|
153
|
+
if (!normalizedRelativePath(item))
|
|
154
|
+
invalid(`/${key}/${index}`, "normalized-relative-path", "nonempty repository-relative path; forward slashes; no trailing slash, dot segments, traversal, or absolute path", item);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
if (Object.hasOwn(value, "budget_estimate")) {
|
|
158
|
+
const budget = value.budget_estimate;
|
|
159
|
+
if (!record(budget))
|
|
160
|
+
invalid("/budget_estimate", "object", "object", budget);
|
|
161
|
+
else {
|
|
162
|
+
const keys = ["proposal_reads", "execution_units"];
|
|
163
|
+
for (const key of Object.keys(budget).filter(key => !keys.includes(key)).sort()) {
|
|
164
|
+
invalid(`/budget_estimate/${pointerToken(key)}`, "unknown-field", "remove-unknown-field", budget[key]);
|
|
165
|
+
}
|
|
166
|
+
for (const key of keys)
|
|
167
|
+
if (!Number.isSafeInteger(budget[key])) {
|
|
168
|
+
invalid(`/budget_estimate/${key}`, Object.hasOwn(budget, key) ? "integer" : "required", "integer", budget[key]);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
136
171
|
}
|
|
172
|
+
if (diagnostics.length > 0)
|
|
173
|
+
throw new OperatorContractError(diagnostics);
|
|
174
|
+
// The shape checks above are batched for repair; semantic checks below remain strict.
|
|
175
|
+
const coverage = value.coverage, uncovered = value.uncovered, negatives = value.negative_handling;
|
|
176
|
+
const budget = value.budget_estimate;
|
|
137
177
|
const requirementIDs = new Set(state.intent.requirements.map(item => item.id));
|
|
138
178
|
const coverageIDs = new Set(), uncoveredIDs = new Set();
|
|
139
|
-
for (const item of
|
|
179
|
+
for (const item of coverage) {
|
|
140
180
|
if (!record(item) || !exact(item, ["requirement_id", "approach", "validation"]) || !identifier(item.requirement_id) ||
|
|
141
181
|
!requirementIDs.has(item.requirement_id) || coverageIDs.has(item.requirement_id) || !text(item.approach) || !text(item.validation))
|
|
142
182
|
throw new Error("operator-proposal-coverage-invalid");
|
|
@@ -159,7 +199,7 @@ function parsePacket(value, state) {
|
|
|
159
199
|
}
|
|
160
200
|
if ([...coverageIDs].some(id => !surfaced.has(id)))
|
|
161
201
|
throw new Error("operator-proposal-existing-surface-incomplete");
|
|
162
|
-
for (const item of
|
|
202
|
+
for (const item of uncovered) {
|
|
163
203
|
if (!record(item) || !exact(item, ["requirement_id", "reason"]) || !identifier(item.requirement_id) || !requirementIDs.has(item.requirement_id) ||
|
|
164
204
|
coverageIDs.has(item.requirement_id) || uncoveredIDs.has(item.requirement_id) || !text(item.reason))
|
|
165
205
|
throw new Error("operator-proposal-uncovered-invalid");
|
|
@@ -169,7 +209,7 @@ function parsePacket(value, state) {
|
|
|
169
209
|
throw new Error("operator-proposal-requirement-coverage-incomplete");
|
|
170
210
|
const negativeIDs = new Set(state.intent.requirements.filter(item => item.kind === "negative").map(item => item.id));
|
|
171
211
|
const handled = new Set();
|
|
172
|
-
for (const item of
|
|
212
|
+
for (const item of negatives) {
|
|
173
213
|
if (!record(item) || !exact(item, ["requirement_id", "handling"]) || !identifier(item.requirement_id) || !negativeIDs.has(item.requirement_id) || handled.has(item.requirement_id) || !text(item.handling))
|
|
174
214
|
throw new Error("operator-proposal-negative-handling-invalid");
|
|
175
215
|
handled.add(item.requirement_id);
|
|
@@ -204,9 +244,17 @@ function parsePacket(value, state) {
|
|
|
204
244
|
if (plan.acceptance.length !== acceptance.length || plan.acceptance.some((item, index) => item !== acceptance[index]))
|
|
205
245
|
throw new Error("operator-proposal-acceptance-rewritten");
|
|
206
246
|
const writes = [...new Set(plan.units.flatMap(unit => unit.write))].sort();
|
|
207
|
-
if (JSON.stringify([...new Set(value.write_scope)].sort()) !== JSON.stringify(writes))
|
|
208
|
-
|
|
209
|
-
|
|
247
|
+
if (JSON.stringify([...new Set(value.write_scope)].sort()) !== JSON.stringify(writes)) {
|
|
248
|
+
proposalError("/write_scope", "operator-proposal-write-scope-mismatch", "exact-union-of-unit-write-scopes", { repair_paths: ["/write_scope", "/plan/units"] });
|
|
249
|
+
}
|
|
250
|
+
for (const [unitIndex, unit] of plan.units.entries())
|
|
251
|
+
for (const [pathIndex, path] of unit.read.entries()) {
|
|
252
|
+
const executionReads = [...value.read_scope, ...plan.units.slice(0, unitIndex + 1).flatMap(item => item.write)];
|
|
253
|
+
if (!executionReads.some(scope => path === scope || path.startsWith(`${scope}/`))) {
|
|
254
|
+
proposalError(`/plan/units/${unitIndex}/read/${pathIndex}`, "operator-proposal-unit-read-scope-expanded", "unit-input-within-declared-read-scope-or-generated-write-scope");
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (budget.proposal_reads !== state.read_count || budget.execution_units !== plan.units.length)
|
|
210
258
|
throw new Error("operator-proposal-budget-estimate-mismatch");
|
|
211
259
|
return { ...structuredClone(materialized), plan };
|
|
212
260
|
}
|
|
@@ -233,6 +281,37 @@ export class OperatorProposalRuntime {
|
|
|
233
281
|
this.projectRoot = resolve(projectRoot);
|
|
234
282
|
}
|
|
235
283
|
file(root) { return join(this.projectRoot, this.profile.stateDirectory, "operator-proposals", `${hash(root)}.json`); }
|
|
284
|
+
spendFile(root) { return join(this.projectRoot, this.profile.stateDirectory, "operator-proposals", `${hash(root)}.spend.json`); }
|
|
285
|
+
async readSpend(root) {
|
|
286
|
+
let source;
|
|
287
|
+
try {
|
|
288
|
+
source = await readFile(this.spendFile(root), "utf8");
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
if (error.code === "ENOENT") {
|
|
292
|
+
return { schema_version: "0.1", profile: this.profile.id, root_session_id: root, reads: 0, submissions: 0 };
|
|
293
|
+
}
|
|
294
|
+
throw error;
|
|
295
|
+
}
|
|
296
|
+
const spend = JSON.parse(source);
|
|
297
|
+
if (spend.schema_version !== "0.1" || spend.profile !== this.profile.id || spend.root_session_id !== root ||
|
|
298
|
+
!Number.isSafeInteger(spend.reads) || spend.reads < 0 || !Number.isSafeInteger(spend.submissions) || spend.submissions < 0) {
|
|
299
|
+
throw new Error("operator-proposal-spend-invalid");
|
|
300
|
+
}
|
|
301
|
+
return spend;
|
|
302
|
+
}
|
|
303
|
+
async saveSpend(spend) {
|
|
304
|
+
const directory = join(this.projectRoot, this.profile.stateDirectory, "operator-proposals");
|
|
305
|
+
await mkdir(directory, { recursive: true });
|
|
306
|
+
const temporary = `${this.spendFile(spend.root_session_id)}.${randomUUID()}.tmp`;
|
|
307
|
+
try {
|
|
308
|
+
await writeFile(temporary, JSON.stringify(spend), { flag: "wx", mode: 0o600 });
|
|
309
|
+
await rename(temporary, this.spendFile(spend.root_session_id));
|
|
310
|
+
}
|
|
311
|
+
finally {
|
|
312
|
+
await rm(temporary, { force: true }).catch(() => undefined);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
236
315
|
async read(root) {
|
|
237
316
|
return this.serial(root, () => this.readUnlocked(root));
|
|
238
317
|
}
|
|
@@ -304,6 +383,11 @@ export class OperatorProposalRuntime {
|
|
|
304
383
|
"Git mutationが明示要件の時だけplan.git_lifecycleを追加する。exact shape: {\"branch_create\":{\"branch\":\"validated destination branch\",\"start_ref\":\"existing commit/ref\"},\"commit\":{\"message\":\"single-line commit message\"},\"post_commit_validation\":[\"exact declared validation command\"]}。post_commit_validationは既存canonical command identityでgoal criterion commandと一致し、final unit.validationの同順・連続suffixでなければならない。全non-post validationを先に実行し、全source write完了後にsuffixを実行する。hostはworker spend前にclean root、existing start_ref、nonexisting destinationを検証し、fixed argvでbranch作成する。suffix先頭command直前に全unit.write union内変更を明示stageしてcommitし、以後のsource writeを拒否する。既存validation executorが各post-commit commandのfresh evidenceを記録し、全evidenceなしのroot acceptanceは禁止。任意Git command、git add -A、commit -a、amend、force、push、既存branch上書きは禁止。git_lifecycle省略時のGit mutationはない。",
|
|
305
384
|
"acceptance_indicesは0-based。各unitのacceptance_indicesに置く全indexについて、そのacceptance_proof[index]が参照するcriterionのresolved validation_commandを同じunit.validationへ文字列完全一致で最低1個含める。範囲外index禁止。さらに各unitはそれ以前のunitにないgoal criterion validation_commandを少なくとも1個validationへ含める。同じvalidation commandだけを全unitで再利用禁止。順序付き2 unitなら例としてunit 1に中間状態を許すcommand、unit 2に最終commandを割り当てる。",
|
|
306
385
|
"existing_surfaceは各covered requirementにつき最低1件。対象codebaseで、その要件が制約・変更する既存構文や既存経路を実際にReadして特定し、そのfileのproject相対pathと見つけた形(form)を書く。formは文面の言い換えでなく、observedした構文形・node種別・dispatch分岐を書く。生成・宣言・束縛の経路と変更・代入の経路は別項目として挙げる。単数形しか要件に書かれていなくても、grammarやdispatchに複数値・分配・入れ子・暗黙形があればそれも挙げる。既存形が無いと判断した場合も、探索したfileのpathと「既存形なし」の根拠をformへ書く。pathはこのTaskで実際にReadしたfileでなければhostが拒否する。",
|
|
386
|
+
"調査順: authoritative_refsと既知entrypointから始める。対象pathが分かっているならdirectory巡回を挟まず直接そこへ進み、path未知の時だけ許可scope内のdirectoryやindexをReadして実在を確認する。名前を推測しただけのReadをしない。各requirementについて、それが変更する既存経路、symbol、validation oracle、build recipeを結び付ける。巨大な概念書やtest群を既定で全文読込せず、既知location・目次・周辺範囲から必要なoffset/limitを選ぶ。部分Readは探索手段であって完了条件ではない。既存分岐、関連呼出先、値の表現、負要件の影響範囲が不明なら範囲を広げ、必要ならfile全体を読む。読込量の少なさ自体を達成度にしない。authoritative Makefile、言語のgenerator directive、package/build scriptは後回しにせず関連実装経路と併せて早期に確認する。同じ内容の再読は、新しい未解決点、参照先、範囲不足、source変化のいずれかを理由とする。Read以外のtoolは要求しない。",
|
|
387
|
+
"記述は既存fieldで簡潔に行う。raw sourceやlogの全文再掲、同じplanの自然文による二重転記をしない。ただしcoverage、existing_surface、ordered validation、read/write、negative_handling、uncoveredは省略しない。初回提出で全requirementを扱えるproposalを目指し、調査できなかった点はuncoveredへ正直に残す。薄いproposalを出してrootへ追加調査を戻す往復を前提にしない。",
|
|
388
|
+
"提出前に正本commandと照合する。観測済みのexecutable、引数、test path、build target範囲をそのまま保持し、全体buildを一部targetへ縮めたり未確認の絶対executable pathを補完したりしない。検証script自身の副作用まで確認し、全生成物をunit.writeへ宣言する。unit.readはread_scopeまたは宣言済みwrite_scope内。再提出は診断fieldとactual_readsだけを直し、既に正しいcommand・scopeを再生成しない。",
|
|
389
|
+
"unit.validationとcriterionのvalidation_commandには実行commandだけを書く。手動GUI確認、root専有review、運用上の予算会計を説明文やラベル付きの疑似commandへ変換しない。fixture・新規Sessionの自動生成は実Backendの既存Session表示の証拠とは限らず、oracleが実際に確認する対象と副作用を照合する。現行契約で表現できない要件はuncoveredへ理由を残し、無関係なtestを全要件の証明として割り当てない。",
|
|
390
|
+
"status=submittedが返ったら、この調査Taskは完了。追加toolなしで親へ返す。汎用の続行指示が来てもoperator_nextやworker起動へ進まない。提出は承認でも実行許可でもない。",
|
|
307
391
|
"許可sourceをReadで1回以上調査後、返答前にproposal packetをsortie_v010_submit_operator_proposalへ提出する。invalid-proposal返却時だけ、そのcodeに該当するfieldを修正する。返却されたactual_readsはhostのcanonical accountingであり、再提出時はcodeに関係なくbudget_estimate.proposal_readsをその値へ一致させる。これはread budgetをresetせず、成功/失敗tool表示からcountを推測しない。失敗したReadが一律に未計上とも仮定しない。有限submission budget内で再提出する。proseだけ返して終了禁止。未対応要件はuncoveredへ明示する。承認・実行を主張しない。"].join("\n") };
|
|
308
392
|
}
|
|
309
393
|
/** Root-visible bounded handle; the canonical prompt remains only in durable host state. */
|
|
@@ -357,9 +441,10 @@ export class OperatorProposalRuntime {
|
|
|
357
441
|
throw new Error("operator-proposal-active-intent-immutable");
|
|
358
442
|
throw new Error("operator-proposal-new-intent-requires-explicit-root-revision");
|
|
359
443
|
}
|
|
444
|
+
const spend = await this.readSpend(root);
|
|
360
445
|
const state = { schema_version: "0.1", profile: this.profile.id, root_session_id: root,
|
|
361
446
|
intent_id: `intent-${intentHash.slice(0, 24)}`, intent_hash: intentHash, intent, created_at: new Date().toISOString(), phase: "investigating",
|
|
362
|
-
goal_binding: null, proposal_call_id: null, proposal_session_id: null, read_count:
|
|
447
|
+
goal_binding: null, proposal_call_id: null, proposal_session_id: null, read_count: spend.reads, read_paths: [], submission_count: spend.submissions, proposal_id: null,
|
|
363
448
|
proposal_revision: null, proposal_hash: null, proposal: null, approval_rationale: null };
|
|
364
449
|
await this.save(state);
|
|
365
450
|
return state;
|
|
@@ -448,9 +533,19 @@ export class OperatorProposalRuntime {
|
|
|
448
533
|
return state;
|
|
449
534
|
}
|
|
450
535
|
async submit(root, actor, raw) {
|
|
451
|
-
return this.serial(root, () => this.submitUnlocked(root, actor, raw));
|
|
536
|
+
return this.serial(root, () => this.submitUnlocked(root, actor, () => raw));
|
|
537
|
+
}
|
|
538
|
+
async submitJSON(root, actor, source) {
|
|
539
|
+
return this.serial(root, () => this.submitUnlocked(root, actor, () => {
|
|
540
|
+
try {
|
|
541
|
+
return JSON.parse(source);
|
|
542
|
+
}
|
|
543
|
+
catch {
|
|
544
|
+
return proposalError("/", "operator-proposal-json-invalid", "json");
|
|
545
|
+
}
|
|
546
|
+
}));
|
|
452
547
|
}
|
|
453
|
-
async submitUnlocked(root, actor,
|
|
548
|
+
async submitUnlocked(root, actor, readPacket) {
|
|
454
549
|
const state = await this.requiredUnlocked(root);
|
|
455
550
|
if (state.proposal_session_id !== actor || state.phase !== "investigating")
|
|
456
551
|
throw new Error("operator-proposal-submit-grant-invalid");
|
|
@@ -458,7 +553,7 @@ export class OperatorProposalRuntime {
|
|
|
458
553
|
throw new Error("operator-proposal-submission-budget-exhausted");
|
|
459
554
|
let packet;
|
|
460
555
|
try {
|
|
461
|
-
packet = parsePacket(
|
|
556
|
+
packet = parsePacket(readPacket(), state);
|
|
462
557
|
}
|
|
463
558
|
catch (error) {
|
|
464
559
|
state.submission_count++;
|
|
@@ -493,6 +588,23 @@ export class OperatorProposalRuntime {
|
|
|
493
588
|
}
|
|
494
589
|
return state;
|
|
495
590
|
}
|
|
591
|
+
/**
|
|
592
|
+
* Release a pre-approval proposal grant on explicit root cancellation. Without this the root holds an
|
|
593
|
+
* immutable investigating intent whose admitted child can no longer bind, so no retry path exists at all.
|
|
594
|
+
* An approved proposal is already connected to the execution lane and stays durable.
|
|
595
|
+
*/
|
|
596
|
+
async discardPreApproval(root) {
|
|
597
|
+
return this.serial(root, async () => {
|
|
598
|
+
const state = await this.readUnlocked(root);
|
|
599
|
+
if (!state || state.phase === "approved")
|
|
600
|
+
return undefined;
|
|
601
|
+
await this.saveSpend({ schema_version: "0.1", profile: this.profile.id, root_session_id: root,
|
|
602
|
+
reads: state.read_count, submissions: state.submission_count });
|
|
603
|
+
await rm(this.file(root), { force: true });
|
|
604
|
+
this.states.delete(root);
|
|
605
|
+
return state;
|
|
606
|
+
});
|
|
607
|
+
}
|
|
496
608
|
async required(root) { const state = await this.read(root); if (!state)
|
|
497
609
|
throw new Error("operator-proposal-missing"); return state; }
|
|
498
610
|
async assertExecutionApproved(root, planHash) {
|
|
@@ -41,6 +41,15 @@ export function operatorGitPathAuthorized(path, scopes, platform = process.platf
|
|
|
41
41
|
}
|
|
42
42
|
const contractError = (diagnostic) => { throw new OperatorContractError([diagnostic]); };
|
|
43
43
|
const planError = (pointer, code, rule, repair_kind = "repair-field") => contractError({ document: "plan", pointer, code, rule, repair_kind });
|
|
44
|
+
function rejectValidationAnnotation(command, pointer) {
|
|
45
|
+
// A bare multi-character label followed by ':' and whitespace is an instruction annotation,
|
|
46
|
+
// not a supported executable validation. Do not interpret or strip it into a fake passing oracle.
|
|
47
|
+
// This is deliberately not a shell parser or a claim of semantic proof; quoted commands, drive
|
|
48
|
+
// paths, URLs in arguments, and shell syntax remain the executor's responsibility.
|
|
49
|
+
if (/^\s*[A-Za-z][A-Za-z0-9_-]*(?:\s+[A-Za-z][A-Za-z0-9_-]*)*:(?:\s|$)/u.test(command)) {
|
|
50
|
+
planError(pointer, "operator-validation-annotation-invalid", "executable-command-not-instruction-label");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
44
53
|
function strings(value, nonempty = false) {
|
|
45
54
|
return Array.isArray(value) && (!nonempty || value.length > 0) && value.every(text);
|
|
46
55
|
}
|
|
@@ -104,6 +113,12 @@ export function parseOperatorPlan(value) {
|
|
|
104
113
|
return contractError({ document: "plan", pointer: `/goal_declaration/criteria/${proofCommands.size}/goal_validation_command`,
|
|
105
114
|
code: "operator-goal-command-required", rule: "declared-validation-command-for-criterion", repair_kind: "repair-proof-mapping",
|
|
106
115
|
repair_paths: [`/goal_declaration/criteria/${proofCommands.size}/goal_validation_command`] });
|
|
116
|
+
const commandOwner = raw.goal_validation_command != null || raw.validation_command != null ? raw
|
|
117
|
+
: record(declaration.defaults) && (declaration.defaults.goal_validation_command != null || declaration.defaults.validation_command != null)
|
|
118
|
+
? declaration.defaults : declaration;
|
|
119
|
+
const commandPrefix = commandOwner === raw ? `/goal_declaration/criteria/${proofCommands.size}`
|
|
120
|
+
: commandOwner === declaration ? "/goal_declaration" : "/goal_declaration/defaults";
|
|
121
|
+
rejectValidationAnnotation(command, `${commandPrefix}/${commandOwner.goal_validation_command != null ? "goal_validation_command" : "validation_command"}`);
|
|
107
122
|
proofCommands.set(id, command);
|
|
108
123
|
}
|
|
109
124
|
if (!Array.isArray(value.acceptance_proof) || value.acceptance_proof.length !== value.acceptance.length ||
|
|
@@ -119,6 +134,7 @@ export function parseOperatorPlan(value) {
|
|
|
119
134
|
!identifier(unit.id) || ids.has(unit.id) || !text(unit.title) || !text(unit.objective) ||
|
|
120
135
|
!strings(unit.read) || !strings(unit.write, true) || !strings(unit.validation, true))
|
|
121
136
|
return planError(`/units/${unitIndex}`, "operator-unit-invalid", "operator-unit-shape");
|
|
137
|
+
unit.validation.forEach((command, index) => rejectValidationAnnotation(command, `/units/${unitIndex}/validation/${index}`));
|
|
122
138
|
ids.add(unit.id);
|
|
123
139
|
if (!Array.isArray(unit.acceptance_indices) || unit.acceptance_indices.length === 0 ||
|
|
124
140
|
unit.acceptance_indices.some(index => !Number.isSafeInteger(index) || index < 0 || index >= acceptanceCount ||
|
package/dist/plugin/profiled.js
CHANGED
|
@@ -286,8 +286,11 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
286
286
|
: { ...(record(stringSchema) ? stringSchema : { type: "string" }), description: OPERATOR_INTENT_CONTRACT };
|
|
287
287
|
const proposalContract = "proposal_json must encode one JSON object with exactly these required fields and types: " +
|
|
288
288
|
'schema_version string "0.1"; revision positive integer, for example "revision":1, never string "revision":"1"; ' +
|
|
289
|
-
"coverage array of {requirement_id:string,approach:string,validation:string};
|
|
290
|
-
"
|
|
289
|
+
"coverage array of {requirement_id:string,approach:string,validation:string}; " +
|
|
290
|
+
"existing_surface array of {requirement_id:string,path:string,form:string}, with at least one observed form per covered requirement and each path actually Read by this child; " +
|
|
291
|
+
"uncovered array of {requirement_id:string,reason:string}; " +
|
|
292
|
+
"negative_handling array of {requirement_id:string,handling:string}, containing only and all IDs whose intent kind is negative; " +
|
|
293
|
+
"read_scope string array; write_scope string array; all scope and existing_surface paths must be normalized repository-relative paths with forward slashes, no trailing slash, dot segments, traversal, or absolute paths; " +
|
|
291
294
|
"budget_estimate object with proposal_reads:integer and execution_units:integer; plan object with schema_version:string, " +
|
|
292
295
|
"acceptance_proof:string[][], source_refs:string[], goal_declaration:object, units:object[], and optional git_lifecycle with exact shape " +
|
|
293
296
|
"Each unit.validation is the complete ordered execution list, not a tests-only list: commands required by observed authoritative Makefiles, language generator directives, or repository scripts for generation, build, formatting, and exact cleanup precede post-commit or canonical criterion tests. Put every required input in unit.read and every persistent or transient generated output in unit.write. Cleanup may remove only declared unit.write outputs; never approve arbitrary ignore rules or removal of undeclared paths. Do not guess a tool-specific command or output, claim an unobserved capability, or add a preparatory or cleanup command as a goal criterion unless it independently proves acceptance. The host preserves declared order and authority but does not statically discover or inject every build dependency or generator output. " +
|
|
@@ -329,7 +332,15 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
329
332
|
await control?.stopAutomaticRecovery(root);
|
|
330
333
|
const proposal = await proposals.read(root);
|
|
331
334
|
if (proposal?.phase === "investigating" && proposal.proposal_call_id !== null) {
|
|
332
|
-
|
|
335
|
+
// The admitted proposal Task already settles this reservation when its child returns. A terminal
|
|
336
|
+
// settlement is final, so an explicit cancellation must release the grant instead of failing on it.
|
|
337
|
+
try {
|
|
338
|
+
await control?.settleProposalBudget(root, proposal.intent_id, proposal.proposal_call_id, "cancelled");
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
if (!(error instanceof Error) || error.message !== "operator-proposal-budget-settlement-conflict")
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
333
344
|
}
|
|
334
345
|
const children = await operators.interrupted(root, reason);
|
|
335
346
|
for (const child of children) {
|
|
@@ -425,7 +436,7 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
425
436
|
const proposal = await proposals.read(context.sessionID);
|
|
426
437
|
const proposalNextAction = proposal?.phase === "investigating"
|
|
427
438
|
? proposal.proposal_call_id !== null
|
|
428
|
-
? "proposal Task is already admitted; do not redispatch it or call operator_next. Continue submission repairs only in the same active claimed child. If that child terminated without submission, report the terminal proposal failure; remaining read or submission capacity does not authorize a new Task, budget reset, or replacement child"
|
|
439
|
+
? "proposal Task is already admitted; do not redispatch it or call operator_next. Continue submission repairs only in the same active claimed child. If that child terminated without submission, report the terminal proposal failure; remaining read or submission capacity does not authorize a new Task, budget reset, or replacement child. Only an explicit root decision to retry may call cancel_operator with no reason to release this grant, which discards the spent proposal accounting and never reuses the terminated child"
|
|
429
440
|
: proposal.submission_count >= proposal.intent.proposal_budget.max_submissions
|
|
430
441
|
? "proposal submission budget exhausted; do not call operator_next; report the bounded proposal failure"
|
|
431
442
|
: "proposal is not submitted; do not call operator_next; complete or repair the bounded proposal submission"
|
|
@@ -441,13 +452,29 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
441
452
|
next_action: proposalNextAction }
|
|
442
453
|
: { status: "absent", profile: profile.id });
|
|
443
454
|
} };
|
|
444
|
-
tools[cancel] = { description: "Revoke this root's operator grant and stop only its owned children before releasing core state. reason is a closed set: omit it for a plain cancellation, including replanning
|
|
455
|
+
tools[cancel] = { description: "Revoke this root's operator grant and stop only its owned children before releasing core state. Before approval it instead releases the bounded proposal grant, including one whose admitted child already terminated, so the root can begin a new investigation; it never reuses that child or restores spent proposal budget. reason is a closed set: omit it for a plain cancellation, including replanning, contract revision, and any pre-approval proposal release, and never send free text such as a written justification. Use reason=acceptance-remediation only for decision=operator-acceptance-remediation-required. At awaiting-acceptance, reason=review-blocking authorizes a same-goal review-remediation replacement only within the exact acceptance, committed head, approved write union, and retained remaining budget.",
|
|
445
456
|
args: { reason: optionalStringSchema }, execute: async (args, context) => {
|
|
446
457
|
await requireRoot(context.sessionID);
|
|
447
458
|
const requestedReason = args.reason;
|
|
448
459
|
if (requestedReason !== undefined && requestedReason !== "review-blocking" && requestedReason !== "acceptance-remediation")
|
|
449
460
|
throw new Error("operator-cancel-reason-invalid");
|
|
450
|
-
const
|
|
461
|
+
const pending = await operators.read(context.sessionID);
|
|
462
|
+
if (!pending) {
|
|
463
|
+
// A pre-approval proposal owns no execution lane. Its admitted child can never be rebound, so without
|
|
464
|
+
// this release the root can neither resume, redispatch, nor start any replacement investigation.
|
|
465
|
+
const proposal = await proposals.read(context.sessionID);
|
|
466
|
+
if (!proposal || proposal.phase === "approved")
|
|
467
|
+
throw new Error("operator-run-missing");
|
|
468
|
+
if (requestedReason !== undefined)
|
|
469
|
+
throw new Error("operator-cancel-reason-invalid");
|
|
470
|
+
await stop(context.sessionID, "explicit-cancellation", false);
|
|
471
|
+
const discarded = await proposals.discardPreApproval(context.sessionID);
|
|
472
|
+
return JSON.stringify({ profile: profile.id, status: "cancelled", scope: "proposal",
|
|
473
|
+
released_proposal: discarded ? proposals.packet(discarded) : null,
|
|
474
|
+
next_action: "the bounded proposal grant is released and its spent reads/submissions are not restored; " +
|
|
475
|
+
"begin a new proposal investigation only on an explicit root decision to retry, and never reuse the cancelled child" });
|
|
476
|
+
}
|
|
477
|
+
const current = pending;
|
|
451
478
|
if (requestedReason === "acceptance-remediation" && current.decision !== "operator-acceptance-remediation-required")
|
|
452
479
|
throw new Error("operator-cancel-reason-invalid");
|
|
453
480
|
if (requestedReason === "review-blocking" && current.phase !== "awaiting-acceptance")
|
|
@@ -610,7 +637,10 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
610
637
|
if (!root)
|
|
611
638
|
throw new Error("operator-proposal-session-inactive");
|
|
612
639
|
try {
|
|
613
|
-
|
|
640
|
+
const submitted = await proposals.submitJSON(root, context.sessionID, args.proposal_json);
|
|
641
|
+
return JSON.stringify({ ...proposals.packet(submitted),
|
|
642
|
+
next_action: "Proposal submitted. This investigation child must now return to its parent without further tools. " +
|
|
643
|
+
"Do not call operator_next, start a worker, or claim approval; the root must compare and approve the proposal." });
|
|
614
644
|
}
|
|
615
645
|
catch (error) {
|
|
616
646
|
const contract = error instanceof OperatorContractError;
|
|
@@ -618,6 +648,7 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
618
648
|
throw error;
|
|
619
649
|
const state = await proposals.required(root);
|
|
620
650
|
return JSON.stringify({ status: "invalid-proposal", code: error.message, actual_reads: state.read_count,
|
|
651
|
+
remaining_reads: state.intent.proposal_budget.max_reads - state.read_count,
|
|
621
652
|
submissions: state.submission_count, remaining_submissions: state.intent.proposal_budget.max_submissions - state.submission_count,
|
|
622
653
|
...(contract ? { diagnostics: error.diagnostics, diagnostics_truncated: error.diagnostics_truncated } : {}) });
|
|
623
654
|
}
|
|
@@ -1118,6 +1149,14 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
1118
1149
|
(output.system ??= []).push(`SORTIE_RUNTIME_PROFILE ${profile.id}; marker ${assetVersion}. ` +
|
|
1119
1150
|
`Shared MkII protocol role names are logical: ${protocolMap}. Use only ${profile.toolPrefix} tools for this profile. ` +
|
|
1120
1151
|
"Never rewrite user acceptance or evidence to rename protocol roles. Final acceptance belongs only to the root coordinator.");
|
|
1152
|
+
const proposal = await proposals.read(root);
|
|
1153
|
+
if (proposal?.phase === "investigating" && proposal.proposal_session_id === request.sessionID) {
|
|
1154
|
+
(output.system ??= []).push(`SORTIE_PROPOSAL_PHASE investigating; intent=${proposal.intent_id}; root=${root}; child=${request.sessionID}. ` +
|
|
1155
|
+
`This durable phase remains authoritative after compaction even when the latest message is a generic continuation. ` +
|
|
1156
|
+
`Continue the admitted read-only investigation and submit through ${submitProposal}. Do not call ${next} or dispatch workers: no execution run exists yet. ` +
|
|
1157
|
+
`actual_reads=${proposal.read_count}; remaining_reads=${proposal.intent.proposal_budget.max_reads - proposal.read_count}; ` +
|
|
1158
|
+
`submissions=${proposal.submission_count}; remaining_submissions=${proposal.intent.proposal_budget.max_submissions - proposal.submission_count}.`);
|
|
1159
|
+
}
|
|
1121
1160
|
},
|
|
1122
1161
|
"experimental.text.complete": async (request, output) => {
|
|
1123
1162
|
const role = (await identity(request.sessionID)).role;
|
|
@@ -1142,6 +1181,18 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
1142
1181
|
if (!root)
|
|
1143
1182
|
return;
|
|
1144
1183
|
if ((await identity(request.sessionID)).role === "dog-operator") {
|
|
1184
|
+
const proposal = await proposals.read(root);
|
|
1185
|
+
if (proposal?.phase === "investigating" && proposal.proposal_session_id === request.sessionID) {
|
|
1186
|
+
(output.context ??= []).push(`Proposal continuation: ${JSON.stringify({
|
|
1187
|
+
root, child: request.sessionID, intent_id: proposal.intent_id, intent_hash: proposal.intent_hash,
|
|
1188
|
+
intent: proposal.intent, goal_binding: proposal.goal_binding, read_paths: proposal.read_paths,
|
|
1189
|
+
actual_reads: proposal.read_count, remaining_reads: proposal.intent.proposal_budget.max_reads - proposal.read_count,
|
|
1190
|
+
submissions: proposal.submission_count, remaining_submissions: proposal.intent.proposal_budget.max_submissions - proposal.submission_count,
|
|
1191
|
+
})}. Preserve the latest proposal draft and field diagnostics in the summary. Continue only in this same claimed read-only proposal child. ` +
|
|
1192
|
+
`Repair only diagnosed fields, then call ${submitProposal}; do not call ${next}, dispatch Tasks, execute work, or reset budgets. ` +
|
|
1193
|
+
"This is investigation, not an execution run. Compaction grants no new reads or submissions.");
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1145
1196
|
const state = await operators.required(root);
|
|
1146
1197
|
(output.context ??= []).push(`Operator continuation: root=${root}; run=${state.runID}; generation=${state.generation}; contract=${state.planHash}. ` +
|
|
1147
1198
|
`Read ${next} for current authoritative state. Do not reconstruct acceptance or reset the queue.`);
|
|
@@ -1150,9 +1201,13 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
1150
1201
|
await core["experimental.session.compacting"]?.(request, output);
|
|
1151
1202
|
},
|
|
1152
1203
|
"experimental.compaction.autocontinue": async (request, output) => {
|
|
1153
|
-
|
|
1204
|
+
const root = await rootFor(request.sessionID);
|
|
1205
|
+
if (!root)
|
|
1154
1206
|
return;
|
|
1155
1207
|
if ((await identity(request.sessionID)).role === "dog-operator") {
|
|
1208
|
+
const proposal = await proposals.read(root);
|
|
1209
|
+
if (proposal?.phase === "investigating" && proposal.proposal_session_id === request.sessionID)
|
|
1210
|
+
return;
|
|
1156
1211
|
output.enabled = false;
|
|
1157
1212
|
return;
|
|
1158
1213
|
}
|
|
@@ -102,9 +102,18 @@ their literal ASCII keys; translated prose belongs in the values. Do not guess a
|
|
|
102
102
|
For a nontrivial request whose source facts, unit boundaries, or exact validation contract still require investigation,
|
|
103
103
|
do not author a giant speculative plan at the root. Freeze the original request once with stable ordered requirement IDs,
|
|
104
104
|
including every negative and quality condition, authoritative references, finite proposal read/submission budgets, and the
|
|
105
|
-
maximum read prefixes. Call ${profile.toolPrefix}begin_operator_proposal. Dispatch its exact ${operator} Task unchanged.
|
|
105
|
+
maximum read prefixes. Call ${profile.toolPrefix}begin_operator_proposal. Dispatch its exact ${operator} Task unchanged.
|
|
106
|
+
Freeze product requirements from the original request, not extra implementation criteria invented from workflow
|
|
107
|
+
bookkeeping. Keep proposal read/submission allowances in proposal_budget and host counters; do not turn spent
|
|
108
|
+
budgets or your own reporting obligations into worker validation commands. Preserve any explicit user requirement.
|
|
106
109
|
That child remains the same logical operations role: it may inspect only the host-approved read prefixes and submit one
|
|
107
110
|
requirement-mapped proposal; it cannot edit, use shell, dispatch a worker/advisor/scout, widen read scope, or execute work.
|
|
111
|
+
An admitted proposal child is never redispatched or replaced. If it terminates without a submitted proposal, report that
|
|
112
|
+
terminal failure first; only an explicit decision to retry may call ${profile.toolPrefix}cancel_operator with no reason to
|
|
113
|
+
release the grant before freezing the requirements again. Spent proposal reads and submissions are never restored.
|
|
114
|
+
Do not cancel/reinvestigate the same known contract defect merely to get another draft. Preserve exact observed
|
|
115
|
+
commands and correction evidence. If a user-only acceptance condition cannot be expressed by the existing contract,
|
|
116
|
+
report that specific limitation for a scope/design decision instead of inventing a passing test or another investigation.
|
|
108
117
|
|
|
109
118
|
The proposal must map every original requirement ID to approach and validation, explicitly list uncovered IDs, preserve
|
|
110
119
|
negative-condition handling, and include exact read/write/unit/GoalDeclaration plans and budget estimates. Its summary is
|
|
@@ -267,10 +276,14 @@ tools:
|
|
|
267
276
|
# ${operator}
|
|
268
277
|
|
|
269
278
|
You operate one coordinator-approved serial queue for runtime ${V010_RUNTIME_ASSET_VERSION}. You are not a second coordinator.
|
|
270
|
-
When the prompt starts SORTIE_OPERATOR_PROPOSAL
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
279
|
+
When the prompt starts SORTIE_OPERATOR_PROPOSAL or the host supplies SORTIE_PROPOSAL_PHASE investigating,
|
|
280
|
+
perform only its bounded read investigation and submit the complete packet
|
|
281
|
+
through ${profile.toolPrefix}submit_operator_proposal before returning; a prose-only return is forbidden. If it returns invalid-proposal,
|
|
282
|
+
repair only the named code (using actual_reads for a budget estimate mismatch) within the finite submission budget.
|
|
283
|
+
A successful status=submitted ends this investigation Task: return to the parent without further tools, even if a generic
|
|
284
|
+
continuation asks for the next step. Submission is not execution admission; never call operator_next or dispatch a worker.
|
|
285
|
+
For an admitted execution queue, call
|
|
286
|
+
${profile.toolPrefix}operator_next. If it returns a task, pass its subagent_type, description, and prompt unchanged to Task.
|
|
274
287
|
After the worker returns, inspect the host's bounded packet and call next again only when the queue still has pending work.
|
|
275
288
|
The worker owns implementation, diagnosis, correction and declared validation inside its Task invocation. Do not duplicate it.
|
|
276
289
|
|
|
@@ -280,8 +293,10 @@ or replace a child to bypass a refusal or a budget. The plugin enforces root/pro
|
|
|
280
293
|
|
|
281
294
|
Keep coordination concise and use the handoff's language. Do not return intermediate progress merely to wake the coordinator.
|
|
282
295
|
On awaiting-decision, cancelled, or awaiting-acceptance, stop and return the packet's status and unresolved evidence. Do not
|
|
283
|
-
claim the feature is accepted; only the root coordinator can do that. After compaction
|
|
284
|
-
|
|
296
|
+
claim the feature is accepted; only the root coordinator can do that. After compaction in the proposal phase, preserve the
|
|
297
|
+
same child, intent, read evidence and remaining budgets; continue investigation/submission repair, never call next.
|
|
298
|
+
Only in the execution phase, call next to read authoritative queue state and its current short Task reference rather than
|
|
299
|
+
reconstructing criteria from a summary. Never use a standalone/generic worker as a fallback.
|
|
285
300
|
`;
|
|
286
301
|
/**
|
|
287
302
|
* A constraint added to an existing construct is only complete when every existing form that reaches
|