@tea-agent/loop-agent 0.19.0 → 0.20.1-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 +19 -0
- package/dist/application/dag/generate-task-dag.js +12 -1
- package/dist/executors/shell-executor.js +189 -2
- package/dist/worker/observe/spec-evidence.js +33 -0
- package/dist/worker/observe/static/views/dag-inspector.js +67 -4
- package/dist/workflows/dag/backend-test-markdown-workflow.js +163 -41
- package/dist/workflows/dag/backend-test-result-contract.js +30 -7
- package/dist/workflows/dag/frontend-prewrite-gate.js +172 -0
- package/dist/workflows/dag/frontend-project-capability.js +6 -2
- package/dist/workflows/dag/frontend-repair.js +7 -1
- package/dist/workflows/dag/frontend-review-context.js +43 -0
- package/dist/workflows/dag/frontend-verification-trace.js +34 -15
- package/dist/workflows/dag/governance-profile.js +14 -6
- package/dist/workflows/dag/init-hybrid.js +144 -399
- package/dist/workflows/dag/types.js +33 -0
- package/dist/workflows/dag/validate.js +22 -1
- package/docs/README.md +1 -0
- package/docs/templates/agent-dag.schema.json +40 -0
- package/docs/templates/backend-test-dag.generate-pytest.prompt.md +23 -192
- package/docs/templates/backend-test-dag.json +8 -8
- package/docs/templates/backend-test-dag.review-cases.prompt.md +22 -75
- package/package.json +1 -1
- package/skills/frontend-design-review/SKILL.md +5 -3
- package/skills/frontend-design-review/references/review-checklist.md +3 -2
- package/skills/frontend-implementation/references/design-spec.md +16 -8
- package/skills/frontend-implementation/references/node-contracts.md +6 -8
- package/skills/frontend-review/SKILL.md +12 -9
- package/skills/frontend-review/references/review-findings.md +5 -1
- package/skills/frontend-verification/SKILL.md +5 -3
- package/skills/frontend-verification/references/verification-checklist.md +3 -2
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { writeDagRunJsonArtifact } from "../../infrastructure/harness/artifact-store.js";
|
|
4
|
+
import { formatFrontendWorktreeDiffStdout, runFrontendWorktreeDiffGate, } from "./frontend-worktree-diff.js";
|
|
5
|
+
export const FRONTEND_REVIEW_CONTEXT_SCHEMA_ID = "frontend-review-context-v1";
|
|
6
|
+
async function readRequiredJson(runDir, relativePath) {
|
|
7
|
+
try {
|
|
8
|
+
return JSON.parse(await readFile(path.join(runDir, relativePath), "utf8"));
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
throw new Error(`frontend review context missing or invalid ${relativePath}`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export async function runFrontendReviewContextGate(input) {
|
|
15
|
+
const diff = await runFrontendWorktreeDiffGate(input);
|
|
16
|
+
const contract = await readRequiredJson(input.runDir, "contracts/frontend-implementation-contract.json");
|
|
17
|
+
const verificationTrace = await readRequiredJson(input.runDir, "contracts/frontend-verification-trace.json");
|
|
18
|
+
const repairAssessment = await readRequiredJson(input.runDir, "contracts/frontend-repair-assessment.json");
|
|
19
|
+
const payload = {
|
|
20
|
+
schemaVersion: 1,
|
|
21
|
+
schemaId: FRONTEND_REVIEW_CONTEXT_SCHEMA_ID,
|
|
22
|
+
contract,
|
|
23
|
+
verificationTrace,
|
|
24
|
+
repairAssessment,
|
|
25
|
+
diff: {
|
|
26
|
+
schemaId: diff.schemaId,
|
|
27
|
+
patchPath: diff.patchPath,
|
|
28
|
+
patchSha256: diff.patchSha256,
|
|
29
|
+
changedFiles: diff.changedFiles,
|
|
30
|
+
untrackedFiles: diff.untrackedFiles,
|
|
31
|
+
empty: diff.empty,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
const artifactPath = await writeDagRunJsonArtifact(input.runDir, "contracts/frontend-review-context.json", payload);
|
|
35
|
+
return { ok: true, artifactPath, diff };
|
|
36
|
+
}
|
|
37
|
+
export function formatFrontendReviewContextStdout(result) {
|
|
38
|
+
return [
|
|
39
|
+
"Frontend review context: pass",
|
|
40
|
+
`Artifact: ${result.artifactPath}`,
|
|
41
|
+
formatFrontendWorktreeDiffStdout(result.diff),
|
|
42
|
+
].join("\n");
|
|
43
|
+
}
|
|
@@ -95,26 +95,45 @@ export async function runFrontendVerificationTraceGate(input) {
|
|
|
95
95
|
}
|
|
96
96
|
throw new Error(`trace: missing static/behavior verify node records (tried ${errors.join(", ")})`);
|
|
97
97
|
}
|
|
98
|
-
const staticNode =
|
|
99
|
-
|
|
98
|
+
const staticNode = input.evidence
|
|
99
|
+
? {
|
|
100
|
+
nodeId: input.evidence.static.nodeId,
|
|
101
|
+
record: {
|
|
102
|
+
status: "FINISHED",
|
|
103
|
+
verifyEvidence: { commandLabels: input.evidence.static.commandLabels },
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
: await loadFirstNode(staticCandidates);
|
|
107
|
+
const behaviorNode = input.evidence
|
|
108
|
+
? {
|
|
109
|
+
nodeId: input.evidence.behavior.nodeId,
|
|
110
|
+
record: {
|
|
111
|
+
status: "FINISHED",
|
|
112
|
+
verifyEvidence: { commandLabels: input.evidence.behavior.commandLabels },
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
: await loadFirstNode(behaviorCandidates);
|
|
100
116
|
assertNodeFinished(staticNode.nodeId, staticNode.record);
|
|
101
117
|
assertNodeFinished(behaviorNode.nodeId, behaviorNode.record);
|
|
102
118
|
const labelOwners = collectCommandLabels({ nodeId: staticNode.nodeId, record: staticNode.record }, { nodeId: behaviorNode.nodeId, record: behaviorNode.record });
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
119
|
+
// Legacy runs may still carry a standalone Mock assessment. New slim runs
|
|
120
|
+
// bind the strategy directly through the validated implementation contract.
|
|
121
|
+
if (!input.evidence) {
|
|
122
|
+
const assessPath = path.join(input.runDir, "frontend-mock-assess-pi.json");
|
|
123
|
+
try {
|
|
124
|
+
const assess = (await readJsonFile(assessPath));
|
|
125
|
+
const text = assess.assistantText?.trim() || assess.stdout?.trim() || "";
|
|
126
|
+
const strategy = parseMockStrategyFromAssess(text);
|
|
127
|
+
if (strategy && strategy !== contract.mockApi.strategy) {
|
|
128
|
+
throw new Error(`trace: mock strategy mismatch: assess=${strategy} contract=${contract.mockApi.strategy}`);
|
|
129
|
+
}
|
|
110
130
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
131
|
+
catch (error) {
|
|
132
|
+
if (error instanceof Error &&
|
|
133
|
+
error.message.startsWith("trace: mock strategy mismatch")) {
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
116
136
|
}
|
|
117
|
-
// assess optional for pure unit fixtures without mock node
|
|
118
137
|
}
|
|
119
138
|
const targets = [];
|
|
120
139
|
const hardIssues = [];
|
|
@@ -104,16 +104,24 @@ function writeSetMatchesAnyPattern(writeSet, patterns) {
|
|
|
104
104
|
return undefined;
|
|
105
105
|
}
|
|
106
106
|
function shellCommandLooksDeterministic(command) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
const normalized = command.replace(/["']/g, "");
|
|
108
|
+
return (/\bnpx vitest run\b/.test(normalized) ||
|
|
109
|
+
/\bnpm (?:run )?(?:lint|typecheck|test)\b/.test(normalized) ||
|
|
110
|
+
/check-repo\.sh/.test(normalized) ||
|
|
111
|
+
/\bshell\.preset\b/.test(normalized) ||
|
|
112
|
+
/loop-agent-standard-verify/.test(normalized));
|
|
112
113
|
}
|
|
113
114
|
function taskHasDeterministicShellVerification(task) {
|
|
114
115
|
if (task.executor !== "shell")
|
|
115
116
|
return false;
|
|
116
|
-
const
|
|
117
|
+
const bundle = task.shell?.frontendVerificationBundle;
|
|
118
|
+
const commands = bundle
|
|
119
|
+
? [
|
|
120
|
+
...bundle.mockCommands,
|
|
121
|
+
...bundle.staticCommands,
|
|
122
|
+
...bundle.behaviorCommands,
|
|
123
|
+
]
|
|
124
|
+
: resolveShellCommands(task.shell ?? { commands: [] });
|
|
117
125
|
return commands.some(shellCommandLooksDeterministic);
|
|
118
126
|
}
|
|
119
127
|
function collectExclusiveWriters(spec) {
|