@tea-agent/loop-agent 0.35.3 → 0.35.4-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,17 @@
1
1
  # 更新日志
2
2
 
3
- ## [Unreleased]
3
+ ## [0.35.4-beta.0] - 2026-08-15
4
+
5
+ > frontend prewrite 阻断误路由修正 + Mock 契约文档落地(beta,发布到 `beta` dist-tag)。
6
+
7
+ ### 改进
8
+
9
+ - prewrite 阻断导致的 writer `SKIPPED`(`skippedReason=frontend-prewrite-not-authorized`)失败路由从 `DependencyFailure`/`unblock dependency` 修正为 `ContractMismatch`/`fix-frontend-task-contract-and-regenerate-dag`;`dag report` / `dag doctor` 透传 `skippedReason`,对「Mock 策略契约缺陷需改 task.json + 重新生成 DAG」这类场景给出可操作建议,而非误导性的「解除依赖」
10
+
11
+ ### 文档
12
+
13
+ - `docs/templates/frontend-task-constraints.md` 的 Mock 约束从 TODO 改为可落地契约:要求从项目**探索**实际存在的确定性 Mock 验证命令(不硬编码命令名)并声明到 `task.json.frontendMock.verifyCommands`,说明生成期 `allowedMockStrategies` 来源、`mock-strategy-outside-allowed` 是生成期契约问题(修复 = 补契约 + 重新生成 DAG,plan-revision 无法修复)
14
+ - `docs/runtime/frontend-implementation-workflow.md` 的 task.json 输入标准补充 Mock 契约声明、命令探索与失败恢复指引
4
15
 
5
16
  ## [0.35.3] - 2026-08-15
6
17
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "0.35.3",
4
- "gitSha": "71e2505bbabe052302d753f25b4971e0bdb0f953",
5
- "builtAt": "2026-08-15T11:42:48.937Z"
3
+ "version": "0.35.4-beta.0",
4
+ "gitSha": "53128a51ad1db646a5fd8184d2de882a1a2211ee",
5
+ "builtAt": "2026-08-15T15:18:26.580Z"
6
6
  }
@@ -44,7 +44,9 @@ function routeToProductLine(input) {
44
44
  case "decision-envelope":
45
45
  return "NeedsHuman";
46
46
  case "skipped":
47
- return "DependencyFailure";
47
+ return input.skippedReason === "frontend-prewrite-not-authorized"
48
+ ? "ContractMismatch"
49
+ : "DependencyFailure";
48
50
  case "shell-command":
49
51
  if (/\b(ENOENT|PATH|command not found|No such file|not found in PATH|bash: .*: No such file)\b/i.test(raw)) {
50
52
  return "EnvFailure";
@@ -90,9 +92,12 @@ export function routeDagFailure(input) {
90
92
  if (!productLineFailureCategory)
91
93
  return {};
92
94
  const recommendedFollowUp = productLineFailureCategory === "ContractMismatch" &&
93
- isFrontendDesignGateNode(input.nodeId ?? "")
94
- ? "frontend-plan-revision-and-rerun"
95
- : FOLLOW_UP_BY_PRODUCT_LINE[productLineFailureCategory];
95
+ input.skippedReason === "frontend-prewrite-not-authorized"
96
+ ? "fix-frontend-task-contract-and-regenerate-dag"
97
+ : productLineFailureCategory === "ContractMismatch" &&
98
+ isFrontendDesignGateNode(input.nodeId ?? "")
99
+ ? "frontend-plan-revision-and-rerun"
100
+ : FOLLOW_UP_BY_PRODUCT_LINE[productLineFailureCategory];
96
101
  return {
97
102
  productLineFailureCategory,
98
103
  recommendedFollowUp,
@@ -743,6 +743,7 @@ function findDoctorFailureNode(state) {
743
743
  nodeId: state.pausedByNodeId,
744
744
  status: node?.status,
745
745
  rawFailureCategory: node?.failureCategory,
746
+ skippedReason: node?.skippedReason,
746
747
  };
747
748
  }
748
749
  const errorEntry = Object.entries(state.nodes).find(([, node]) => node.status === "ERROR");
@@ -758,6 +759,7 @@ function findDoctorFailureNode(state) {
758
759
  nodeId: selected[0],
759
760
  status: selected[1].status,
760
761
  rawFailureCategory: selected[1].failureCategory,
762
+ skippedReason: selected[1].skippedReason,
761
763
  };
762
764
  }
763
765
  async function readRunOwnedBackendTestClassification(runDir) {
@@ -786,6 +788,7 @@ async function resolveDoctorFailureRouting(input) {
786
788
  normalizedFailureCategory: "unknown",
787
789
  nodeId: input.nodeId,
788
790
  productLineFailureCategory: classifiedCategory,
791
+ skippedReason: input.skippedReason,
789
792
  });
790
793
  }
791
794
  return routeDagFailure(input);
@@ -807,6 +810,7 @@ async function formatDagDoctorMarkdown(repoRoot, runId) {
807
810
  rawFailureCategory,
808
811
  normalizedFailureCategory: normalizedCategory,
809
812
  nodeId: failure.nodeId,
813
+ skippedReason: failure.skippedReason,
810
814
  });
811
815
  const evidence = failure.nodeId
812
816
  ? path.join(located.runDir, failure.nodeId, "result.summary.md")
@@ -441,6 +441,7 @@ export async function buildDagRunReportEntry(input) {
441
441
  normalizedFailureCategory,
442
442
  nodeId,
443
443
  executor: node.executor,
444
+ skippedReason: node.skippedReason,
444
445
  });
445
446
  const followUp = node.status === "ERROR" || node.status === "SKIPPED"
446
447
  ? recommendFollowUpForFailureCategory(node.failureCategory)
@@ -505,6 +506,7 @@ export async function buildDagRunReportEntry(input) {
505
506
  normalizedFailureCategory: normalizeDagFailureCategory(pausedNode.failureCategory, pausedNode.status),
506
507
  failureCategory: pausedNode.failureCategory,
507
508
  nodeId: input.state.pausedByNodeId,
509
+ skippedReason: pausedNode.skippedReason,
508
510
  }
509
511
  : firstActionableNode
510
512
  ? {
@@ -513,6 +515,7 @@ export async function buildDagRunReportEntry(input) {
513
515
  normalizeDagFailureCategory(firstActionableNode.failureCategory, firstActionableNode.status),
514
516
  failureCategory: firstActionableNode.failureCategory,
515
517
  nodeId: firstActionableNode.nodeId,
518
+ skippedReason: input.state.nodes[firstActionableNode.nodeId]?.skippedReason,
516
519
  }
517
520
  : {
518
521
  status: input.state.status,
@@ -529,6 +532,9 @@ export async function buildDagRunReportEntry(input) {
529
532
  rawFailureCategory: runRecoverySource.failureCategory,
530
533
  normalizedFailureCategory: runRecoverySource.normalizedFailureCategory,
531
534
  nodeId: "nodeId" in runRecoverySource ? runRecoverySource.nodeId : undefined,
535
+ skippedReason: "skippedReason" in runRecoverySource
536
+ ? runRecoverySource.skippedReason
537
+ : undefined,
532
538
  });
533
539
  const runFollowUp = input.state.status === "failed" ||
534
540
  input.state.status === "partial_failed"
@@ -18,13 +18,19 @@ TODO
18
18
 
19
19
  ## Mock 约束(数据型任务)
20
20
 
21
- - Mock/API/schema 规范路径:TODO
22
- - 既有 Mock service root、handler/fixture/bootstrap:TODO
23
- - 既有 browser/e2e interception request adapter/DI seam:TODO
24
- - 启动、健康检查和专项验证命令:TODO
25
- - production 禁用边界:TODO
26
- - 真实请求默认路径与 Mock 显式启用方式:TODO
27
- - `task.json.frontendMock.policy`:`auto | required | disabled`
21
+ 本段决定生成期 `allowedMockStrategies` 与冻结的 Mock 验证命令。只有当生成期**能证明任务确实包含 Mock 且有确定性验证命令**时,DAG 才会放行 `native` / `browser-intercept` / `request-adapter`;否则自动收敛为 `not-needed`,design review 若按项目规范改选 `native`,会在 prewrite 被 `mock-strategy-outside-allowed` 拦截、`implement` 被跳过。
22
+
23
+ - **何时必须填**:项目规范(`openspec/project-specs/**`、`ai_workspace/**`、mock 规则或类似 DEC-* 决策)要求/建议 Mock;或需求涉及远端接口且后端未就绪。规范要求 Mock 时,优先 `task.json.frontendMock.policy: "required"`。
24
+ - **命令要探索、不要硬编码**:到项目 `package.json` scripts 里找实际存在的 Mock 相关脚本(如 `mock`、`mock:*`、`dev:mock`,或名字含 mock 的脚本),结合既有 service root 的 handler/fixture/bootstrap 启动方式,确定一条**真实存在、确定、可自终止**(启动→断言→退出 0)的验证命令。常驻 dev server 必须包装成自终止脚本(start→assert→stop),否则 verify shell 会超时 fail-closed。
25
+ - **写入 task.json**:把探索到的命令原样写进 `frontendMock.verifyCommands`(`label` 唯一、`command` 与项目脚本一致)。`label` 会进入冻结命令集,implementation contract 的 `verificationTarget.commandLabel` 必须逐字引用它。
26
+ - **命令来源白名单**:只允许项目 `package.json` 已有脚本、Mock capability seed、或本段声明的 `verifyCommands`;plan/design 阶段不能发明 shell 命令。
27
+ - **Mock/API/schema 规范路径**:TODO
28
+ - **既有 Mock service root、handler/fixture/bootstrap**:TODO
29
+ - **既有 browser/e2e interception 或 request adapter/DI seam**:TODO
30
+ - **production 禁用边界**:TODO
31
+ - **真实请求默认路径与 Mock 显式启用方式**:TODO
32
+ - **`task.json.frontendMock.policy`**:`auto | required | disabled`(规范强制 Mock 用 `required`)
33
+ - **被拦截时怎么修**:`mock-strategy-outside-allowed` / `no authorized Mock verification commands` 是**生成期契约问题,不是 plan 问题**——补 `frontendMock.verifyCommands`(或 `policy: "required"` + 命令)后**重新生成 DAG** 再跑,plan-revision 无法修复它。
28
34
 
29
35
  ## allowedPaths
30
36
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tea-agent/loop-agent",
3
- "version": "0.35.3",
3
+ "version": "0.35.4-beta.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "loop-agent": "bin/loop-agent.js",