@tea-agent/loop-agent 0.26.3 → 0.26.5-beta.0
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/CHANGELOG.md +20 -0
- package/dist/executors/dag-pi-executor.js +26 -1
- package/dist/executors/pi-executor.js +233 -147
- package/dist/executors/pi-sdk-executor.js +140 -81
- package/dist/executors/pi-writer-tool-policy.js +266 -0
- package/dist/executors/shell-executor.js +355 -52
- package/dist/governance/document-index-closure.js +164 -0
- package/dist/worker/observe/node-input.js +8 -11
- package/dist/workflows/dag/convergence/controller.js +100 -3
- package/dist/workflows/dag/frontend-implementation-contract.js +6 -3
- package/dist/workflows/dag/frontend-prewrite-gate.js +6 -1
- package/dist/workflows/dag/init-hybrid.js +51 -11
- package/dist/workflows/dag/repair-artifact.js +100 -4
- package/dist/workflows/dag/rerun-plan.js +14 -7
- package/dist/workflows/dag/types.js +6 -0
- package/dist/workflows/dag/workspace-checkpoint.js +42 -0
- package/docs/templates/init-managed-agents.md +3 -3
- package/harness.json +1 -1
- package/package.json +1 -1
- package/skills/loop-agent/references/orchestrator-and-interventions.md +13 -6
|
@@ -9,17 +9,31 @@ export const repairFailureClassSchema = z.enum([
|
|
|
9
9
|
"governance",
|
|
10
10
|
"unknown",
|
|
11
11
|
]);
|
|
12
|
+
export const repairDispositionSchema = z.enum([
|
|
13
|
+
"repairable",
|
|
14
|
+
"blocked-boundary",
|
|
15
|
+
"no-op-pass",
|
|
16
|
+
]);
|
|
12
17
|
export const repairArtifactSchema = z.object({
|
|
13
18
|
schemaVersion: z.literal(1),
|
|
14
19
|
verdict: z.enum(["pass", "request-revision"]),
|
|
15
20
|
failureClass: repairFailureClassSchema,
|
|
16
|
-
rootCause: z
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
rootCause: z.string().min(1, {
|
|
22
|
+
message: 'repair artifact field "rootCause" must be a non-empty string',
|
|
23
|
+
}),
|
|
19
24
|
fixScope: z.array(z.string().min(1)).default([]),
|
|
20
25
|
invariant: z.string().min(1),
|
|
21
26
|
evidenceRefs: z.array(z.string().min(1)).default([]),
|
|
22
27
|
rawLogFallbackAllowed: z.boolean().default(false),
|
|
28
|
+
/**
|
|
29
|
+
* Optional disposition for convergence routing. Omitted values are treated as
|
|
30
|
+
* `repairable` for backward compatibility with older artifacts.
|
|
31
|
+
*/
|
|
32
|
+
disposition: repairDispositionSchema.optional(),
|
|
33
|
+
/**
|
|
34
|
+
* Report-only out-of-boundary paths. Never expands write authority.
|
|
35
|
+
*/
|
|
36
|
+
requiredScope: z.array(z.string().min(1)).optional(),
|
|
23
37
|
});
|
|
24
38
|
/**
|
|
25
39
|
* Canonical rootCause substituted when a `verdict: "pass"` repair artifact
|
|
@@ -117,7 +131,23 @@ function scopeWithinPatterns(scope, patterns) {
|
|
|
117
131
|
}
|
|
118
132
|
export function validateRepairArtifactScope(input) {
|
|
119
133
|
const { artifact, repairTask } = input;
|
|
134
|
+
const disposition = artifact.disposition ?? "repairable";
|
|
135
|
+
const requiredScope = artifact.requiredScope ?? [];
|
|
136
|
+
for (const scope of requiredScope) {
|
|
137
|
+
if (!isPathLikeScope(scope)) {
|
|
138
|
+
return {
|
|
139
|
+
ok: false,
|
|
140
|
+
reason: `requiredScope "${scope}" must be a concrete path or glob (report-only; does not expand write authority)`,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
120
144
|
if (artifact.verdict === "pass") {
|
|
145
|
+
if (disposition === "blocked-boundary") {
|
|
146
|
+
return {
|
|
147
|
+
ok: false,
|
|
148
|
+
reason: "pass repair artifact cannot use blocked-boundary disposition",
|
|
149
|
+
};
|
|
150
|
+
}
|
|
121
151
|
if (artifact.fixScope.length > 0) {
|
|
122
152
|
return {
|
|
123
153
|
ok: false,
|
|
@@ -126,6 +156,67 @@ export function validateRepairArtifactScope(input) {
|
|
|
126
156
|
}
|
|
127
157
|
return { ok: true, artifact };
|
|
128
158
|
}
|
|
159
|
+
if (disposition === "no-op-pass") {
|
|
160
|
+
return {
|
|
161
|
+
ok: false,
|
|
162
|
+
reason: "request-revision repair artifact cannot use no-op-pass disposition",
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
// blocked-boundary may report empty fixScope (nothing in-boundary to fix)
|
|
166
|
+
// while requiredScope describes the out-of-boundary need.
|
|
167
|
+
if (disposition === "blocked-boundary") {
|
|
168
|
+
if (requiredScope.length === 0) {
|
|
169
|
+
return {
|
|
170
|
+
ok: false,
|
|
171
|
+
reason: "blocked-boundary request-revision requires non-empty requiredScope (report-only)",
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
if (!repairTask) {
|
|
175
|
+
return {
|
|
176
|
+
ok: false,
|
|
177
|
+
reason: "repair artifact gate cannot find downstream repair task",
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const allowed = repairTask.allowedPaths ?? [];
|
|
181
|
+
const writeSet = repairTask.writeSet ?? [];
|
|
182
|
+
const forbidden = repairTask.forbiddenPaths ?? [];
|
|
183
|
+
const hasOutOfBoundaryRequiredScope = requiredScope.some((scope) => {
|
|
184
|
+
const normalized = normalizePath(scope);
|
|
185
|
+
return (!scopeWithinPatterns(scope, allowed) ||
|
|
186
|
+
!scopeWithinPatterns(scope, writeSet) ||
|
|
187
|
+
forbidden.some((pattern) => pathMatchesPattern(normalized, pattern)));
|
|
188
|
+
});
|
|
189
|
+
if (!hasOutOfBoundaryRequiredScope) {
|
|
190
|
+
return {
|
|
191
|
+
ok: false,
|
|
192
|
+
reason: "blocked-boundary requiredScope must include a path outside repair task allowedPaths/writeSet",
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
if (artifact.fixScope.length > 0) {
|
|
196
|
+
for (const scope of artifact.fixScope) {
|
|
197
|
+
if (!isPathLikeScope(scope)) {
|
|
198
|
+
return {
|
|
199
|
+
ok: false,
|
|
200
|
+
reason: `fixScope "${scope}" must be a concrete path or glob inside repair task allowedPaths/writeSet`,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
if (!scopeWithinPatterns(scope, allowed) ||
|
|
204
|
+
!scopeWithinPatterns(scope, writeSet)) {
|
|
205
|
+
return {
|
|
206
|
+
ok: false,
|
|
207
|
+
reason: `fixScope "${scope}" is outside repair task allowedPaths/writeSet`,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
if (forbidden.some((pattern) => pathMatchesPattern(normalizePath(scope), pattern))) {
|
|
211
|
+
return {
|
|
212
|
+
ok: false,
|
|
213
|
+
reason: `fixScope "${scope}" matches repair task forbiddenPaths`,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return { ok: true, artifact };
|
|
219
|
+
}
|
|
129
220
|
if (artifact.fixScope.length === 0) {
|
|
130
221
|
return {
|
|
131
222
|
ok: false,
|
|
@@ -148,7 +239,8 @@ export function validateRepairArtifactScope(input) {
|
|
|
148
239
|
reason: `fixScope "${scope}" must be a concrete path or glob inside repair task allowedPaths/writeSet`,
|
|
149
240
|
};
|
|
150
241
|
}
|
|
151
|
-
if (!scopeWithinPatterns(scope, allowed) ||
|
|
242
|
+
if (!scopeWithinPatterns(scope, allowed) ||
|
|
243
|
+
!scopeWithinPatterns(scope, writeSet)) {
|
|
152
244
|
return {
|
|
153
245
|
ok: false,
|
|
154
246
|
reason: `fixScope "${scope}" is outside repair task allowedPaths/writeSet`,
|
|
@@ -255,12 +347,16 @@ export function resolveRepairTaskForGate(input) {
|
|
|
255
347
|
};
|
|
256
348
|
}
|
|
257
349
|
export function formatRepairArtifactForPrompt(artifact) {
|
|
350
|
+
const disposition = artifact.disposition ?? "repairable";
|
|
351
|
+
const requiredScope = artifact.requiredScope ?? [];
|
|
258
352
|
return [
|
|
259
353
|
"Repair artifact:",
|
|
260
354
|
`- verdict: ${artifact.verdict}`,
|
|
261
355
|
`- failureClass: ${artifact.failureClass}`,
|
|
262
356
|
`- rootCause: ${artifact.rootCause}`,
|
|
357
|
+
`- disposition: ${disposition}`,
|
|
263
358
|
`- fixScope: ${artifact.fixScope.length > 0 ? artifact.fixScope.join(", ") : "(none)"}`,
|
|
359
|
+
`- requiredScope: ${requiredScope.length > 0 ? requiredScope.join(", ") : "(none)"}`,
|
|
264
360
|
`- invariant: ${artifact.invariant}`,
|
|
265
361
|
`- evidenceRefs: ${artifact.evidenceRefs.length > 0 ? artifact.evidenceRefs.join(", ") : "(none)"}`,
|
|
266
362
|
].join("\n");
|
|
@@ -254,7 +254,10 @@ export function computeResetClosure(spec, effectiveFromNodeId) {
|
|
|
254
254
|
if (!tasks.has(effectiveFromNodeId)) {
|
|
255
255
|
return [];
|
|
256
256
|
}
|
|
257
|
-
return [
|
|
257
|
+
return [
|
|
258
|
+
effectiveFromNodeId,
|
|
259
|
+
...collectReachableDescendants(spec, effectiveFromNodeId),
|
|
260
|
+
].sort();
|
|
258
261
|
}
|
|
259
262
|
export function assessResetClosureSafety(spec, resetNodeIds) {
|
|
260
263
|
const tasks = taskById(spec);
|
|
@@ -355,7 +358,8 @@ function computeVerificationPolicy(spec, parentRunId, resetNodeIds, importedNode
|
|
|
355
358
|
function deriveSuggestedAction(input) {
|
|
356
359
|
if (input.eligible)
|
|
357
360
|
return "dag-rerun";
|
|
358
|
-
if (input.workerManaged ||
|
|
361
|
+
if (input.workerManaged ||
|
|
362
|
+
input.reasonCodes.includes("worker-managed-rerun-unsupported")) {
|
|
359
363
|
return "worker-task-retry";
|
|
360
364
|
}
|
|
361
365
|
if (input.parentLifecycle === "paused")
|
|
@@ -449,7 +453,8 @@ export async function evaluateDagRerunPlan(input) {
|
|
|
449
453
|
input.currentControllerFingerprint === input.parentControllerFingerprint;
|
|
450
454
|
const workspaceMatched = input.parentTerminalWorkspace?.fingerprint !== undefined &&
|
|
451
455
|
input.currentWorkspace?.fingerprint !== undefined &&
|
|
452
|
-
input.parentTerminalWorkspace.fingerprint ===
|
|
456
|
+
input.parentTerminalWorkspace.fingerprint ===
|
|
457
|
+
input.currentWorkspace.fingerprint;
|
|
453
458
|
const skillSnapshotVerified = input.skillSnapshotOk !== false;
|
|
454
459
|
const sourceBindingMatched = input.bindingStatus?.sourceBindingMatched ?? true;
|
|
455
460
|
const taskContractBindingMatched = input.bindingStatus?.taskContractBindingMatched ?? true;
|
|
@@ -499,7 +504,8 @@ export async function evaluateDagRerunPlan(input) {
|
|
|
499
504
|
blockedReasons.push("workspace-checkpoint-missing");
|
|
500
505
|
}
|
|
501
506
|
else if (input.currentWorkspace?.fingerprint &&
|
|
502
|
-
input.parentTerminalWorkspace.fingerprint !==
|
|
507
|
+
input.parentTerminalWorkspace.fingerprint !==
|
|
508
|
+
input.currentWorkspace.fingerprint) {
|
|
503
509
|
reasonCodes.push("workspace-drift");
|
|
504
510
|
blockedReasons.push("workspace-drift");
|
|
505
511
|
}
|
|
@@ -566,9 +572,10 @@ export async function evaluateDagRerunPlan(input) {
|
|
|
566
572
|
const totalPi = countExecutorNodes(input.parentSpec, allNodeIds, "pi");
|
|
567
573
|
const resetPi = countExecutorNodes(input.parentSpec, resetNodeIds, "pi");
|
|
568
574
|
const resetShell = countExecutorNodes(input.parentSpec, resetNodeIds, "shell");
|
|
569
|
-
const workerAssociation = input.workerAssociation ??
|
|
570
|
-
|
|
571
|
-
|
|
575
|
+
const workerAssociation = input.workerAssociation ??
|
|
576
|
+
(input.workerManaged
|
|
577
|
+
? { kind: "worker-managed" }
|
|
578
|
+
: { kind: "standalone" });
|
|
572
579
|
const planWithoutHash = {
|
|
573
580
|
schemaVersion: 1,
|
|
574
581
|
eligible,
|
|
@@ -391,6 +391,12 @@ export const dagShellConfigSchema = z.object({
|
|
|
391
391
|
repairArtifactGate: dagRepairArtifactGateSchema.optional(),
|
|
392
392
|
/** fail (default): any nonzero command fails the node. record: finish node FINISHED with failure facts for downstream assess/repair. */
|
|
393
393
|
nonZeroExitPolicy: z.enum(["fail", "record"]).optional(),
|
|
394
|
+
/**
|
|
395
|
+
* When true/omitted, stop after the first failed command (runtime default true).
|
|
396
|
+
* When false, continue executing remaining commands and aggregate failures.
|
|
397
|
+
* Optional on the schema so existing shell literals stay valid; executor applies default.
|
|
398
|
+
*/
|
|
399
|
+
failFast: z.boolean().optional(),
|
|
394
400
|
envAllowlist: z.array(z.string()).optional(),
|
|
395
401
|
timeoutMs: z.number().optional(),
|
|
396
402
|
cwd: z.string().optional(),
|
|
@@ -94,11 +94,47 @@ function computeFingerprint(input) {
|
|
|
94
94
|
gitHeadSha: input.gitHeadSha,
|
|
95
95
|
statusPorcelainSha256: input.statusPorcelainSha256,
|
|
96
96
|
changedPaths: input.changedPaths,
|
|
97
|
+
stableGovernanceInputs: input.stableGovernanceInputs,
|
|
97
98
|
});
|
|
98
99
|
return sha256Hex(payload);
|
|
99
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Stable Task Contract governance inputs for same-run hard-verify reuse.
|
|
103
|
+
* Explicitly excludes `.harness/dag-runs/**` and other runner-owned volatile facts.
|
|
104
|
+
*/
|
|
105
|
+
export function listStableGovernanceInputPaths(taskId) {
|
|
106
|
+
const safeTaskId = String(taskId ?? "").trim();
|
|
107
|
+
if (!safeTaskId ||
|
|
108
|
+
safeTaskId.includes("..") ||
|
|
109
|
+
safeTaskId.includes("/") ||
|
|
110
|
+
safeTaskId.includes("\\")) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
const base = `.harness/tasks/${safeTaskId}`;
|
|
114
|
+
return [
|
|
115
|
+
`${base}/task.json`,
|
|
116
|
+
`${base}/dag.json`,
|
|
117
|
+
`${base}/source/需求.md`,
|
|
118
|
+
`${base}/source/执行约束.md`,
|
|
119
|
+
`${base}/source/source-manifest.json`,
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
async function hashStableGovernanceInputs(repoRoot, taskId) {
|
|
123
|
+
const paths = listStableGovernanceInputPaths(taskId);
|
|
124
|
+
const entries = [];
|
|
125
|
+
for (const relPath of paths) {
|
|
126
|
+
const hashed = await hashWorkspacePath(repoRoot, relPath, "modified");
|
|
127
|
+
entries.push({
|
|
128
|
+
path: relPath,
|
|
129
|
+
contentSha256: hashed.contentSha256 ?? sha256Hex(`missing:${relPath}`),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
entries.sort((a, b) => a.path.localeCompare(b.path));
|
|
133
|
+
return entries;
|
|
134
|
+
}
|
|
100
135
|
/**
|
|
101
136
|
* Capture a workspace checkpoint for the current working tree.
|
|
137
|
+
* When `taskId` is provided, fingerprint includes stable ignored Task Contract inputs.
|
|
102
138
|
*/
|
|
103
139
|
export async function captureWorkspaceCheckpoint(repoRoot, options) {
|
|
104
140
|
const capturedAt = (options?.now ?? new Date()).toISOString();
|
|
@@ -127,10 +163,15 @@ export async function captureWorkspaceCheckpoint(repoRoot, options) {
|
|
|
127
163
|
});
|
|
128
164
|
}
|
|
129
165
|
changedPaths.sort((a, b) => a.path.localeCompare(b.path));
|
|
166
|
+
const taskId = options?.taskId?.trim();
|
|
167
|
+
const stableGovernanceInputs = taskId
|
|
168
|
+
? await hashStableGovernanceInputs(repoRoot, taskId)
|
|
169
|
+
: undefined;
|
|
130
170
|
const fingerprint = computeFingerprint({
|
|
131
171
|
gitHeadSha,
|
|
132
172
|
statusPorcelainSha256,
|
|
133
173
|
changedPaths,
|
|
174
|
+
stableGovernanceInputs,
|
|
134
175
|
});
|
|
135
176
|
return {
|
|
136
177
|
schemaVersion: 1,
|
|
@@ -138,6 +179,7 @@ export async function captureWorkspaceCheckpoint(repoRoot, options) {
|
|
|
138
179
|
gitHeadSha,
|
|
139
180
|
statusPorcelainSha256,
|
|
140
181
|
changedPaths,
|
|
182
|
+
...(stableGovernanceInputs ? { stableGovernanceInputs } : {}),
|
|
141
183
|
fingerprint,
|
|
142
184
|
};
|
|
143
185
|
}
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
主会话(含 openCode、Cursor Chat、其他宿主 agent)= **operator-only**;skills 与本文件是纪律文档,**不能**替代 `task.json` / DAG `writeSet` / runtime 执法。
|
|
30
30
|
|
|
31
31
|
| 类别 | 规则 |
|
|
32
|
-
|
|
32
|
+
| --- | --- |
|
|
33
33
|
| **允许** | 已发布 `loop-agent` / `agent-worker` CLI;只读 status/doctor/report/inspect/observe;准备 `source/*` 与 `task.json` 边界;human gate;shell 验证与 handoff。 |
|
|
34
34
|
| **禁止** | 绕过 CLI 用宿主 Edit/Write/ApplyPatch 直接改业务实现;CLI/DAG 失败后「救火改文件」;用聊天自述代替 shell 验证。 |
|
|
35
35
|
| **失败时** | `dag doctor` / `dag report` / `dag reconcile-run`(及适用 worker reconcile);修正任务源/`task.json`/DAG 后经 CLI 重跑。 |
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
### 自然语言入口路由
|
|
41
41
|
|
|
42
42
|
| 用户表达 | 入口 | 执行动作 |
|
|
43
|
-
|
|
43
|
+
| --- | --- | --- |
|
|
44
44
|
| loop-agent 初始化 / loop agent 初始化 / loop agent初始化 / 初始化 loop-agent | 初始化 | 完成确定性初始化闭环 |
|
|
45
45
|
| 初始化更新校验 / 检查初始化更新 / loop-agent 初始化更新校验 / loop agent初始化更新校验 | 更新校验 | 只读报告,不写入 |
|
|
46
46
|
| 初始化对齐 / 升级后对齐 / reconcile 初始化 / loop-agent 初始化对齐 | 升级对齐 | 自动应用确定性安全动作;surface 缺失、人工决策、活跃 DAG/Worker 或 Worker 状态无法确认时零写入 |
|
|
@@ -127,7 +127,7 @@ agent-worker console serve --repo . --port 8790 # 兼容入口,等价于上
|
|
|
127
127
|
|
|
128
128
|
live run 先用 `loop-agent dag status --run-id <run-id>` 看 lifecycle 与 liveness;用 `loop-agent dag report --run-id <run-id> --markdown` 读 facts;失败/paused 用 `loop-agent dag doctor --run-id <run-id> --markdown`。生命周期对齐先只读运行 `loop-agent dag reconcile-run --run-id <run-id>`;只有 runner 已停止且 operator 明确提供 `--action supersede|abandon --reason "..."` 时才允许变更。失败 run 用 `loop-agent dag closeout-draft --run-id <run-id>` 生成 failure handoff,不要写成成功 closeout。
|
|
129
129
|
|
|
130
|
-
Runner heartbeat 只证明 lease
|
|
130
|
+
Operator 须持续监控 live run,直到 controller 报告 run 已结束(节点/流程终态如 `FINISHED`、`FAILED` 或 `partial_failed`),或 Decision Gate **需要 approve**;不要在节点仍运行时假定完成。判活须组合 runner heartbeat、session events 与 `dag doctor` liveness/provider meaningful progress;Runner heartbeat 只证明 lease,alone ≠ progress,不得仅凭运行时长结束节点。exclusive writer(如 `implement-pi` / `repair-pi`)运行期间:主会话与其他 writer **不得并发修改工作区**,以免 write-guard 错误归因;只读 status/doctor/report/observe 与 approve/reject/resume CLI 仍允许。恢复:status/doctor/report → classify → reconcile/replan → CLI 重跑 → shell verify。**禁止**把主会话直接 Edit 业务代码当作恢复手段。
|
|
131
131
|
|
|
132
132
|
### 运行态与验证
|
|
133
133
|
|
package/harness.json
CHANGED
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"pi": {
|
|
60
60
|
"description": "Pi planning, review, diagnosis, and bounded writing when DAG toolProfile=write",
|
|
61
61
|
"LOW": "minimax-m3",
|
|
62
|
-
"MED":
|
|
62
|
+
"MED": "grok-4.5",
|
|
63
63
|
"HIGH": "gpt-5.6-sol"
|
|
64
64
|
}
|
|
65
65
|
}
|
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@ main session 是 decision-maker 与 scheduler,**不是** implementer。稀缺
|
|
|
15
15
|
## 默认执行模型
|
|
16
16
|
|
|
17
17
|
| Actor | 主角色 | 避免 |
|
|
18
|
-
|
|
18
|
+
| --- | --- | --- |
|
|
19
19
|
| Main session (Operator Assist) | Objective、contract、**CLI 编排**、DAG review、routing、failure triage(doctor/reconcile)、approve/reject/resume、shell verify、handoff | 成为 coder;绕过 CLI 写业务树;失败后直接 Edit |
|
|
20
20
|
| Agent DAG runner | 可恢复 multi-node orchestration、write policy、run artifacts | 不更新 DAG/source 的 ad-hoc replanning |
|
|
21
21
|
| Cursor one-shot prompt | 显式有界诊断/介入;非默认 | 当 DAG executor、自动写入、无 path scope 宽写、失败默认恢复 |
|
|
@@ -48,7 +48,7 @@ sidecar intervention 是一次性 Pi 或 Cursor prompt,用于 unblock 主 work
|
|
|
48
48
|
典型 routing:
|
|
49
49
|
|
|
50
50
|
| 情况 | 使用 |
|
|
51
|
-
|
|
51
|
+
| --- | --- |
|
|
52
52
|
| 需 quick root-cause analysis、plan critique、log 解读 | one-shot Pi prompt,read-only |
|
|
53
53
|
| 需 codebase-indexed multi-file 诊断或 bounded patch | one-shot Cursor prompt |
|
|
54
54
|
| 需确定性 evidence | shell command / shell DAG node |
|
|
@@ -94,14 +94,20 @@ P5 验证:future agent 可从 practice report + completed node artifacts 继
|
|
|
94
94
|
main session 编排;不是默认 implementer。in-flight run 期间:
|
|
95
95
|
|
|
96
96
|
| Action | 何时 | 记录位置 |
|
|
97
|
-
|
|
97
|
+
| -------- | ------ | ---------- |
|
|
98
98
|
| Inspect status / node artifacts | 始终允许 | progress 或 sidecar output 中的 notes |
|
|
99
99
|
| Sidecar read-only Pi/Cursor prompt | 诊断、plan critique、log 解读 | resume 前的 findings |
|
|
100
|
-
| Surgical patch |
|
|
101
|
-
| DAG/source repair | topology、writeSet 或 prompt contract
|
|
100
|
+
| Surgical patch | **不适用于** exclusive writer(如 `implement-pi` / `repair-pi`)active 期间的工作区写;仅当无 active exclusive writer、变更极窄且可 verify 的治理元数据时才可考虑 | `ai_workspace/loop-agent/reports/` 或 exec plan,含 scope + verification |
|
|
101
|
+
| DAG/source repair | topology、writeSet 或 prompt contract 错误;writer 非 active 时修正元数据 | 编辑平台临时目录中的 DAG 或 plan;re-validate;rerun |
|
|
102
102
|
| Approve/reject/resume | Decision Gate `pause-on-human` | 仅 CLI artifacts |
|
|
103
103
|
| Post-DAG closeout | promotion、report、plan archive、indexes | `promote-run`、`closeout task`、`ai_workspace/loop-agent/reports/`、`ai_workspace/loop-agent/progress`、exec-plan indexes — 非 root `artifacts/`,除非 explicit narrow writeSet |
|
|
104
104
|
|
|
105
|
+
**Live-run 纪律(与目标项目 AGENTS managed block 同口径)**:
|
|
106
|
+
|
|
107
|
+
- Operator 须持续监控 run,直到 controller 报告 run 已结束(节点/流程终态如 `FINISHED`、`FAILED` 或 `partial_failed`),或 Decision Gate **需要 approve**;不要在节点仍运行时假定完成。
|
|
108
|
+
- 判活须组合 runner heartbeat、session events 与 `dag doctor` liveness/provider meaningful progress;Runner heartbeat 只证明 lease,alone ≠ progress。
|
|
109
|
+
- exclusive writer 运行期间:主会话与其他 writer **不得并发修改工作区**,以免 write-guard 错误归因;只读 status/doctor/report/observe/console 与 approve/reject/resume CLI 仍允许。
|
|
110
|
+
|
|
105
111
|
**Verdict 与 Decision Gate 提醒**(authoring guidance,非 runtime 变更):
|
|
106
112
|
|
|
107
113
|
- `shell.verdictGate` 后的 review/supervisor node:首条非空行须精确为 `VERDICT: pass` 或 `VERDICT: request-revision`(P2/P4)。
|
|
@@ -117,7 +123,7 @@ main session 编排;不是默认 implementer。in-flight run 期间:
|
|
|
117
123
|
1. 变更不触及业务功能逻辑;通常是 task source、DAG JSON typo、doc index、或删除 scratch。
|
|
118
124
|
2. 原因已知;不需要 broad system understanding。
|
|
119
125
|
3. 不改变 product requirement 语义、architecture、public API、data model 或 cross-platform contract。
|
|
120
|
-
4. 不与 active DAG exclusive `writeSet`
|
|
126
|
+
4. 不与 active DAG exclusive `writeSet` 冲突;且 exclusive writer 运行期间**一律禁止**主会话/其他 writer 并发改工作区(不仅是路径重叠判断),以免 write-guard 错误归因。
|
|
121
127
|
5. 可立即 shell verify。
|
|
122
128
|
6. 记录 scope + verification;且**下一步仍是 CLI re-validate / rerun**,不是「主会话继续实现」。
|
|
123
129
|
|
|
@@ -134,6 +140,7 @@ main session 编排;不是默认 implementer。in-flight run 期间:
|
|
|
134
140
|
- 手工按计划实现功能或修测试失败。
|
|
135
141
|
- 改 API/contract 语义或 refactor 子系统。
|
|
136
142
|
- 编辑 in-flight exclusive DAG node 拥有的实现文件。
|
|
143
|
+
- exclusive writer active 时主会话或其他 writer 并发修改工作区(即使自认无 writeSet 冲突)。
|
|
137
144
|
- 用 `cursor-prompt` / 宿主 Write 代替 `implement-pi` / `repair-pi`。
|
|
138
145
|
|
|
139
146
|
失败默认序列(替代旧 surgical-patch 心智):
|