@tea-agent/loop-agent 0.39.0-beta.5 → 0.39.0-beta.6
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 +5 -0
- package/dist/build-stamp.json +3 -3
- package/dist/executors/dag-pi-executor.js +94 -25
- package/dist/executors/pi-playwright-cli-tool.js +23 -16
- package/dist/executors/shell-executor.js +25 -8
- package/dist/shared/dag-failure-category.js +138 -0
- package/dist/task/config-types.js +6 -0
- package/dist/task/source-references.js +22 -1
- package/dist/worker/console/chat/operation-card.js +8 -1
- package/dist/worker/console/chat/routes.js +5 -3
- package/dist/worker/console/inspect-split.js +18 -0
- package/dist/worker/console/operation-run-facts.js +19 -3
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-zsNyvGaH.js → abnfDiagram-N423BO3Z-CfDSkLtp.js} +1 -1
- package/dist/worker/console/static/assets/{arc-BDjZ5kE1.js → arc-BlefM6I5.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-BkgaujnN.js → architectureDiagram-T3A2C74G-C4tHfzHn.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CqxOJi0j.js → blockDiagram-VBNYF7ZC-G-G77Rh1.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-C2JHLdRi.js → c4Diagram-5PPSVZJV-DWgxmY72.js} +1 -1
- package/dist/worker/console/static/assets/channel-DsdX3ZUX.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DF_YSvOS.js → chunk-2GRJ4B5K-CJQcfbCY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-D3Ort5Vo.js → chunk-2Q5K7J3B-vUPviH9y.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-XfAfZeUC.js → chunk-5RXB4S5H-DzRXSTeh.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-SLBibhf5.js → chunk-5VM5RSS4-CWfgqm07.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-Dz29Ye_f.js → chunk-6Q2QTUOP-BtTDauXA.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-DB2G4H8V.js → chunk-GF5L2VYU-Bc-NNt84.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CIoGkxdu.js → chunk-JWPE2WC7-B_f-emL4.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-CG78FGkn.js → chunk-KBJHAD2P-DV-jgJ66.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-ZDHAvXPd.js → chunk-RYQCIY6F-DMqzn4RZ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BKkkC4VA.js → chunk-XXDRQBXY-CVNtlAm4.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-CrvCyJ-8.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-CrvCyJ-8.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-SposUZsO.js → cose-bilkent-JH36ORCC-BBZBhVTd.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BSCtQpD7.js → cynefin-VYW2F7L2-DbMU2PZ2.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-DgFHrw9l.js → cynefinDiagram-MW4NZA55-Dj0v8rjc.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BVBasYbj.js → dagre-VZM6K2ZE-BW8R4WvJ.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-6r_4rN_c.js → diagram-7IWD3JNH-DEgWz3ld.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-JmuaB41P.js → diagram-B4RE2ZJO-BAA01rlR.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-xXvAY3sk.js → diagram-LBJQPF4R-Cau6qqpW.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D2B3lY2o.js → diagram-Q27KOJAE-8HiPsLos.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-tK54tMEX.js → diagram-UB23O5K3-CmkXuTYL.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-CNtELzV-.js → ebnfDiagram-BXEA7PRR-DCV_0seW.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-C1xKMKcW.js → erDiagram-JOGREHBK-gckfWESj.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CZ7dlPMz.js → flowDiagram-UKHOOZJN-D2mT2BUu.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-BqGb42Xe.js → ganttDiagram-PKOTCBZU-B_HR5qEw.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-D_BlXEtt.js → gitGraphDiagram-DS77QQ5N-BUw4fpSa.js} +1 -1
- package/dist/worker/console/static/assets/{index-D5zGX6fG.js → index-DPbsruV5.js} +81 -79
- package/dist/worker/console/static/assets/index-DwOGauxU.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-Vce2vmIe.js → infoDiagram-6WML65LV-BSRAqEj0.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-DDwVBAvI.js → ishikawaDiagram-WSZJBQD7-CLxE8hUp.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-y_Ew-wv4.js → journeyDiagram-NVQOT4AX-juFzVXIj.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CjXJFp1K.js → kanban-definition-27J2QSJJ-CwLHVhOU.js} +1 -1
- package/dist/worker/console/static/assets/{linear-1itMst1Q.js → linear-9LfUyPAs.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-C-D3ZRUa.js → mermaid.core-TPPHfQbe.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-BiYywmZc.js → mindmap-definition-FAOFIHXS-Dn_Z9RxF.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-l2fydNWA.js → pegDiagram-VL7TDLO6-CiEMKTgm.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-Dz0hNbYW.js → pieDiagram-7S7Q4E2Y-JASaGxjZ.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-BujI_mLZ.js → quadrantDiagram-CIZ2JOQS-ChmH0ply.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-pWe84beb.js → railroadDiagram-AXF67PYL-CtyM-HGQ.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-ByYu2frg.js → requirementDiagram-LRYGKXZP-BN9y_Bvs.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-CoPnilFP.js → sankeyDiagram-W5VNT64P-IyS0GQ1-.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-1Md2LnS0.js → sequenceDiagram-SI44F4Z6-DjxGXurN.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DqqqAoSH.js → sizeCapture-X5ZJPWSS-Cr3Z0S8y.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-C4sGkf--.js → stateDiagram-OKZ733FA-BjJFC6pW.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Bq44O9Nt.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-ByxJrUzW.js → swimlanes-SLNWSIFB-DeMUJQiW.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BT4tWyW3.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-D8MLgM0O.js → timeline-definition-Z64GVDOM-Bc2Rv2Oo.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DnBis-wZ.js → vennDiagram-T6HMQDX7-CDhRUeqG.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-uuyYT7UO.js → wardleyDiagram-T6FBY63Y-DaN_z3Xy.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-BjgabhwK.js → xychartDiagram-ELKLHX3M-C1vTLBDG.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +38 -2
- package/dist/worker/console/static-src/app/useRunProgress.js +46 -0
- package/dist/worker/console/static-src/operator-chat/cards/failure-category-advice.js +25 -0
- package/dist/worker/console/static-src/operator-chat/input-history.js +76 -0
- package/dist/worker/console/static-src/operator-chat/turn-submission.js +13 -1
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +29 -9
- package/dist/worker/console/static-src/operator-chat/useComposer.js +32 -0
- package/dist/worker/console/static-src/pages/tasks/run-id-resolution.js +79 -0
- package/dist/worker/console/static-src/pages/tasks/run-ownership-verify.js +23 -0
- package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +110 -0
- package/dist/worker/materialize/harness-task-lineage.js +5 -2
- package/dist/worker/observability/read-model.js +22 -0
- package/dist/workflows/dag/dynamic-runtime/loop-until.js +1 -1
- package/dist/workflows/dag/dynamic-runtime/map.js +1 -1
- package/dist/workflows/dag/failure-category.js +7 -128
- package/dist/workflows/dag/frontend-design-policy.js +12 -7
- package/dist/workflows/dag/frontend-implementation-contract.js +4 -1
- package/dist/workflows/dag/frontend-shadow-dual-write.js +24 -10
- package/dist/workflows/dag/frontend-test-case-checklist.js +4 -2
- package/dist/workflows/dag/frontend-test-case-manifest.js +6 -4
- package/dist/workflows/dag/frontend-test-case-quality.js +6 -3
- package/dist/workflows/dag/frontend-test-environment-probe.js +10 -7
- package/dist/workflows/dag/frontend-test-html-report.js +3 -1
- package/dist/workflows/dag/frontend-test-l5-report.js +3 -1
- package/dist/workflows/dag/frontend-test-layout.js +159 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +28 -14
- package/dist/workflows/dag/frontend-test-standard-scenarios.js +3 -1
- package/dist/workflows/dag/init-hybrid.js +59 -14
- package/dist/workflows/dag/node-execution.js +11 -1
- package/dist/workflows/dag/retry-policy.js +10 -0
- package/dist/workflows/dag/types.js +12 -0
- package/dist/workflows/dag/validate.js +23 -19
- package/docs/templates/frontend-test-case-checklist.md +2 -2
- package/package.json +1 -1
- package/skills/fe-test-ui-scout/SKILL.md +4 -4
- package/skills/fe-test-ui-scout/references/ledger-schema.md +1 -1
- package/skills/playwright-cli/SKILL.md +1 -1
- package/skills/playwright-cli-case-generator/SKILL.md +10 -8
- package/dist/worker/console/static/assets/channel-BuqaGKAT.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DNdSHjH9.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DNdSHjH9.js +0 -1
- package/dist/worker/console/static/assets/index-DBbhESQ_.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8rmop_9.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B0ky-kBM.js +0 -8
|
@@ -28,6 +28,7 @@ import { resolveExecutorModelMatrices } from "../../executors/model-routing.js";
|
|
|
28
28
|
import { normalizeTaskRequirementText, resolveTaskDagTemplateSelection, } from "./task-demand-routing.js";
|
|
29
29
|
import { BACKEND_TEST_EXECUTION_DEFAULT_TEST_ROOT, buildBackendTestExecutionPreflightShellSnippet, } from "./backend-test-execution-contract.js";
|
|
30
30
|
import { resolveBackendTestLayout, } from "./backend-test-layout.js";
|
|
31
|
+
import { applyFrontendTestLayoutToText, resolveFrontendTestLayout, } from "./frontend-test-layout.js";
|
|
31
32
|
import { buildBackendTestOutcomeGateShellSnippet } from "./backend-test-result-contract.js";
|
|
32
33
|
import { buildBackendTestIntakeContext } from "./backend-test-intake-context.js";
|
|
33
34
|
import { buildFrontendTestOutcomeGateShellSnippet } from "./frontend-test-result-contract.js";
|
|
@@ -2985,7 +2986,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
2985
2986
|
outputContract: "Short Markdown narrative plus incremental typed plan record tools (record_target_surface / record_component_choice / record_state_flow / record_data_flow / record_mock_api / record_design_deviation / record_dependency / record_plan_requirement / record_plan_verification_target / record_plan_evidence_gap) and exactly one finalize_plan terminal call. finalize_plan assembles the canonical editable patch from the committed ledger facts (requirements / verificationTargets / evidenceGaps / residualRisks / realIntegrationGap). Commit requirements and verification targets incrementally — one entry per record call — so the plan never needs a single large output; evidence gaps are optional and only needed for genuine gaps. Implementation steps are NOT part of the plan — the implementer designs its own ordering. Omit protected fields: schemaVersion, sourceBinding, riskLevel, targets.files, and mockApi.productionDefaultOff. The runtime compiles facts ⊕ skeleton into canonical full-contract JSON for downstream review. No file writes.",
|
|
2986
2987
|
subtask_prompt: [
|
|
2987
2988
|
"Use frontend-contract-pi typed requirement facts (stable REQ/BR/AC identifiers, dispositions, evidence expectations, and frontend-test handoff intents), frontend-scout-pi, task sources, and the generation-time Mock capability evidence to fill the runtime-owned frontend contract skeleton. Record the plan decision ledger through the incremental record_* tools, then call finalize_plan exactly once. The runtime already owns schemaVersion, sourceBinding, riskLevel, targets.files, and mockApi.productionDefaultOff; omit those protected paths even when their values look obvious.",
|
|
2988
|
-
"Commit requirements with record_plan_requirement (one entry per call) and verification targets with record_plan_verification_target (one entry per call). Evidence gaps are optional — call record_plan_evidence_gap only for genuine gaps. Never bundle these into finalize_plan arguments — finalize_plan only closes the plan.
|
|
2989
|
+
"Commit requirements with record_plan_requirement (one entry per call) and verification targets with record_plan_verification_target (one entry per call). Omit expectedOutcome in requirements — the runtime derives it from the contract; keep each entry to id + targets + verificationTargetIds. Evidence gaps are optional — call record_plan_evidence_gap only for genuine gaps. Never bundle these into finalize_plan arguments — finalize_plan only closes the plan. CRITICAL: emit exactly ONE record tool call per assistant message — never batch multiple record_* calls in the same message, even though they look independent. Parallel-batching them makes a single message as large as the old full-contract output and re-introduces the truncation bug. One call per message, many messages, then finalize_plan exactly once at the end. Do NOT plan implementation steps: the implementer decides ordering itself.",
|
|
2989
2990
|
...(requiresOpenspecClassification ? ["Additionally consume the frontend-contract-pi openspec classification. Successfully read every required selection (including mandatory paths) and declare any used component specReference in the typed decision ledger; relevant/irrelevant selections are not forced into the ledger unless the plan actually uses them."] : []),
|
|
2990
2991
|
"The incremental record_* facts become the complete implementation plan after deterministic merge with the protected skeleton. Do not treat leftover JSON in the narrative as the compile authority.",
|
|
2991
2992
|
"Select the Mock / API strategy only through record_mock_api. Encode endpoint/fixture mapping, explicit activation, verification commands, and Real Integration Gap in schema-defined fields; productionDefaultOff comes from the protected skeleton and there is no second plan output.",
|
|
@@ -4862,11 +4863,57 @@ function applyBackendTestLayoutToDagSpec(spec, layout) {
|
|
|
4862
4863
|
spec.successCriteria = spec.successCriteria?.map(rewrite);
|
|
4863
4864
|
spec.backendTestLayout = layout;
|
|
4864
4865
|
}
|
|
4866
|
+
/** Rewrite layout-dependent strings across a compiled frontend-test DAG spec. */
|
|
4867
|
+
function applyFrontendTestLayoutToDagSpec(spec, layout) {
|
|
4868
|
+
if (layout.isDefault) {
|
|
4869
|
+
spec.frontendTestLayout = layout;
|
|
4870
|
+
return;
|
|
4871
|
+
}
|
|
4872
|
+
const rewrite = (value) => applyFrontendTestLayoutToText(value, layout);
|
|
4873
|
+
for (const task of spec.tasks) {
|
|
4874
|
+
task.subtask_prompt = rewrite(task.subtask_prompt);
|
|
4875
|
+
if (task.outputContract)
|
|
4876
|
+
task.outputContract = rewrite(task.outputContract);
|
|
4877
|
+
task.writeSet = task.writeSet?.map(rewrite);
|
|
4878
|
+
task.allowedPaths = task.allowedPaths?.map(rewrite);
|
|
4879
|
+
task.forbiddenPaths = task.forbiddenPaths?.map(rewrite);
|
|
4880
|
+
if (task.dynamicExpansion?.childTask) {
|
|
4881
|
+
const child = task.dynamicExpansion.childTask;
|
|
4882
|
+
if (typeof child.subtaskPromptTemplate === "string") {
|
|
4883
|
+
child.subtaskPromptTemplate = rewrite(child.subtaskPromptTemplate);
|
|
4884
|
+
}
|
|
4885
|
+
if (typeof child.outputContract === "string") {
|
|
4886
|
+
child.outputContract = rewrite(child.outputContract);
|
|
4887
|
+
}
|
|
4888
|
+
if (Array.isArray(child.allowedPaths)) {
|
|
4889
|
+
child.allowedPaths = child.allowedPaths.map((entry) => typeof entry === "string" ? rewrite(entry) : entry);
|
|
4890
|
+
}
|
|
4891
|
+
if (Array.isArray(child.writeSet)) {
|
|
4892
|
+
child.writeSet = child.writeSet.map((entry) => typeof entry === "string" ? rewrite(entry) : entry);
|
|
4893
|
+
}
|
|
4894
|
+
if (Array.isArray(child.forbiddenPaths)) {
|
|
4895
|
+
child.forbiddenPaths = child.forbiddenPaths.map((entry) => typeof entry === "string" ? rewrite(entry) : entry);
|
|
4896
|
+
}
|
|
4897
|
+
}
|
|
4898
|
+
if (task.shell?.commands) {
|
|
4899
|
+
task.shell.commands = task.shell.commands.map(rewrite);
|
|
4900
|
+
}
|
|
4901
|
+
}
|
|
4902
|
+
spec.globalConstraints = spec.globalConstraints?.map(rewrite);
|
|
4903
|
+
spec.successCriteria = spec.successCriteria?.map(rewrite);
|
|
4904
|
+
spec.frontendTestLayout = layout;
|
|
4905
|
+
}
|
|
4906
|
+
function allowedPathsCoverFrontendTestRoot(allowedPaths, testRoot) {
|
|
4907
|
+
const probe = `${testRoot}/_layout_probe_`;
|
|
4908
|
+
return allowedPaths.some((pattern) => pathMatchesPattern(testRoot, pattern) ||
|
|
4909
|
+
pathMatchesPattern(probe, pattern));
|
|
4910
|
+
}
|
|
4865
4911
|
// ---------------------------------------------------------------------------
|
|
4866
4912
|
// Frontend browser-test RAG DAG template
|
|
4867
4913
|
// ---------------------------------------------------------------------------
|
|
4868
4914
|
function buildFrontendTestHybridDag(sources) {
|
|
4869
4915
|
const rawFrontendTest = sources.taskConfig.frontendTest;
|
|
4916
|
+
const layout = resolveFrontendTestLayout(rawFrontendTest);
|
|
4870
4917
|
const config = {
|
|
4871
4918
|
// Default 32: common FE suites cover ~24 AC with multi-dimension cases; 20 caused map maxExpandedNodes failures.
|
|
4872
4919
|
maxCasesPerBatch: rawFrontendTest?.maxCasesPerBatch ?? 32,
|
|
@@ -4891,9 +4938,9 @@ function buildFrontendTestHybridDag(sources) {
|
|
|
4891
4938
|
// Optional project-local UI anchor ledger: only mention it in prompts when it
|
|
4892
4939
|
// exists so ledger-less projects keep generating without noise.
|
|
4893
4940
|
const hasUiAnchorsLedger = Boolean(sources.repoRoot &&
|
|
4894
|
-
existsSync(path.join(sources.repoRoot, "
|
|
4941
|
+
existsSync(path.join(sources.repoRoot, layout.ragDir, "ui-anchors.md")));
|
|
4895
4942
|
const uiAnchorsLedgerInstruction = hasUiAnchorsLedger
|
|
4896
|
-
?
|
|
4943
|
+
? `Also read ${layout.ragDir}/ui-anchors.md (UI anchor ledger). Every passing find assertion in generated cases must quote a ledger row whose 状态 is 有效 for the matching page x state section; rows marked 不可断言/失效 must not be used as passing assertions. When an AC names a control absent from the ledger section for that state, emit a blocked case note (blockedReason unique-control-unavailable) instead of exploratory find steps, and follow ledger 备注 alternatives (split-node short literals) exactly.`
|
|
4897
4944
|
: "";
|
|
4898
4945
|
const reviewMode = config.reviewMode;
|
|
4899
4946
|
const blockingReview = reviewMode === "blocking";
|
|
@@ -4901,22 +4948,19 @@ function buildFrontendTestHybridDag(sources) {
|
|
|
4901
4948
|
const maxRerunAttempts = config.maxRerunAttempts;
|
|
4902
4949
|
const enableRetrospect = config.reports?.retrospect === true;
|
|
4903
4950
|
const enableL5Report = config.reports?.l5 !== false;
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
pattern === "**");
|
|
4907
|
-
if (!hasFrontendTestWriteScope) {
|
|
4908
|
-
throw new Error('frontend-test requires task.json allowedPaths to include "testcase/frontend/**" (or an explicit containing glob).');
|
|
4951
|
+
if (!allowedPathsCoverFrontendTestRoot(sources.taskConfig.allowedPaths, layout.testRoot)) {
|
|
4952
|
+
throw new Error(`frontend-test requires task.json allowedPaths to cover "${layout.testRoot}/**" (or an explicit containing glob).`);
|
|
4909
4953
|
}
|
|
4910
4954
|
const forbidden = commonForbiddenPaths(sources);
|
|
4911
|
-
const ragWriteSet = [
|
|
4955
|
+
const ragWriteSet = [`${layout.ragDir}/**`];
|
|
4912
4956
|
const caseDraftWriteSet = [
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4957
|
+
`${layout.casesDir}/FE-*.md`,
|
|
4958
|
+
`${layout.casesDir}/index.md`,
|
|
4959
|
+
`${layout.casesDir}/manifest.draft.json`,
|
|
4916
4960
|
];
|
|
4917
4961
|
// The shell materializer alone owns the final manifest boundary.
|
|
4918
|
-
const casesWriteSet = [
|
|
4919
|
-
const evidenceRoot =
|
|
4962
|
+
const casesWriteSet = [`${layout.casesDir}/**`];
|
|
4963
|
+
const evidenceRoot = layout.evidenceDir;
|
|
4920
4964
|
const declaredAcIdsLiteral = JSON.stringify(declaredAcIds);
|
|
4921
4965
|
const maxCasesPerBatchLiteral = String(config.maxCasesPerBatch);
|
|
4922
4966
|
const checklistScript = [
|
|
@@ -5508,6 +5552,7 @@ function buildFrontendTestHybridDag(sources) {
|
|
|
5508
5552
|
verifyStrategy: resolveDagVerifyStrategy(sources.taskConfig),
|
|
5509
5553
|
tasks,
|
|
5510
5554
|
};
|
|
5555
|
+
applyFrontendTestLayoutToDagSpec(spec, layout);
|
|
5511
5556
|
applyDefaultReadOnlyRetryPolicy(spec);
|
|
5512
5557
|
parseDagSpec(spec);
|
|
5513
5558
|
assertValidDagSpec(spec);
|
|
@@ -238,6 +238,16 @@ function buildAttemptPrompt(task, basePrompt, attemptNumber, previousFailureCate
|
|
|
238
238
|
"</retry_instruction>",
|
|
239
239
|
].join("\n");
|
|
240
240
|
}
|
|
241
|
+
if (previousFailureCategory === "read-burst") {
|
|
242
|
+
return [
|
|
243
|
+
basePrompt,
|
|
244
|
+
"",
|
|
245
|
+
"<retry_instruction>",
|
|
246
|
+
"Previous plan attempt issued too many read-only tool calls (read/grep/ls/find) and blew up the context window. Trust the upstream frontend-contract-pi typed requirement facts and frontend-scout-pi target surface already provided — do NOT re-read contract/scout stdout, PRD/source files, or component sources you already inspected.",
|
|
247
|
+
"Minimize discovery reads: only read what you genuinely need, once. Commit record_plan_requirement / record_plan_verification_target / record_* facts directly from the facts already in context (one tool call per message), then call finalize_plan exactly once.",
|
|
248
|
+
"</retry_instruction>",
|
|
249
|
+
].join("\n");
|
|
250
|
+
}
|
|
241
251
|
if (previousFailureCategory === "invalid-output" &&
|
|
242
252
|
task.structuredContractOutput &&
|
|
243
253
|
previousProtocolReason) {
|
|
@@ -279,7 +289,7 @@ function buildAttemptPrompt(task, basePrompt, attemptNumber, previousFailureCate
|
|
|
279
289
|
"",
|
|
280
290
|
"<retry_instruction>",
|
|
281
291
|
"Previous attempt was truncated by the provider (stopReason=length) before the plan facts were fully committed.",
|
|
282
|
-
"Re-commit the missing record_* facts and call finalize_plan exactly once; the committed typed ledger is the only compile authority.",
|
|
292
|
+
"Re-commit the missing record_* facts and call finalize_plan exactly once; the committed typed ledger is the only compile authority. Do NOT re-read contract/scout outputs or source files — use the facts already in context. Commit record_* facts one tool call per message, then finalize_plan immediately.",
|
|
283
293
|
"Do not emit a fenced JSON contract, protected skeleton fields, or an openspec-citations block; narrative JSON is not validated and wastes the output budget.",
|
|
284
294
|
"</retry_instruction>",
|
|
285
295
|
].join("\n");
|
|
@@ -32,6 +32,14 @@ export const STRUCTURED_OUTPUT_TRUNCATED_RETRY_CATEGORY = "structured-output-tru
|
|
|
32
32
|
* a corrected instruction; the typed terminal is the only authority.
|
|
33
33
|
*/
|
|
34
34
|
export const REVIEW_TERMINAL_MISSING_RETRY_CATEGORY = "review-terminal-missing";
|
|
35
|
+
/**
|
|
36
|
+
* The plan node issued too many read-only tool calls (read/grep/ls/find),
|
|
37
|
+
* blowing up the context window and eventually a 400 request-too-large. The
|
|
38
|
+
* plan should trust upstream typed facts instead of re-reading contract/scout
|
|
39
|
+
* outputs and source files repeatedly. Read-only and safe to retry with a
|
|
40
|
+
* reduced-reading instruction.
|
|
41
|
+
*/
|
|
42
|
+
export const READ_BURST_RETRY_CATEGORY = "read-burst";
|
|
35
43
|
/** Retry only a proven no-op from an explicitly opt-in bounded Pi writer. */
|
|
36
44
|
export const WRITER_EMPTY_DIFF_RETRY_CATEGORY = "writer-empty-diff";
|
|
37
45
|
/** Retry when a backend-test writer finished but Completeness Gate found missing/broken targets. */
|
|
@@ -65,6 +73,7 @@ export const ALL_DAG_RETRY_CATEGORIES = [
|
|
|
65
73
|
INCOMPLETE_WRITE_SET_RETRY_CATEGORY,
|
|
66
74
|
WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY,
|
|
67
75
|
REVIEW_TERMINAL_MISSING_RETRY_CATEGORY,
|
|
76
|
+
READ_BURST_RETRY_CATEGORY,
|
|
68
77
|
];
|
|
69
78
|
const RETRY_SAFE_PI_ROLES = new Set([
|
|
70
79
|
"planner",
|
|
@@ -523,5 +532,6 @@ export const FRONTEND_PLAN_LADDER_RETRY_POLICY = {
|
|
|
523
532
|
retryCategories: [
|
|
524
533
|
...DEFAULT_DAG_RETRY_CATEGORIES,
|
|
525
534
|
STRUCTURED_ARTIFACT_INVALID_RETRY_CATEGORY,
|
|
535
|
+
READ_BURST_RETRY_CATEGORY,
|
|
526
536
|
],
|
|
527
537
|
};
|
|
@@ -1159,6 +1159,18 @@ export const dagSpecSchema = z
|
|
|
1159
1159
|
isDefault: z.boolean(),
|
|
1160
1160
|
})
|
|
1161
1161
|
.optional(),
|
|
1162
|
+
/** Frozen frontend-test artifact layout; absent = historical testcase/frontend/ layout. */
|
|
1163
|
+
frontendTestLayout: z
|
|
1164
|
+
.object({
|
|
1165
|
+
testRoot: z.string(),
|
|
1166
|
+
ragDir: z.string(),
|
|
1167
|
+
casesDir: z.string(),
|
|
1168
|
+
evidenceDir: z.string(),
|
|
1169
|
+
reportsDir: z.string(),
|
|
1170
|
+
fixturesDir: z.string(),
|
|
1171
|
+
isDefault: z.boolean(),
|
|
1172
|
+
})
|
|
1173
|
+
.optional(),
|
|
1162
1174
|
/** Bound user shared-setup document (plan B); absent = no shared setup references expected. */
|
|
1163
1175
|
backendTestSharedSetup: z
|
|
1164
1176
|
.object({
|
|
@@ -5,6 +5,7 @@ import { resolveRepairTaskForGate } from "./repair-artifact.js";
|
|
|
5
5
|
import { topoSortToRanks } from "./topo.js";
|
|
6
6
|
import { isSafeReadOnlyPiRetryCandidate, isWriterEmptyDiffRetryCandidate, isWriterTransportRetryCandidate, INCOMPLETE_WRITE_SET_RETRY_CATEGORY, WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY, WRITER_EMPTY_DIFF_RETRY_CATEGORY, } from "./retry-policy.js";
|
|
7
7
|
import { isBackendTestCompletenessRetryCandidate } from "./backend-test-writer-completeness.js";
|
|
8
|
+
import { defaultFrontendTestLayout, frontendTestLayoutFromSpec, } from "./frontend-test-layout.js";
|
|
8
9
|
const GOVERNANCE_WARNING_TYPES = new Set([
|
|
9
10
|
"read-only-missing-artifacts-forbidden",
|
|
10
11
|
"read-only-prompt-mentions-artifact-writes",
|
|
@@ -690,22 +691,23 @@ function validateWriterOutcomePolicyTaskConfig(task, issues) {
|
|
|
690
691
|
});
|
|
691
692
|
}
|
|
692
693
|
}
|
|
693
|
-
function isFrontendEvidenceWriteSet(writeSet) {
|
|
694
|
+
function isFrontendEvidenceWriteSet(writeSet, layout = defaultFrontendTestLayout()) {
|
|
694
695
|
const entries = writeSet ?? [];
|
|
695
696
|
if (entries.length === 0)
|
|
696
697
|
return false;
|
|
698
|
+
const prefix = `${layout.evidenceDir}/`;
|
|
699
|
+
const glob = `${layout.evidenceDir}/**`;
|
|
697
700
|
return entries.every((entry) => {
|
|
698
701
|
const normalized = entry.trim().replace(/\\/g, "/").replace(/^\.\//, "");
|
|
699
|
-
return
|
|
700
|
-
normalized.startsWith("testcase/frontend/evidence/"));
|
|
702
|
+
return normalized === glob || normalized.startsWith(prefix);
|
|
701
703
|
});
|
|
702
704
|
}
|
|
703
|
-
function isFrontendBrowserExecutorTask(task) {
|
|
705
|
+
function isFrontendBrowserExecutorTask(task, layout = defaultFrontendTestLayout()) {
|
|
704
706
|
const skills = task.skills ?? [];
|
|
705
707
|
return (task.executor === "pi" &&
|
|
706
708
|
task.toolProfile === "write" &&
|
|
707
709
|
skills.includes("playwright-cli") &&
|
|
708
|
-
isFrontendEvidenceWriteSet(task.writeSet));
|
|
710
|
+
isFrontendEvidenceWriteSet(task.writeSet, layout));
|
|
709
711
|
}
|
|
710
712
|
function taskMentionsPlaywrightCliContract(task) {
|
|
711
713
|
const prompt = `${task.subtask_prompt}\n${task.outputContract ?? ""}`;
|
|
@@ -717,10 +719,11 @@ function taskMentionsPlaywrightCliContract(task) {
|
|
|
717
719
|
* - capability is granted without skill/prompt contract or outside evidence writeSet
|
|
718
720
|
* - non-browser nodes receive command capability
|
|
719
721
|
*/
|
|
720
|
-
function validateCommandPolicyTaskConfig(task, issues) {
|
|
722
|
+
function validateCommandPolicyTaskConfig(task, issues, spec) {
|
|
723
|
+
const layout = frontendTestLayoutFromSpec(spec);
|
|
721
724
|
const policy = resolveDagCommandPolicy(task.commandPolicy);
|
|
722
725
|
const allowsPlaywright = dagCommandPolicyAllows(task.commandPolicy, "playwright-cli");
|
|
723
|
-
const isBrowserExecutor = isFrontendBrowserExecutorTask(task);
|
|
726
|
+
const isBrowserExecutor = isFrontendBrowserExecutorTask(task, layout);
|
|
724
727
|
if (isBrowserExecutor && !allowsPlaywright) {
|
|
725
728
|
issues.push({
|
|
726
729
|
type: "invalid-command-policy",
|
|
@@ -751,10 +754,10 @@ function validateCommandPolicyTaskConfig(task, issues) {
|
|
|
751
754
|
message: `task ${task.id} playwright-cli command capability requires prompt/outputContract to reference playwright_cli`,
|
|
752
755
|
});
|
|
753
756
|
}
|
|
754
|
-
if (!isFrontendEvidenceWriteSet(task.writeSet)) {
|
|
757
|
+
if (!isFrontendEvidenceWriteSet(task.writeSet, layout)) {
|
|
755
758
|
issues.push({
|
|
756
759
|
type: "invalid-command-policy",
|
|
757
|
-
message: `task ${task.id} playwright-cli command capability requires frontend evidence writeSet under
|
|
760
|
+
message: `task ${task.id} playwright-cli command capability requires frontend evidence writeSet under ${layout.evidenceDir}/`,
|
|
758
761
|
});
|
|
759
762
|
}
|
|
760
763
|
if (task.toolProfile !== "write") {
|
|
@@ -765,7 +768,7 @@ function validateCommandPolicyTaskConfig(task, issues) {
|
|
|
765
768
|
}
|
|
766
769
|
}
|
|
767
770
|
}
|
|
768
|
-
function validateDynamicChildCommandPolicy(task, issues) {
|
|
771
|
+
function validateDynamicChildCommandPolicy(task, issues, spec) {
|
|
769
772
|
const expansion = task.dynamicExpansion;
|
|
770
773
|
if (!expansion)
|
|
771
774
|
return;
|
|
@@ -786,7 +789,7 @@ function validateDynamicChildCommandPolicy(task, issues) {
|
|
|
786
789
|
writeSet: child.writeSet,
|
|
787
790
|
outputContract: child.outputContract,
|
|
788
791
|
};
|
|
789
|
-
validateCommandPolicyTaskConfig(synthetic, issues);
|
|
792
|
+
validateCommandPolicyTaskConfig(synthetic, issues, spec);
|
|
790
793
|
}
|
|
791
794
|
function validateDecisionGateTaskConfig(task, issues) {
|
|
792
795
|
if (!task.decisionGate?.enabled) {
|
|
@@ -893,12 +896,13 @@ export function collectForbiddenExecutorIssues(spec, forbiddenExecutors) {
|
|
|
893
896
|
}));
|
|
894
897
|
}
|
|
895
898
|
/** Revalidate a rendered dynamic child before it enters state or executes. */
|
|
896
|
-
export function assertValidMaterializedDagTask(task) {
|
|
899
|
+
export function assertValidMaterializedDagTask(task, parentSpec) {
|
|
897
900
|
const issues = [];
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
901
|
+
const spec = {
|
|
902
|
+
version: 2,
|
|
903
|
+
tasks: [task],
|
|
904
|
+
frontendTestLayout: parentSpec?.frontendTestLayout,
|
|
905
|
+
};
|
|
902
906
|
validateTaskWritePolicy(task, spec, issues);
|
|
903
907
|
validateShellTaskConfig(task, spec, issues);
|
|
904
908
|
validateStaticTaskConfig(task, issues);
|
|
@@ -906,7 +910,7 @@ export function assertValidMaterializedDagTask(task) {
|
|
|
906
910
|
validateRetryPolicyTaskConfig(task, issues);
|
|
907
911
|
validateOutputProtocolTaskConfig(task, issues);
|
|
908
912
|
validateWriterOutcomePolicyTaskConfig(task, issues);
|
|
909
|
-
validateCommandPolicyTaskConfig(task, issues);
|
|
913
|
+
validateCommandPolicyTaskConfig(task, issues, spec);
|
|
910
914
|
if (issues.length > 0) {
|
|
911
915
|
throw new Error(`invalid materialized dynamic child ${task.id}: ${issues.map((issue) => issue.message).join("; ")}`);
|
|
912
916
|
}
|
|
@@ -962,8 +966,8 @@ export function validateDagSpec(spec) {
|
|
|
962
966
|
validateRetryPolicyTaskConfig(task, issues);
|
|
963
967
|
validateOutputProtocolTaskConfig(task, issues);
|
|
964
968
|
validateWriterOutcomePolicyTaskConfig(task, issues);
|
|
965
|
-
validateCommandPolicyTaskConfig(task, issues);
|
|
966
|
-
validateDynamicChildCommandPolicy(task, issues);
|
|
969
|
+
validateCommandPolicyTaskConfig(task, issues, spec);
|
|
970
|
+
validateDynamicChildCommandPolicy(task, issues, spec);
|
|
967
971
|
validateProjectGovernanceTaskConfig(task, spec, issues);
|
|
968
972
|
validatePiExtensionsTaskConfig(task, issues);
|
|
969
973
|
validateFailureAwareDependsOn(task, spec, issues);
|
|
@@ -15,7 +15,7 @@ LLM review (when `frontendTest.reviewMode=blocking`) must not invent blocking ru
|
|
|
15
15
|
| `unknown-ac` | When task `sourceBinding.requirementIds` lists ACs, every `acIds` entry must be in that set |
|
|
16
16
|
| `case-id-shape` | `caseId` matches `FE-<FEATURE>-<NNN>-...` (never `AC-FE-*`) |
|
|
17
17
|
| `case-id-is-ac` | Do not use acceptance id as `caseId` / filename |
|
|
18
|
-
| `case-path-mismatch` | `casePath === testcase/frontend/cases/{caseId}.md
|
|
18
|
+
| `case-path-mismatch` | `casePath === {casesDir}/{caseId}.md`(默认 `testcase/frontend/cases/{caseId}.md`) |
|
|
19
19
|
| `case-file-missing` | `casePath` exists |
|
|
20
20
|
|
|
21
21
|
## Tool guidance (non-blocking)
|
|
@@ -40,5 +40,5 @@ LLM review (when `frontendTest.reviewMode=blocking`) must not invent blocking ru
|
|
|
40
40
|
|
|
41
41
|
## Pipeline vs quality
|
|
42
42
|
|
|
43
|
-
- **Pipeline acceptance**: final `
|
|
43
|
+
- **Pipeline acceptance**: final `{reportsDir}/frontend-test-retrospect-*.md` exists after result materialize (default `testcase/frontend/reports/`)
|
|
44
44
|
- **Quality**: `frontend-test-result-v1.outcome=passed` with 0 blocked/failed (opt-in via `frontendTest.strictOutcomeGate`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: fe-test-ui-scout
|
|
3
|
-
description: 在写 frontend-test Wave PRD/回归目录之前,对真实运行的被测页面做只读锚点侦察,产出并维护 testcase/frontend/rag/ui-anchors.md
|
|
3
|
+
description: 在写 frontend-test Wave PRD/回归目录之前,对真实运行的被测页面做只读锚点侦察,产出并维护 {testRoot}/rag/ui-anchors.md 锚点台账(默认 testcase/frontend/rag/ui-anchors.md)。用 playwright-cli snapshot/find 逐字面验证命中数与唯一性,防止 PRD 验收标准引用不存在或不唯一的文案。触发词:frontend-test、锚点、台账、ui-anchors、侦察、写 PRD 前、snapshot/find 校准、Wave PRD。
|
|
4
4
|
references:
|
|
5
5
|
- path: references/ledger-schema.md
|
|
6
6
|
required: true
|
|
@@ -13,7 +13,7 @@ references:
|
|
|
13
13
|
## 定位
|
|
14
14
|
|
|
15
15
|
写 Wave PRD / 校准回归目录 AC **之前**的前置侦察。产出锚点台账
|
|
16
|
-
`testcase/frontend/rag/ui-anchors.md
|
|
16
|
+
`{testRoot}/rag/ui-anchors.md`(默认 `testcase/frontend/rag/ui-anchors.md`):每个「页面 × 状态」下真实可见、
|
|
17
17
|
`find` 可命中的字面清单。PRD 的每条 `find "..."` 断言必须能在台账找到对应行;
|
|
18
18
|
台账没有的字面禁止写进验收标准。
|
|
19
19
|
|
|
@@ -23,7 +23,7 @@ references:
|
|
|
23
23
|
|
|
24
24
|
## 输入
|
|
25
25
|
|
|
26
|
-
- 被测 baseUrl(来自 `
|
|
26
|
+
- 被测 baseUrl(来自 `{testRoot}/rag/context.md` 的 `environmentProbe=reachable` URL;默认 `testcase/frontend/rag/context.md`)
|
|
27
27
|
- 目标 Wave 的 AC 清单(回归目录中本 Wave 范围的行)
|
|
28
28
|
- 既有台账(增量更新,不整表重写)
|
|
29
29
|
|
|
@@ -56,7 +56,7 @@ references:
|
|
|
56
56
|
|
|
57
57
|
## 输出
|
|
58
58
|
|
|
59
|
-
- `testcase/frontend/rag/ui-anchors.md
|
|
59
|
+
- `{testRoot}/rag/ui-anchors.md`(增量更新;默认 `testcase/frontend/rag/ui-anchors.md`)
|
|
60
60
|
- 侦察小结(对话内输出,不落盘;结论回写 PRD 草稿的锚点引用)
|
|
61
61
|
|
|
62
62
|
## References
|
|
@@ -55,7 +55,7 @@ request
|
|
|
55
55
|
|
|
56
56
|
## 推荐执行流程
|
|
57
57
|
|
|
58
|
-
使用 `testcase/frontend/rag/context.md
|
|
58
|
+
使用 `{testRoot}/rag/context.md`(默认 `testcase/frontend/rag/context.md`)中已由 environment shell 标记为 `reachable` 的非生产 `baseUrl` 和默认浏览器 session;不得创建 named session。交互前先获取 snapshot,并只使用 snapshot 中可见的 ref 或已知安全 locator。截图写入当前 case 的 `{evidenceDir}/<caseId>/`,canonical 文件名仍是 `final.png`。
|
|
59
59
|
|
|
60
60
|
```text
|
|
61
61
|
playwright-cli open --browser=chrome http://localhost:5173
|
|
@@ -13,10 +13,12 @@ description: 根据 FE-test RAG 知识包生成可由受限 playwright_cli runti
|
|
|
13
13
|
|
|
14
14
|
## 输入与边界
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
|
|
16
|
+
默认产物根是 `testcase/frontend/`。若任务配置了 `frontendTest.testRoot`,把下列路径中的 `testcase/frontend/` 整段替换为该根(不要只替换 `testcase/`)。
|
|
17
|
+
|
|
18
|
+
- 只读取 `{testRoot}/rag/context.md`、`coverage-map.md` 与已有 `{testRoot}/cases/`。
|
|
19
|
+
- 若存在 `{testRoot}/rag/ui-anchors.md` 锚点台账,必须一并读取并作为 UI 断言的唯一事实源;台账缺失时按 RAG 知识包生成,但不得引用台账外的具体控件字面(缺失信息标 `blocked`)。
|
|
20
|
+
- 只写 `{testRoot}/cases/**`;不得回读 PRD、读取 `.harness/`,或写
|
|
21
|
+
`{testRoot}/evidence/**`。
|
|
20
22
|
- 所有 API、字段限制、状态流转、数据来源、SLA、URL 与账号要求必须能在 RAG 知识包中追溯。
|
|
21
23
|
缺失信息标记 `blocked` 或“需人工确认”,不得猜测。
|
|
22
24
|
|
|
@@ -38,10 +40,10 @@ description: 根据 FE-test RAG 知识包生成可由受限 playwright_cli runti
|
|
|
38
40
|
(例:`FE-LOGIN-001-core`)。禁止把验收标准写成 caseId。
|
|
39
41
|
- `acIds` = **验收标准 ID 列表**,形态 `AC-FE-*` / `AC-*`
|
|
40
42
|
(例:`["AC-FE-001"]`)。禁止把用例 ID 放进 acIds。
|
|
41
|
-
- `casePath` 必须等于 `testcase/frontend/cases/<caseId>.md
|
|
42
|
-
`evidenceDir` 必须等于 `testcase/frontend/evidence/<caseId
|
|
43
|
+
- `casePath` 必须等于 `{casesDir}/<caseId>.md`(默认 `testcase/frontend/cases/<caseId>.md`);
|
|
44
|
+
`evidenceDir` 必须等于 `{evidenceDir}/<caseId>/`(默认 `testcase/frontend/evidence/<caseId>/`)。
|
|
43
45
|
- `manifest` 使用 `schemaVersion: 1`,每项只含 `caseId`、`casePath`、`dimension`、`acIds`、
|
|
44
|
-
`evidenceDir`。所有 ID、路径和 evidenceDir 必须唯一,并位于 `
|
|
46
|
+
`evidenceDir`。所有 ID、路径和 evidenceDir 必须唯一,并位于 `{testRoot}/` 内。
|
|
45
47
|
- `index.md` 按功能点列出 case、维度、AC、数据依赖、API 映射和预期执行状态。
|
|
46
48
|
|
|
47
49
|
每个 case 必须包含:
|
|
@@ -73,7 +75,7 @@ description: 根据 FE-test RAG 知识包生成可由受限 playwright_cli runti
|
|
|
73
75
|
7. 明确的 UI/API 预期与数据清理结果;无法满足的环境或数据依赖必须写为 `blocked`。
|
|
74
76
|
|
|
75
77
|
所有文件型输出均由 controller 绑定到执行节点提供的
|
|
76
|
-
`testcase/frontend/evidence/<case-id
|
|
78
|
+
`{evidenceDir}/<case-id>/` 工作目录(默认 `testcase/frontend/evidence/<case-id>/`)。截图一律使用 canonical 语法
|
|
77
79
|
`playwright-cli screenshot --filename final.png`;如确有从最新 snapshot 解析出的真实元素 ref,写为
|
|
78
80
|
`playwright-cli screenshot e5 --filename final.png`。`pdf` 必须写为
|
|
79
81
|
`playwright-cli pdf --filename final.pdf`。`snapshot` 无 filename 时仅返回响应;需要文件时使用
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{ag as o,ah as n}from"./mermaid.core-C-D3ZRUa.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-GF5L2VYU-DB2G4H8V.js";import{_ as i}from"./mermaid.core-C-D3ZRUa.js";import"./chunk-5VM5RSS4-SLBibhf5.js";import"./chunk-XXDRQBXY-BKkkC4VA.js";import"./chunk-KBJHAD2P-CG78FGkn.js";import"./chunk-2GRJ4B5K-DF_YSvOS.js";import"./index-D5zGX6fG.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-GF5L2VYU-DB2G4H8V.js";import{_ as i}from"./mermaid.core-C-D3ZRUa.js";import"./chunk-5VM5RSS4-SLBibhf5.js";import"./chunk-XXDRQBXY-BKkkC4VA.js";import"./chunk-KBJHAD2P-CG78FGkn.js";import"./chunk-2GRJ4B5K-DF_YSvOS.js";import"./index-D5zGX6fG.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
|