@tea-agent/loop-agent 0.16.15-beta.0 → 0.16.16
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 +58 -0
- package/dist/executors/pi-executor.js +0 -4
- package/dist/executors/shell-executor.js +10 -0
- package/dist/worker/delivery/git-transaction.js +4 -1
- package/dist/worker/materialize/harness-task-materializer.js +14 -1
- package/dist/worker/outcomes/adapters.js +26 -17
- package/dist/worker/outcomes/projector.js +21 -0
- package/dist/worker/run-task/run-task.js +8 -2
- package/dist/workflows/dag/backend-test-execution-contract.js +37 -6
- package/dist/workflows/dag/dynamic-runtime/map.js +33 -1
- package/dist/workflows/dag/dynamic-runtime/shared.js +25 -8
- package/dist/workflows/dag/frontend-test-result-contract.js +165 -0
- package/dist/workflows/dag/init-hybrid.js +153 -21
- package/dist/workflows/dag/node-execution.js +14 -0
- package/dist/workflows/dag/types.js +1 -0
- package/docs/templates/frontend-test-dag.generate-cases.prompt.md +18 -1
- package/docs/templates/frontend-test-dag.json +399 -9
- package/docs/templates/frontend-test-dag.retrospect.prompt.md +1 -1
- package/docs/templates/frontend-test-dag.review-cases.prompt.md +3 -1
- package/docs/templates/frontend-test-dag.review-execution.prompt.md +1 -1
- package/package.json +1 -1
|
@@ -2,22 +2,412 @@
|
|
|
2
2
|
"$schema": "./agent-dag.schema.json",
|
|
3
3
|
"version": 3,
|
|
4
4
|
"title": "Frontend test RAG DAG template",
|
|
5
|
-
"runtimeContract": {
|
|
5
|
+
"runtimeContract": {
|
|
6
|
+
"schemaVersion": 1,
|
|
7
|
+
"agentRuntime": "pi-only",
|
|
8
|
+
"repairWriterProtocol": "explicit-node-v1"
|
|
9
|
+
},
|
|
6
10
|
"objective": "Build a frontend test RAG package, generate Markdown cases, execute each case serially through playwright-cli, and retain browser evidence.",
|
|
7
11
|
"globalConstraints": [
|
|
8
12
|
"Do not generate pytest or Playwright source code.",
|
|
9
13
|
"Only use declared isolated test environments; production URLs and real credentials are blocked.",
|
|
10
|
-
"Every generated browser start command is playwright-cli open --browser=chrome --headed <base-url
|
|
14
|
+
"Every generated browser start command is playwright-cli open --browser=chrome --headed <base-url>; subsequent commands stay in that default session and must not use unverified named-session flags.",
|
|
15
|
+
"A frontend case review must emit VERDICT: pass before manifest materialization; request-revision blocks browser execution.",
|
|
11
16
|
"Case children execute serially. Persist each case result, logs and browser evidence before the next child starts.",
|
|
12
17
|
"A token threshold is a post-case stop check, not a model hard token cap; unstarted cases must be recorded as blocked: token-budget-exhausted."
|
|
13
18
|
],
|
|
14
19
|
"tasks": [
|
|
15
|
-
{
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
{
|
|
21
|
+
"id": "retrieve-frontend-test-context-pi",
|
|
22
|
+
"depends_on": [],
|
|
23
|
+
"executor": "pi",
|
|
24
|
+
"role": "planner",
|
|
25
|
+
"toolProfile": "write",
|
|
26
|
+
"complexity": "HIGH",
|
|
27
|
+
"writePolicy": "exclusive",
|
|
28
|
+
"writeSet": [
|
|
29
|
+
"testcase/frontend/rag/**"
|
|
30
|
+
],
|
|
31
|
+
"allowedPaths": [
|
|
32
|
+
"REPLACE/WITH/SOURCE/PATH/**",
|
|
33
|
+
"testcase/frontend/rag/**"
|
|
34
|
+
],
|
|
35
|
+
"forbiddenPaths": [
|
|
36
|
+
".harness/**",
|
|
37
|
+
"artifacts/**"
|
|
38
|
+
],
|
|
39
|
+
"outputContract": "RAG context.md and coverage-map.md.",
|
|
40
|
+
"subtask_prompt_markdown": "./frontend-test-dag.retrieve-context.prompt.md"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "generate-frontend-functional-cases-pi",
|
|
44
|
+
"depends_on": [
|
|
45
|
+
"retrieve-frontend-test-context-pi"
|
|
46
|
+
],
|
|
47
|
+
"executor": "pi",
|
|
48
|
+
"role": "implementer",
|
|
49
|
+
"toolProfile": "write",
|
|
50
|
+
"complexity": "HIGH",
|
|
51
|
+
"writePolicy": "exclusive",
|
|
52
|
+
"writeSet": [
|
|
53
|
+
"testcase/frontend/cases/**"
|
|
54
|
+
],
|
|
55
|
+
"allowedPaths": [
|
|
56
|
+
"testcase/frontend/rag/**",
|
|
57
|
+
"testcase/frontend/cases/**"
|
|
58
|
+
],
|
|
59
|
+
"forbiddenPaths": [
|
|
60
|
+
".harness/**",
|
|
61
|
+
"artifacts/**"
|
|
62
|
+
],
|
|
63
|
+
"outputContract": "Markdown cases, index.md and manifest.json schemaVersion 1; no test source code.",
|
|
64
|
+
"subtask_prompt_markdown": "./frontend-test-dag.generate-cases.prompt.md"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"id": "review-frontend-cases-pi",
|
|
68
|
+
"depends_on": [
|
|
69
|
+
"generate-frontend-functional-cases-pi"
|
|
70
|
+
],
|
|
71
|
+
"executor": "pi",
|
|
72
|
+
"role": "reviewer",
|
|
73
|
+
"complexity": "HIGH",
|
|
74
|
+
"writePolicy": "read-only",
|
|
75
|
+
"allowedPaths": [
|
|
76
|
+
"testcase/frontend/rag/**",
|
|
77
|
+
"testcase/frontend/cases/**"
|
|
78
|
+
],
|
|
79
|
+
"forbiddenPaths": [
|
|
80
|
+
".harness/**",
|
|
81
|
+
"artifacts/**"
|
|
82
|
+
],
|
|
83
|
+
"outputContract": "First line VERDICT: pass or VERDICT: request-revision; request-revision blocks manifest materialization; no writes.",
|
|
84
|
+
"subtask_prompt_markdown": "./frontend-test-dag.review-cases.prompt.md"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"id": "review-frontend-cases-gate-shell",
|
|
88
|
+
"depends_on": [
|
|
89
|
+
"review-frontend-cases-pi"
|
|
90
|
+
],
|
|
91
|
+
"executor": "shell",
|
|
92
|
+
"role": "verifier",
|
|
93
|
+
"complexity": "LOW",
|
|
94
|
+
"writePolicy": "read-only",
|
|
95
|
+
"allowedPaths": [
|
|
96
|
+
"testcase/frontend/rag/**",
|
|
97
|
+
"testcase/frontend/cases/**"
|
|
98
|
+
],
|
|
99
|
+
"forbiddenPaths": [
|
|
100
|
+
".harness/**",
|
|
101
|
+
"artifacts/**"
|
|
102
|
+
],
|
|
103
|
+
"outputContract": "Deterministic gate: block manifest materialization and browser execution unless review-frontend-cases-pi emits VERDICT: pass.",
|
|
104
|
+
"subtask_prompt": "Enforce the frontend case review verdict before manifest materialization.",
|
|
105
|
+
"shell": {
|
|
106
|
+
"commands": [],
|
|
107
|
+
"verdictGate": {
|
|
108
|
+
"fromNodeId": "review-frontend-cases-pi",
|
|
109
|
+
"accept": [
|
|
110
|
+
"VERDICT: pass"
|
|
111
|
+
],
|
|
112
|
+
"label": "frontend case review",
|
|
113
|
+
"lineMode": "first-verdict-line"
|
|
114
|
+
},
|
|
115
|
+
"cwd": ".",
|
|
116
|
+
"timeoutMs": 60000
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"id": "revise-frontend-cases-pi",
|
|
121
|
+
"depends_on": [
|
|
122
|
+
"review-frontend-cases-pi"
|
|
123
|
+
],
|
|
124
|
+
"runIf": "$.nodes['review-frontend-cases-pi'].firstVerdictLine == 'VERDICT: request-revision'",
|
|
125
|
+
"executor": "pi",
|
|
126
|
+
"role": "implementer",
|
|
127
|
+
"toolProfile": "write",
|
|
128
|
+
"complexity": "HIGH",
|
|
129
|
+
"writePolicy": "exclusive",
|
|
130
|
+
"writeSet": [
|
|
131
|
+
"testcase/frontend/cases/**"
|
|
132
|
+
],
|
|
133
|
+
"allowedPaths": [
|
|
134
|
+
"testcase/frontend/rag/**",
|
|
135
|
+
"testcase/frontend/cases/**"
|
|
136
|
+
],
|
|
137
|
+
"forbiddenPaths": [
|
|
138
|
+
".harness/**",
|
|
139
|
+
"artifacts/**"
|
|
140
|
+
],
|
|
141
|
+
"outputContract": "Apply the one permitted frontend case revision; no browser execution or evidence writes.",
|
|
142
|
+
"subtask_prompt": "Apply the sole allowed case revision from the first review. Change only testcase/frontend/cases/**; preserve AC traceability. Do not execute browsers or write evidence."
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"id": "review-frontend-cases-final-pi",
|
|
146
|
+
"depends_on": [
|
|
147
|
+
"revise-frontend-cases-pi"
|
|
148
|
+
],
|
|
149
|
+
"runIf": "$.nodes['review-frontend-cases-pi'].firstVerdictLine == 'VERDICT: request-revision'",
|
|
150
|
+
"executor": "pi",
|
|
151
|
+
"role": "reviewer",
|
|
152
|
+
"complexity": "HIGH",
|
|
153
|
+
"writePolicy": "read-only",
|
|
154
|
+
"allowedPaths": [
|
|
155
|
+
"testcase/frontend/rag/**",
|
|
156
|
+
"testcase/frontend/cases/**"
|
|
157
|
+
],
|
|
158
|
+
"forbiddenPaths": [
|
|
159
|
+
".harness/**",
|
|
160
|
+
"artifacts/**"
|
|
161
|
+
],
|
|
162
|
+
"outputContract": "First line VERDICT: pass or VERDICT: request-revision after the single allowed revision; no writes.",
|
|
163
|
+
"subtask_prompt_markdown": "./frontend-test-dag.review-cases.prompt.md"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"id": "final-frontend-case-review-gate-shell",
|
|
167
|
+
"depends_on": [
|
|
168
|
+
"review-frontend-cases-pi",
|
|
169
|
+
"review-frontend-cases-final-pi"
|
|
170
|
+
],
|
|
171
|
+
"executor": "shell",
|
|
172
|
+
"role": "verifier",
|
|
173
|
+
"complexity": "LOW",
|
|
174
|
+
"writePolicy": "read-only",
|
|
175
|
+
"allowedPaths": [
|
|
176
|
+
"testcase/frontend/rag/**",
|
|
177
|
+
"testcase/frontend/cases/**"
|
|
178
|
+
],
|
|
179
|
+
"forbiddenPaths": [
|
|
180
|
+
".harness/**",
|
|
181
|
+
"artifacts/**"
|
|
182
|
+
],
|
|
183
|
+
"outputContract": "Pass-only effective case review gate; final review takes precedence when revision ran.",
|
|
184
|
+
"subtask_prompt": "Enforce the frontend case review verdict before manifest materialization.",
|
|
185
|
+
"shell": {
|
|
186
|
+
"commands": [],
|
|
187
|
+
"verdictGate": {
|
|
188
|
+
"fromNodeId": "review-frontend-cases-final-pi",
|
|
189
|
+
"fallbackFromNodeIds": [
|
|
190
|
+
"review-frontend-cases-pi"
|
|
191
|
+
],
|
|
192
|
+
"accept": [
|
|
193
|
+
"VERDICT: pass"
|
|
194
|
+
],
|
|
195
|
+
"label": "effective frontend case review",
|
|
196
|
+
"lineMode": "first-verdict-line"
|
|
197
|
+
},
|
|
198
|
+
"cwd": ".",
|
|
199
|
+
"timeoutMs": 60000
|
|
200
|
+
},
|
|
201
|
+
"dependsPolicy": "all-or-condition-skip"
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"id": "materialize-frontend-case-manifest-shell",
|
|
205
|
+
"depends_on": [
|
|
206
|
+
"final-frontend-case-review-gate-shell"
|
|
207
|
+
],
|
|
208
|
+
"executor": "shell",
|
|
209
|
+
"role": "verifier",
|
|
210
|
+
"complexity": "LOW",
|
|
211
|
+
"writePolicy": "read-only",
|
|
212
|
+
"allowedPaths": [
|
|
213
|
+
"testcase/frontend/cases/**"
|
|
214
|
+
],
|
|
215
|
+
"forbiddenPaths": [
|
|
216
|
+
".harness/**",
|
|
217
|
+
"artifacts/**"
|
|
218
|
+
],
|
|
219
|
+
"outputContract": "Validated frontend manifest payload { cases: [...] }; shell command echo is permitted only as the prefix before exactly one final JSON line.",
|
|
220
|
+
"subtask_prompt": "Validate and materialize the generated frontend case manifest.",
|
|
221
|
+
"shell": {
|
|
222
|
+
"commands": [
|
|
223
|
+
"node -e \"const fs=require('fs'),path=require('path');const file='testcase/frontend/cases/manifest.json';if(!fs.existsSync(file))throw new Error('missing '+file);const manifest=JSON.parse(fs.readFileSync(file,'utf8'));if(manifest.schemaVersion!==1||!Array.isArray(manifest.cases))throw new Error('invalid frontend case manifest');const dims=new Set(['core','boundary','flow','backend']);const ids=new Set(),paths=new Set(),evidence=new Set();for(const c of manifest.cases){if(!c||typeof c.caseId!=='string'||!/^FE-[A-Za-z0-9][A-Za-z0-9-]*$/.test(c.caseId)||ids.has(c.caseId))throw new Error('invalid or duplicate caseId');ids.add(c.caseId);if(!dims.has(c.dimension))throw new Error('invalid dimension');if(!Array.isArray(c.acIds)||!c.acIds.length||c.acIds.some(a=>typeof a!=='string'||!a.trim()))throw new Error('invalid acIds');for(const key of ['casePath','evidenceDir']){const value=c[key];if(typeof value!=='string'||path.isAbsolute(value)||value.includes('..'))throw new Error('unsafe '+key);}if(c.casePath!=='testcase/frontend/cases/'+c.caseId+'.md')throw new Error('casePath must match caseId');if(!c.evidenceDir.startsWith('testcase/frontend/evidence/'+c.caseId+'/'))throw new Error('case path escapes frontend test roots');if(paths.has(c.casePath))throw new Error('duplicate casePath');paths.add(c.casePath);if(evidence.has(c.evidenceDir))throw new Error('duplicate evidenceDir');evidence.add(c.evidenceDir);}process.stdout.write(JSON.stringify({cases:manifest.cases}));\""
|
|
224
|
+
],
|
|
225
|
+
"cwd": ".",
|
|
226
|
+
"timeoutMs": 120000
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"id": "execute-frontend-cases-map",
|
|
231
|
+
"depends_on": [
|
|
232
|
+
"materialize-frontend-case-manifest-shell"
|
|
233
|
+
],
|
|
234
|
+
"executor": "static",
|
|
235
|
+
"role": "verifier",
|
|
236
|
+
"complexity": "LOW",
|
|
237
|
+
"writePolicy": "none",
|
|
238
|
+
"allowedPaths": [],
|
|
239
|
+
"forbiddenPaths": [
|
|
240
|
+
".harness/**",
|
|
241
|
+
"artifacts/**"
|
|
242
|
+
],
|
|
243
|
+
"outputContract": "Serial aggregate of browser case results.",
|
|
244
|
+
"subtask_prompt": "Expand the validated manifest into serial browser case children.",
|
|
245
|
+
"static": {
|
|
246
|
+
"resultMarkdown": "Frontend case map expansion barrier."
|
|
247
|
+
},
|
|
248
|
+
"dynamicExpansion": {
|
|
249
|
+
"type": "map_agent",
|
|
250
|
+
"workflowNodeId": "execute-frontend-cases-map",
|
|
251
|
+
"itemsFrom": "$.nodes['materialize-frontend-case-manifest-shell'].output.cases",
|
|
252
|
+
"itemName": "case",
|
|
253
|
+
"maxItems": 20,
|
|
254
|
+
"maxExpandedNodes": 20,
|
|
255
|
+
"childIdPrefix": "execute-frontend-case",
|
|
256
|
+
"tokenBudget": {
|
|
257
|
+
"maxTokensPerCase": 20000,
|
|
258
|
+
"maxTotalTokens": 200000
|
|
259
|
+
},
|
|
260
|
+
"childTask": {
|
|
261
|
+
"executor": "pi",
|
|
262
|
+
"role": "verifier",
|
|
263
|
+
"skills": [
|
|
264
|
+
"playwright-cli",
|
|
265
|
+
"webapp-testing"
|
|
266
|
+
],
|
|
267
|
+
"toolProfile": "write",
|
|
268
|
+
"complexity": "MED",
|
|
269
|
+
"subtaskPromptTemplate": "Execute {{case.caseId}} from {{case.casePath}} in a fresh Pi session. Use exactly this browser start command prefix: playwright-cli open --browser=chrome --headed <base-url>. Do not put session flags before open. Every later playwright-cli command must use that same default browser session; must not use -s=<case-id>, -s=, or any named-session flag because no session binding is verified. For every sub-scenario, record fixture/reset, UI reset, and a fresh snapshot before using element references. If the isolated environment is missing, write blocked evidence before any browser command; do not open or connect to a browser. Before returning, always persist {{case.evidenceDir}}execution.md and {{case.evidenceDir}}case-result.json. case-result.json must be JSON with matching caseId, status as passed, failed, or blocked, and evidencePaths array; a blocked result must include non-empty blockedReason and must never imply pass. Run a local deterministic validation before returning: node -e \"const fs=require('fs');const p='{{case.evidenceDir}}';const r=JSON.parse(fs.readFileSync(p+'case-result.json','utf8'));if(!fs.existsSync(p+'execution.md')||r.caseId!=='{{case.caseId}}'||!['passed','failed','blocked'].includes(r.status)||(r.status==='blocked'&&!(typeof r.blockedReason==='string'&&r.blockedReason.trim())))process.exit(1)\". Persist screenshots/trace/video/logs when actually available. execution.md must record the executed or blocked steps, base URL safety decision, fixture/reset and request-observation availability, and evidence file list. A business failed or blocked case is a recorded result, not a node failure. Close the session and return only compact JSON (<=1200 chars): {caseId,status,evidencePaths,errorSummary,tokens}.",
|
|
270
|
+
"outputContract": "Compact JSON <=1200 chars.",
|
|
271
|
+
"writePolicy": "exclusive",
|
|
272
|
+
"allowedPaths": [
|
|
273
|
+
"testcase/frontend/cases/{{case.caseId}}.md",
|
|
274
|
+
"testcase/frontend/rag/context.md",
|
|
275
|
+
"testcase/frontend/rag/coverage-map.md",
|
|
276
|
+
"testcase/frontend/evidence/{{case.caseId}}/**"
|
|
277
|
+
],
|
|
278
|
+
"forbiddenPaths": [
|
|
279
|
+
".harness/**",
|
|
280
|
+
"artifacts/**"
|
|
281
|
+
],
|
|
282
|
+
"writeSet": [
|
|
283
|
+
"testcase/frontend/evidence/{{case.caseId}}/**"
|
|
284
|
+
]
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"id": "validate-frontend-case-evidence-shell",
|
|
290
|
+
"depends_on": [
|
|
291
|
+
"execute-frontend-cases-map"
|
|
292
|
+
],
|
|
293
|
+
"executor": "shell",
|
|
294
|
+
"role": "verifier",
|
|
295
|
+
"complexity": "LOW",
|
|
296
|
+
"writePolicy": "read-only",
|
|
297
|
+
"allowedPaths": [
|
|
298
|
+
"testcase/frontend/cases/**",
|
|
299
|
+
"testcase/frontend/evidence/**"
|
|
300
|
+
],
|
|
301
|
+
"forbiddenPaths": [
|
|
302
|
+
".harness/**",
|
|
303
|
+
"artifacts/**"
|
|
304
|
+
],
|
|
305
|
+
"outputContract": "Deterministic validation that every manifest case has execution.md and valid matching case-result.json; blocked results require blockedReason.",
|
|
306
|
+
"subtask_prompt": "Validate all frontend case evidence before evidence review; fail closed on missing or malformed records.",
|
|
307
|
+
"shell": {
|
|
308
|
+
"commands": [
|
|
309
|
+
"node -e \"const fs=require('fs'),path=require('path');const file='testcase/frontend/cases/manifest.json';if(!fs.existsSync(file))throw new Error('missing '+file);const manifest=JSON.parse(fs.readFileSync(file,'utf8'));const statuses=new Set(['passed','failed','blocked']);let failed=false;for(const c of manifest.cases||[]){const dir=c&&c.evidenceDir;const execution=path.join(dir||'','execution.md'),resultPath=path.join(dir||'','case-result.json');if(!c||typeof c.caseId!=='string'||typeof dir!=='string'||path.isAbsolute(dir)||dir.includes('..')||!fs.existsSync(execution)||!fs.existsSync(resultPath)){failed=true;continue;}let r;try{r=JSON.parse(fs.readFileSync(resultPath,'utf8'));}catch{failed=true;continue;}if(!r||r.caseId!==c.caseId||!statuses.has(r.status)||!Array.isArray(r.evidencePaths)||(r.status==='blocked'&&!(typeof r.blockedReason==='string'&&r.blockedReason.trim())))failed=true;}if(failed)process.exit(1);\""
|
|
310
|
+
],
|
|
311
|
+
"cwd": ".",
|
|
312
|
+
"timeoutMs": 120000
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"id": "finalize-frontend-test-result-shell",
|
|
317
|
+
"depends_on": [
|
|
318
|
+
"validate-frontend-case-evidence-shell"
|
|
319
|
+
],
|
|
320
|
+
"executor": "shell",
|
|
321
|
+
"role": "verifier",
|
|
322
|
+
"complexity": "LOW",
|
|
323
|
+
"writePolicy": "read-only",
|
|
324
|
+
"allowedPaths": [
|
|
325
|
+
"testcase/frontend/cases/**",
|
|
326
|
+
"testcase/frontend/evidence/**"
|
|
327
|
+
],
|
|
328
|
+
"forbiddenPaths": [
|
|
329
|
+
".harness/**",
|
|
330
|
+
"artifacts/**"
|
|
331
|
+
],
|
|
332
|
+
"outputContract": "Run-owned hash-bound frontend-test-result-v1 from manifest and validated evidence.",
|
|
333
|
+
"subtask_prompt": "Materialize the authoritative frontend-test-result-v1; never consume Pi prose.",
|
|
334
|
+
"shell": {
|
|
335
|
+
"commands": [],
|
|
336
|
+
"jsonArtifactGate": {
|
|
337
|
+
"fromNodeId": "validate-frontend-case-evidence-shell",
|
|
338
|
+
"schemaId": "frontend-test-result-v1",
|
|
339
|
+
"artifactName": "frontend-test-result.json",
|
|
340
|
+
"outputDir": "contracts"
|
|
341
|
+
},
|
|
342
|
+
"cwd": ".",
|
|
343
|
+
"timeoutMs": 120000
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"id": "frontend-outcome-gate-shell",
|
|
348
|
+
"depends_on": [
|
|
349
|
+
"finalize-frontend-test-result-shell"
|
|
350
|
+
],
|
|
351
|
+
"executor": "shell",
|
|
352
|
+
"role": "verifier",
|
|
353
|
+
"complexity": "LOW",
|
|
354
|
+
"writePolicy": "read-only",
|
|
355
|
+
"allowedPaths": [],
|
|
356
|
+
"forbiddenPaths": [
|
|
357
|
+
".harness/**",
|
|
358
|
+
"artifacts/**"
|
|
359
|
+
],
|
|
360
|
+
"outputContract": "Pass only for a real passed frontend-test-result-v1.",
|
|
361
|
+
"subtask_prompt": "Fail closed unless the final frontend test result records a real pass.",
|
|
362
|
+
"shell": {
|
|
363
|
+
"commands": [
|
|
364
|
+
"test -n \"${HARNESS_DAG_RUN_DIR:-}\" || { echo \"missing HARNESS_DAG_RUN_DIR for frontend-test outcome gate\" >&2; exit 2; }; RESULT=\"${HARNESS_DAG_RUN_DIR}/contracts/frontend-test-result.json\"; test -f \"${RESULT}\" || { echo \"missing frontend-test result: ${RESULT}\" >&2; exit 2; }; node -e 'const fs=require(\"fs\");const r=JSON.parse(fs.readFileSync(process.argv[1],\"utf8\"));const ok=r.outcome===\"passed\"&&r.integrationMode===\"real\"&&Number(r.totals?.failed||0)===0&&Number(r.totals?.blocked||0)===0&&Array.isArray(r.acceptanceCoverage?.missing)&&r.acceptanceCoverage.missing.length===0;if(!ok)process.exit(1);' \"${RESULT}\""
|
|
365
|
+
],
|
|
366
|
+
"cwd": ".",
|
|
367
|
+
"timeoutMs": 60000
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
"id": "review-frontend-execution-pi",
|
|
372
|
+
"depends_on": [
|
|
373
|
+
"frontend-outcome-gate-shell"
|
|
374
|
+
],
|
|
375
|
+
"executor": "pi",
|
|
376
|
+
"role": "reviewer",
|
|
377
|
+
"complexity": "HIGH",
|
|
378
|
+
"writePolicy": "read-only",
|
|
379
|
+
"allowedPaths": [
|
|
380
|
+
"testcase/frontend/**"
|
|
381
|
+
],
|
|
382
|
+
"forbiddenPaths": [
|
|
383
|
+
".harness/**",
|
|
384
|
+
"artifacts/**"
|
|
385
|
+
],
|
|
386
|
+
"outputContract": "AC-to-case-to-browser-evidence review.",
|
|
387
|
+
"subtask_prompt_markdown": "./frontend-test-dag.review-execution.prompt.md"
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
"id": "frontend-test-retrospect-pi",
|
|
391
|
+
"depends_on": [
|
|
392
|
+
"review-frontend-execution-pi"
|
|
393
|
+
],
|
|
394
|
+
"executor": "pi",
|
|
395
|
+
"role": "closeout",
|
|
396
|
+
"toolProfile": "write",
|
|
397
|
+
"complexity": "MED",
|
|
398
|
+
"writePolicy": "exclusive",
|
|
399
|
+
"writeSet": [
|
|
400
|
+
"testcase/frontend/reports/**"
|
|
401
|
+
],
|
|
402
|
+
"allowedPaths": [
|
|
403
|
+
"testcase/frontend/**"
|
|
404
|
+
],
|
|
405
|
+
"forbiddenPaths": [
|
|
406
|
+
".harness/**",
|
|
407
|
+
"artifacts/**"
|
|
408
|
+
],
|
|
409
|
+
"outputContract": "Write testcase/frontend/reports/frontend-test-retrospect-<date>.md with coverage, pass/fail/blocked, risks, findings, and A/B/C/D rating.",
|
|
410
|
+
"subtask_prompt": "Write the frontend test retrospective under testcase/frontend/reports/. Summarize coverage, passed/failed/blocked cases (including token-budget-exhausted), review findings, browser anomalies, residual risks, and A/B/C/D rating. Blocked cases never count as passed. Do not write docs/**."
|
|
411
|
+
}
|
|
22
412
|
]
|
|
23
413
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
# Frontend test retrospective
|
|
2
2
|
|
|
3
|
-
Write a dated report under `
|
|
3
|
+
Write a dated report under `testcase/frontend/reports/` covering case coverage, passed/failed/blocked results, review findings, browser anomalies, residual risks, and an A/B/C/D maturity rating. Cite the deterministic case-evidence validation outcome. Blocked cases never count as passed; a missing or malformed `execution.md` / `case-result.json` is a verification gap, not a pass. Do not write under `docs/**`.
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
# Review frontend cases
|
|
2
2
|
|
|
3
|
-
Read the RAG files and Markdown cases only. First line must be `VERDICT: pass` or `VERDICT: request-revision`. Report AC coverage, case independence, evidence completeness, unsafe environment/data dependencies, and manifest issues.
|
|
3
|
+
Read the RAG files and Markdown cases only. First line must be `VERDICT: pass` or `VERDICT: request-revision`. Report AC coverage, case independence, evidence completeness, unsafe environment/data dependencies, and manifest issues. This verdict is a deterministic safety gate: `request-revision` blocks manifest materialization and browser execution.
|
|
4
|
+
|
|
5
|
+
Every case must retain the exact browser-start command prefix `playwright-cli open --browser=chrome --headed <base-url>`; session flags must not precede `open`, and subsequent commands must remain in its default session without `-s=` or assumed named-session binding. Verify every executable sub-scenario specifies fixture/reset, UI reset, fresh snapshot before element refs, and an evidence write point. Verify each case requires both `execution.md` and `case-result.json` under its own evidence directory. The JSON result must contain matching `caseId`, `status` (`passed`, `failed`, or `blocked`) and `evidencePaths`; blocked cases must name a non-empty `blockedReason` and cannot count as passed.
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
# Review frontend execution evidence
|
|
2
2
|
|
|
3
|
-
Review AC → case → browser-evidence traceability. A passed case needs assertions and screenshot or equivalent browser evidence. Failed and blocked cases need an explicit cause. Treat `token-budget-exhausted` as blocked; do not substitute model conclusions or static checks for browser evidence.
|
|
3
|
+
Review AC → case → browser-evidence traceability only after deterministic evidence validation. Each manifest case must have `execution.md` and a valid `case-result.json` with matching `caseId`, `status`, and `evidencePaths`; `blocked` requires a non-empty `blockedReason`. A passed case additionally needs assertions and screenshot or equivalent browser evidence. Failed and blocked cases need an explicit cause. Treat `token-budget-exhausted` as blocked; do not substitute model conclusions or static checks for browser evidence.
|