@tea-agent/loop-agent 0.39.0-beta.11 → 0.39.0-beta.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-stamp.json +3 -3
- package/dist/workflows/dag/init-hybrid.js +60 -0
- package/dist/workflows/dag/node-execution.js +110 -26
- package/docs/templates/README.md +1 -0
- package/docs/templates/frontend-implementation-dag.json +89 -0
- package/package.json +1 -1
- package/skills/loop-agent/references/hybrid-dag.md +1 -1
package/dist/build-stamp.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.39.0-beta.
|
|
4
|
-
"gitSha": "
|
|
5
|
-
"builtAt": "2026-08-
|
|
3
|
+
"version": "0.39.0-beta.12",
|
|
4
|
+
"gitSha": "f6c24c6c6eaacfcc975732dec41df6ef5027f16c",
|
|
5
|
+
"builtAt": "2026-08-28T08:40:28.786Z"
|
|
6
6
|
}
|
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { access, readdir, readFile, realpath } from "node:fs/promises";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
5
6
|
import { writeJsonAtomic } from "../../infrastructure/harness/atomic-write.js";
|
|
6
7
|
import { assertValidDagSpec } from "./validate.js";
|
|
7
8
|
import { DAG_AGENT_RUNTIME_PI_ONLY, DAG_REPAIR_WRITER_PROTOCOL_EXPLICIT_NODE_V1, DAG_RUNTIME_CONTRACT_SCHEMA_VERSION, DEFAULT_DAG_OUTPUT_LANGUAGE, DEFAULT_DAG_EXECUTOR_MODELS, parseDagSpec, } from "./types.js";
|
|
@@ -2764,6 +2765,37 @@ function resolveFrontendShapeCapsuleGenerationInput(sources) {
|
|
|
2764
2765
|
}
|
|
2765
2766
|
return explicit ?? autoloaded;
|
|
2766
2767
|
}
|
|
2768
|
+
/** Standard frontend DAG template (docs/templates/frontend-implementation-dag.json).
|
|
2769
|
+
* It is the topology source of truth: node set, execution order, and depends_on
|
|
2770
|
+
* come from the template; the runtime generator assembles each node's full
|
|
2771
|
+
* configuration (budgets, retry, skeleton, skills, dynamic prompt sections).
|
|
2772
|
+
* Resolution order: repoRoot copy (dev workspace) → bundled package copy. */
|
|
2773
|
+
export async function loadFrontendDagTemplate(repoRoot) {
|
|
2774
|
+
const candidates = [];
|
|
2775
|
+
if (repoRoot) {
|
|
2776
|
+
candidates.push(path.join(repoRoot, "docs", "templates", "frontend-implementation-dag.json"));
|
|
2777
|
+
}
|
|
2778
|
+
candidates.push(fileURLToPath(new URL("../../../docs/templates/frontend-implementation-dag.json", import.meta.url)));
|
|
2779
|
+
for (const candidate of candidates) {
|
|
2780
|
+
try {
|
|
2781
|
+
const parsed = JSON.parse(await readFile(candidate, "utf8"));
|
|
2782
|
+
if (!Array.isArray(parsed.tasks))
|
|
2783
|
+
continue;
|
|
2784
|
+
const tasks = parsed.tasks.filter((item) => typeof item === "object" &&
|
|
2785
|
+
item !== null &&
|
|
2786
|
+
typeof item.id === "string" &&
|
|
2787
|
+
Array.isArray(item.depends_on) &&
|
|
2788
|
+
typeof item.executor === "string");
|
|
2789
|
+
if (tasks.length === 0)
|
|
2790
|
+
continue;
|
|
2791
|
+
return { tasks };
|
|
2792
|
+
}
|
|
2793
|
+
catch {
|
|
2794
|
+
// try next candidate
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
return null;
|
|
2798
|
+
}
|
|
2767
2799
|
async function buildFrontendHybridDagFromTask(sources) {
|
|
2768
2800
|
const { taskConfig } = sources;
|
|
2769
2801
|
const mockCapability = sources.frontendMockCapability ?? {
|
|
@@ -3461,6 +3493,34 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3461
3493
|
},
|
|
3462
3494
|
],
|
|
3463
3495
|
};
|
|
3496
|
+
// The static DAG template is the topology source of truth: the generated
|
|
3497
|
+
// chain must match the template's node set and order, and the template's
|
|
3498
|
+
// depends_on must be a subset of the generated one (the runtime may add
|
|
3499
|
+
// dependencies, e.g. Mock-required planning depending on the contract
|
|
3500
|
+
// node's Mock facts). The template's own tasks carry simplified placeholder
|
|
3501
|
+
// configs; the generated node definitions (budgets, retry, skeleton,
|
|
3502
|
+
// skills, prompts) and any runtime-added dependencies win.
|
|
3503
|
+
const frontendTemplate = await loadFrontendDagTemplate(sources.repoRoot);
|
|
3504
|
+
if (frontendTemplate) {
|
|
3505
|
+
const byId = new Map(spec.tasks.map((task) => [task.id, task]));
|
|
3506
|
+
const templateIds = frontendTemplate.tasks.map((task) => task.id);
|
|
3507
|
+
const missing = templateIds.filter((id) => !byId.has(id));
|
|
3508
|
+
if (missing.length > 0) {
|
|
3509
|
+
throw new Error(`frontend DAG template topology drift: generated chain is missing template node(s): ${missing.join(", ")}`);
|
|
3510
|
+
}
|
|
3511
|
+
for (const templateTask of frontendTemplate.tasks) {
|
|
3512
|
+
const generated = byId.get(templateTask.id);
|
|
3513
|
+
const generatedDeps = new Set(generated.depends_on);
|
|
3514
|
+
const templateOnly = templateTask.depends_on.filter((dep) => !generatedDeps.has(dep));
|
|
3515
|
+
if (templateOnly.length > 0) {
|
|
3516
|
+
throw new Error(`frontend DAG template topology drift: generated node ${templateTask.id} is missing template dependency ${templateOnly.join(", ")}`);
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
3519
|
+
const templateSet = new Set(templateIds);
|
|
3520
|
+
const ordered = templateIds.map((id) => byId.get(id));
|
|
3521
|
+
const extra = spec.tasks.filter((task) => !templateSet.has(task.id));
|
|
3522
|
+
spec.tasks = [...ordered, ...extra];
|
|
3523
|
+
}
|
|
3464
3524
|
if (frontendTaskShape.shape === "split-required") {
|
|
3465
3525
|
spec.tasks = pruneFrontendTasksForSplitRequired(spec.tasks);
|
|
3466
3526
|
}
|
|
@@ -254,11 +254,25 @@ function planInputStrings(value) {
|
|
|
254
254
|
? value.filter((item) => typeof item === "string")
|
|
255
255
|
: [];
|
|
256
256
|
}
|
|
257
|
+
/** Input bound for the planner evidence block; protects the model input budget. */
|
|
258
|
+
const FRONTEND_PLAN_INPUT_MAX_CHARS = 12_000;
|
|
259
|
+
const FRONTEND_PLAN_INPUT_CAP_LADDER = [
|
|
260
|
+
{ text: 240, array: 40 },
|
|
261
|
+
{ text: 120, array: 20 },
|
|
262
|
+
{ text: 60, array: 10 },
|
|
263
|
+
{ text: 24, array: 4 },
|
|
264
|
+
];
|
|
257
265
|
/**
|
|
258
266
|
* Render the planner's complete-but-bounded evidence handoff from committed
|
|
259
267
|
* typed facts. It deliberately excludes upstream response prose and artifact
|
|
260
268
|
* paths: Contract and Scout have already established these facts, so Plan
|
|
261
269
|
* should decide and commit rather than spend another model turn reading them.
|
|
270
|
+
*
|
|
271
|
+
* The block is always valid JSON under the char bound: field texts shrink
|
|
272
|
+
* through a cap ladder before any fact is dropped, and the last-resort
|
|
273
|
+
* fallback keeps every requirement id (with `text: "(truncated)"`) while
|
|
274
|
+
* declaring the degradation, so the planner records targeted evidence gaps
|
|
275
|
+
* instead of receiving a silently corrupted tail.
|
|
262
276
|
*/
|
|
263
277
|
export function renderFrontendPlanInputContext(input) {
|
|
264
278
|
const committedFacts = (records) => records.flatMap((record) => record.phase === "committed" && planInputRecord(record.fact)
|
|
@@ -277,45 +291,109 @@ export function renderFrontendPlanInputContext(input) {
|
|
|
277
291
|
const targetSurface = scoutFacts
|
|
278
292
|
.filter((fact) => fact.kind === "target-surface" && fact.origin === "scout")
|
|
279
293
|
.map((fact) => ({
|
|
280
|
-
completeness:
|
|
281
|
-
entrypoint:
|
|
282
|
-
routeOrMount:
|
|
283
|
-
implementationPaths:
|
|
284
|
-
testPaths:
|
|
285
|
-
dataSource:
|
|
286
|
-
allowedPathConflicts:
|
|
287
|
-
unresolvedPaths:
|
|
294
|
+
completeness: fact.completeness,
|
|
295
|
+
entrypoint: fact.entrypoint,
|
|
296
|
+
routeOrMount: fact.routeOrMount,
|
|
297
|
+
implementationPaths: fact.implementationPaths,
|
|
298
|
+
testPaths: fact.testPaths,
|
|
299
|
+
dataSource: fact.dataSource,
|
|
300
|
+
allowedPathConflicts: fact.allowedPathConflicts,
|
|
301
|
+
unresolvedPaths: fact.unresolvedPaths,
|
|
288
302
|
}));
|
|
289
303
|
const designEvidence = scoutFacts
|
|
290
304
|
.filter((fact) => fact.kind === "design-evidence" && fact.origin === "scout")
|
|
291
305
|
.map((fact) => ({
|
|
292
|
-
source:
|
|
293
|
-
paths:
|
|
294
|
-
conflicts:
|
|
306
|
+
source: fact.source,
|
|
307
|
+
paths: fact.paths,
|
|
308
|
+
conflicts: fact.conflicts,
|
|
295
309
|
}));
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
310
|
+
const serializeAtCap = (cap) => JSON.stringify({
|
|
311
|
+
requirements: requirements.map((requirement) => ({
|
|
312
|
+
id: requirement.id,
|
|
313
|
+
text: planInputText(requirement.text, cap.text),
|
|
314
|
+
sourceFragmentIds: planInputStrings(requirement.sourceFragmentIds).slice(0, cap.array),
|
|
315
|
+
})),
|
|
316
|
+
targetSurface: targetSurface.map((surface) => ({
|
|
317
|
+
completeness: planInputText(surface.completeness, 32),
|
|
318
|
+
entrypoint: planInputText(surface.entrypoint, cap.text),
|
|
319
|
+
routeOrMount: planInputText(surface.routeOrMount, cap.text),
|
|
320
|
+
implementationPaths: planInputStrings(surface.implementationPaths).slice(0, cap.array),
|
|
321
|
+
testPaths: planInputStrings(surface.testPaths).slice(0, cap.array),
|
|
322
|
+
dataSource: planInputText(surface.dataSource, cap.text),
|
|
323
|
+
allowedPathConflicts: planInputStrings(surface.allowedPathConflicts).slice(0, cap.array),
|
|
324
|
+
unresolvedPaths: planInputStrings(surface.unresolvedPaths).slice(0, cap.array),
|
|
325
|
+
})),
|
|
326
|
+
designEvidence: designEvidence.map((evidence) => ({
|
|
327
|
+
source: planInputText(evidence.source, cap.text),
|
|
328
|
+
paths: planInputStrings(evidence.paths).slice(0, cap.array),
|
|
329
|
+
conflicts: planInputStrings(evidence.conflicts).slice(0, cap.array),
|
|
330
|
+
})),
|
|
331
|
+
});
|
|
332
|
+
let serialized = serializeAtCap(FRONTEND_PLAN_INPUT_CAP_LADDER[0]);
|
|
333
|
+
for (const cap of FRONTEND_PLAN_INPUT_CAP_LADDER.slice(1)) {
|
|
334
|
+
if (serialized.length <= FRONTEND_PLAN_INPUT_MAX_CHARS)
|
|
335
|
+
break;
|
|
336
|
+
serialized = serializeAtCap(cap);
|
|
337
|
+
}
|
|
338
|
+
if (serialized.length > FRONTEND_PLAN_INPUT_MAX_CHARS) {
|
|
339
|
+
// Last resort: keep every requirement id (ids are short and the plan
|
|
340
|
+
// prompt separately lists them) but drop their texts, shrink scout facts
|
|
341
|
+
// to the minimum, and declare the degradation instead of corrupting JSON.
|
|
342
|
+
let fallback = {
|
|
343
|
+
degraded: "requirement-texts-truncated",
|
|
344
|
+
requirements: requirements.map((requirement) => ({
|
|
345
|
+
id: requirement.id,
|
|
346
|
+
text: "(truncated)",
|
|
347
|
+
})),
|
|
348
|
+
targetSurface: targetSurface.map((surface) => ({
|
|
349
|
+
completeness: planInputText(surface.completeness, 32),
|
|
350
|
+
implementationPaths: planInputStrings(surface.implementationPaths).slice(0, FRONTEND_PLAN_INPUT_CAP_LADDER[3].array),
|
|
351
|
+
})),
|
|
352
|
+
designEvidence: [],
|
|
353
|
+
};
|
|
354
|
+
let bounded = JSON.stringify(fallback);
|
|
355
|
+
let keep = fallback.requirements.length;
|
|
356
|
+
while (bounded.length > FRONTEND_PLAN_INPUT_MAX_CHARS &&
|
|
357
|
+
keep > 0) {
|
|
358
|
+
keep = Math.max(0, Math.floor(keep / 2));
|
|
359
|
+
fallback = { ...fallback, requirements: fallback.requirements.slice(0, keep) };
|
|
360
|
+
bounded = JSON.stringify({
|
|
361
|
+
...fallback,
|
|
362
|
+
requirementIdsTruncated: keep < requirements.length,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
serialized = bounded;
|
|
366
|
+
}
|
|
300
367
|
return [
|
|
301
368
|
"<frontend_plan_input>",
|
|
302
369
|
"Committed Contract/Scout facts, compiled by the runner. Treat them as the complete planning evidence.",
|
|
303
|
-
|
|
370
|
+
serialized,
|
|
304
371
|
"Do not read upstream artifacts, task sources, or repository files. If this input cannot support a decision, record a genuine evidence gap.",
|
|
305
372
|
"</frontend_plan_input>",
|
|
306
373
|
].join("\n");
|
|
307
374
|
}
|
|
308
|
-
async function buildFrontendPlanInputContext(runDir) {
|
|
375
|
+
export async function buildFrontendPlanInputContext(runDir) {
|
|
376
|
+
const contractFactsPath = path.join(runDir, "frontend-contract-pi", "contract-typed-facts.jsonl");
|
|
377
|
+
const scoutFactsPath = path.join(runDir, "frontend-scout-pi", "scout-typed-facts.jsonl");
|
|
378
|
+
let contractRecords;
|
|
379
|
+
let scoutRecords;
|
|
309
380
|
try {
|
|
310
|
-
|
|
311
|
-
readTypedEventStoreFromJsonl(
|
|
312
|
-
readTypedEventStoreFromJsonl(
|
|
381
|
+
[contractRecords, scoutRecords] = await Promise.all([
|
|
382
|
+
readTypedEventStoreFromJsonl(contractFactsPath),
|
|
383
|
+
readTypedEventStoreFromJsonl(scoutFactsPath),
|
|
313
384
|
]);
|
|
314
|
-
return renderFrontendPlanInputContext({ contractRecords, scoutRecords });
|
|
315
385
|
}
|
|
316
|
-
catch {
|
|
317
|
-
|
|
386
|
+
catch (error) {
|
|
387
|
+
// A missing/unreadable upstream store is a broken pipeline, not an
|
|
388
|
+
// evidence gap; fail before spending a model turn on a prompt that
|
|
389
|
+
// forbids reading anything.
|
|
390
|
+
throw new Error(`frontend-plan-input-unavailable: cannot read committed typed facts (${error instanceof Error ? error.message : String(error)})`);
|
|
318
391
|
}
|
|
392
|
+
const committedCount = [...contractRecords, ...scoutRecords].filter((record) => record.phase === "committed").length;
|
|
393
|
+
if (committedCount === 0) {
|
|
394
|
+
throw new Error(`frontend-plan-input-unavailable: no committed Contract/Scout facts in ${contractFactsPath} / ${scoutFactsPath}`);
|
|
395
|
+
}
|
|
396
|
+
return renderFrontendPlanInputContext({ contractRecords, scoutRecords });
|
|
319
397
|
}
|
|
320
398
|
export function buildAttemptPrompt(task, basePrompt, attemptNumber, previousFailureCategory, previousProtocolReason, recoveryTargetPaths, recoveryDiagnostics, frontendPlanRetryStep) {
|
|
321
399
|
if (attemptNumber <= 1)
|
|
@@ -1018,9 +1096,15 @@ export async function executeDagNode(input) {
|
|
|
1018
1096
|
return;
|
|
1019
1097
|
}
|
|
1020
1098
|
if (isFrontendPlanLadderTask(task)) {
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1099
|
+
let planInputContext;
|
|
1100
|
+
try {
|
|
1101
|
+
planInputContext = await buildFrontendPlanInputContext(runDir);
|
|
1102
|
+
}
|
|
1103
|
+
catch (error) {
|
|
1104
|
+
await failBeforePrompt(error, "frontend-plan-input-unavailable");
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
prompt = `${prompt}\n\n${planInputContext}`;
|
|
1024
1108
|
}
|
|
1025
1109
|
node.resolvedSkills = resolvedSkills;
|
|
1026
1110
|
await writeNodeSkillArtifacts(runDir, nodeId, resolvedSkills);
|
package/docs/templates/README.md
CHANGED
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
- `frontend-task-requirement.md`、`frontend-task-constraints.md`、`frontend-design-contract.md` — 前端任务输入与设计合同。
|
|
40
40
|
- `frontend-implementation-contract.schema.json` — 前端实现合同 schema(sourceBinding v2 ledger 字段 all-or-none:任一出现则四项齐全,禁止 v1+optional 冒充 v2)。`requirements[].sourceRefs[].items` 使用合法 `$ref: #/$defs/path`(相对路径、无 `..` 逃逸、无反斜杠),模板 JSON 无非法键,运行时加载器可直接编译(AC-003)。
|
|
41
|
+
- `frontend-implementation-dag.json` — 自包含的前端实现 DAG 模板:契约 → 侦察 → 计划 → 设计策略/评审 → writer 准入 → 有界写入 → 验证 → 真实 diff 审查 → closeout。它不依赖 runtime builder 或其他模板;执行前必须将所有 `REPLACE/WITH/...` 路径和确定性命令替换为当前项目的具体值。
|
|
41
42
|
- `frontend-test-dag.json` — frontend-test DAG 模板;其 deterministic checklist 会拒绝 alternative executable instructions,仅允许 allowlisted `playwright-cli` command。Pi generator/reviser 的 runtime writeSet 仅授权下列目标项目生成路径(不是本仓库文档索引目标):
|
|
42
43
|
|
|
43
44
|
~~~text
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./agent-dag.schema.json",
|
|
3
|
+
"version": 2,
|
|
4
|
+
"title": "Frontend implementation independent DAG template",
|
|
5
|
+
"objective": "Replace with the concrete, bounded frontend implementation objective for this DAG run.",
|
|
6
|
+
"successCriteria": [
|
|
7
|
+
"The contract covers every required user-visible behavior, state, and interaction.",
|
|
8
|
+
"The implementation is admitted only after a concrete, narrow writeSet is reviewed.",
|
|
9
|
+
"Static and behavior verification are recorded before final review.",
|
|
10
|
+
"The final closeout distinguishes passed, not-run, and residual-risk evidence."
|
|
11
|
+
],
|
|
12
|
+
"globalConstraints": [
|
|
13
|
+
"This is a self-contained static template: do not generate, import, or merge a DAG from a runtime builder before execution.",
|
|
14
|
+
"Replace every REPLACE/WITH/... placeholder with concrete repository paths or deterministic commands before execution.",
|
|
15
|
+
"Do not commit runtime traces under .harness/dag-runs/.",
|
|
16
|
+
"Every node must explicitly declare its executor; defaults.executor is schema-only and not a runtime fallback.",
|
|
17
|
+
"Only frontend-implement-pi may write project files, and only inside its concrete writeSet.",
|
|
18
|
+
"Read-only nodes must not write root artifacts/**; root artifacts/ is not a per-node scratchpad.",
|
|
19
|
+
"Do not infer missing behavior, states, interactions, design decisions, or verification commands. Stop for an explicit task-contract revision instead.",
|
|
20
|
+
"Do not add dependencies unless they are already declared by the project contract."
|
|
21
|
+
],
|
|
22
|
+
"defaults": {"executor":"pi","contextProfile":"slim","skills":["ai-engineering-context"],"writePolicy":"read-only"},
|
|
23
|
+
"skillsByRole": {
|
|
24
|
+
"planner":["loop-agent"], "scout":["ai-engineering-context"], "implementer":["verification-before-completion"], "reviewer":["requesting-code-review"], "verifier":["verification-before-completion","systematic-debugging"], "closeout":["loop-agent","verification-before-completion"]
|
|
25
|
+
},
|
|
26
|
+
"executorModels": {"pi":{"LOW":"gpt-5.3-codex-spark","MED":"gpt-5.5","HIGH":"gpt-5.5"}},
|
|
27
|
+
"tasks": [
|
|
28
|
+
{
|
|
29
|
+
"id":"frontend-contract-pi", "depends_on":[], "complexity":"MED", "executor":"pi", "role":"planner", "writePolicy":"read-only", "allowedPaths":["**"], "forbiddenPaths":[".harness/**","artifacts/**"],
|
|
30
|
+
"outputContract":"Plain Markdown frontend contract: required behavior, states, interactions, non-goals, concrete verification expectations, and unresolved gaps; no file writes.",
|
|
31
|
+
"subtask_prompt":"Read the task inputs. Produce a concise frontend contract that maps each required behavior, UI state, and interaction to observable outcomes. Identify missing or conflicting requirements as blockers; do not infer them and do not edit files."
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id":"frontend-scout-pi", "depends_on":["frontend-contract-pi"], "complexity":"LOW", "executor":"pi", "role":"scout", "writePolicy":"read-only", "allowedPaths":["REPLACE/WITH/FRONTEND/SOURCE/PATH/**","REPLACE/WITH/FRONTEND/TEST/PATH/**"], "forbiddenPaths":[".harness/**","artifacts/**"],
|
|
35
|
+
"outputContract":"Plain Markdown source and verification reconnaissance: relevant routes, components, styles, state patterns, focused tests, and risks; no file writes.",
|
|
36
|
+
"subtask_prompt":"Inspect only the declared frontend source and test paths. Report reusable component, styling, accessibility, state-management, and verification patterns that satisfy the contract. Do not edit files or broaden the allowed paths."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id":"frontend-plan-pi", "depends_on":["frontend-scout-pi"], "complexity":"MED", "executor":"pi", "role":"planner", "writePolicy":"read-only", "allowedPaths":["REPLACE/WITH/FRONTEND/SOURCE/PATH/**","REPLACE/WITH/FRONTEND/TEST/PATH/**"], "forbiddenPaths":[".harness/**","artifacts/**"],
|
|
40
|
+
"outputContract":"Plain Markdown implementation plan: ordered changes, concrete target files, behavior/state/interaction coverage, and focused verification mapping; no file writes.",
|
|
41
|
+
"subtask_prompt":"Turn the contract and reconnaissance into a minimal implementation plan. Every target file and verification step must be concrete. Preserve existing design and accessibility conventions. If any behavior lacks a verifiable target, report it as blocked instead of guessing."
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id":"frontend-design-policy-shell", "depends_on":["frontend-plan-pi"], "complexity":"LOW", "executor":"shell", "role":"verifier", "writePolicy":"read-only", "allowedPaths":["REPLACE/WITH/FRONTEND/SOURCE/PATH/**","REPLACE/WITH/FRONTEND/TEST/PATH/**"], "forbiddenPaths":[".harness/**","artifacts/**"],
|
|
45
|
+
"outputContract":"Archived deterministic design-policy result proving the plan uses declared dependencies, concrete requirements, and approved design evidence; no worktree writes.",
|
|
46
|
+
"subtask_prompt":"Run the project-specific deterministic design-policy check selected for this task. It must reject missing requirement coverage, unauthorized dependencies, and unresolved design conflicts.",
|
|
47
|
+
"shell":{"commands":["REPLACE/WITH/DESIGN-POLICY/COMMAND"],"cwd":".","timeoutMs":300000}
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"id":"frontend-design-review-pi", "depends_on":["frontend-design-policy-shell"], "complexity":"MED", "executor":"pi", "role":"reviewer", "writePolicy":"read-only", "allowedPaths":["REPLACE/WITH/FRONTEND/SOURCE/PATH/**","REPLACE/WITH/FRONTEND/TEST/PATH/**"], "forbiddenPaths":[".harness/**","artifacts/**"],
|
|
51
|
+
"outputContract":"Plain Markdown design review: approved plan or explicit blocking findings for behavior, state, interaction, design-system, and accessibility coverage; no file writes.",
|
|
52
|
+
"subtask_prompt":"Review the approved plan against the frontend contract and design-policy result. Reject missing visible states, interaction behavior, accessibility obligations, or unsupported design claims. Do not edit files."
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id":"frontend-writer-admission-shell", "depends_on":["frontend-design-review-pi"], "complexity":"LOW", "executor":"shell", "role":"verifier", "writePolicy":"read-only", "allowedPaths":["REPLACE/WITH/FRONTEND/TARGET/PATH/**"], "forbiddenPaths":[".harness/**","artifacts/**"],
|
|
56
|
+
"outputContract":"Archived deterministic writer-admission result with a concrete, narrow writeSet and pre-write baseline; no worktree writes.",
|
|
57
|
+
"subtask_prompt":"Run the project-specific writer-admission check. It must derive and record a concrete writeSet from the approved plan, reject broad paths and scope drift, and capture the pre-write baseline before authorizing the writer.",
|
|
58
|
+
"shell":{"commands":["REPLACE/WITH/WRITER-ADMISSION/COMMAND"],"cwd":".","timeoutMs":300000}
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id":"frontend-implement-pi", "depends_on":["frontend-writer-admission-shell"], "complexity":"HIGH", "executor":"pi", "role":"implementer", "toolProfile":"write", "writePolicy":"exclusive", "writeSet":["REPLACE/WITH/FRONTEND/TARGET/PATH/**"], "allowedPaths":["REPLACE/WITH/FRONTEND/TARGET/PATH/**"], "forbiddenPaths":[".harness/**","artifacts/**"],
|
|
62
|
+
"outputContract":"Implementation summary with changed files, contract coverage, focused checks run, and residual risks.",
|
|
63
|
+
"subtask_prompt":"Implement only the admitted frontend changes. Preserve established component, styling, accessibility, and state-management conventions. Cover every approved behavior, state, and interaction, then report changed files and focused verification. Do not broaden writeSet or add undeclared dependencies."
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id":"frontend-verify-shell", "depends_on":["frontend-implement-pi"], "complexity":"LOW", "executor":"shell", "role":"verifier", "writePolicy":"read-only", "allowedPaths":["REPLACE/WITH/FRONTEND/TARGET/PATH/**"], "forbiddenPaths":[".harness/**","artifacts/**"],
|
|
67
|
+
"outputContract":"Archived static and behavior verification output with command exit codes; no worktree writes.",
|
|
68
|
+
"subtask_prompt":"Run the contract-selected deterministic static and behavior verification commands. Record all exit codes. A failed required command is terminal for this DAG run; do not silently substitute a different command.",
|
|
69
|
+
"shell":{"preset":"loop-agent-standard-verify","cwd":".","timeoutMs":300000}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"id":"frontend-review-context-shell", "depends_on":["frontend-verify-shell"], "complexity":"LOW", "executor":"shell", "role":"verifier", "writePolicy":"read-only", "allowedPaths":["REPLACE/WITH/FRONTEND/TARGET/PATH/**"], "forbiddenPaths":[".harness/**","artifacts/**"],
|
|
73
|
+
"outputContract":"Archived deterministic final-review context: admitted writeSet, cumulative diff, and required verification evidence; no worktree writes.",
|
|
74
|
+
"subtask_prompt":"Run the project-specific review-context collector. It must bind the admitted writeSet, actual cumulative diff, and required verification results so final review cannot rely on summaries alone.",
|
|
75
|
+
"shell":{"commands":["REPLACE/WITH/REVIEW-CONTEXT/COMMAND"],"cwd":".","timeoutMs":300000}
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"id":"frontend-review-pi", "depends_on":["frontend-review-context-shell"], "complexity":"MED", "executor":"pi", "role":"reviewer", "writePolicy":"read-only", "allowedPaths":["REPLACE/WITH/FRONTEND/TARGET/PATH/**"], "forbiddenPaths":[".harness/**","artifacts/**"],
|
|
79
|
+
"outputContract":"Plain Markdown final review: approved result or explicit findings for contract coverage, regression risk, accessibility, and verification gaps; no file writes.",
|
|
80
|
+
"subtask_prompt":"Review the actual diff and archived verification evidence against the frontend contract. Do not treat missing, weak, mock-only, or not-run evidence as passed. Report explicit residual risks; do not edit files."
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"id":"frontend-closeout-shell", "depends_on":["frontend-review-pi"], "complexity":"LOW", "executor":"shell", "role":"closeout", "writePolicy":"read-only", "allowedPaths":["REPLACE/WITH/FRONTEND/TARGET/PATH/**"], "forbiddenPaths":[".harness/**","artifacts/**"],
|
|
84
|
+
"outputContract":"Archived deterministic closeout summarizing contract coverage, verification status, review findings, evidence gaps, and residual risks; no worktree writes.",
|
|
85
|
+
"subtask_prompt":"Run the project-specific deterministic closeout renderer. Preserve evidence status exactly: passed, failed, not-run, mock-backed, and baseline debt must never be promoted to a stronger result.",
|
|
86
|
+
"shell":{"commands":["REPLACE/WITH/CLOSEOUT/COMMAND"],"cwd":".","timeoutMs":300000}
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
package/package.json
CHANGED
|
@@ -91,7 +91,7 @@ loop-agent dag execute --dag <temp-dir>/hybrid-dag.json --init-only --canvas-pat
|
|
|
91
91
|
- **Source binding / recovery**:新生成 DAG 在顶层冻结 `sourceBinding`(任务源相对路径、SHA-256、显式 `REQ/BR/AC`)。前端计划在存在显式编号时经过 `frontend-requirement-coverage-shell`;修订计划为 primary,只在条件分支未产生输出时 fallback 到原计划。主来源存在但缺号时仍在 writer 前 fail closed。中断后重新生成完整 DAG,不要从二手摘要拼接 impl-only DAG;v3 孤立 exclusive writer 若无 `sourceBinding` 且没有只读 planner 上游,会被 strict governance 拒绝。
|
|
92
92
|
- **勿宣称 live smoke 已通过**,除非真实 `dag execute` execution 中 Pi read-only、Pi writer、显式 Cursor 或 shell node 均按 DAG 完成。
|
|
93
93
|
|
|
94
|
-
可复用 template:`docs/templates/agent-dag.base.json`(model 生成 DAG 的首选 base template)、`docs/templates/agent-dag.schema.json`(JSON Schema)、`docs/templates/agent-dag.supervised-implementation.json`(supervised implementation:writeSet audit、soft/hard verify、process supervisor、repair、review verdict gate)、`docs/templates/backend-test-dag.json`(后端测试专用模板)、`docs/templates/frontend-test-dag.json`(FE-test RAG:Markdown case manifest、串行 Playwright CLI case 子节点与逐 case 证据)、`docs/templates/agent-dag-process-supervisor.prompt.md`、`docs/templates/agent-dag-review-verdict.prompt.md`、`docs/templates/agent-dag-authority-surface-audit.prompt.md`(可选 authority surface verifier;authority signal 或显式 enablement 匹配时由 `dag init-hybrid` 插入)、`examples/hybrid-loop-agent-dag.json`、`docs/templates/hybrid-dag.json`。
|
|
94
|
+
可复用 template:`docs/templates/agent-dag.base.json`(model 生成 DAG 的首选 base template)、`docs/templates/agent-dag.schema.json`(JSON Schema)、`docs/templates/agent-dag.supervised-implementation.json`(supervised implementation:writeSet audit、soft/hard verify、process supervisor、repair、review verdict gate)、`docs/templates/frontend-implementation-dag.json`(自包含的前端实现完整默认链:contract、scout、plan、design policy/review、writer admission、implement、verify、review context/review、closeout;不依赖 runtime builder 或其他模板,执行前替换全部 `REPLACE/WITH/...` 值)、`docs/templates/backend-test-dag.json`(后端测试专用模板)、`docs/templates/frontend-test-dag.json`(FE-test RAG:Markdown case manifest、串行 Playwright CLI case 子节点与逐 case 证据)、`docs/templates/agent-dag-process-supervisor.prompt.md`、`docs/templates/agent-dag-review-verdict.prompt.md`、`docs/templates/agent-dag-authority-surface-audit.prompt.md`(可选 authority surface verifier;authority signal 或显式 enablement 匹配时由 `dag init-hybrid` 插入)、`examples/hybrid-loop-agent-dag.json`、`docs/templates/hybrid-dag.json`。
|
|
95
95
|
|
|
96
96
|
### Supervised implementation flow(减少 operator 中途介入)
|
|
97
97
|
|