@tea-agent/loop-agent 0.35.1-beta.0 → 0.35.1-beta.2
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/AGENTS.md +110 -108
- package/CHANGELOG.md +24 -26
- package/README.md +165 -165
- package/bin/agent-worker.js +0 -0
- package/bin/loop-agent.js +57 -21
- package/dist/application/task-lifecycle/advance.js +0 -1
- package/dist/build-stamp.json +6 -0
- package/dist/cli/program.js +2 -2
- package/dist/commands/cursor-prompt.js +6 -6
- package/dist/commands/init-upgrade.js +19 -351
- package/dist/commands/init.js +67 -14
- package/dist/commands/loop-benchmark.js +11 -11
- package/dist/commands/pi-reuse-benchmark.js +16 -16
- package/dist/commands/run-dag-progress.js +0 -14
- package/dist/commands/task-advance.js +3 -33
- package/dist/executors/dag-pi-executor.js +44 -0
- package/dist/shared/operator/capabilities.js +1 -38
- package/dist/shared/package-metadata.js +42 -0
- package/dist/sidecars/cursor-prompt/executor.js +1 -1
- package/dist/worker/console/chat/pi-runtime.js +25 -41
- package/dist/worker/console/chat/routes.js +4 -27
- package/dist/worker/console/operation-runner.js +0 -24
- package/dist/worker/console/operator-actions.js +0 -58
- package/dist/worker/console/static/assets/index-CvsQgALl.js +56 -0
- package/dist/worker/console/static/assets/{index-Dups4sSM.css → index-hJqCPs_g.css} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +5 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +2 -13
- package/dist/worker/console/static-src/operator-chat/useComposer.js +7 -30
- package/dist/worker/loop-agent/loop-agent-client.js +17 -3
- package/dist/worker/observability/read-model.js +20 -0
- package/dist/worker/observe/static/copy.js +67 -67
- package/dist/worker/observe/static/dag-layout.d.ts +36 -36
- package/dist/worker/observe/static/dom.js +220 -220
- package/dist/worker/observe/static/relations.js +133 -133
- package/dist/worker/observe/static/run-processing.js +148 -148
- package/dist/worker/observe/static/views/batch.js +227 -227
- package/dist/worker/observe/static/views/failures.js +143 -143
- package/dist/worker/observe/static/views/feature.js +492 -492
- package/dist/worker/observe/static/views/run.js +453 -453
- package/dist/worker/observe/static/views/shell.js +7 -7
- package/dist/worker/observe/static/views/timeline.js +163 -163
- package/dist/worker/preflight.js +2 -1
- package/dist/workflows/dag/backend-test-scenario-param.js +33 -23
- package/dist/workflows/dag/canvas-observer.js +275 -275
- package/dist/workflows/dag/contract-output-registry.js +14 -0
- package/dist/workflows/dag/contract-validator-registrations.js +8 -0
- package/dist/workflows/dag/dynamic-runtime/shared.js +9 -1
- package/dist/workflows/dag/frontend-implementation-contract.js +233 -39
- package/dist/workflows/dag/frontend-prewrite-gate.js +364 -61
- package/dist/workflows/dag/frontend-recovery-plan.js +73 -0
- package/dist/workflows/dag/frontend-recovery-root-manifest.js +123 -0
- package/dist/workflows/dag/frontend-recovery-run.js +539 -0
- package/dist/workflows/dag/frontend-repair.js +219 -18
- package/dist/workflows/dag/frontend-verification-trace.js +47 -32
- package/dist/workflows/dag/frontend-writer-recovery.js +106 -0
- package/dist/workflows/dag/frontend-writer-rollback.js +821 -0
- package/dist/workflows/dag/init-hybrid.js +41 -24
- package/dist/workflows/dag/node-execution.js +89 -0
- package/dist/workflows/dag/recovery-recommendation.js +58 -0
- package/dist/workflows/dag/runner.js +245 -11
- package/dist/workflows/dag/scheduler.js +257 -3
- package/dist/workflows/dag/types.js +130 -2
- package/docs/architecture/evolution.md +73 -73
- package/docs/architecture/system-overview.md +100 -100
- package/docs/architecture/worker-and-feature.md +122 -122
- package/docs/skills/README.md +7 -7
- package/docs/templates/adr.md +60 -60
- package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -94
- package/docs/templates/agent-dag-decision-envelope.schema.json +213 -213
- package/docs/templates/agent-dag-decision-gate.prompt.md +246 -246
- package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -98
- package/docs/templates/agent-dag-report.schema.json +473 -473
- package/docs/templates/agent-dag-review-verdict.prompt.md +68 -68
- package/docs/templates/backend-test-result.schema.json +99 -99
- package/docs/templates/evaluation/agents-map-slim-v1.md +87 -87
- package/docs/templates/evaluation/agents-map-verbose-v0.md +153 -153
- package/docs/templates/feature-spec.md +53 -53
- package/docs/templates/frontend-design-contract.md +42 -42
- package/docs/templates/frontend-eval/fixtures/failures/01-type-build-error.md +17 -17
- package/docs/templates/frontend-eval/fixtures/failures/02-unit-component-test-fail.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/03-fixture-schema-drift.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/04-missing-loading-empty-error-state.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/05-forbidden-write-writeset-expansion.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/06-unapproved-dependency-add.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/07-mock-production-on.md +21 -21
- package/docs/templates/frontend-eval/fixtures/functional/01-simple-component-style.md +29 -29
- package/docs/templates/frontend-eval/fixtures/functional/02-form-validation.md +28 -28
- package/docs/templates/frontend-eval/fixtures/functional/03-list-detail-page.md +28 -28
- package/docs/templates/frontend-eval/fixtures/functional/04-api-mock.md +29 -29
- package/docs/templates/frontend-eval/fixtures/functional/05-permission-auth-gated-ui.md +27 -27
- package/docs/templates/frontend-eval/fixtures/functional/06-ssr-server-client-boundary.md +28 -28
- package/docs/templates/frontend-eval/fixtures/functional/07-shared-public-component-api.md +28 -28
- package/docs/templates/frontend-eval/fixtures/functional/08-pure-local-no-remote.md +27 -27
- package/docs/templates/frontend-eval/metrics.md +138 -138
- package/docs/templates/frontend-eval/smoke-targets.md +53 -53
- package/docs/templates/frontend-task-constraints.md +35 -35
- package/docs/templates/frontend-task-requirement.md +70 -70
- package/docs/templates/init-evolution-review.md +35 -35
- package/docs/templates/init-managed-agents.md +154 -156
- package/docs/templates/interactive-ui-round2-experiment.md +66 -66
- package/docs/templates/knowledge-graph-bootstrap-dag.json +118 -118
- package/docs/templates/knowledge-sync-dag.json +178 -178
- package/docs/templates/knowledge-sync-draft.schema.json +71 -71
- package/docs/templates/product-line/closeout.yaml +9 -9
- package/docs/templates/product-line/design.md +13 -13
- package/docs/templates/product-line/links.md +10 -10
- package/docs/templates/product-line/requirement.md +17 -17
- package/docs/templates/product-line/test-plan.md +7 -7
- package/docs/templates/project-start-checklist.md +9 -9
- package/docs/templates/qa-report.md +48 -48
- package/docs/templates/sprint-contract.md +29 -29
- package/docs/templates/worker-dogfood-evidence.md +80 -80
- package/docs/templates/worker-dogfood-setup.md +68 -68
- package/harness.json +2 -5
- package/package.json +2 -2
- package/scripts/kb-bootstrap-init-skeleton.sh +0 -0
- package/scripts/kb-graph-incremental-prepare.mjs +0 -0
- package/scripts/kb-graph-materialize.mjs +105 -105
- package/scripts/kb-graph-promote.mjs +164 -164
- package/scripts/kb-query.mjs +554 -554
- package/skills/agent-worker/SKILL.md +48 -48
- package/skills/agent-worker/references/agent-worker-operator.md +159 -159
- package/skills/ai-engineering-context/SKILL.md +48 -48
- package/skills/analyze-product-dependencies/scripts/test-validators.mjs +0 -0
- package/skills/analyze-product-dependencies/scripts/validate-api-documentation.mjs +0 -0
- package/skills/analyze-product-dependencies/scripts/validate-dependency-analysis.mjs +0 -0
- package/skills/analyze-product-dependencies/scripts/validate-product-requirement-input.mjs +0 -0
- package/skills/analyze-product-requirements/scripts/compute-source-identity.mjs +0 -0
- package/skills/analyze-product-requirements/scripts/test-validators.mjs +0 -0
- package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +0 -0
- package/skills/analyze-product-requirements/scripts/validate-product-requirement.mjs +0 -0
- package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +0 -0
- package/skills/browser-tools/browser-content.js +103 -103
- package/skills/browser-tools/browser-cookies.js +35 -35
- package/skills/browser-tools/browser-eval.js +53 -53
- package/skills/browser-tools/browser-hn-scraper.js +108 -108
- package/skills/browser-tools/browser-nav.js +44 -44
- package/skills/browser-tools/browser-pick.js +162 -162
- package/skills/browser-tools/browser-screenshot.js +34 -34
- package/skills/browser-tools/browser-start.js +86 -86
- package/skills/browser-tools/package-lock.json +2556 -2556
- package/skills/browser-tools/package.json +19 -19
- package/skills/code-review-core/SKILL.md +20 -20
- package/skills/codebase-scout/SKILL.md +19 -19
- package/skills/grill-me/SKILL.md +10 -10
- package/skills/local-jacoco-coverage/scripts/run-coverage-analysis.sh +0 -0
- package/skills/local-jacoco-coverage/scripts/start-jacoco-agent.sh +0 -0
- package/skills/loop-agent/SKILL.md +0 -1
- package/skills/loop-agent/references/command-reference.md +639 -641
- package/skills/loop-agent/references/docs-converge.md +126 -126
- package/skills/loop-agent/references/learned/README.md +21 -21
- package/skills/loop-agent/references/pi-prompt.md +23 -23
- package/skills/loop-agent/references/pi-subagent-assisted-mode.md +84 -84
- package/skills/playwright-cli/references/element-attributes.md +23 -23
- package/skills/playwright-cli/references/playwright-tests.md +39 -39
- package/skills/playwright-cli/references/request-mocking.md +87 -87
- package/skills/playwright-cli/references/running-code.md +241 -241
- package/skills/playwright-cli/references/session-management.md +225 -225
- package/skills/playwright-cli/references/storage-state.md +275 -275
- package/skills/playwright-cli/references/test-generation.md +433 -433
- package/skills/requesting-code-review/SKILL.md +101 -101
- package/skills/requesting-code-review/code-reviewer.md +168 -168
- package/skills/systematic-debugging/CREATION-LOG.md +119 -119
- package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
- package/skills/systematic-debugging/condition-based-waiting.md +115 -115
- package/skills/systematic-debugging/defense-in-depth.md +122 -122
- package/skills/systematic-debugging/find-polluter.sh +63 -63
- package/skills/systematic-debugging/root-cause-tracing.md +169 -169
- package/skills/systematic-debugging/test-academic.md +14 -14
- package/skills/systematic-debugging/test-pressure-1.md +58 -58
- package/skills/systematic-debugging/test-pressure-2.md +68 -68
- package/skills/systematic-debugging/test-pressure-3.md +69 -69
- package/skills/using-git-worktrees/SKILL.md +215 -215
- package/skills/verification-before-completion/SKILL.md +154 -154
- package/skills/webapp-testing/SKILL.md +19 -19
- package/dist/worker/console/operation-wait.js +0 -241
- package/dist/worker/console/static/assets/index-SjjjZnV3.js +0 -56
- package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +0 -141
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
1
3
|
import { isPauseOnHumanDecisionGate } from "./decision-envelope.js";
|
|
2
4
|
import { evaluateConditionExpression } from "./dynamic-runtime/condition.js";
|
|
5
|
+
import { sha256Hex } from "./frontend-implementation-contract.js";
|
|
6
|
+
import { frontendPrewriteResultV1Schema, FRONTEND_PREWRITE_RESULT_ARTIFACT_NAME, } from "./frontend-prewrite-gate.js";
|
|
7
|
+
import { FRONTEND_RECOVERY_IMPORT_MANIFEST_REL_PATH, FRONTEND_RECOVERY_INTENT_REL_DIR, } from "./frontend-recovery-plan.js";
|
|
3
8
|
export function isConditionSkippedReason(reason) {
|
|
4
9
|
return Boolean(reason?.startsWith("condition "));
|
|
5
10
|
}
|
|
@@ -104,6 +109,58 @@ async function mapConcurrent(items, limit, fn) {
|
|
|
104
109
|
}
|
|
105
110
|
await Promise.all(executing);
|
|
106
111
|
}
|
|
112
|
+
export const FRONTEND_WRITER_NODE_IDS = [
|
|
113
|
+
"frontend-implement-pi",
|
|
114
|
+
"frontend-repair-pi",
|
|
115
|
+
];
|
|
116
|
+
/** Run-relative artifact location written by the prewrite gate generator. */
|
|
117
|
+
export const FRONTEND_PREWRITE_RESULT_SOURCE_ARTIFACT = path.posix.join("contracts", FRONTEND_PREWRITE_RESULT_ARTIFACT_NAME);
|
|
118
|
+
/**
|
|
119
|
+
* Read and validate the frontend-prewrite-result-v1 artifact. Fail-closed:
|
|
120
|
+
* a missing file or invalid payload returns ok:false and never throws.
|
|
121
|
+
*/
|
|
122
|
+
export async function readFrontendPrewriteResult(runDir) {
|
|
123
|
+
const artifactPath = path.join(runDir, "contracts", FRONTEND_PREWRITE_RESULT_ARTIFACT_NAME);
|
|
124
|
+
let raw;
|
|
125
|
+
try {
|
|
126
|
+
raw = await readFile(artifactPath, "utf8");
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
if (error.code === "ENOENT") {
|
|
130
|
+
return {
|
|
131
|
+
ok: false,
|
|
132
|
+
reason: `frontend prewrite result artifact missing: ${artifactPath}`,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
const artifactHash = sha256Hex(raw);
|
|
138
|
+
let parsed;
|
|
139
|
+
try {
|
|
140
|
+
parsed = JSON.parse(raw);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
return {
|
|
144
|
+
ok: false,
|
|
145
|
+
reason: `frontend prewrite result artifact is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const result = frontendPrewriteResultV1Schema.safeParse(parsed);
|
|
149
|
+
if (!result.success) {
|
|
150
|
+
return {
|
|
151
|
+
ok: false,
|
|
152
|
+
reason: "frontend prewrite result artifact failed schema validation",
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return { ok: true, result: result.data, artifactHash };
|
|
156
|
+
}
|
|
157
|
+
/** Authorize only accepted / accepted-normalized classifications. */
|
|
158
|
+
export function isFrontendWriterAuthorized(result) {
|
|
159
|
+
return result.classification === "accepted" ||
|
|
160
|
+
result.classification === "accepted-normalized"
|
|
161
|
+
? "authorized"
|
|
162
|
+
: "denied";
|
|
163
|
+
}
|
|
107
164
|
/** Fail-closed: leave no PENDING nodes that look "still scheduled" after abort. */
|
|
108
165
|
export function markPendingNodesControllerInterrupted(state, reason = "run aborted by controller (abortSignal)") {
|
|
109
166
|
const affected = [];
|
|
@@ -122,8 +179,166 @@ export function markPendingNodesControllerInterrupted(state, reason = "run abort
|
|
|
122
179
|
}
|
|
123
180
|
return affected;
|
|
124
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* True when this run is a candidate-continuation child: it has a recovery
|
|
184
|
+
* lineage whose attemptIndex is >= 1 and whose recovery root is another run.
|
|
185
|
+
* The root/parent keeps `recoveryRootRunId === state.runId`.
|
|
186
|
+
*/
|
|
187
|
+
export function isFrontendRecoveryChild(state) {
|
|
188
|
+
const recovery = state.frontendRecoveryState;
|
|
189
|
+
return Boolean(recovery &&
|
|
190
|
+
recovery.attemptIndex >= 1 &&
|
|
191
|
+
recovery.recoveryRootRunId !== state.runId);
|
|
192
|
+
}
|
|
193
|
+
function isValidFrontendRecoveryActivationMarker(raw) {
|
|
194
|
+
return (raw.schemaVersion === 1 &&
|
|
195
|
+
typeof raw.requestId === "string" &&
|
|
196
|
+
raw.requestId.length > 0 &&
|
|
197
|
+
typeof raw.parentRunId === "string" &&
|
|
198
|
+
raw.parentRunId.length > 0 &&
|
|
199
|
+
typeof raw.recoveryRootRunId === "string" &&
|
|
200
|
+
raw.recoveryRootRunId.length > 0 &&
|
|
201
|
+
typeof raw.childRunId === "string" &&
|
|
202
|
+
raw.childRunId.length > 0 &&
|
|
203
|
+
typeof raw.importManifestSha256 === "string" &&
|
|
204
|
+
/^[a-f0-9]{64}$/.test(raw.importManifestSha256));
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Activation marker gate (phase 3c AC-1). Fail-closed: an active child is only
|
|
208
|
+
* executable when its parent is `child-running`, points at this child, carries
|
|
209
|
+
* a structurally valid activation marker whose lineage matches, and the marker
|
|
210
|
+
* hash matches the child's import manifest. Read-only and never throws.
|
|
211
|
+
*/
|
|
212
|
+
export async function checkFrontendRecoveryActivation(state, childRunDir) {
|
|
213
|
+
if (!isFrontendRecoveryChild(state)) {
|
|
214
|
+
return { applicable: false };
|
|
215
|
+
}
|
|
216
|
+
const recovery = state.frontendRecoveryState;
|
|
217
|
+
const parentRunId = recovery.parentRunId;
|
|
218
|
+
const parentRunDir = path.join(path.dirname(childRunDir), parentRunId);
|
|
219
|
+
let parentState;
|
|
220
|
+
try {
|
|
221
|
+
parentState = JSON.parse(await readFile(path.join(parentRunDir, "state.json"), "utf8"));
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
return { applicable: true, ok: false, reason: "parent state unreadable" };
|
|
225
|
+
}
|
|
226
|
+
const parentRecovery = parentState.frontendRecoveryState;
|
|
227
|
+
if (parentRecovery?.phase !== "child-running") {
|
|
228
|
+
return {
|
|
229
|
+
applicable: true,
|
|
230
|
+
ok: false,
|
|
231
|
+
reason: "parent phase not child-running",
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
if (parentRecovery.childRunId !== state.runId) {
|
|
235
|
+
return {
|
|
236
|
+
applicable: true,
|
|
237
|
+
ok: false,
|
|
238
|
+
reason: "parent childRunId mismatch",
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
const markerPath = path.join(parentRunDir, FRONTEND_RECOVERY_INTENT_REL_DIR, `${recovery.requestId}.json`);
|
|
242
|
+
let markerRaw;
|
|
243
|
+
try {
|
|
244
|
+
markerRaw = JSON.parse(await readFile(markerPath, "utf8"));
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
return {
|
|
248
|
+
applicable: true,
|
|
249
|
+
ok: false,
|
|
250
|
+
reason: "activation marker missing",
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
if (typeof markerRaw !== "object" ||
|
|
254
|
+
markerRaw === null ||
|
|
255
|
+
!isValidFrontendRecoveryActivationMarker(markerRaw)) {
|
|
256
|
+
return {
|
|
257
|
+
applicable: true,
|
|
258
|
+
ok: false,
|
|
259
|
+
reason: "activation marker invalid",
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
const marker = markerRaw;
|
|
263
|
+
if (marker.requestId !== recovery.requestId) {
|
|
264
|
+
return {
|
|
265
|
+
applicable: true,
|
|
266
|
+
ok: false,
|
|
267
|
+
reason: "marker requestId mismatch",
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
if (marker.parentRunId !== parentRunId) {
|
|
271
|
+
return {
|
|
272
|
+
applicable: true,
|
|
273
|
+
ok: false,
|
|
274
|
+
reason: "marker parentRunId mismatch",
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
if (marker.recoveryRootRunId !== recovery.recoveryRootRunId) {
|
|
278
|
+
return {
|
|
279
|
+
applicable: true,
|
|
280
|
+
ok: false,
|
|
281
|
+
reason: "marker recoveryRootRunId mismatch",
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
if (marker.childRunId !== state.runId) {
|
|
285
|
+
return {
|
|
286
|
+
applicable: true,
|
|
287
|
+
ok: false,
|
|
288
|
+
reason: "marker childRunId mismatch",
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
let manifestSha256;
|
|
292
|
+
try {
|
|
293
|
+
manifestSha256 = sha256Hex(await readFile(path.join(childRunDir, FRONTEND_RECOVERY_IMPORT_MANIFEST_REL_PATH), "utf8"));
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
return {
|
|
297
|
+
applicable: true,
|
|
298
|
+
ok: false,
|
|
299
|
+
reason: "import manifest unreadable",
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
if (manifestSha256 !== marker.importManifestSha256) {
|
|
303
|
+
return {
|
|
304
|
+
applicable: true,
|
|
305
|
+
ok: false,
|
|
306
|
+
reason: "import manifest sha256 mismatch",
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
applicable: true,
|
|
311
|
+
ok: true,
|
|
312
|
+
requestId: recovery.requestId,
|
|
313
|
+
childRunId: state.runId,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
125
316
|
export async function executeDagRanksOnce(input) {
|
|
126
317
|
let pausedByNodeId;
|
|
318
|
+
// Activation marker gate (phase 3c AC-1): a recovery child without a valid
|
|
319
|
+
// activation marker must never be scheduled or executed. Fail-closed before
|
|
320
|
+
// any executeScheduledNode call, so zero writer/provider invocations happen.
|
|
321
|
+
if (input.runDir) {
|
|
322
|
+
const hasPending = Object.values(input.state.nodes).some((node) => node.status === "PENDING");
|
|
323
|
+
if (hasPending) {
|
|
324
|
+
const activation = await checkFrontendRecoveryActivation(input.state, input.runDir);
|
|
325
|
+
if (activation.applicable && !activation.ok) {
|
|
326
|
+
const finishedAt = new Date().toISOString();
|
|
327
|
+
let affected = 0;
|
|
328
|
+
for (const node of Object.values(input.state.nodes)) {
|
|
329
|
+
if (node.status !== "PENDING")
|
|
330
|
+
continue;
|
|
331
|
+
node.status = "SKIPPED";
|
|
332
|
+
node.skippedReason = "frontend-recovery-child-not-activated";
|
|
333
|
+
node.finishedAt = finishedAt;
|
|
334
|
+
affected += 1;
|
|
335
|
+
}
|
|
336
|
+
if (affected > 0)
|
|
337
|
+
await input.persistState();
|
|
338
|
+
return undefined;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
127
342
|
for (const rank of input.ranks) {
|
|
128
343
|
if (input.abortSignal?.aborted) {
|
|
129
344
|
const marked = markPendingNodesControllerInterrupted(input.state, input.abortSignal.reason
|
|
@@ -168,6 +383,45 @@ export async function executeDagRanksOnce(input) {
|
|
|
168
383
|
await input.persistState();
|
|
169
384
|
const conditionSkippedSet = new Set(conditionSettled);
|
|
170
385
|
const actuallyRunnable = runnable.filter((id) => !conditionSkippedSet.has(id));
|
|
386
|
+
const frontendAdmissionSettled = [];
|
|
387
|
+
if (input.runDir) {
|
|
388
|
+
for (const id of actuallyRunnable) {
|
|
389
|
+
if (!FRONTEND_WRITER_NODE_IDS.includes(id))
|
|
390
|
+
continue;
|
|
391
|
+
const node = input.state.nodes[id];
|
|
392
|
+
const checkedAt = new Date().toISOString();
|
|
393
|
+
const admission = await readFrontendPrewriteResult(input.runDir);
|
|
394
|
+
if (!admission.ok) {
|
|
395
|
+
node.status = "SKIPPED";
|
|
396
|
+
node.skippedReason = "frontend-prewrite-not-authorized";
|
|
397
|
+
node.finishedAt = checkedAt;
|
|
398
|
+
frontendAdmissionSettled.push(id);
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
const decision = isFrontendWriterAuthorized(admission.result);
|
|
402
|
+
node.frontendWriterAdmission = {
|
|
403
|
+
schemaVersion: 1,
|
|
404
|
+
writerNodeId: id,
|
|
405
|
+
decision,
|
|
406
|
+
sourceArtifact: FRONTEND_PREWRITE_RESULT_SOURCE_ARTIFACT,
|
|
407
|
+
artifactHash: admission.artifactHash,
|
|
408
|
+
checkedAt,
|
|
409
|
+
reason: decision === "denied"
|
|
410
|
+
? `classification: ${admission.result.classification}`
|
|
411
|
+
: null,
|
|
412
|
+
};
|
|
413
|
+
if (decision === "denied") {
|
|
414
|
+
node.status = "SKIPPED";
|
|
415
|
+
node.skippedReason = "frontend-prewrite-not-authorized";
|
|
416
|
+
node.finishedAt = checkedAt;
|
|
417
|
+
frontendAdmissionSettled.push(id);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
if (frontendAdmissionSettled.length > 0)
|
|
421
|
+
await input.persistState();
|
|
422
|
+
}
|
|
423
|
+
const frontendAdmissionSkippedSet = new Set(frontendAdmissionSettled);
|
|
424
|
+
const runnableAfterAdmission = actuallyRunnable.filter((id) => !frontendAdmissionSkippedSet.has(id));
|
|
171
425
|
const blocked = pending.filter((id) => {
|
|
172
426
|
const task = input.tasksById.get(id);
|
|
173
427
|
return (dependencyReadiness(task, input.state.nodes, input.tasksById) === "skip");
|
|
@@ -182,12 +436,12 @@ export async function executeDagRanksOnce(input) {
|
|
|
182
436
|
if (blocked.length > 0) {
|
|
183
437
|
await input.persistState();
|
|
184
438
|
}
|
|
185
|
-
const pauseGateRunnable =
|
|
439
|
+
const pauseGateRunnable = runnableAfterAdmission.filter((id) => {
|
|
186
440
|
const task = input.tasksById.get(id);
|
|
187
441
|
return isPauseOnHumanDecisionGate(task);
|
|
188
442
|
});
|
|
189
|
-
const regularRunnable =
|
|
190
|
-
const rankWriterNodeIds =
|
|
443
|
+
const regularRunnable = runnableAfterAdmission.filter((id) => !pauseGateRunnable.includes(id));
|
|
444
|
+
const rankWriterNodeIds = runnableAfterAdmission.filter((id) => {
|
|
191
445
|
const task = input.tasksById.get(id);
|
|
192
446
|
return (task?.executor === "pi" &&
|
|
193
447
|
task.toolProfile === "write" &&
|
|
@@ -654,8 +654,15 @@ export const dagConvergenceSpecSchema = z
|
|
|
654
654
|
chainNodeIds: z.array(z.string()).optional(),
|
|
655
655
|
})
|
|
656
656
|
.optional();
|
|
657
|
-
|
|
658
|
-
|
|
657
|
+
/**
|
|
658
|
+
* Schema ids that may opt a node into producing-node structured contract
|
|
659
|
+
* self-validation. Validators are registered in the contract output registry
|
|
660
|
+
* (src/workflows/dag/contract-output-registry.ts).
|
|
661
|
+
*/
|
|
662
|
+
export const structuredContractOutputSchemaIds = [
|
|
663
|
+
"frontend-implementation-contract-v1",
|
|
664
|
+
];
|
|
665
|
+
export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/, "task id must be kebab-case"),
|
|
659
666
|
depends_on: z.array(z.string()).default([]),
|
|
660
667
|
/**
|
|
661
668
|
* How SKIPPED upstreams affect readiness.
|
|
@@ -704,6 +711,20 @@ export const dagTaskSchema = z.object({
|
|
|
704
711
|
* on safe read-only Pi nodes before the node becomes ERROR.
|
|
705
712
|
*/
|
|
706
713
|
outputProtocol: dagOutputProtocolSchema.optional(),
|
|
714
|
+
/**
|
|
715
|
+
* Node-level structured contract self-check: after the Pi output is
|
|
716
|
+
* produced, validate it against the named contract schema at the node so
|
|
717
|
+
* schema/typo/null violations become invalid-output (retryable on the node)
|
|
718
|
+
* instead of failing later at a downstream deterministic gate. Validators
|
|
719
|
+
* are resolved via the contract output registry keyed by schemaId.
|
|
720
|
+
*/
|
|
721
|
+
structuredContractOutput: z
|
|
722
|
+
.object({
|
|
723
|
+
schemaId: z.enum(structuredContractOutputSchemaIds),
|
|
724
|
+
retryOnInvalid: z.boolean().default(true),
|
|
725
|
+
})
|
|
726
|
+
.strict()
|
|
727
|
+
.optional(),
|
|
707
728
|
/**
|
|
708
729
|
* Fail-closed outcome/diff consistency contract for bounded Pi writers.
|
|
709
730
|
* The writer must begin with IMPLEMENTATION_OUTCOME: changed,
|
|
@@ -874,6 +895,113 @@ export const dagSpecSchema = z
|
|
|
874
895
|
});
|
|
875
896
|
}
|
|
876
897
|
});
|
|
898
|
+
export const FRONTEND_RECOVERY_STATE_SCHEMA_VERSION = 1;
|
|
899
|
+
export const FRONTEND_RECOVERY_RESULT_SCHEMA_VERSION = 1;
|
|
900
|
+
/**
|
|
901
|
+
* Frontend candidate-continuation recovery phase (decision B, phase-3 subset).
|
|
902
|
+
* Phase 3a parents converge first, so `rollback-pending` is not produced and
|
|
903
|
+
* was removed by AC-1; the phase starts at `child-staging` and ends at
|
|
904
|
+
* `settled`.
|
|
905
|
+
*/
|
|
906
|
+
export const frontendRecoveryPhaseSchema = z.enum([
|
|
907
|
+
"child-staging",
|
|
908
|
+
"child-activating",
|
|
909
|
+
"child-running",
|
|
910
|
+
"settled",
|
|
911
|
+
]);
|
|
912
|
+
/**
|
|
913
|
+
* Single-writer recovery intent/lineage stored on `DagRunState`. The parent run
|
|
914
|
+
* owns the authoritative copy and advances it via revision-guarded CAS writes;
|
|
915
|
+
* the child carries a frozen lineage snapshot so the activation gate can prove
|
|
916
|
+
* it is the reserved child of a child-running parent.
|
|
917
|
+
*
|
|
918
|
+
* Invariants: `attemptIndex ∈ {0,1}`; `continuationCount ∈ {0,1}`; a child's
|
|
919
|
+
* `recoveryRootRunId` always equals the root runId; `revision` is the CAS
|
|
920
|
+
* pre-comparison counter. `revision` monotonicity is enforced at runtime by the
|
|
921
|
+
* runner's `(existing?.revision ?? 0) + 1` CAS write (the schema only bounds it
|
|
922
|
+
* non-negative). `childRunId` may be absent through `child-activating` and must
|
|
923
|
+
* be non-empty from `child-running` onward.
|
|
924
|
+
*/
|
|
925
|
+
export const frontendRecoveryStateSchema = z
|
|
926
|
+
.object({
|
|
927
|
+
schemaVersion: z.literal(FRONTEND_RECOVERY_STATE_SCHEMA_VERSION),
|
|
928
|
+
phase: frontendRecoveryPhaseSchema,
|
|
929
|
+
requestId: z.string().min(1),
|
|
930
|
+
recoveryRootRunId: z.string().min(1),
|
|
931
|
+
parentRunId: z.string().min(1),
|
|
932
|
+
childRunId: z.string().min(1).optional(),
|
|
933
|
+
attemptId: z.string().min(1),
|
|
934
|
+
attemptIndex: z.number().int().min(0).max(1),
|
|
935
|
+
continuationCount: z.number().int().min(0).max(1),
|
|
936
|
+
/** Node id of the reset-closure root: frontend-plan-pi /
|
|
937
|
+
* frontend-plan-revision-pi / frontend-prewrite-gate-shell /
|
|
938
|
+
* frontend-implement-pi (writer partial write). */
|
|
939
|
+
failureSource: z.string().min(1).optional(),
|
|
940
|
+
revision: z.number().int().nonnegative(),
|
|
941
|
+
})
|
|
942
|
+
.strict()
|
|
943
|
+
.superRefine((value, ctx) => {
|
|
944
|
+
const requiresChild = value.phase === "child-running" || value.phase === "settled";
|
|
945
|
+
if (requiresChild && !value.childRunId) {
|
|
946
|
+
ctx.addIssue({
|
|
947
|
+
code: z.ZodIssueCode.custom,
|
|
948
|
+
message: `phase ${value.phase} requires a non-empty childRunId`,
|
|
949
|
+
path: ["childRunId"],
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
});
|
|
953
|
+
export const frontendRecoveryOutcomeSchema = z.enum([
|
|
954
|
+
"none",
|
|
955
|
+
"recovered",
|
|
956
|
+
"candidate-contract-invalid",
|
|
957
|
+
"prewrite-blocked",
|
|
958
|
+
"repair-exhausted",
|
|
959
|
+
"auto-recovery-blocked",
|
|
960
|
+
]);
|
|
961
|
+
export const frontendRecoveryOriginSchema = z
|
|
962
|
+
.object({
|
|
963
|
+
kind: z.enum(["frontend-prewrite-gate", "frontend-writer"]),
|
|
964
|
+
parentRunId: z.string().min(1),
|
|
965
|
+
childRunId: z.string().min(1).optional(),
|
|
966
|
+
requestId: z.string().min(1),
|
|
967
|
+
failedNodeId: z.string(),
|
|
968
|
+
})
|
|
969
|
+
.strict();
|
|
970
|
+
export const frontendRecoveryFailureClassSchema = z
|
|
971
|
+
.object({
|
|
972
|
+
code: z.enum([
|
|
973
|
+
"candidate-contract-invalid",
|
|
974
|
+
"prewrite-blocked",
|
|
975
|
+
"staging-failed",
|
|
976
|
+
"writer-transient-partial-write",
|
|
977
|
+
]),
|
|
978
|
+
classification: z.string(),
|
|
979
|
+
reason: z.string(),
|
|
980
|
+
})
|
|
981
|
+
.strict();
|
|
982
|
+
export const frontendRecoveryEvidenceRefSchema = z
|
|
983
|
+
.object({
|
|
984
|
+
runId: z.string().min(1),
|
|
985
|
+
relativePath: z.string().min(1),
|
|
986
|
+
sha256: z.string().min(1),
|
|
987
|
+
})
|
|
988
|
+
.strict();
|
|
989
|
+
/**
|
|
990
|
+
* Terminal frontend recovery result. `failureClass` is absent for `none`,
|
|
991
|
+
* `recovered` and `auto-recovery-blocked` outcomes. The schema intentionally
|
|
992
|
+
* stays permissive (failureClass optional, evidenceRefs may be empty) so it
|
|
993
|
+
* accepts the runner's actual phase-3a products, which do not always carry a
|
|
994
|
+
* failure class or evidence reference.
|
|
995
|
+
*/
|
|
996
|
+
export const frontendRecoveryResultSchema = z
|
|
997
|
+
.object({
|
|
998
|
+
schemaVersion: z.literal(FRONTEND_RECOVERY_RESULT_SCHEMA_VERSION),
|
|
999
|
+
outcome: frontendRecoveryOutcomeSchema,
|
|
1000
|
+
origin: frontendRecoveryOriginSchema,
|
|
1001
|
+
failureClass: frontendRecoveryFailureClassSchema.optional(),
|
|
1002
|
+
evidenceRefs: z.array(frontendRecoveryEvidenceRefSchema),
|
|
1003
|
+
})
|
|
1004
|
+
.strict();
|
|
877
1005
|
export const DEFAULT_DAG_EXECUTOR_MODELS = {
|
|
878
1006
|
pi: {
|
|
879
1007
|
LOW: "gpt-5.3-codex-spark",
|
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
# 架构演进:当前 vs 未来
|
|
2
|
-
|
|
3
|
-
本页区分 loop-agent **当前已实现**的架构能力与**未来规划**。当前事实以代码、发布 CLI、已完成计划为准;未来能力一律标「规划 / 未实现 / 前瞻」。权威源:`CHANGELOG.md`、`docs/reports/current-capability-summary.md`、ADR 0001–0005、`docs/exec-plans/completed/` 与 `docs/exec-plans/active/`。
|
|
4
|
-
|
|
5
|
-
**对照版本:`@tea-agent/loop-agent@0.24.0`(2026-07-27)**。细节版本条目见根 `CHANGELOG.md`;本表只保留架构层可读摘要。
|
|
6
|
-
|
|
7
|
-
## 当前已实现(累计至 0.24.0)
|
|
8
|
-
|
|
9
|
-
| 域 | 现状 | 权威入口 |
|
|
10
|
-
| --- | --- | --- |
|
|
11
|
-
| 受治理 Agent runtime | **Pi-only**;`cursor-prompt` 仅显式 one-shot sidecar | ADR 0001、`CHANGELOG.md [0.10.0]` |
|
|
12
|
-
| Agent DAG 主链 | `dag run-task` → generate → validate → `run-dag`;report/doctor/reconcile-run | `dag-execution.md`、`test/cli-contract.test.ts` |
|
|
13
|
-
| Dynamic Workflow | `WorkflowSpec` → validate → compile → 同一 `run-dag`;agent-like 节点仅 `pi\|static` | `src/workflows/dynamic/**`、`website/docs/guides/dynamic-workflow.md` |
|
|
14
|
-
| 版本化自举 | controller identity + run-owned skill snapshot + deterministic canary | `docs/reports/feature/2026-07-13-versioned-self-hosting-bootstrap.md` |
|
|
15
|
-
| Feature 交付(M2) | review/run/approve-followup/delivery/closeout/verify-final | `docs/reports/feature/2026-07-12-m2-completion-audit.md`、`CHANGELOG.md [0.10.0]` |
|
|
16
|
-
| Task Pool | 唯一根 `.harness/task-pool/`;**feature-scoped** state v2(`TaskPoolTaskRef`) | ADR 0002、ADR 0004、`CHANGELOG.md [0.13.0]` |
|
|
17
|
-
| Ready Planner | priority 选择;`batch plan-ready` 零写入;`run-ready` 冻结 plan;产物门禁 | `CHANGELOG.md [0.13.0]` / `[0.16.0]` |
|
|
18
|
-
| TaskSpec → workflow | 可选 `execution.workflow` → `taskKind`;typed Outcome;`fullstack-v1` Verification Bundle | completed `2026-07-19-taskspec-workflow-routing.md`、`CHANGELOG.md [0.16.0]` |
|
|
19
|
-
| 专用 taskKind DAG | `frontend-implementation` / `frontend-test` / `backend-test` / knowledge* | `website/docs/guides/agent-dag.md`、`CHANGELOG.md [0.12.0]`–`[0.17.x]` |
|
|
20
|
-
| Backend-test | **Markdown-first 8 节点**;Markdown 映射限定 pytest 执行,生成 Markdown 与分层 HTML/脱敏请求响应详情 | completed `2026-07-24-backend-test-scoped-execution-report-ui.md`;`CHANGELOG.md [0.21.0]` / `[0.22.0]` |
|
|
21
|
-
| Frontend-test | 默认 short-chain RAG;内容/evidence 质量进入 advisory findings,路径与安全边界仍 fail-closed | `CHANGELOG.md [0.21.0]` / `[0.22.0]` |
|
|
22
|
-
| Frontend-implementation | Contract / trace / repair / Mock assess;`ai_workspace` + 知识库 + `openspec` 规范发现;lint 基线债务隔离 | `CHANGELOG.md [0.22.0]` |
|
|
23
|
-
| Inspect(原 Observe) | 统一 Console 的 `/inspect/` 只读运营面与富时间线;独立 `observe serve` 已硬下线 | `website/docs/guides/observe-ui.md`、`CHANGELOG.md [Unreleased]` |
|
|
24
|
-
| Local Operator Console | `agent-worker console\|doctor`(loopback;默认打开系统浏览器,`--no-open` 禁止);Operate + Inspect、Happy Path / Interview / split view / recovery CTA;**0.23.x 起含 General Operator Chat**(Pi SDK services session、operator-only tool surface);**非**远端多用户 Console | unify design;ADR 0005;completed `2026-07-25-console-phase-4-general-operator-chat.md` |
|
|
25
|
-
| Task Contract / operator surface | journaled `task contract *`;machine envelope;**DagSpec v4 `taskContractBinding`**(exclusive writer 强制) | `CHANGELOG.md [0.17.0]`–`[0.17.2]`;ADR 0005 |
|
|
26
|
-
| DagSpec / repair | v3 `runtimeContract` + 显式 `repairNodeId`;新 writer 生成默认 v4 binding | `dag-execution.md`、`CHANGELOG.md [0.11.0]` / `[0.17.0]` |
|
|
27
|
-
| 自适应 liveness | runner lease、Provider/tool/output 活动与 meaningful progress 分离;静默 stall 受控终止,退出未确认时禁止自动重试 | `dag-execution.md`、active `2026-07-23-dag-adaptive-liveness.md` |
|
|
28
|
-
| 运行恢复 | 节点内协议纠错重试、`dag rerun` 安全子图续跑、`dag rerun-task` 完整任务重跑与 Console Recovery | `CHANGELOG.md [0.20.1]`、active `2026-07-23-operator-rerun-hardening.md` |
|
|
29
|
-
| Eval Lab | corpus / campaign / promote(人工门禁);`autoPromote=false`;**非** RSI Level 1 | `CHANGELOG.md [0.15.0]`;active plan 可归档 |
|
|
30
|
-
| 文档双树 | `website/docs/` 用法 vs `docs/` 治理;docs-converge | ADR 0003 |
|
|
31
|
-
| 文档治理 | 任务式 Start Here、机器可读身份清单、目录默认身份与版本/索引语义检查 | 本目录 README、`docs/README.md`、`docs/document-catalog.json` |
|
|
32
|
-
|
|
33
|
-
### 受治理 runtime 的边界(已实现、不变式)
|
|
34
|
-
|
|
35
|
-
- DAG writer 固定 `implement-pi` / `repair-pi`;`executor: "cursor"`、`implement-cursor` / `repair-cursor`、Cursor worker、`cursor-fix` 已从受治理路径移除。
|
|
36
|
-
- 完成权威 = shell verification;model verdict / Observe / 报告是 derived/advisory。
|
|
37
|
-
- Worker 通过已发布 `loop-agent` 子进程执行,不 in-process import runtime kernel(governance 机器校验)。
|
|
38
|
-
- Console / Compatibility 主会话不得绕过 CLI 直接改业务实现(ADR 0005)。
|
|
39
|
-
|
|
40
|
-
## 未来规划(第 3–6 月,**未实现**)
|
|
41
|
-
|
|
42
|
-
以下能力来自 `docs/design/active/六个月规划.md` 与相关设计输入(页首校准条之后的未交付 phase 为**设计输入**)。它们**当前不存在于产品声称的线上形态**,或仅有本地/部分替代:
|
|
43
|
-
|
|
44
|
-
| 未来方向 | 状态 | 规划来源 |
|
|
45
|
-
| --- | --- | --- |
|
|
46
|
-
| 远程 PR / CI 自动创建与写回 | 规划 / 未实现 | `docs/design/active/六个月规划.md`(第 3 个月起) |
|
|
47
|
-
| 线上 / 云 Worker | 规划 / 未实现 | 同上 |
|
|
48
|
-
| 云 Task Pool / SQL / Orchestrator | 规划 / 未实现 | 同上(第 2 月原始设计已调整为本地 Feature 闭环) |
|
|
49
|
-
| 多仓库平台 | 规划 / 未实现 | 同上 |
|
|
50
|
-
| 组织级服务 | 规划 / 未实现 | 同上 |
|
|
51
|
-
| Web Console(远端多用户) | 规划 / 未实现;**不等于**已发布的本地 loopback Operator Console | 同上;对照 `docs/design/archive/2026-07-21-local-operator-console-from-pi-web.md` |
|
|
52
|
-
| fullstack Delivery / Final Verification live 闭环 | 进行中(active train) | `docs/exec-plans/active/2026-07-19-fullstack-dogfood-016x-release-train.md` |
|
|
53
|
-
| Dynamic Workflow runtime limits 强执法、更广 profile | 设计输入 | `docs/design/active/dynamic-workflow-dag-engine-roadmap.md` |
|
|
54
|
-
| Loop 与 Dynamic Workflow 更深双向集成 / 自动恢复 | 设计输入 | 同上;当前已有基础 `workflow` action |
|
|
55
|
-
| Operator Chat 作为主操作会话(裸 bash 收敛、真 human gate、durable session/turn、Operation 闭环、完整 CLI action coverage、默认入口切换) | 设计输入 / 未达主会话门槛 | `docs/design/active/operator-chat-primary-session-roadmap.md`;Phase 4 General Operator Chat 本体已实现归档(`docs/exec-plans/completed/2026-07-25-console-phase-4-general-operator-chat.md`) |
|
|
56
|
-
| Eval Lab auto-promote / RSI Level 1 产品声称 | **禁止**写成已交付 | Eval Lab design + active plan |
|
|
57
|
-
|
|
58
|
-
> 本地 Operator Console 统一面(实际首发 0.19.0)已发布:单仓库、loopback、随 `@tea-agent/loop-agent` 同包、canonical mutation 只经 sibling CLI,Inspect 复用 GET-only Observe read model。它不是远端 Web Console,也不能把多租户/云编排需求偷渡进本地 Console。
|
|
59
|
-
|
|
60
|
-
> 注意:`docs/design/active/dynamic-workflow-dag-engine-roadmap.md` 含 2026-07-04 历史叙述;文中凡把 Cursor 写成受治理 executor 或 `loop` 的 `cursor-fix` 动作,均为**历史叙述**,现状以 Pi-only + 显式 `cursor-prompt` sidecar 为准。
|
|
61
|
-
|
|
62
|
-
## 已收敛为 archive / 历史基线(非未来)
|
|
63
|
-
|
|
64
|
-
- 第 1–2 月规划已收敛为 archive/reports 指针,不在本文展开:`docs/design/archive/2026-07-12-第二月规划.md`。
|
|
65
|
-
- `docs/reports/2026-07-02-repository-analysis.md` 自 2026-07-14 起冻结为**历史基线快照**(允许有限再采样),不当第二 CHANGELOG。
|
|
66
|
-
- 活能力短摘要在 `docs/reports/current-capability-summary.md`。
|
|
67
|
-
- 专题证据按类型落在 `docs/reports/{feature,init-evolution,merge,fullstack-dogfood,dogfood}/`。
|
|
68
|
-
|
|
69
|
-
## 文档本体的演进边界
|
|
70
|
-
|
|
71
|
-
- 本目录新增文档随发布包发布(`package.json` `files` + manifest `packageRequired` 显式条目),但 **不**投影到目标项目 init surface;目标项目 init 仍只投影语言无关的 `runtime-boundaries.md`。
|
|
72
|
-
- `runtime-boundaries.md` 是边界真源;本目录其他文档交叉引用,不复制其 import 方向表 / governance-hook 表 / 版本化自举边界表。
|
|
73
|
-
- 未来如有能力落地,应先改 `src/` / CLI / ADR / completed plan,再回写本目录的「已实现」表与站上 `website/docs/overview/*` 短索引。
|
|
1
|
+
# 架构演进:当前 vs 未来
|
|
2
|
+
|
|
3
|
+
本页区分 loop-agent **当前已实现**的架构能力与**未来规划**。当前事实以代码、发布 CLI、已完成计划为准;未来能力一律标「规划 / 未实现 / 前瞻」。权威源:`CHANGELOG.md`、`docs/reports/current-capability-summary.md`、ADR 0001–0005、`docs/exec-plans/completed/` 与 `docs/exec-plans/active/`。
|
|
4
|
+
|
|
5
|
+
**对照版本:`@tea-agent/loop-agent@0.24.0`(2026-07-27)**。细节版本条目见根 `CHANGELOG.md`;本表只保留架构层可读摘要。
|
|
6
|
+
|
|
7
|
+
## 当前已实现(累计至 0.24.0)
|
|
8
|
+
|
|
9
|
+
| 域 | 现状 | 权威入口 |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| 受治理 Agent runtime | **Pi-only**;`cursor-prompt` 仅显式 one-shot sidecar | ADR 0001、`CHANGELOG.md [0.10.0]` |
|
|
12
|
+
| Agent DAG 主链 | `dag run-task` → generate → validate → `run-dag`;report/doctor/reconcile-run | `dag-execution.md`、`test/cli-contract.test.ts` |
|
|
13
|
+
| Dynamic Workflow | `WorkflowSpec` → validate → compile → 同一 `run-dag`;agent-like 节点仅 `pi\|static` | `src/workflows/dynamic/**`、`website/docs/guides/dynamic-workflow.md` |
|
|
14
|
+
| 版本化自举 | controller identity + run-owned skill snapshot + deterministic canary | `docs/reports/feature/2026-07-13-versioned-self-hosting-bootstrap.md` |
|
|
15
|
+
| Feature 交付(M2) | review/run/approve-followup/delivery/closeout/verify-final | `docs/reports/feature/2026-07-12-m2-completion-audit.md`、`CHANGELOG.md [0.10.0]` |
|
|
16
|
+
| Task Pool | 唯一根 `.harness/task-pool/`;**feature-scoped** state v2(`TaskPoolTaskRef`) | ADR 0002、ADR 0004、`CHANGELOG.md [0.13.0]` |
|
|
17
|
+
| Ready Planner | priority 选择;`batch plan-ready` 零写入;`run-ready` 冻结 plan;产物门禁 | `CHANGELOG.md [0.13.0]` / `[0.16.0]` |
|
|
18
|
+
| TaskSpec → workflow | 可选 `execution.workflow` → `taskKind`;typed Outcome;`fullstack-v1` Verification Bundle | completed `2026-07-19-taskspec-workflow-routing.md`、`CHANGELOG.md [0.16.0]` |
|
|
19
|
+
| 专用 taskKind DAG | `frontend-implementation` / `frontend-test` / `backend-test` / knowledge* | `website/docs/guides/agent-dag.md`、`CHANGELOG.md [0.12.0]`–`[0.17.x]` |
|
|
20
|
+
| Backend-test | **Markdown-first 8 节点**;Markdown 映射限定 pytest 执行,生成 Markdown 与分层 HTML/脱敏请求响应详情 | completed `2026-07-24-backend-test-scoped-execution-report-ui.md`;`CHANGELOG.md [0.21.0]` / `[0.22.0]` |
|
|
21
|
+
| Frontend-test | 默认 short-chain RAG;内容/evidence 质量进入 advisory findings,路径与安全边界仍 fail-closed | `CHANGELOG.md [0.21.0]` / `[0.22.0]` |
|
|
22
|
+
| Frontend-implementation | Contract / trace / repair / Mock assess;`ai_workspace` + 知识库 + `openspec` 规范发现;lint 基线债务隔离 | `CHANGELOG.md [0.22.0]` |
|
|
23
|
+
| Inspect(原 Observe) | 统一 Console 的 `/inspect/` 只读运营面与富时间线;独立 `observe serve` 已硬下线 | `website/docs/guides/observe-ui.md`、`CHANGELOG.md [Unreleased]` |
|
|
24
|
+
| Local Operator Console | `agent-worker console\|doctor`(loopback;默认打开系统浏览器,`--no-open` 禁止);Operate + Inspect、Happy Path / Interview / split view / recovery CTA;**0.23.x 起含 General Operator Chat**(Pi SDK services session、operator-only tool surface);**非**远端多用户 Console | unify design;ADR 0005;completed `2026-07-25-console-phase-4-general-operator-chat.md` |
|
|
25
|
+
| Task Contract / operator surface | journaled `task contract *`;machine envelope;**DagSpec v4 `taskContractBinding`**(exclusive writer 强制) | `CHANGELOG.md [0.17.0]`–`[0.17.2]`;ADR 0005 |
|
|
26
|
+
| DagSpec / repair | v3 `runtimeContract` + 显式 `repairNodeId`;新 writer 生成默认 v4 binding | `dag-execution.md`、`CHANGELOG.md [0.11.0]` / `[0.17.0]` |
|
|
27
|
+
| 自适应 liveness | runner lease、Provider/tool/output 活动与 meaningful progress 分离;静默 stall 受控终止,退出未确认时禁止自动重试 | `dag-execution.md`、active `2026-07-23-dag-adaptive-liveness.md` |
|
|
28
|
+
| 运行恢复 | 节点内协议纠错重试、`dag rerun` 安全子图续跑、`dag rerun-task` 完整任务重跑与 Console Recovery | `CHANGELOG.md [0.20.1]`、active `2026-07-23-operator-rerun-hardening.md` |
|
|
29
|
+
| Eval Lab | corpus / campaign / promote(人工门禁);`autoPromote=false`;**非** RSI Level 1 | `CHANGELOG.md [0.15.0]`;active plan 可归档 |
|
|
30
|
+
| 文档双树 | `website/docs/` 用法 vs `docs/` 治理;docs-converge | ADR 0003 |
|
|
31
|
+
| 文档治理 | 任务式 Start Here、机器可读身份清单、目录默认身份与版本/索引语义检查 | 本目录 README、`docs/README.md`、`docs/document-catalog.json` |
|
|
32
|
+
|
|
33
|
+
### 受治理 runtime 的边界(已实现、不变式)
|
|
34
|
+
|
|
35
|
+
- DAG writer 固定 `implement-pi` / `repair-pi`;`executor: "cursor"`、`implement-cursor` / `repair-cursor`、Cursor worker、`cursor-fix` 已从受治理路径移除。
|
|
36
|
+
- 完成权威 = shell verification;model verdict / Observe / 报告是 derived/advisory。
|
|
37
|
+
- Worker 通过已发布 `loop-agent` 子进程执行,不 in-process import runtime kernel(governance 机器校验)。
|
|
38
|
+
- Console / Compatibility 主会话不得绕过 CLI 直接改业务实现(ADR 0005)。
|
|
39
|
+
|
|
40
|
+
## 未来规划(第 3–6 月,**未实现**)
|
|
41
|
+
|
|
42
|
+
以下能力来自 `docs/design/active/六个月规划.md` 与相关设计输入(页首校准条之后的未交付 phase 为**设计输入**)。它们**当前不存在于产品声称的线上形态**,或仅有本地/部分替代:
|
|
43
|
+
|
|
44
|
+
| 未来方向 | 状态 | 规划来源 |
|
|
45
|
+
| --- | --- | --- |
|
|
46
|
+
| 远程 PR / CI 自动创建与写回 | 规划 / 未实现 | `docs/design/active/六个月规划.md`(第 3 个月起) |
|
|
47
|
+
| 线上 / 云 Worker | 规划 / 未实现 | 同上 |
|
|
48
|
+
| 云 Task Pool / SQL / Orchestrator | 规划 / 未实现 | 同上(第 2 月原始设计已调整为本地 Feature 闭环) |
|
|
49
|
+
| 多仓库平台 | 规划 / 未实现 | 同上 |
|
|
50
|
+
| 组织级服务 | 规划 / 未实现 | 同上 |
|
|
51
|
+
| Web Console(远端多用户) | 规划 / 未实现;**不等于**已发布的本地 loopback Operator Console | 同上;对照 `docs/design/archive/2026-07-21-local-operator-console-from-pi-web.md` |
|
|
52
|
+
| fullstack Delivery / Final Verification live 闭环 | 进行中(active train) | `docs/exec-plans/active/2026-07-19-fullstack-dogfood-016x-release-train.md` |
|
|
53
|
+
| Dynamic Workflow runtime limits 强执法、更广 profile | 设计输入 | `docs/design/active/dynamic-workflow-dag-engine-roadmap.md` |
|
|
54
|
+
| Loop 与 Dynamic Workflow 更深双向集成 / 自动恢复 | 设计输入 | 同上;当前已有基础 `workflow` action |
|
|
55
|
+
| Operator Chat 作为主操作会话(裸 bash 收敛、真 human gate、durable session/turn、Operation 闭环、完整 CLI action coverage、默认入口切换) | 设计输入 / 未达主会话门槛 | `docs/design/active/operator-chat-primary-session-roadmap.md`;Phase 4 General Operator Chat 本体已实现归档(`docs/exec-plans/completed/2026-07-25-console-phase-4-general-operator-chat.md`) |
|
|
56
|
+
| Eval Lab auto-promote / RSI Level 1 产品声称 | **禁止**写成已交付 | Eval Lab design + active plan |
|
|
57
|
+
|
|
58
|
+
> 本地 Operator Console 统一面(实际首发 0.19.0)已发布:单仓库、loopback、随 `@tea-agent/loop-agent` 同包、canonical mutation 只经 sibling CLI,Inspect 复用 GET-only Observe read model。它不是远端 Web Console,也不能把多租户/云编排需求偷渡进本地 Console。
|
|
59
|
+
|
|
60
|
+
> 注意:`docs/design/active/dynamic-workflow-dag-engine-roadmap.md` 含 2026-07-04 历史叙述;文中凡把 Cursor 写成受治理 executor 或 `loop` 的 `cursor-fix` 动作,均为**历史叙述**,现状以 Pi-only + 显式 `cursor-prompt` sidecar 为准。
|
|
61
|
+
|
|
62
|
+
## 已收敛为 archive / 历史基线(非未来)
|
|
63
|
+
|
|
64
|
+
- 第 1–2 月规划已收敛为 archive/reports 指针,不在本文展开:`docs/design/archive/2026-07-12-第二月规划.md`。
|
|
65
|
+
- `docs/reports/2026-07-02-repository-analysis.md` 自 2026-07-14 起冻结为**历史基线快照**(允许有限再采样),不当第二 CHANGELOG。
|
|
66
|
+
- 活能力短摘要在 `docs/reports/current-capability-summary.md`。
|
|
67
|
+
- 专题证据按类型落在 `docs/reports/{feature,init-evolution,merge,fullstack-dogfood,dogfood}/`。
|
|
68
|
+
|
|
69
|
+
## 文档本体的演进边界
|
|
70
|
+
|
|
71
|
+
- 本目录新增文档随发布包发布(`package.json` `files` + manifest `packageRequired` 显式条目),但 **不**投影到目标项目 init surface;目标项目 init 仍只投影语言无关的 `runtime-boundaries.md`。
|
|
72
|
+
- `runtime-boundaries.md` 是边界真源;本目录其他文档交叉引用,不复制其 import 方向表 / governance-hook 表 / 版本化自举边界表。
|
|
73
|
+
- 未来如有能力落地,应先改 `src/` / CLI / ADR / completed plan,再回写本目录的「已实现」表与站上 `website/docs/overview/*` 短索引。
|