@tea-agent/loop-agent 0.29.0 → 0.29.1
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 +29 -0
- package/dist/commands/init.js +1 -1
- package/dist/executors/dag-pi-executor.js +3 -1
- package/dist/executors/pi-playwright-cli-tool.js +14 -8
- package/dist/executors/shell-executor.js +153 -0
- package/dist/task/config-types.js +21 -0
- package/dist/workflows/dag/frontend-test-case-checklist.js +94 -15
- package/dist/workflows/dag/frontend-test-case-manifest.js +104 -0
- package/dist/workflows/dag/frontend-test-html-report.js +106 -24
- package/dist/workflows/dag/frontend-test-result-contract.js +3 -0
- package/dist/workflows/dag/init-hybrid.js +167 -102
- package/dist/workflows/dag/types.js +40 -0
- package/dist/workflows/dag/validate.js +4 -1
- package/docs/templates/README.md +1 -0
- package/docs/templates/agent-dag.schema.json +6 -1
- package/docs/templates/frontend-test-case-checklist.md +1 -1
- package/docs/templates/frontend-test-dag.generate-cases.prompt.md +9 -1
- package/docs/templates/frontend-test-dag.json +125 -267
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +7 -1
- package/docs/templates/frontend-test-dag.review-cases.prompt.md +1 -1
- package/docs/templates/frontend-test-standard-scenarios.v1.json +114 -0
- package/package.json +1 -1
- package/skills/playwright-cli/SKILL.md +1 -1
- package/skills/playwright-cli-case-generator/SKILL.md +1 -1
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
"agentRuntime": "pi-only",
|
|
8
8
|
"repairWriterProtocol": "explicit-node-v1"
|
|
9
9
|
},
|
|
10
|
-
"objective": "Build
|
|
10
|
+
"objective": "Build frontend test RAG package, generate Markdown cases, checklist+manifest, execute serially via playwright-cli, finalize frontend-test-result-v1, and write main HTML report (pipeline acceptance).",
|
|
11
11
|
"globalConstraints": [
|
|
12
12
|
"Do not generate pytest or Playwright source code.",
|
|
13
13
|
"Only use declared isolated test environments; production URLs and real credentials are blocked.",
|
|
14
|
-
"Every generated browser start command uses playwright-cli open --browser=chrome --
|
|
14
|
+
"Every generated browser start command uses playwright-cli open --browser=chrome --headless followed by the concrete controller-resolved URL (from task source config.md when present, else http://localhost:5173); executable case lines never retain an angle-bracket URL/ref placeholder; subsequent commands stay in that default session and must not use unverified named-session flags.",
|
|
15
15
|
"Case children execute serially. Persist each case result, logs and browser evidence before the next child starts.",
|
|
16
16
|
"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.",
|
|
17
|
-
"Default pipeline acceptance is the final
|
|
17
|
+
"Default pipeline acceptance is the final frontend-test-result-v1 plus testcase/frontend/reports/frontend-test-report.html",
|
|
18
18
|
"Default frontendTest.reviewMode=off uses mechanical checklist-shell before materialize; set reviewMode=blocking for legacy dual LLM review gate.",
|
|
19
19
|
"playwright-cli-only: generators and executors may call only skill-declared playwright-cli commands; bare playwright / npx playwright / @playwright/test / Playwright source are forbidden with no native Playwright fallback.",
|
|
20
20
|
"Browser-tool preflight (preflight-frontend-browser-tool-shell) must reject CODE_AGENT_PI_BACKEND=cli-only, verify the Pi SDK structured custom-tool surface, freeze baseUrl from hash-bound task source config.md (or controller default localhost), and confirm the verified playwright-cli launcher + --help contract before any frontend-test Pi node; missing capability/CLI fails with zero Pi calls.",
|
|
@@ -47,34 +47,10 @@
|
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
|
-
"id": "
|
|
50
|
+
"id": "prepare-frontend-test-package-shell",
|
|
51
51
|
"depends_on": [
|
|
52
52
|
"preflight-frontend-browser-tool-shell"
|
|
53
53
|
],
|
|
54
|
-
"executor": "pi",
|
|
55
|
-
"role": "planner",
|
|
56
|
-
"toolProfile": "write",
|
|
57
|
-
"complexity": "MED",
|
|
58
|
-
"writePolicy": "exclusive",
|
|
59
|
-
"writeSet": [
|
|
60
|
-
"testcase/frontend/rag/**"
|
|
61
|
-
],
|
|
62
|
-
"allowedPaths": [
|
|
63
|
-
"REPLACE/WITH/SOURCE/PATH/**",
|
|
64
|
-
"testcase/frontend/rag/**"
|
|
65
|
-
],
|
|
66
|
-
"forbiddenPaths": [
|
|
67
|
-
".harness/**",
|
|
68
|
-
"artifacts/**"
|
|
69
|
-
],
|
|
70
|
-
"outputContract": "RAG context.md and coverage-map.md with baseUrl, baseUrlSource, environmentProbe=pending.",
|
|
71
|
-
"subtask_prompt_markdown": "./frontend-test-dag.retrieve-context.prompt.md"
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
"id": "materialize-frontend-test-execution-shell",
|
|
75
|
-
"depends_on": [
|
|
76
|
-
"retrieve-frontend-test-context-pi"
|
|
77
|
-
],
|
|
78
54
|
"executor": "shell",
|
|
79
55
|
"role": "verifier",
|
|
80
56
|
"complexity": "LOW",
|
|
@@ -89,101 +65,75 @@
|
|
|
89
65
|
".harness/**",
|
|
90
66
|
"artifacts/**"
|
|
91
67
|
],
|
|
92
|
-
"outputContract": "
|
|
93
|
-
"subtask_prompt": "
|
|
68
|
+
"outputContract": "Write testcase/frontend/rag/standard-scenarios.v1.json for generate-time standard scenario coverage.",
|
|
69
|
+
"subtask_prompt": "Materialize frontend-test standard scenarios v1 into the RAG package.",
|
|
94
70
|
"shell": {
|
|
95
71
|
"commands": [
|
|
96
|
-
"node -e \"
|
|
72
|
+
"node -e \"const fs=require('fs'),path=require('path');const dest='testcase/frontend/rag/standard-scenarios.v1.json';const candidates=[path.join('docs','templates','frontend-test-standard-scenarios.v1.json')];let src=null;for(const c of candidates){if(fs.existsSync(c)){src=c;break;}}fs.mkdirSync(path.dirname(dest),{recursive:true});if(src){fs.copyFileSync(src,dest);process.stdout.write(JSON.stringify({status:'copied',from:src,to:dest}));}else{const minimal={schemaVersion:1,id:'frontend-test-standard-scenarios-v1',scenarios:[{id:'STD-FE-SMOKE-ENTRY',title:'入口可打开',category:'smoke',priority:'must',testPoints:['open'],minCases:1}]};fs.writeFileSync(dest,JSON.stringify(minimal,null,2)+'\\n');process.stdout.write(JSON.stringify({status:'fallback',to:dest}));}"
|
|
97
73
|
],
|
|
98
74
|
"cwd": ".",
|
|
99
75
|
"timeoutMs": 60000
|
|
100
76
|
}
|
|
101
77
|
},
|
|
102
78
|
{
|
|
103
|
-
"id": "
|
|
79
|
+
"id": "retrieve-frontend-test-context-pi",
|
|
104
80
|
"depends_on": [
|
|
105
|
-
"
|
|
81
|
+
"prepare-frontend-test-package-shell"
|
|
106
82
|
],
|
|
107
83
|
"executor": "pi",
|
|
108
|
-
"role": "
|
|
84
|
+
"role": "planner",
|
|
109
85
|
"toolProfile": "write",
|
|
110
|
-
"complexity": "
|
|
86
|
+
"complexity": "MED",
|
|
111
87
|
"writePolicy": "exclusive",
|
|
112
88
|
"writeSet": [
|
|
113
|
-
"testcase/frontend/
|
|
114
|
-
"testcase/frontend/cases/index.md",
|
|
115
|
-
"testcase/frontend/cases/manifest.draft.json"
|
|
116
|
-
],
|
|
117
|
-
"allowedPaths": [
|
|
118
|
-
"testcase/frontend/rag/**",
|
|
119
|
-
"testcase/frontend/cases/**"
|
|
120
|
-
],
|
|
121
|
-
"forbiddenPaths": [
|
|
122
|
-
".harness/**",
|
|
123
|
-
"artifacts/**"
|
|
124
|
-
],
|
|
125
|
-
"outputContract": "Markdown cases, index.md and manifest.draft.json schemaVersion 1 only; the exclusive materializer promotes the validated draft to manifest.json, and this generator must not write manifest.json; no test source code.",
|
|
126
|
-
"subtask_prompt_markdown": "./frontend-test-dag.generate-cases.prompt.md"
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
"id": "review-frontend-cases-pi",
|
|
130
|
-
"depends_on": [
|
|
131
|
-
"generate-frontend-functional-cases-pi"
|
|
89
|
+
"testcase/frontend/rag/**"
|
|
132
90
|
],
|
|
133
|
-
"executor": "pi",
|
|
134
|
-
"role": "reviewer",
|
|
135
|
-
"complexity": "HIGH",
|
|
136
|
-
"writePolicy": "read-only",
|
|
137
91
|
"allowedPaths": [
|
|
138
|
-
"
|
|
139
|
-
"testcase/frontend/
|
|
92
|
+
"REPLACE/WITH/SOURCE/PATH/**",
|
|
93
|
+
"testcase/frontend/rag/**"
|
|
140
94
|
],
|
|
141
95
|
"forbiddenPaths": [
|
|
142
96
|
".harness/**",
|
|
143
97
|
"artifacts/**"
|
|
144
98
|
],
|
|
145
|
-
"outputContract": "
|
|
146
|
-
"subtask_prompt_markdown": "./frontend-test-dag.
|
|
99
|
+
"outputContract": "RAG context.md and coverage-map.md with baseUrl, baseUrlSource, environmentProbe=pending.",
|
|
100
|
+
"subtask_prompt_markdown": "./frontend-test-dag.retrieve-context.prompt.md",
|
|
101
|
+
"writeGuardPolicy": "tools-only"
|
|
147
102
|
},
|
|
148
103
|
{
|
|
149
|
-
"id": "
|
|
104
|
+
"id": "materialize-frontend-test-execution-shell",
|
|
150
105
|
"depends_on": [
|
|
151
|
-
"
|
|
106
|
+
"retrieve-frontend-test-context-pi"
|
|
152
107
|
],
|
|
153
108
|
"executor": "shell",
|
|
154
109
|
"role": "verifier",
|
|
155
110
|
"complexity": "LOW",
|
|
156
|
-
"writePolicy": "
|
|
111
|
+
"writePolicy": "exclusive",
|
|
112
|
+
"writeSet": [
|
|
113
|
+
"testcase/frontend/rag/**"
|
|
114
|
+
],
|
|
157
115
|
"allowedPaths": [
|
|
158
|
-
"testcase/frontend/rag/**"
|
|
159
|
-
"testcase/frontend/cases/**"
|
|
116
|
+
"testcase/frontend/rag/**"
|
|
160
117
|
],
|
|
161
118
|
"forbiddenPaths": [
|
|
162
119
|
".harness/**",
|
|
163
120
|
"artifacts/**"
|
|
164
121
|
],
|
|
165
|
-
"outputContract": "
|
|
166
|
-
"subtask_prompt": "
|
|
122
|
+
"outputContract": "Fail-closed environment preflight: absolute non-production baseUrl + curl HTTP reachability; writes environmentProbe facts; unreachable => blockedReason frontend-base-url-unreachable (node ERROR so generate/map do not run).",
|
|
123
|
+
"subtask_prompt": "Parse frozen baseUrl from context.md (config.md preferred, else http://localhost:5173). Reject production / non-http(s). Probe with curl (HEAD then GET fallback; connect/max-time; no auth/cookie). 2xx/3xx => reachable and continue. 4xx/5xx/DNS/timeout/connection refused/TLS => blockedReason frontend-base-url-unreachable. Missing curl => blockedReason curl-unavailable. Do not start the app. Runtime hybrid generator embeds the authoritative probe script.",
|
|
167
124
|
"shell": {
|
|
168
|
-
"commands": [
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
"accept": [
|
|
172
|
-
"VERDICT: pass"
|
|
173
|
-
],
|
|
174
|
-
"label": "frontend case review",
|
|
175
|
-
"lineMode": "first-verdict-line"
|
|
176
|
-
},
|
|
125
|
+
"commands": [
|
|
126
|
+
"node -e \"console.log('template placeholder: runtime hybrid DAG embeds curl preflight; do not use this static command as source of truth')\""
|
|
127
|
+
],
|
|
177
128
|
"cwd": ".",
|
|
178
129
|
"timeoutMs": 60000
|
|
179
130
|
}
|
|
180
131
|
},
|
|
181
132
|
{
|
|
182
|
-
"id": "
|
|
133
|
+
"id": "generate-frontend-functional-cases-pi",
|
|
183
134
|
"depends_on": [
|
|
184
|
-
"
|
|
135
|
+
"materialize-frontend-test-execution-shell"
|
|
185
136
|
],
|
|
186
|
-
"runIf": "$.nodes['review-frontend-cases-pi'].firstVerdictLine == 'VERDICT: request-revision'",
|
|
187
137
|
"executor": "pi",
|
|
188
138
|
"role": "implementer",
|
|
189
139
|
"toolProfile": "write",
|
|
@@ -202,119 +152,35 @@
|
|
|
202
152
|
".harness/**",
|
|
203
153
|
"artifacts/**"
|
|
204
154
|
],
|
|
205
|
-
"outputContract": "
|
|
206
|
-
"
|
|
207
|
-
|
|
208
|
-
{
|
|
209
|
-
"id": "review-frontend-cases-final-pi",
|
|
210
|
-
"depends_on": [
|
|
211
|
-
"revise-frontend-cases-pi"
|
|
212
|
-
],
|
|
213
|
-
"runIf": "$.nodes['review-frontend-cases-pi'].firstVerdictLine == 'VERDICT: request-revision'",
|
|
214
|
-
"executor": "pi",
|
|
215
|
-
"role": "reviewer",
|
|
216
|
-
"complexity": "HIGH",
|
|
217
|
-
"writePolicy": "read-only",
|
|
218
|
-
"allowedPaths": [
|
|
219
|
-
"testcase/frontend/rag/**",
|
|
220
|
-
"testcase/frontend/cases/**"
|
|
221
|
-
],
|
|
222
|
-
"forbiddenPaths": [
|
|
223
|
-
".harness/**",
|
|
224
|
-
"artifacts/**"
|
|
225
|
-
],
|
|
226
|
-
"outputContract": "First line VERDICT: pass or VERDICT: request-revision after the single allowed revision; no writes.",
|
|
227
|
-
"subtask_prompt_markdown": "./frontend-test-dag.review-cases.prompt.md"
|
|
155
|
+
"outputContract": "Markdown cases, index.md and manifest.draft.json schemaVersion 1 only; the exclusive materializer promotes the validated draft to manifest.json, and this generator must not write manifest.json; no test source code.",
|
|
156
|
+
"subtask_prompt_markdown": "./frontend-test-dag.generate-cases.prompt.md",
|
|
157
|
+
"writeGuardPolicy": "tools-only"
|
|
228
158
|
},
|
|
229
159
|
{
|
|
230
|
-
"id": "
|
|
160
|
+
"id": "checklist-and-materialize-manifest-shell",
|
|
231
161
|
"depends_on": [
|
|
232
|
-
"
|
|
233
|
-
"review-frontend-cases-final-pi"
|
|
162
|
+
"generate-frontend-functional-cases-pi"
|
|
234
163
|
],
|
|
235
|
-
"executor": "shell",
|
|
236
164
|
"role": "verifier",
|
|
237
|
-
"complexity": "LOW",
|
|
238
|
-
"writePolicy": "read-only",
|
|
239
|
-
"allowedPaths": [
|
|
240
|
-
"testcase/frontend/rag/**",
|
|
241
|
-
"testcase/frontend/cases/**"
|
|
242
|
-
],
|
|
243
|
-
"forbiddenPaths": [
|
|
244
|
-
".harness/**",
|
|
245
|
-
"artifacts/**"
|
|
246
|
-
],
|
|
247
|
-
"outputContract": "Pass-only effective case review gate; final review takes precedence when revision ran.",
|
|
248
|
-
"subtask_prompt": "Enforce the frontend case review verdict before manifest materialization.",
|
|
249
|
-
"shell": {
|
|
250
|
-
"commands": [],
|
|
251
|
-
"verdictGate": {
|
|
252
|
-
"fromNodeId": "review-frontend-cases-final-pi",
|
|
253
|
-
"fallbackFromNodeIds": [
|
|
254
|
-
"review-frontend-cases-pi"
|
|
255
|
-
],
|
|
256
|
-
"accept": [
|
|
257
|
-
"VERDICT: pass"
|
|
258
|
-
],
|
|
259
|
-
"label": "effective frontend case review",
|
|
260
|
-
"lineMode": "first-verdict-line"
|
|
261
|
-
},
|
|
262
|
-
"cwd": ".",
|
|
263
|
-
"timeoutMs": 60000
|
|
264
|
-
},
|
|
265
|
-
"dependsPolicy": "all-or-condition-skip"
|
|
266
|
-
},
|
|
267
|
-
{
|
|
268
|
-
"id": "frontend-case-checklist-shell",
|
|
269
|
-
"depends_on": [
|
|
270
|
-
"final-frontend-case-review-gate-shell"
|
|
271
|
-
],
|
|
272
165
|
"executor": "shell",
|
|
273
|
-
"role": "verifier",
|
|
274
|
-
"complexity": "LOW",
|
|
275
|
-
"writePolicy": "read-only",
|
|
276
|
-
"allowedPaths": [
|
|
277
|
-
"testcase/frontend/rag/**",
|
|
278
|
-
"testcase/frontend/cases/**"
|
|
279
|
-
],
|
|
280
|
-
"forbiddenPaths": [
|
|
281
|
-
".harness/**",
|
|
282
|
-
"artifacts/**"
|
|
283
|
-
],
|
|
284
|
-
"outputContract": "Mechanical checklist: manifest/case paths, Case ID, non-production playwright-cli open prefix, AC mapping, and executable command gate; rejects alternative executable instructions and non-allowlisted playwright-cli commands with ruleId-tagged location evidence.",
|
|
285
|
-
"subtask_prompt": "Inspect and reject alternative executable instructions in fenced command code, list/indented steps, and explicit shell/terminal command lines. Only allowlisted playwright-cli command instructions may pass; ordinary prose and explicit blocked reasons may describe prohibitions. Run the native deterministic frontend case checklist without spawning Bash, PowerShell, or node -e.",
|
|
286
|
-
"shell": {
|
|
287
|
-
"commands": [],
|
|
288
|
-
"frontendTestCaseChecklist": {},
|
|
289
|
-
"cwd": ".",
|
|
290
|
-
"timeoutMs": 120000
|
|
291
|
-
}
|
|
292
|
-
},
|
|
293
|
-
{
|
|
294
|
-
"id": "materialize-frontend-case-manifest-shell",
|
|
295
|
-
"depends_on": [
|
|
296
|
-
"frontend-case-checklist-shell"
|
|
297
|
-
],
|
|
298
|
-
"executor": "shell",
|
|
299
|
-
"role": "verifier",
|
|
300
166
|
"complexity": "LOW",
|
|
301
167
|
"writePolicy": "exclusive",
|
|
302
168
|
"writeSet": [
|
|
303
169
|
"testcase/frontend/cases/**"
|
|
304
170
|
],
|
|
305
171
|
"allowedPaths": [
|
|
172
|
+
"testcase/frontend/rag/**",
|
|
306
173
|
"testcase/frontend/cases/**"
|
|
307
174
|
],
|
|
308
175
|
"forbiddenPaths": [
|
|
309
176
|
".harness/**",
|
|
310
|
-
"
|
|
177
|
+
"src/**"
|
|
311
178
|
],
|
|
312
|
-
"outputContract": "
|
|
313
|
-
"subtask_prompt": "
|
|
179
|
+
"outputContract": "Mechanical checklist then atomic manifest.json materialization; stdout one final JSON line {cases}.",
|
|
180
|
+
"subtask_prompt": "Run deterministic checklist then materialize manifest.json from draft.",
|
|
314
181
|
"shell": {
|
|
315
|
-
"commands": [
|
|
316
|
-
|
|
317
|
-
],
|
|
182
|
+
"commands": [],
|
|
183
|
+
"frontendTestCaseManifest": {},
|
|
318
184
|
"cwd": ".",
|
|
319
185
|
"timeoutMs": 120000
|
|
320
186
|
}
|
|
@@ -322,7 +188,7 @@
|
|
|
322
188
|
{
|
|
323
189
|
"id": "execute-frontend-cases-map",
|
|
324
190
|
"depends_on": [
|
|
325
|
-
"
|
|
191
|
+
"checklist-and-materialize-manifest-shell"
|
|
326
192
|
],
|
|
327
193
|
"executor": "static",
|
|
328
194
|
"role": "verifier",
|
|
@@ -341,7 +207,7 @@
|
|
|
341
207
|
"dynamicExpansion": {
|
|
342
208
|
"type": "map_agent",
|
|
343
209
|
"workflowNodeId": "execute-frontend-cases-map",
|
|
344
|
-
"itemsFrom": "$.nodes['
|
|
210
|
+
"itemsFrom": "$.nodes['checklist-and-materialize-manifest-shell'].output.cases",
|
|
345
211
|
"itemName": "case",
|
|
346
212
|
"maxItems": 20,
|
|
347
213
|
"maxExpandedNodes": 20,
|
|
@@ -379,19 +245,23 @@
|
|
|
379
245
|
],
|
|
380
246
|
"writeSet": [
|
|
381
247
|
"testcase/frontend/evidence/{{case.caseId}}/**"
|
|
382
|
-
]
|
|
248
|
+
],
|
|
249
|
+
"writeGuardPolicy": "tools-only"
|
|
383
250
|
}
|
|
384
251
|
}
|
|
385
252
|
},
|
|
386
253
|
{
|
|
387
|
-
"id": "
|
|
254
|
+
"id": "select-frontend-rerun-candidates-shell",
|
|
388
255
|
"depends_on": [
|
|
389
256
|
"execute-frontend-cases-map"
|
|
390
257
|
],
|
|
391
258
|
"executor": "shell",
|
|
392
259
|
"role": "verifier",
|
|
393
260
|
"complexity": "LOW",
|
|
394
|
-
"writePolicy": "
|
|
261
|
+
"writePolicy": "exclusive",
|
|
262
|
+
"writeSet": [
|
|
263
|
+
"testcase/frontend/evidence/**"
|
|
264
|
+
],
|
|
395
265
|
"allowedPaths": [
|
|
396
266
|
"testcase/frontend/cases/**",
|
|
397
267
|
"testcase/frontend/evidence/**"
|
|
@@ -400,131 +270,117 @@
|
|
|
400
270
|
".harness/**",
|
|
401
271
|
"artifacts/**"
|
|
402
272
|
],
|
|
403
|
-
"outputContract": "
|
|
404
|
-
"subtask_prompt": "
|
|
273
|
+
"outputContract": "Stdout final JSON line {cases:[...]} for blocked or missing-result-file cases with rerunAttempt < 2.",
|
|
274
|
+
"subtask_prompt": "Select frontend-test cases eligible for bounded rerun.",
|
|
405
275
|
"shell": {
|
|
406
276
|
"commands": [
|
|
407
|
-
"node -e \"const fs=require('fs'),path=require('path');const
|
|
277
|
+
"node -e \"const fs=require('fs'),path=require('path');const manifestPath='testcase/frontend/cases/manifest.json';if(!fs.existsSync(manifestPath)){process.stdout.write(JSON.stringify({cases:[]}));process.exit(0);}const manifest=JSON.parse(fs.readFileSync(manifestPath,'utf8'));const cases=[];for(const c of (manifest.cases||[])){const evidenceDir=(c.evidenceDir||('testcase/frontend/evidence/'+c.caseId+'/')).replace(/\\/+$/,'')+'/';const resultPath=path.join(evidenceDir,'case-result.json');const execPath=path.join(evidenceDir,'execution.md');let reason=null;let attempt=0;let missing=false;if(!fs.existsSync(resultPath)){missing=true;reason='missing-result-files';}else{try{const r=JSON.parse(fs.readFileSync(resultPath,'utf8'));attempt=Number(r.rerunAttempt||0)||0;if(r.status==='blocked')reason='blocked';if(!r.status){missing=true;reason='missing-result-files';}}catch(e){missing=true;reason='missing-result-files';}}if(!fs.existsSync(execPath)){const st=reason;if(st!=='blocked'){missing=true;reason=reason||'missing-result-files';}}const should=(reason==='blocked'||missing)&&attempt<2;if(should){cases.push({caseId:c.caseId,casePath:c.casePath||('testcase/frontend/cases/'+c.caseId+'.md'),evidenceDir,dimension:c.dimension||'core',acIds:c.acIds||[],rerunAttempt:attempt+1,reason:reason||'blocked'});}}const out={schemaVersion:1,cases};fs.mkdirSync('testcase/frontend/evidence',{recursive:true});fs.writeFileSync('testcase/frontend/evidence/rerun-candidates.json',JSON.stringify(out,null,2)+'\\n');process.stdout.write(JSON.stringify({cases}));"
|
|
408
278
|
],
|
|
409
279
|
"cwd": ".",
|
|
410
280
|
"timeoutMs": 120000
|
|
411
281
|
}
|
|
412
282
|
},
|
|
413
283
|
{
|
|
414
|
-
"id": "
|
|
284
|
+
"id": "rerun-frontend-cases-map",
|
|
415
285
|
"depends_on": [
|
|
416
|
-
"
|
|
286
|
+
"select-frontend-rerun-candidates-shell"
|
|
417
287
|
],
|
|
418
|
-
"executor": "
|
|
288
|
+
"executor": "static",
|
|
419
289
|
"role": "verifier",
|
|
420
290
|
"complexity": "LOW",
|
|
421
|
-
"writePolicy": "
|
|
422
|
-
"allowedPaths": [
|
|
423
|
-
"testcase/frontend/cases/**",
|
|
424
|
-
"testcase/frontend/evidence/**"
|
|
425
|
-
],
|
|
291
|
+
"writePolicy": "none",
|
|
292
|
+
"allowedPaths": [],
|
|
426
293
|
"forbiddenPaths": [
|
|
427
294
|
".harness/**",
|
|
428
295
|
"artifacts/**"
|
|
429
296
|
],
|
|
430
|
-
"outputContract": "
|
|
431
|
-
"subtask_prompt": "
|
|
432
|
-
"
|
|
433
|
-
"
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
297
|
+
"outputContract": "Serial rerun of blocked/missing-result frontend cases.",
|
|
298
|
+
"subtask_prompt": "Expand rerun candidates into serial browser case children.",
|
|
299
|
+
"static": {
|
|
300
|
+
"resultMarkdown": "Frontend case map expansion barrier."
|
|
301
|
+
},
|
|
302
|
+
"dynamicExpansion": {
|
|
303
|
+
"type": "map_agent",
|
|
304
|
+
"workflowNodeId": "rerun-frontend-cases-map",
|
|
305
|
+
"itemsFrom": "$.nodes['select-frontend-rerun-candidates-shell'].output.cases",
|
|
306
|
+
"itemName": "case",
|
|
307
|
+
"maxItems": 20,
|
|
308
|
+
"maxExpandedNodes": 20,
|
|
309
|
+
"childIdPrefix": "rerun-frontend-case",
|
|
310
|
+
"tokenBudget": {
|
|
311
|
+
"maxTokensPerCase": 20000,
|
|
312
|
+
"maxTotalTokens": 200000
|
|
439
313
|
},
|
|
440
|
-
"
|
|
441
|
-
|
|
314
|
+
"childTask": {
|
|
315
|
+
"executor": "pi",
|
|
316
|
+
"role": "implementer",
|
|
317
|
+
"skills": [
|
|
318
|
+
"playwright-cli"
|
|
319
|
+
],
|
|
320
|
+
"toolProfile": "write",
|
|
321
|
+
"commandPolicy": {
|
|
322
|
+
"mode": "capability-allowlist",
|
|
323
|
+
"capabilities": [
|
|
324
|
+
"playwright-cli"
|
|
325
|
+
]
|
|
326
|
+
},
|
|
327
|
+
"complexity": "MED",
|
|
328
|
+
"subtaskPromptTemplate": "RERUN attempt {{case.rerunAttempt}} for {{case.caseId}} (reason={{case.reason}}). Rewrite authoritative evidenceDir case-result.json and execution.md; set rerunAttempt={{case.rerunAttempt}}. Primary job: EXECUTE {{case.caseId}} from {{case.casePath}} with skill playwright-cli (fresh Pi session). Use the structured playwright_cli tool for every browser action. Do not request or search for bash. Translate each playwright-cli line in the case Markdown into one playwright_cli tool call. The controller-owned browser capability freezes baseUrl from hash-bound task source config.md or the localhost default; context/case prose may reference but cannot establish or override that origin. Start only with playwright_cli command=open and controller-injected concrete frozen URL args (default session; no -s=). Dynamic refs: literal eX/eY are documentation placeholders, not tool args. Immediately before every structured playwright_cli call that references an element, parse the actual eNN from the immediately preceding latest snapshot and pass only that actual eNN. Never send literal eX/eY, and never reuse a stale ref after a new snapshot. File outputs are canonical: screenshot uses [--filename, final.png] (or [e5, --filename, final.png] only for a real target), pdf uses [--filename, final.pdf], and snapshot writes a file only with [--filename, snapshot.txt]; snapshot without filename is response-only. Never use --path, --output, --file, or an output path as a positional target. Passed authority requires same-child ordered controller receipts: successful open → successful find → successful post-execution cleanup. Only successful find is meaningful; snapshot, goto, screenshot, request/console, and ordinary interactions cannot establish passed. Only when preflight or playwright_cli tool explicitly fails may you write blocked evidence. Always persist {{case.evidenceDir}}execution.md and {{case.evidenceDir}}case-result.json (caseId, status passed|failed|blocked, evidencePaths; blocked needs blockedReason). Business failed/blocked is not a node failure. Close via playwright_cli command=close. Return compact JSON (<=1200 chars): {caseId,status,evidencePaths,errorSummary,tokens}.",
|
|
329
|
+
"outputContract": "Compact JSON <=1200 chars. Browser actions must use structured playwright_cli tool.",
|
|
330
|
+
"writePolicy": "exclusive",
|
|
331
|
+
"allowedPaths": [
|
|
332
|
+
"testcase/frontend/cases/{{case.caseId}}.md",
|
|
333
|
+
"testcase/frontend/rag/context.md",
|
|
334
|
+
"testcase/frontend/rag/coverage-map.md",
|
|
335
|
+
"testcase/frontend/evidence/{{case.caseId}}/**"
|
|
336
|
+
],
|
|
337
|
+
"forbiddenPaths": [
|
|
338
|
+
".harness/**",
|
|
339
|
+
"artifacts/**"
|
|
340
|
+
],
|
|
341
|
+
"writeSet": [
|
|
342
|
+
"testcase/frontend/evidence/{{case.caseId}}/**"
|
|
343
|
+
],
|
|
344
|
+
"writeGuardPolicy": "tools-only"
|
|
345
|
+
}
|
|
442
346
|
}
|
|
443
347
|
},
|
|
444
348
|
{
|
|
445
|
-
"id": "frontend-
|
|
349
|
+
"id": "finalize-frontend-test-result-shell",
|
|
446
350
|
"depends_on": [
|
|
447
|
-
"
|
|
351
|
+
"rerun-frontend-cases-map"
|
|
448
352
|
],
|
|
449
|
-
"executor": "shell",
|
|
450
353
|
"role": "verifier",
|
|
451
|
-
"complexity": "LOW",
|
|
452
|
-
"writePolicy": "read-only",
|
|
453
|
-
"allowedPaths": [],
|
|
454
|
-
"forbiddenPaths": [
|
|
455
|
-
".harness/**",
|
|
456
|
-
"artifacts/**"
|
|
457
|
-
],
|
|
458
|
-
"outputContract": "Pass only for a real passed frontend-test-result-v1.",
|
|
459
|
-
"subtask_prompt": "Delivery/Worker gate for authoritative frontend-test result. Retrospective does not depend on this node so failed runs can still write reports.",
|
|
460
|
-
"shell": {
|
|
461
|
-
"commands": [
|
|
462
|
-
"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}\""
|
|
463
|
-
],
|
|
464
|
-
"cwd": ".",
|
|
465
|
-
"timeoutMs": 60000
|
|
466
|
-
}
|
|
467
|
-
},
|
|
468
|
-
{
|
|
469
|
-
"id": "frontend-test-retrospect-pi",
|
|
470
|
-
"depends_on": [
|
|
471
|
-
"finalize-frontend-test-result-shell",
|
|
472
|
-
"frontend-test-l5-report-shell"
|
|
473
|
-
],
|
|
474
|
-
"executor": "pi",
|
|
475
|
-
"role": "closeout",
|
|
476
|
-
"toolProfile": "write",
|
|
477
|
-
"complexity": "MED",
|
|
478
|
-
"writePolicy": "exclusive",
|
|
479
|
-
"writeSet": [
|
|
480
|
-
"testcase/frontend/reports/**"
|
|
481
|
-
],
|
|
482
|
-
"allowedPaths": [
|
|
483
|
-
"testcase/frontend/**"
|
|
484
|
-
],
|
|
485
|
-
"forbiddenPaths": [
|
|
486
|
-
".harness/**",
|
|
487
|
-
"artifacts/**"
|
|
488
|
-
],
|
|
489
|
-
"outputContract": "Write testcase/frontend/reports/frontend-test-retrospect-<date>.md with coverage, pass/fail/blocked, execution evidence review, risks, findings, and A/B/C/D rating — even when outcome is failed/incomplete.",
|
|
490
|
-
"subtask_prompt": "Write the frontend test retrospective under testcase/frontend/reports/ after result materialization (do not wait for outcome=pass). Combine AC→case→browser-evidence review with the closeout report. Blocked cases never count as passed. Do not write docs/**."
|
|
491
|
-
},
|
|
492
|
-
{
|
|
493
|
-
"id": "frontend-test-l5-report-shell",
|
|
494
|
-
"depends_on": [
|
|
495
|
-
"finalize-frontend-test-result-shell"
|
|
496
|
-
],
|
|
497
354
|
"executor": "shell",
|
|
498
|
-
"role": "verifier",
|
|
499
355
|
"complexity": "LOW",
|
|
500
356
|
"writePolicy": "exclusive",
|
|
501
357
|
"writeSet": [
|
|
502
|
-
"testcase/frontend/
|
|
358
|
+
"testcase/frontend/evidence/**"
|
|
503
359
|
],
|
|
504
360
|
"allowedPaths": [
|
|
505
|
-
"testcase/frontend/**"
|
|
361
|
+
"testcase/frontend/cases/**",
|
|
362
|
+
"testcase/frontend/evidence/**"
|
|
506
363
|
],
|
|
507
364
|
"forbiddenPaths": [
|
|
508
365
|
".harness/**",
|
|
509
|
-
"
|
|
366
|
+
"src/**"
|
|
510
367
|
],
|
|
511
|
-
"outputContract": "
|
|
512
|
-
"subtask_prompt": "
|
|
368
|
+
"outputContract": "Evidence validation (advisory missing/malformed does not fail the node; hard-fail only path escape) then hash-bound frontend-test-result-v1. Node success means result-v1 was written, not that all cases passed.",
|
|
369
|
+
"subtask_prompt": "Validate case evidence then materialize authoritative frontend-test-result-v1. Missing/malformed case evidence is advisory; only unsafe evidence paths hard-fail.",
|
|
513
370
|
"shell": {
|
|
514
371
|
"commands": [],
|
|
515
|
-
"
|
|
372
|
+
"frontendTestResultFinalize": {},
|
|
516
373
|
"cwd": ".",
|
|
517
374
|
"timeoutMs": 120000
|
|
518
375
|
}
|
|
519
376
|
},
|
|
520
377
|
{
|
|
521
|
-
"id": "frontend-test-
|
|
378
|
+
"id": "frontend-test-reports-shell",
|
|
522
379
|
"depends_on": [
|
|
523
|
-
"frontend-test-
|
|
524
|
-
"frontend-test-l5-report-shell"
|
|
380
|
+
"finalize-frontend-test-result-shell"
|
|
525
381
|
],
|
|
526
|
-
"executor": "shell",
|
|
527
382
|
"role": "verifier",
|
|
383
|
+
"executor": "shell",
|
|
528
384
|
"complexity": "LOW",
|
|
529
385
|
"writePolicy": "exclusive",
|
|
530
386
|
"writeSet": [
|
|
@@ -535,13 +391,15 @@
|
|
|
535
391
|
],
|
|
536
392
|
"forbiddenPaths": [
|
|
537
393
|
".harness/**",
|
|
538
|
-
"
|
|
394
|
+
"src/**"
|
|
539
395
|
],
|
|
540
|
-
"outputContract": "
|
|
541
|
-
"subtask_prompt": "Render
|
|
396
|
+
"outputContract": "Main frontend-test-report.md/html from frontend-test-result-v1; optional L-5. Pipeline acceptance = result-v1 + main HTML.",
|
|
397
|
+
"subtask_prompt": "Render operational reports from frontend-test-result-v1 only.",
|
|
542
398
|
"shell": {
|
|
543
399
|
"commands": [],
|
|
544
|
-
"
|
|
400
|
+
"frontendTestReports": {
|
|
401
|
+
"l5": true
|
|
402
|
+
},
|
|
545
403
|
"cwd": ".",
|
|
546
404
|
"timeoutMs": 120000
|
|
547
405
|
}
|
|
@@ -10,6 +10,12 @@ The preflight controller resolves and freezes one absolute browser base URL from
|
|
|
10
10
|
- Never use production hosts or credentials.
|
|
11
11
|
- Write `environmentProbe: pending`. The environment preflight shell will replace this with `reachable`, `unreachable`, or `curl-unavailable` plus a structured `blockedReason` (for example `frontend-base-url-unreachable`).
|
|
12
12
|
- Include the exact browser start prefix using the supplied controller-frozen URL:
|
|
13
|
-
`playwright-cli open --browser=chrome --
|
|
13
|
+
`playwright-cli open --browser=chrome --headless <controller-frozen-base-url>`.
|
|
14
14
|
|
|
15
15
|
Do not claim the environment is reachable until preflight completes. Preflight does not start the application.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Standard scenario coverage
|
|
19
|
+
|
|
20
|
+
- Copy or reference `docs/templates/frontend-test-standard-scenarios.v1.json` into `testcase/frontend/rag/standard-scenarios.v1.json` when available.
|
|
21
|
+
- Add `## Standard scenario coverage` to coverage-map.md with planned/n/a for each must scenario.
|
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
|
|
3
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
4
|
|
|
5
|
-
Every case must retain the exact browser-start command prefix with the resolved absolute baseUrl from `testcase/frontend/rag/context.md` (prefer task source `config.md`, else `http://localhost:5173`): `playwright-cli open --browser=chrome --
|
|
5
|
+
Every case must retain the exact browser-start command prefix with the resolved absolute baseUrl from `testcase/frontend/rag/context.md` (prefer task source `config.md`, else `http://localhost:5173`): `playwright-cli open --browser=chrome --headless <resolved-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, a successful `playwright-cli find ...` semantic assertion after `open`, and an evidence write point. `snapshot`, `goto`, `screenshot`, `request`/`console`, and ordinary interactions cannot independently authorize passed. 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.
|