@epoint-testtech/ep-stage-skill 0.0.3-alpha.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.
Files changed (151) hide show
  1. package/SKILL.md +27 -0
  2. package/codex-skill/ep-stage/create-project/SKILL.md +59 -0
  3. package/codex-skill/ep-stage/glue-test/SKILL.md +258 -0
  4. package/codex-skill/ep-stage/glue-test/references/crud-pipeline.md +139 -0
  5. package/codex-skill/ep-stage/glue-test/references/gap-review-protocol.md +43 -0
  6. package/codex-skill/ep-stage/glue-test/references/harness-principles.md +46 -0
  7. package/codex-skill/ep-stage/glue-test/scripts/generate-crud-spec.mjs +149 -0
  8. package/codex-skill/ep-stage/glue-testcase/SKILL.md +31 -0
  9. package/codex-skill/ep-stage/glue-testcase/examples/observable-testcase.json +40 -0
  10. package/codex-skill/ep-stage/glue-testcase/references/testcase-schema.md +67 -0
  11. package/codex-skill/ep-stage/recording-to-glue/SKILL.md +27 -0
  12. package/codex-skill/ep-stage/scripts/validate-skill.mjs +73 -0
  13. package/dist/src/capability/coverage-diff.d.ts +34 -0
  14. package/dist/src/capability/coverage-diff.d.ts.map +1 -0
  15. package/dist/src/capability/coverage-diff.js +91 -0
  16. package/dist/src/capability/page-structure.d.ts +31 -0
  17. package/dist/src/capability/page-structure.d.ts.map +1 -0
  18. package/dist/src/capability/page-structure.js +50 -0
  19. package/dist/src/capability/scenario-inference.d.ts +36 -0
  20. package/dist/src/capability/scenario-inference.d.ts.map +1 -0
  21. package/dist/src/capability/scenario-inference.js +114 -0
  22. package/dist/src/cli/generate-crud-contract.d.ts +2 -0
  23. package/dist/src/cli/generate-crud-contract.d.ts.map +1 -0
  24. package/dist/src/cli/generate-crud-contract.js +77 -0
  25. package/dist/src/cli/generate-playwright-tests.d.ts +30 -0
  26. package/dist/src/cli/generate-playwright-tests.d.ts.map +1 -0
  27. package/dist/src/cli/generate-playwright-tests.js +81 -0
  28. package/dist/src/cli/run-gap-pipeline.d.ts +256 -0
  29. package/dist/src/cli/run-gap-pipeline.d.ts.map +1 -0
  30. package/dist/src/cli/run-gap-pipeline.js +1468 -0
  31. package/dist/src/context/stage-context.d.ts +63 -0
  32. package/dist/src/context/stage-context.d.ts.map +1 -0
  33. package/dist/src/context/stage-context.js +297 -0
  34. package/dist/src/contracts/crud-business-module.d.ts +645 -0
  35. package/dist/src/contracts/crud-business-module.d.ts.map +1 -0
  36. package/dist/src/contracts/crud-business-module.js +1 -0
  37. package/dist/src/contracts/gap-inference.d.ts +213 -0
  38. package/dist/src/contracts/gap-inference.d.ts.map +1 -0
  39. package/dist/src/contracts/gap-inference.js +11 -0
  40. package/dist/src/contracts/observable-chain.d.ts +250 -0
  41. package/dist/src/contracts/observable-chain.d.ts.map +1 -0
  42. package/dist/src/contracts/observable-chain.js +1 -0
  43. package/dist/src/extractors/code-list.d.ts +40 -0
  44. package/dist/src/extractors/code-list.d.ts.map +1 -0
  45. package/dist/src/extractors/code-list.js +225 -0
  46. package/dist/src/extractors/html-page.d.ts +67 -0
  47. package/dist/src/extractors/html-page.d.ts.map +1 -0
  48. package/dist/src/extractors/html-page.js +195 -0
  49. package/dist/src/extractors/java-action.d.ts +8 -0
  50. package/dist/src/extractors/java-action.d.ts.map +1 -0
  51. package/dist/src/extractors/java-action.js +53 -0
  52. package/dist/src/extractors/spec-yaml.d.ts +28 -0
  53. package/dist/src/extractors/spec-yaml.d.ts.map +1 -0
  54. package/dist/src/extractors/spec-yaml.js +29 -0
  55. package/dist/src/gap-planner/action-candidates.d.ts +9 -0
  56. package/dist/src/gap-planner/action-candidates.d.ts.map +1 -0
  57. package/dist/src/gap-planner/action-candidates.js +66 -0
  58. package/dist/src/gap-planner/list-gap-workflows.d.ts +17 -0
  59. package/dist/src/gap-planner/list-gap-workflows.d.ts.map +1 -0
  60. package/dist/src/gap-planner/list-gap-workflows.js +47 -0
  61. package/dist/src/gap-planner/plan-agent-workflows.d.ts +26 -0
  62. package/dist/src/gap-planner/plan-agent-workflows.d.ts.map +1 -0
  63. package/dist/src/gap-planner/plan-agent-workflows.js +116 -0
  64. package/dist/src/gap-planner/skeleton-coverage.d.ts +9 -0
  65. package/dist/src/gap-planner/skeleton-coverage.d.ts.map +1 -0
  66. package/dist/src/gap-planner/skeleton-coverage.js +41 -0
  67. package/dist/src/gap-planner/stable-id.d.ts +16 -0
  68. package/dist/src/gap-planner/stable-id.d.ts.map +1 -0
  69. package/dist/src/gap-planner/stable-id.js +19 -0
  70. package/dist/src/generalization/generalization-eval.d.ts +71 -0
  71. package/dist/src/generalization/generalization-eval.d.ts.map +1 -0
  72. package/dist/src/generalization/generalization-eval.js +53 -0
  73. package/dist/src/generators/agent-inferred-workflow-script.d.ts +22 -0
  74. package/dist/src/generators/agent-inferred-workflow-script.d.ts.map +1 -0
  75. package/dist/src/generators/agent-inferred-workflow-script.js +230 -0
  76. package/dist/src/generators/stage-skeleton-script.d.ts +107 -0
  77. package/dist/src/generators/stage-skeleton-script.d.ts.map +1 -0
  78. package/dist/src/generators/stage-skeleton-script.js +607 -0
  79. package/dist/src/index.d.ts +52 -0
  80. package/dist/src/index.d.ts.map +1 -0
  81. package/dist/src/index.js +26 -0
  82. package/dist/src/material/material-inventory.d.ts +92 -0
  83. package/dist/src/material/material-inventory.d.ts.map +1 -0
  84. package/dist/src/material/material-inventory.js +191 -0
  85. package/dist/src/normalizers/crud-contract.d.ts +107 -0
  86. package/dist/src/normalizers/crud-contract.d.ts.map +1 -0
  87. package/dist/src/normalizers/crud-contract.js +1068 -0
  88. package/dist/src/testcase/testcase-generator.d.ts +43 -0
  89. package/dist/src/testcase/testcase-generator.d.ts.map +1 -0
  90. package/dist/src/testcase/testcase-generator.js +152 -0
  91. package/dist/src/testcase/testcase-skeleton.d.ts +91 -0
  92. package/dist/src/testcase/testcase-skeleton.d.ts.map +1 -0
  93. package/dist/src/testcase/testcase-skeleton.js +121 -0
  94. package/dist/src/testcase/testcase-spec-assembly.d.ts +11 -0
  95. package/dist/src/testcase/testcase-spec-assembly.d.ts.map +1 -0
  96. package/dist/src/testcase/testcase-spec-assembly.js +24 -0
  97. package/dist/src/trace/review-summary.d.ts +17 -0
  98. package/dist/src/trace/review-summary.d.ts.map +1 -0
  99. package/dist/src/trace/review-summary.js +34 -0
  100. package/dist/src/trace/trace-writer.d.ts +17 -0
  101. package/dist/src/trace/trace-writer.d.ts.map +1 -0
  102. package/dist/src/trace/trace-writer.js +81 -0
  103. package/dist/test/crud-contract.test.d.ts +2 -0
  104. package/dist/test/crud-contract.test.d.ts.map +1 -0
  105. package/dist/test/crud-contract.test.js +819 -0
  106. package/dist/test/gap-inference.test.d.ts +2 -0
  107. package/dist/test/gap-inference.test.d.ts.map +1 -0
  108. package/dist/test/gap-inference.test.js +597 -0
  109. package/dist/test/generalization.test.d.ts +2 -0
  110. package/dist/test/generalization.test.d.ts.map +1 -0
  111. package/dist/test/generalization.test.js +73 -0
  112. package/dist/test/material-inventory.test.d.ts +2 -0
  113. package/dist/test/material-inventory.test.d.ts.map +1 -0
  114. package/dist/test/material-inventory.test.js +141 -0
  115. package/dist/test/observable-chain.test.d.ts +2 -0
  116. package/dist/test/observable-chain.test.d.ts.map +1 -0
  117. package/dist/test/observable-chain.test.js +123 -0
  118. package/dist/test/observable-pipeline.test.d.ts +2 -0
  119. package/dist/test/observable-pipeline.test.d.ts.map +1 -0
  120. package/dist/test/observable-pipeline.test.js +461 -0
  121. package/dist/test/page-structure.test.d.ts +2 -0
  122. package/dist/test/page-structure.test.d.ts.map +1 -0
  123. package/dist/test/page-structure.test.js +45 -0
  124. package/dist/test/scenario-inference.test.d.ts +2 -0
  125. package/dist/test/scenario-inference.test.d.ts.map +1 -0
  126. package/dist/test/scenario-inference.test.js +73 -0
  127. package/dist/test/stage-context.test.d.ts +2 -0
  128. package/dist/test/stage-context.test.d.ts.map +1 -0
  129. package/dist/test/stage-context.test.js +263 -0
  130. package/dist/test/testcase-generator.test.d.ts +2 -0
  131. package/dist/test/testcase-generator.test.d.ts.map +1 -0
  132. package/dist/test/testcase-generator.test.js +276 -0
  133. package/dist/test/testcase-skeleton.test.d.ts +2 -0
  134. package/dist/test/testcase-skeleton.test.d.ts.map +1 -0
  135. package/dist/test/testcase-skeleton.test.js +185 -0
  136. package/dist/test/testcase-spec-assembly.test.d.ts +2 -0
  137. package/dist/test/testcase-spec-assembly.test.d.ts.map +1 -0
  138. package/dist/test/testcase-spec-assembly.test.js +105 -0
  139. package/dist/vitest.config.d.ts +3 -0
  140. package/dist/vitest.config.d.ts.map +1 -0
  141. package/dist/vitest.config.js +7 -0
  142. package/docs/README.md +134 -0
  143. package/docs/mvp-usage-guide.md +298 -0
  144. package/examples/schemeresource-observable-docs/schemeresource.context.md +20 -0
  145. package/examples/schemeresource.module-hints.json +38 -0
  146. package/examples/schemeresource.observable.code_list.md +37 -0
  147. package/examples/zwplace-observable-docs/zwplace.context.md +16 -0
  148. package/examples/zwplace-placecategory-validation.json +29 -0
  149. package/examples/zwplace.module-hints.json +69 -0
  150. package/examples/zwplace.observable.code_list.md +37 -0
  151. package/package.json +38 -0
@@ -0,0 +1,66 @@
1
+ /**
2
+ * 从 CrudBusinessModuleContract 的页面按钮、行操作、弹窗动作归一化 ActionCandidate。
3
+ *
4
+ * 只收集事实,不判断是否生成测试。证据来源通过 sourceRefToEvidenceRef 转换保障类型安全。
5
+ */
6
+ import { stableActionId } from './stable-id.js';
7
+ /**
8
+ * 将 SourceRef 转换为 EvidenceRef,确保类型安全。
9
+ */
10
+ function sourceRefToEvidenceRef(source, overrides) {
11
+ return {
12
+ kind: overrides.kind,
13
+ path: source.path,
14
+ locator: overrides.locator,
15
+ evidenceText: overrides.evidenceText ?? source.evidence,
16
+ confidence: source.confidence,
17
+ };
18
+ }
19
+ function pageCandidates(moduleId, page) {
20
+ const buttonCandidates = page.buttons.map((button) => ({
21
+ actionId: stableActionId({
22
+ moduleId,
23
+ pageId: page.pageId,
24
+ actionType: 'page_button',
25
+ label: button.label,
26
+ }),
27
+ moduleId,
28
+ pageId: page.pageId,
29
+ pageRole: page.role,
30
+ actionType: 'page_button',
31
+ label: button.label,
32
+ onclick: button.onclick,
33
+ locator: button.locator,
34
+ evidence: button.sources.map((source) => sourceRefToEvidenceRef(source, {
35
+ kind: 'html_button',
36
+ locator: button.locator,
37
+ evidenceText: button.label,
38
+ })),
39
+ sourceRefs: button.sources,
40
+ }));
41
+ const rowActionCandidates = page.grids.flatMap((grid) => (grid.rowActions ?? []).map((action) => ({
42
+ actionId: stableActionId({
43
+ moduleId,
44
+ pageId: page.pageId,
45
+ actionType: 'row_action',
46
+ label: action.label,
47
+ }),
48
+ moduleId,
49
+ pageId: page.pageId,
50
+ pageRole: page.role,
51
+ actionType: 'row_action',
52
+ label: action.label,
53
+ onclick: action.onclick,
54
+ locator: action.locator,
55
+ evidence: action.sources.map((source) => sourceRefToEvidenceRef(source, {
56
+ kind: 'grid_row_action',
57
+ locator: action.locator,
58
+ evidenceText: action.label,
59
+ })),
60
+ sourceRefs: action.sources,
61
+ })));
62
+ return [...buttonCandidates, ...rowActionCandidates];
63
+ }
64
+ export function collectActionCandidates(contract) {
65
+ return Object.values(contract.pages).flatMap((page) => pageCandidates(contract.module.id, page));
66
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Gap 清单生成:把契约中的 agentInferredWorkflows 整理为可供人工筛选的清单。
3
+ *
4
+ * 这是「gap 人工筛选 gate」的离线纯逻辑:物料钻探后、进入 runtime 推理前,
5
+ * 列出每个 gap 的状态、是否已有业务意图、建议进入的推理模式,供人工剔除无效 gap。
6
+ *
7
+ * @module gap-planner/list-gap-workflows
8
+ */
9
+ import type { CrudBusinessModuleContract } from '../contracts/crud-business-module.js';
10
+ import type { GapListItem } from '../contracts/gap-inference.js';
11
+ /**
12
+ * 把契约中的 gap 工作流整理为人工筛选清单。
13
+ * @param contract CRUD 业务模块契约
14
+ * @returns gap 清单条目数组
15
+ */
16
+ export declare function listGapWorkflows(contract: CrudBusinessModuleContract): GapListItem[];
17
+ //# sourceMappingURL=list-gap-workflows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-gap-workflows.d.ts","sourceRoot":"","sources":["../../../src/gap-planner/list-gap-workflows.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AACvF,OAAO,KAAK,EAAyB,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAoBxF;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,0BAA0B,GAAG,WAAW,EAAE,CAgBpF"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Gap 清单生成:把契约中的 agentInferredWorkflows 整理为可供人工筛选的清单。
3
+ *
4
+ * 这是「gap 人工筛选 gate」的离线纯逻辑:物料钻探后、进入 runtime 推理前,
5
+ * 列出每个 gap 的状态、是否已有业务意图、建议进入的推理模式,供人工剔除无效 gap。
6
+ *
7
+ * @module gap-planner/list-gap-workflows
8
+ */
9
+ /**
10
+ * 根据 gap 状态建议进入的 runtime 模式。
11
+ * - 已处理(resolved/generated/passed/failed)→ skip
12
+ * - 有业务意图(planned)→ infer(引导模式)
13
+ * - 其余(candidate/unresolved/needs_review)→ explore(探索模式)
14
+ * @param workflow gap 工作流
15
+ * @returns 建议模式
16
+ */
17
+ function suggestMode(workflow) {
18
+ if (['resolved', 'generated', 'passed', 'failed'].includes(workflow.status)) {
19
+ return 'skip';
20
+ }
21
+ if (workflow.status === 'planned') {
22
+ return 'infer';
23
+ }
24
+ return 'explore';
25
+ }
26
+ /**
27
+ * 把契约中的 gap 工作流整理为人工筛选清单。
28
+ * @param contract CRUD 业务模块契约
29
+ * @returns gap 清单条目数组
30
+ */
31
+ export function listGapWorkflows(contract) {
32
+ return (contract.agentInferredWorkflows ?? []).map((workflow) => {
33
+ const candidate = workflow.actionCandidates[0];
34
+ return {
35
+ workflowId: workflow.workflowId,
36
+ label: candidate?.label ?? workflow.workflowId,
37
+ status: workflow.status,
38
+ actionType: candidate?.actionType ?? 'page_button',
39
+ pageRole: candidate?.pageRole ?? 'unknown',
40
+ hasIntent: workflow.businessIntent.status === 'resolved',
41
+ hasAssertion: workflow.assertionExpectation.status === 'resolved',
42
+ suggestedMode: suggestMode(workflow),
43
+ evidenceSummary: Array.from(new Set(workflow.evidence.map((item) => item.kind))).join(', '),
44
+ triggerReason: workflow.triggerReason,
45
+ };
46
+ });
47
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Gap Planner:执行 coverage diff,输出 AgentInferredWorkflow[]。
3
+ *
4
+ * 核心逻辑:
5
+ * 1. 从契约收集全部 ActionCandidate
6
+ * 2. 构造 SkeletonCoverage 获取已覆盖标签
7
+ * 3. 差集 = 未覆盖动作
8
+ * 4. 按证据强度分级:planned / candidate / unresolved
9
+ *
10
+ * 证据组合规则(设计文档 §6.1):
11
+ * - HTML 按钮 + ModuleHints(expectedIntent + expectedAssertion) → planned
12
+ * - HTML 按钮 + Java Action messages → planned(断言来自 Java)
13
+ * - 只有 HTML 按钮 → candidate
14
+ * - ModuleHints include: false → 排除
15
+ */
16
+ import type { CrudBusinessModuleContract, ModuleHints } from '../contracts/crud-business-module.js';
17
+ import type { AgentInferredWorkflow } from '../contracts/gap-inference.js';
18
+ import type { ExtractedJavaAction } from '../extractors/java-action.js';
19
+ type PlanInput = {
20
+ contract: CrudBusinessModuleContract;
21
+ actions: ExtractedJavaAction[];
22
+ hints?: ModuleHints;
23
+ };
24
+ export declare function planAgentInferredWorkflows(input: PlanInput): AgentInferredWorkflow[];
25
+ export {};
26
+ //# sourceMappingURL=plan-agent-workflows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-agent-workflows.d.ts","sourceRoot":"","sources":["../../../src/gap-planner/plan-agent-workflows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,0BAA0B,EAAe,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACjH,OAAO,KAAK,EAAE,qBAAqB,EAAqC,MAAM,+BAA+B,CAAC;AAC9G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAKxE,KAAK,SAAS,GAAG;IACf,QAAQ,EAAE,0BAA0B,CAAC;IACrC,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AAsHF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,SAAS,GAAG,qBAAqB,EAAE,CASpF"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Gap Planner:执行 coverage diff,输出 AgentInferredWorkflow[]。
3
+ *
4
+ * 核心逻辑:
5
+ * 1. 从契约收集全部 ActionCandidate
6
+ * 2. 构造 SkeletonCoverage 获取已覆盖标签
7
+ * 3. 差集 = 未覆盖动作
8
+ * 4. 按证据强度分级:planned / candidate / unresolved
9
+ *
10
+ * 证据组合规则(设计文档 §6.1):
11
+ * - HTML 按钮 + ModuleHints(expectedIntent + expectedAssertion) → planned
12
+ * - HTML 按钮 + Java Action messages → planned(断言来自 Java)
13
+ * - 只有 HTML 按钮 → candidate
14
+ * - ModuleHints include: false → 排除
15
+ */
16
+ import { collectActionCandidates } from './action-candidates.js';
17
+ import { createSkeletonCoverage } from './skeleton-coverage.js';
18
+ import { stableWorkflowId } from './stable-id.js';
19
+ function allCoveredLabels(coverage) {
20
+ return new Set(Object.values(coverage.coveredActionLabels).flatMap((labels) => labels ?? []));
21
+ }
22
+ function hintForCandidate(candidate, hints) {
23
+ return hints?.customWorkflows?.find((workflow) => workflow.label === candidate.label || workflow.actionAlias?.includes(candidate.label));
24
+ }
25
+ function hintEvidence(label) {
26
+ return {
27
+ kind: 'test_policy_hint',
28
+ path: 'ModuleHints.customWorkflows',
29
+ evidenceText: label,
30
+ confidence: 'high',
31
+ };
32
+ }
33
+ /**
34
+ * 从 Java Action 中提取与候选动作匹配的回调消息证据。
35
+ * 优先使用 HTML 中的 onclick 方法名绑定 Java Action,避免中文标签模糊匹配。
36
+ */
37
+ function onclickMethodNames(onclick) {
38
+ if (!onclick) {
39
+ return [];
40
+ }
41
+ return Array.from(new Set(Array.from(onclick.matchAll(/([A-Za-z_][A-Za-z0-9_]*)\s*\(/g), (match) => match[1])));
42
+ }
43
+ function matchesJavaMethod(methodName, action) {
44
+ return action.methods.some((method) => method === methodName);
45
+ }
46
+ function javaActionEvidence(candidate, actions) {
47
+ const methodNames = onclickMethodNames(candidate.onclick);
48
+ if (methodNames.length === 0) {
49
+ return [];
50
+ }
51
+ const matchedActions = actions.filter((action) => methodNames.some((methodName) => matchesJavaMethod(methodName, action)));
52
+ return matchedActions.flatMap((action) => [
53
+ ...methodNames
54
+ .filter((methodName) => matchesJavaMethod(methodName, action))
55
+ .map((methodName) => ({
56
+ kind: 'java_action_method',
57
+ path: action.path,
58
+ evidenceText: methodName,
59
+ confidence: 'high',
60
+ })),
61
+ ...action.messages.map((message) => ({
62
+ kind: 'java_callback_message',
63
+ path: action.path,
64
+ evidenceText: message,
65
+ confidence: 'medium',
66
+ })),
67
+ ]);
68
+ }
69
+ function buildWorkflow(candidate, javaEvidence, hints) {
70
+ const hint = hintForCandidate(candidate, hints);
71
+ if (hint?.include === false) {
72
+ return undefined;
73
+ }
74
+ const hasIntent = Boolean(hint?.expectedIntent);
75
+ const javaAssertion = javaEvidence.find((item) => item.kind === 'java_callback_message')?.evidenceText;
76
+ const hasAssertion = Boolean(hint?.expectedAssertion ?? javaAssertion);
77
+ const status = hasIntent && hasAssertion ? 'planned' : 'candidate';
78
+ const evidence = [
79
+ ...candidate.evidence,
80
+ ...(hint ? [hintEvidence(candidate.label)] : []),
81
+ ...javaEvidence,
82
+ ];
83
+ return {
84
+ workflowId: stableWorkflowId({
85
+ moduleId: candidate.moduleId,
86
+ pageId: candidate.pageId,
87
+ actionType: candidate.actionType,
88
+ label: candidate.label,
89
+ }),
90
+ workflowKind: 'custom',
91
+ executionSource: 'agent_inferred',
92
+ status,
93
+ businessIntent: hasIntent
94
+ ? { status: 'resolved', value: hint.expectedIntent }
95
+ : { status: 'unresolved', unresolved: { reason: '缺少业务意图证据', expectedProvider: 'tester', suggestedFormat: '{ "customWorkflows": [{ "expectedIntent": "..." }] }' } },
96
+ elementActions: { status: 'resolved', value: [`点击「${candidate.label}」按钮`] },
97
+ assertionExpectation: hasAssertion
98
+ ? { status: 'resolved', value: hint?.expectedAssertion ?? javaAssertion }
99
+ : { status: 'unresolved', unresolved: { reason: '缺少断言预期证据', expectedProvider: 'tester', suggestedFormat: '{ "customWorkflows": [{ "expectedAssertion": "..." }] }' } },
100
+ actionCandidates: [candidate],
101
+ triggerReason: '该动作不属于 CRUD + Export 骨架覆盖范围。',
102
+ evidence,
103
+ reviewReason: status === 'planned'
104
+ ? '由 gap-planner 基于上游物料和 ModuleHints 识别为自定义业务动作。'
105
+ : `已发现「${candidate.label}」动作,但缺少业务意图或断言预期证据。`,
106
+ };
107
+ }
108
+ export function planAgentInferredWorkflows(input) {
109
+ const coverage = createSkeletonCoverage(input.contract, input.hints);
110
+ const coveredLabels = allCoveredLabels(coverage);
111
+ const candidates = collectActionCandidates(input.contract);
112
+ return candidates
113
+ .filter((candidate) => !coveredLabels.has(candidate.label))
114
+ .map((candidate) => buildWorkflow(candidate, javaActionEvidence(candidate, input.actions), input.hints))
115
+ .filter((workflow) => Boolean(workflow));
116
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 从标准 flow、ModuleHints.buttonAliases、内置辅助动作集合构造 SkeletonCoverage。
3
+ *
4
+ * 覆盖模型不绑定具体系统页面,只描述能力边界。
5
+ */
6
+ import type { SkeletonCoverage } from '../contracts/gap-inference.js';
7
+ import type { CrudBusinessModuleContract, ModuleHints } from '../contracts/crud-business-module.js';
8
+ export declare function createSkeletonCoverage(contract: CrudBusinessModuleContract, hints?: ModuleHints): SkeletonCoverage;
9
+ //# sourceMappingURL=skeleton-coverage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skeleton-coverage.d.ts","sourceRoot":"","sources":["../../../src/gap-planner/skeleton-coverage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,KAAK,EAAE,0BAA0B,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAMpG,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,0BAA0B,EACpC,KAAK,CAAC,EAAE,WAAW,GAClB,gBAAgB,CAgClB"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * 从标准 flow、ModuleHints.buttonAliases、内置辅助动作集合构造 SkeletonCoverage。
3
+ *
4
+ * 覆盖模型不绑定具体系统页面,只描述能力边界。
5
+ */
6
+ function compact(values) {
7
+ return Array.from(new Set(values.filter((value) => Boolean(value))));
8
+ }
9
+ export function createSkeletonCoverage(contract, hints) {
10
+ return {
11
+ coverageId: 'crud-plus-export/v1',
12
+ coveredWorkflows: ['crud.create', 'crud.read', 'crud.update', 'crud.delete', 'export.run'],
13
+ coveredActionLabels: {
14
+ create: compact([
15
+ contract.flows.create?.entryButton.label,
16
+ ...(hints?.buttonAliases?.create ?? []),
17
+ ]),
18
+ read: compact([
19
+ contract.flows.search?.submitControl.label,
20
+ ...(hints?.buttonAliases?.search ?? []),
21
+ ]),
22
+ update: compact([
23
+ contract.flows.update?.entryAction.label,
24
+ ...(hints?.buttonAliases?.edit ?? []),
25
+ ]),
26
+ delete: compact([
27
+ contract.flows.delete?.entryButton.label,
28
+ ...(hints?.buttonAliases?.delete ?? []),
29
+ ]),
30
+ export: compact(hints?.buttonAliases?.export ?? ['导出']),
31
+ internal: compact([
32
+ '保存并关闭',
33
+ '确定',
34
+ '取消',
35
+ '关闭',
36
+ ...(hints?.buttonAliases?.saveAndClose ?? []),
37
+ ...(hints?.buttonAliases?.confirm ?? []),
38
+ ]),
39
+ },
40
+ };
41
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * 稳定主键生成器。
3
+ *
4
+ * 基于 moduleId/pageId/actionType/label 生成确定性 SHA1 hash,
5
+ * 禁止 Agent 自由翻译中文按钮。确保跨运行幂等。
6
+ */
7
+ export type StableActionIdentity = {
8
+ moduleId: string;
9
+ pageId: string;
10
+ actionType: string;
11
+ label: string;
12
+ };
13
+ export declare function stableActionHash(input: StableActionIdentity): string;
14
+ export declare function stableActionId(input: StableActionIdentity): string;
15
+ export declare function stableWorkflowId(input: StableActionIdentity): string;
16
+ //# sourceMappingURL=stable-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stable-id.d.ts","sourceRoot":"","sources":["../../../src/gap-planner/stable-id.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAKpE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAElE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAEpE"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 稳定主键生成器。
3
+ *
4
+ * 基于 moduleId/pageId/actionType/label 生成确定性 SHA1 hash,
5
+ * 禁止 Agent 自由翻译中文按钮。确保跨运行幂等。
6
+ */
7
+ import { createHash } from 'node:crypto';
8
+ export function stableActionHash(input) {
9
+ return createHash('sha1')
10
+ .update(`${input.moduleId}|${input.pageId}|${input.actionType}|${input.label}`)
11
+ .digest('hex')
12
+ .slice(0, 8);
13
+ }
14
+ export function stableActionId(input) {
15
+ return `${input.moduleId}.${input.pageId}.${input.actionType}.${stableActionHash(input)}`;
16
+ }
17
+ export function stableWorkflowId(input) {
18
+ return `${input.moduleId}.custom_${stableActionHash(input)}`;
19
+ }
@@ -0,0 +1,71 @@
1
+ import type { CoverageDiffTrace, ScenarioInferenceTrace } from '../contracts/observable-chain.js';
2
+ import type { ResolvedMaterialInventory } from '../material/material-inventory.js';
3
+ /**
4
+ * 物料解析槽位状态(与 MaterialResolution/ValueResolution 的 status 同构)。
5
+ */
6
+ export type MaterialResolutionStatus = 'resolved' | 'candidate' | 'missing';
7
+ /**
8
+ * 单次 run 的泛化指标快照,供跨系统/跨 run 对比。
9
+ *
10
+ * @property materialResolution - 四类目录槽位的解析状态。
11
+ * @property scenarios - 推理出的场景候选。
12
+ * @property coverage - covered/gapCandidate/nestedCrud 计数。
13
+ * @property baselineWorkflowCount - 基线 workflow 总数。
14
+ */
15
+ export type GeneralizationRunMetrics = {
16
+ moduleId: string;
17
+ materialResolution: {
18
+ docsDir: MaterialResolutionStatus;
19
+ webappDir: MaterialResolutionStatus;
20
+ javaActionsDir: MaterialResolutionStatus;
21
+ hintsPath: MaterialResolutionStatus;
22
+ };
23
+ scenarios: string[];
24
+ coverage: {
25
+ covered: number;
26
+ gapCandidate: number;
27
+ nestedCrud: number;
28
+ };
29
+ baselineWorkflowCount: number;
30
+ };
31
+ /**
32
+ * summarizeGeneralizationRun 的入参。
33
+ */
34
+ export type SummarizeGeneralizationRunInput = {
35
+ moduleId: string;
36
+ resolvedInputs: ResolvedMaterialInventory;
37
+ scenario: ScenarioInferenceTrace;
38
+ coverageDiff: CoverageDiffTrace;
39
+ };
40
+ /**
41
+ * 两个系统/两次 run 的泛化差异对比结果。
42
+ */
43
+ export type GeneralizationComparison = {
44
+ moduleIds: [string, string];
45
+ scenariosOnlyInA: string[];
46
+ scenariosOnlyInB: string[];
47
+ coverageDelta: {
48
+ covered: number;
49
+ gapCandidate: number;
50
+ nestedCrud: number;
51
+ };
52
+ materialResolutionDivergence: string[];
53
+ };
54
+ /**
55
+ * 把单次 run 的可观测数据汇总为可对比的泛化指标快照。
56
+ *
57
+ * 只做指标汇总,不改任何阶段判定;"泛化是否达标"由人工解读对比结果。
58
+ *
59
+ * @param input - 物料解析、场景推理、coverage diff。
60
+ * @returns 泛化指标快照。
61
+ */
62
+ export declare function summarizeGeneralizationRun(input: SummarizeGeneralizationRunInput): GeneralizationRunMetrics;
63
+ /**
64
+ * 对比两个系统/两次 run 的泛化指标差异。
65
+ *
66
+ * @param a - 系统 A 的指标。
67
+ * @param b - 系统 B 的指标。
68
+ * @returns 场景差异、coverage delta(A-B)、物料解析分歧槽位。
69
+ */
70
+ export declare function compareObservableRuns(a: GeneralizationRunMetrics, b: GeneralizationRunMetrics): GeneralizationComparison;
71
+ //# sourceMappingURL=generalization-eval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generalization-eval.d.ts","sourceRoot":"","sources":["../../../src/generalization/generalization-eval.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,sBAAsB,EACvB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAEnF;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,CAAC;AAE5E;;;;;;;GAOG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE;QAClB,OAAO,EAAE,wBAAwB,CAAC;QAClC,SAAS,EAAE,wBAAwB,CAAC;QACpC,cAAc,EAAE,wBAAwB,CAAC;QACzC,SAAS,EAAE,wBAAwB,CAAC;KACrC,CAAC;IACF,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IACxE,qBAAqB,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,+BAA+B,GAAG;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,yBAAyB,CAAC;IAC1C,QAAQ,EAAE,sBAAsB,CAAC;IACjC,YAAY,EAAE,iBAAiB,CAAC;CACjC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7E,4BAA4B,EAAE,MAAM,EAAE,CAAC;CACxC,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,+BAA+B,GAAG,wBAAwB,CAmB3G;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,CAAC,EAAE,wBAAwB,EAC3B,CAAC,EAAE,wBAAwB,GAC1B,wBAAwB,CAkB1B"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * 把单次 run 的可观测数据汇总为可对比的泛化指标快照。
3
+ *
4
+ * 只做指标汇总,不改任何阶段判定;"泛化是否达标"由人工解读对比结果。
5
+ *
6
+ * @param input - 物料解析、场景推理、coverage diff。
7
+ * @returns 泛化指标快照。
8
+ */
9
+ export function summarizeGeneralizationRun(input) {
10
+ const baseline = input.coverageDiff.baseline;
11
+ return {
12
+ moduleId: input.moduleId,
13
+ materialResolution: {
14
+ docsDir: input.resolvedInputs.docsDir.status,
15
+ webappDir: input.resolvedInputs.webappDir.status,
16
+ javaActionsDir: input.resolvedInputs.javaActionsDir.status,
17
+ hintsPath: input.resolvedInputs.hintsPath.status,
18
+ },
19
+ scenarios: input.scenario.candidates.map((candidate) => candidate.scenario),
20
+ coverage: {
21
+ covered: input.coverageDiff.covered.length,
22
+ gapCandidate: input.coverageDiff.gapCandidates.length,
23
+ nestedCrud: input.coverageDiff.nestedCrudCandidates.length,
24
+ },
25
+ baselineWorkflowCount: baseline.plannedWorkflows.length + baseline.candidateWorkflows.length + baseline.unresolvedWorkflows.length,
26
+ };
27
+ }
28
+ /**
29
+ * 对比两个系统/两次 run 的泛化指标差异。
30
+ *
31
+ * @param a - 系统 A 的指标。
32
+ * @param b - 系统 B 的指标。
33
+ * @returns 场景差异、coverage delta(A-B)、物料解析分歧槽位。
34
+ */
35
+ export function compareObservableRuns(a, b) {
36
+ const keys = [
37
+ 'docsDir',
38
+ 'webappDir',
39
+ 'javaActionsDir',
40
+ 'hintsPath',
41
+ ];
42
+ return {
43
+ moduleIds: [a.moduleId, b.moduleId],
44
+ scenariosOnlyInA: a.scenarios.filter((scenario) => !b.scenarios.includes(scenario)),
45
+ scenariosOnlyInB: b.scenarios.filter((scenario) => !a.scenarios.includes(scenario)),
46
+ coverageDelta: {
47
+ covered: a.coverage.covered - b.coverage.covered,
48
+ gapCandidate: a.coverage.gapCandidate - b.coverage.gapCandidate,
49
+ nestedCrud: a.coverage.nestedCrud - b.coverage.nestedCrud,
50
+ },
51
+ materialResolutionDivergence: keys.filter((key) => a.materialResolution[key] !== b.materialResolution[key]),
52
+ };
53
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Agent 推理 Custom workflow 测试片段生成器。
3
+ *
4
+ * 渲染 executionSource='agent_inferred' 的 Playwright test 片段,
5
+ * 并提供幂等追加函数,避免重复追加到同一 spec。
6
+ */
7
+ import type { AgentInferredWorkflow } from '../contracts/gap-inference.js';
8
+ type RenderOptions = {
9
+ moduleId: string;
10
+ suiteTitle: string;
11
+ workflow: AgentInferredWorkflow;
12
+ };
13
+ type AppendOptions = {
14
+ source: string;
15
+ workflowId: string;
16
+ testTitle: string;
17
+ testSource: string;
18
+ };
19
+ export declare function renderAgentWorkflowTest(options: RenderOptions): string;
20
+ export declare function appendAgentWorkflowTest(options: AppendOptions): string;
21
+ export {};
22
+ //# sourceMappingURL=agent-inferred-workflow-script.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-inferred-workflow-script.d.ts","sourceRoot":"","sources":["../../../src/generators/agent-inferred-workflow-script.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAE3E,KAAK,aAAa,GAAG;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,qBAAqB,CAAC;CACjC,CAAC;AAEF,KAAK,aAAa,GAAG;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AA6LF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CA0DtE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAkBtE"}