@tea-agent/loop-agent 0.39.0-beta.6 → 0.39.0-beta.8
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 +14 -1
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +7 -0
- package/dist/cli/program.js +5 -0
- package/dist/commands/client-recovery.js +3 -0
- package/dist/commands/dag-follow-up.js +138 -0
- package/dist/commands/task-advance.js +19 -0
- package/dist/executors/dag-pi-executor.js +44 -11
- package/dist/executors/pi-executor.js +7 -0
- package/dist/executors/shell-executor.js +1 -1
- package/dist/shared/dag-failure-category.js +12 -0
- package/dist/task/config-types.js +15 -0
- package/dist/task/frontend-project-capability.js +19 -0
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-CfDSkLtp.js → abnfDiagram-N423BO3Z-bPer1waP.js} +1 -1
- package/dist/worker/console/static/assets/{arc-BlefM6I5.js → arc-BLdkwucW.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-C4tHfzHn.js → architectureDiagram-T3A2C74G-Bw4wo4I1.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-G-G77Rh1.js → blockDiagram-VBNYF7ZC-DUUkc0gr.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DWgxmY72.js → c4Diagram-5PPSVZJV-BC3O0nob.js} +1 -1
- package/dist/worker/console/static/assets/channel-AbFtVnFm.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-CJQcfbCY.js → chunk-2GRJ4B5K-Dd4g2nti.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-vUPviH9y.js → chunk-2Q5K7J3B-s2EDm7-n.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DzRXSTeh.js → chunk-5RXB4S5H-GqtHM2yQ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CWfgqm07.js → chunk-5VM5RSS4-BzRFrN8j.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BtTDauXA.js → chunk-6Q2QTUOP-Bw9nFjCJ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Bc-NNt84.js → chunk-GF5L2VYU-B4n-03Qm.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-B_f-emL4.js → chunk-JWPE2WC7-Dt75ZdBD.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DV-jgJ66.js → chunk-KBJHAD2P-B_afMhHB.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DMqzn4RZ.js → chunk-RYQCIY6F-Bd8Rqqg9.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CVNtlAm4.js → chunk-XXDRQBXY-9boOMji4.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-n1yf2Jcv.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-n1yf2Jcv.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BBZBhVTd.js → cose-bilkent-JH36ORCC-BtCycfCc.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DbMU2PZ2.js → cynefin-VYW2F7L2-eTOts9L1.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Dj0v8rjc.js → cynefinDiagram-MW4NZA55-Cibd2mjg.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BW8R4WvJ.js → dagre-VZM6K2ZE-CDjDZtDV.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DEgWz3ld.js → diagram-7IWD3JNH-Btexn-JJ.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BAA01rlR.js → diagram-B4RE2ZJO-Cyl8sdpM.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-Cau6qqpW.js → diagram-LBJQPF4R-Ev-83E1t.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-8HiPsLos.js → diagram-Q27KOJAE-41LqE0Mc.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-CmkXuTYL.js → diagram-UB23O5K3-BC39T5R3.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DCV_0seW.js → ebnfDiagram-BXEA7PRR-DzcNRuri.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-gckfWESj.js → erDiagram-JOGREHBK-B5Frvvnz.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-D2mT2BUu.js → flowDiagram-UKHOOZJN-BapxROIM.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-B_HR5qEw.js → ganttDiagram-PKOTCBZU-Cd4d4wQ0.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-BUw4fpSa.js → gitGraphDiagram-DS77QQ5N-CrL6buFW.js} +1 -1
- package/dist/worker/console/static/assets/{index-DPbsruV5.js → index-DBh_x_Ek.js} +46 -46
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BSRAqEj0.js → infoDiagram-6WML65LV-BrSiNxyd.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-CLxE8hUp.js → ishikawaDiagram-WSZJBQD7-gvlvY-n9.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-juFzVXIj.js → journeyDiagram-NVQOT4AX-D-YaPzRQ.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CwLHVhOU.js → kanban-definition-27J2QSJJ-BsmDv9E_.js} +1 -1
- package/dist/worker/console/static/assets/{linear-9LfUyPAs.js → linear-UWaoDN3h.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-TPPHfQbe.js → mermaid.core-BlvpGVG8.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-Dn_Z9RxF.js → mindmap-definition-FAOFIHXS-CUCIP5CP.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-CiEMKTgm.js → pegDiagram-VL7TDLO6-CB1aQUAK.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-JASaGxjZ.js → pieDiagram-7S7Q4E2Y-BwehHxhf.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-ChmH0ply.js → quadrantDiagram-CIZ2JOQS-CFz30APB.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-CtyM-HGQ.js → railroadDiagram-AXF67PYL-BwNmw1i6.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BN9y_Bvs.js → requirementDiagram-LRYGKXZP-jg-tPXlI.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-IyS0GQ1-.js → sankeyDiagram-W5VNT64P-BlyReDKl.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DjxGXurN.js → sequenceDiagram-SI44F4Z6-DBUzW1f3.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-Cr3Z0S8y.js → sizeCapture-X5ZJPWSS-BYcI2RAr.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BjJFC6pW.js → stateDiagram-OKZ733FA-D7AiyJdo.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BBSH5c8m.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DeMUJQiW.js → swimlanes-SLNWSIFB-BHGt7Kkv.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-D2F7-U4H.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Bc2Rv2Oo.js → timeline-definition-Z64GVDOM-OPjooWcX.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-CDhRUeqG.js → vennDiagram-T6HMQDX7-ggsWDvrO.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DaN_z3Xy.js → wardleyDiagram-T6FBY63Y-DjNe-BjZ.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-C1vTLBDG.js → xychartDiagram-ELKLHX3M-Byp63Hei.js} +1 -1
- package/dist/worker/console/static/index.html +1 -1
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +29 -1
- package/dist/workflows/dag/backend-test-markdown-workflow.js +176 -1
- package/dist/workflows/dag/backend-test-pytest-collection.js +87 -4
- package/dist/workflows/dag/backend-test-writer-completeness.js +10 -5
- package/dist/workflows/dag/frontend-design-policy.js +19 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +82 -3
- package/dist/workflows/dag/frontend-review-findings.js +270 -0
- package/dist/workflows/dag/frontend-shape.js +5 -3
- package/dist/workflows/dag/init-hybrid.js +94 -1
- package/dist/workflows/dag/node-execution.js +7 -0
- package/dist/workflows/dag/report.js +37 -1
- package/dist/workflows/dag/rerun-feedback.js +124 -1
- package/dist/workflows/dag/retry-policy.js +29 -9
- package/dist/workflows/dag/runner.js +89 -7
- package/dist/workflows/dag/types.js +16 -0
- package/dist/workflows/dag/validate.js +9 -3
- package/docs/templates/agent-dag-report.schema.json +8 -2
- package/docs/templates/agent-dag.schema.json +1 -1
- package/package.json +1 -1
- package/dist/worker/console/static/assets/channel-DsdX3ZUX.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-CrvCyJ-8.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-CrvCyJ-8.js +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Bq44O9Nt.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BT4tWyW3.js +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
- 前端 design-review 拒绝纳入自动恢复(方向 A 姊妹实现):`request_design_changes`(definite finding)不再让 writer-admission blocked 变成 terminal failed——runner 终态判定识别 committed design fact 后生成 recovery trigger(new-dag,从 plan 重启)+ 渲染 design findings PRD 草稿 + `deriveDagRerunFeedback` 增加 design typed-fact 通道(findings 注入下一轮 `<rerun_feedback>`),受同一 `frontendRecoveryQuota` 控制;无 design fact 的 blocked admission 仍保持 terminal failed。design-review 发现"AC 未被契约/测试锚定"这类真问题会在写代码前自动进入修正轮,而非一次失败即停。
|
|
6
|
+
- 前端 DAG 两个闭环补齐(P0-1 / P2-4):
|
|
7
|
+
- **P0-1 前端 recovery 配额可配置**:`task.json frontendRecovery.maxContinuations`(默认 1,可配 2-5)冻结进 DAG spec(`spec.frontendRecovery`),runner 的 `finalizeTerminalRunStatus` 4 处硬编码 `< 1` 改为按配额判定——verify 偶发失败导致 run 真失败的场景可放宽到 2-3 次自动恢复,不必人工介入。
|
|
8
|
+
- **P2-4 `dag follow-up` 命令**:`loop-agent dag follow-up <parent-run-id> [--task-id <id>] [--execute] [--profile <p>]`——读取 parent run 的 `contracts/frontend-review-findings-prd.md`,createTask + importPrd(taskKind=frontend-implementation + PRD 内容)生成下游修复任务,`--execute` 直接 advance 走前端 DAG。review findings 从"只落文件的草稿"变成"一步进入下一轮 DAG 的任务",闭环补齐。
|
|
9
|
+
- frontend-plan-pi 重试上限从 5 收紧到 3(`FRONTEND_PLAN_LADDER_RETRY_POLICY.maxAttempts`):每次 attempt 都是完整跑一遍 plan(读上下文 + 提交 record_* ledger),5 次在坏情况下等于 5 倍 plan token 成本;3 次仍覆盖常见失败(invalid-output 格式滑动、read-burst、传输类),四档阶梯只部分可遍历即判 non-converging,减少单节点无限烧 token 风险。
|
|
10
|
+
|
|
11
|
+
- 前端 review `request_review_changes` 不再被静默当作成功(方向 A):`frontend-review-pi` 提交 typed `request_review_changes` 时,run 终态判定为「未通过」(`partial_failed` + `terminalReason=review-request-changes`,即使全部节点 FINISHED 也不显示成功),committed findings 自动渲染为 run-owned 下游任务 PRD 草稿 `contracts/frontend-review-findings-prd.md`(含 issueCategory/evidenceRefs/findings/restartPhase 指引),并生成 recovery trigger(issueCategory→failureOwner 映射,new-dag)。`deriveDagRerunFeedback` 新增 typed-fact 通道:无文本 verdict、无 convergence 的 frontend 场景也能把 committed `request_review_changes` findings 注入下一轮 `<rerun_feedback>`,问题不再丢失、下一轮从 restartPhase 定点修复而非重复流程。
|
|
12
|
+
- 前端 review findings PRD 草稿携带修复范围路由信号(方向 A 补充):`deriveFrontendReviewFixScope` 从 committed findings 确定性推导建议 shape(单文件无远程 API→micro、同目录集中多文件→small、跨目录/API 信号→standard)与建议 complexity、目标文件、建议 allowedPaths;PRD 标题改为「修复 review 发现(<shape>):<目标文件>」,正文新增「修复范围(供下一轮任务生成路由)」段(含 small 信号词,如「单文件局部修复」「无远程接口」),下一轮任务以该 PRD 生成 DAG 时,`resolveFrontendTaskShape` 能把小修复路由到 micro=7/small=10 节点拓扑,而非默认 11 节点 standard 全流程。
|
|
13
|
+
- 前端 shape 分类 API 否定词覆盖中文空格形式(冒烟暴露边界):`无 API/网络调用`、`无 API` 等表述不再被误判为远程 API 信号(原 `\b(...)\b` 对 CJK 开头无词边界),`resolveFrontendTaskShape` 与 `deriveFrontendReviewFixScope` 的 hasApiSignal 判定同步修正——带「无 API/无接口」声明的单文件小修复正确路由 micro/small,而非误升级 standard。
|
|
14
|
+
- 前端实现契约对 `decision=new` 的退化空 `specReference` 归一化兜底(冒烟暴露 plan invalid-output):模型偶发把 new 组件输出成 `specReference:{path:"",section:"",line:null}`(空对象而非 prompt 要求的 null),原校验直接判 `invalid-output` 触发完整 plan 重试;`frontendImplementationContractSchema` preprocess 新增 `normalizeNewComponentSpecReference`——new 选择携带空 path 的退化 specReference 时按 null 意图归一化为缺省(非空 path 的 `specified` 命中原样保留,真实 new+specReference 仍拒绝),一次格式滑动不再烧掉整个 plan attempt。
|
|
5
15
|
- 深度合入 origin/main(source e68dfbe1):以 frontend-node typed-tools 拓扑为基底移植 main 硬化——canonical artifact 消费、fail-closed 无验证 marker、风险分级细化(DEPENDENCY_INSTALL_LANGUAGE)、`HARNESS_DAG_RERUN_PROMPT_OVERRIDE` prompt override、Pi SDK 运行时依赖 0.83.0 与 `engines node>=22.19.0`、next-release scripts 入包;废弃节点(verify-assess/reverify/repair-pi/plan-revision/final-design-review/lint-baseline shell)能力确认被当前流程覆盖后裁剪对应实现(repair-gate 机制、lint-baseline shell 类型),plan retry 文案对齐 committed ledger;affected 392 + M2-M9 303 测试全绿,fast 3909/3910、integration 397/398(各 1 个 fn 既有失败)。
|
|
6
16
|
|
|
7
17
|
### 重点更新
|
|
@@ -10,7 +20,10 @@
|
|
|
10
20
|
- Operator Chat 输入框两项交互增强:① **↑/↓ 历史输入召回**——每次发送的文本进入输入历史(连续重复折叠、上限 100 条),光标在首行按 ↑ 逐条向旧回溯、按 ↓ 向新前进,越过最新一条时恢复回溯前**未发送的草稿**(草稿不会被历史浏览冲掉);多行文本中部的方向键仍是普通光标移动,IME 组词与 @文件/命令面板打开时不抢占。② **运行中排队下一轮指令**——当前回合运行中 Enter 不再被吞掉,消息作为「排队消息」绑定当前 Turn(服务端 `PUT deferred-turn` 从仅限 stopping 放开为 running/stopping,错误码 `TURN_NOT_STOPPING`→`TURN_NOT_ACTIVE`),当前回合结束(正常完成或停止)后自动发送;排队条提供「立即发送」(停止当前回复并立即发出)、「编辑」(移回输入框改写后再排)、「删除」三种操作,运行中输入框右侧提供排队发送按钮(与停止按钮并列);排队条单行展示(状态 + 省略号预览 + 操作按钮一行),输入框默认一行高(自动增高上限 280px 不变)。
|
|
11
21
|
- 向导「运行」面板结构化与预期管理(A2/C9):新增实时运行进度块——节点完成计数与百分比进度条(含 progressbar 无障碍语义)、当前运行节点名称与已运行时长、失败节点与错误摘要,数据来自 `/api/dag-runs/:id` 每 5 秒轮询并在终态自动停止;`effectiveStatus=running-suspected-stall` 时显示橙色「疑似停滞」提示条(建议打开检视查看节点活动或到恢复页协作中止/诊断),把 20–40 分钟长跑的「日志噪音等待」变为有阶段感、可判断的等待。原始 SSE 事件日志默认折叠为「原始事件日志(最近 80 条)」,需要排查时再展开。
|
|
12
22
|
- Console 前端三项体验优化(P0/P2):① Recovery 页 DAG 紧凑检视侧栏(InspectSplitPanel)从一次性快照改为实时刷新——运行中每 5 秒自动拉取节点/事件并新增「进行中/已结束」刷新状态行,到达终态(finished/failed/partial_failed/superseded/abandoned)自动停止轮询,刷新轮不清空既有内容避免闪烁;② Chat 任务旅程卡失败态新增按归一化 failureCategory 的可操作「建议」行(validation/executor/write-guard/timeout/auth/human-required/human-rejected/shell-command/static-error/decision-envelope/unknown 各映射到具体下一步:从失败节点重跑、修正契约后重新生成、跳「模型与认证」页等),不再只报「原因分类为 X」;③ Recovery 页 interrupt 资格轮询退避:选中 run 已终态(含 interrupted)时从 4 秒降到 30 秒,运行中保持 4 秒。
|
|
13
|
-
- Console
|
|
23
|
+
- Console 对话页 Models 面板的模型管理增强:以弹框形式增/改/删 Pi 的模型条目,支持 6 个定义字段(id、name、contextWindow、maxTokens、input、reasoning)。新增时 Provider 用下拉选择、其余字段自动补默认值;编辑时 Provider 只读且仅在原位置就地替换,只改表单内字段、不覆盖配置里页面未展示的其它字段(如 cost/compat);不读写 API key(凭据走 Pi CLI)。编辑已存在的模型不再被移到最后。(该能力仅在对话页提供,设置页不再重复入口。)
|
|
24
|
+
- 修复 `markdown-traceability` pipeline 未将冻结 `backendLayout` 传给 `validateBackendMarkdownTraceability`,自定义 layout 下不再回落默认 `testcase/md` ENOENT;新增回归用例以非默认 nested layout(`testRoot=testcase/elb/remote`、`markdownRel=md`)执行真实 shell pipeline,traceability 从自定义 Markdown 目录读取并 PASS,默认 layout 行为不变。(验证:`npm run typecheck`、`npx vitest run test/backend-test-markdown-workflow.test.ts test/backend-test-layout.test.ts`、`npm run build`、`git diff --check`。)
|
|
25
|
+
- backend-test 快速补强(backend-test-fast-failclosed-attribution):两处 fail-open 缺口已 fail-closed。① 当前轮次 Markdown 作用域严格依赖唯一有效 `## Module Index`——README 缺失 / 不可读 / `## Module Index` 重复(或缺标题)/ 唯一但空或不可解析任一状态,`markdownFiles` 与 coverage `markdownModuleFiles` 均返回空作用域而非回退全目录扫描,历史 `.md` 不再渗入 Case/coverage/partition facts,`resolveBackendTestManifestModules` 新增可区分 `manifestStatus`(valid/missing/unreadable/duplicate/empty-unparsable)与 `moduleIndexHeadingCount`,`validateBackendMarkdownCases` 与 `analyzeBackendTestCaseCoverage` 为非法状态各推 FAIL finding,默认与自定义 `backendTest` 布局一致。② collection 分类器不再凭 `__NNN_*` 裸 import 名称单独判 REPAIRABLE:inventory 构建时预计算每个 mapped script 的裸本地 helper import 证据(`scriptBareImports`),只有「存在同名 generated-local helper asset 且 mapped script 源码确实 import 该裸模块」才判 REPAIRABLE 并给精确 repairPaths;无 helper asset / mapped script 无对应 import / 无法精确归因一律 `BLOCKED`(`missing-generated-local-helper-without-evidence`。真第三方模块缺失保持 BLOCKED,不通过 PYTHONPATH/sys.path/conftest/ImportError fallback 绕行。(验证:`npm run typecheck`、backend-test 四聚焦 vitest、`npm run build`、`git diff --check`。)
|
|
26
|
+
- backend-test 后续修复(backend-test-fast-manifest-import-fix):Markdown 用例校验与 coverage 分析改为按 README `## Module Index` 清单限定本轮有效模块资产——未声明旧 `.md`(如 legacy.md)不再进入 Case/coverage/partition 统计,报告改为显式非静默 unmanifested advisory;“清单声明但文件缺失” fail-closed 明确失败,默认与自定义 `backendTest` 布局统一生效。generated pytest 裸本地 helper import(`from __551_manager_helpers import ...`)在 writer completeness gate 判为 recoverable 并给出精确 mapped 脚本 target path;collection 分类器对可归因到 mapped 资产的裸本地 helper `ModuleNotFoundError` 判 REPAIRABLE 并提供精确 repairPaths,真正第三方依赖缺失仍保持 BLOCKED。不放宽 self-contained、writeSet、forbiddenPaths、skip/xfail、断言与单次业务执行约束(验证:`npm run typecheck`、backend-test 聚焦 vitest、`npm run build`、`git diff --check`)。
|
|
14
27
|
- 前端风险分级细分 manifest 触及语义:`allowedPaths` 含 `package.json`/lockfile 或需求文本提到 package.json 不再单独触发 `new-dependency` high-risk(改一行 script、调构建参数的任务不再被顶到强制全门禁并误标高风险),只有任务文本同时出现安装语言(npm install / yarn add / pnpm add / 新增依赖等)才升级;仅路径触及时记 `new-dependency:manifest-path-without-install-language` 拒绝信号,审计可溯。
|
|
15
28
|
- 前端验证 fallback 生成期诚实化:真实项目无可读取的 `package.json` scripts 且未探测到本地 tsc 时,不再注入必然失败的 `npm run typecheck` / `npm run build` / `npm test`(纯静态 / vanilla JS 站点原会在 verify 阶段假失败),改为执行显式 no-op 标记命令(`No frontend verification command configured`,进入冻结命令集可被 contract 引用,trace 如实记录 not-run),并写生成期 advisory 提示用 `--verify` 或执行约束显式声明验证命令后重新生成 DAG。无 repoRoot 的单测 fixture 保留历史 generic fallback。**标记不构成验证证据(fail-closed)**:verification trace 将绑定标记的 target 判为失败、只含标记的 static 证据判为 unavailable,repair 分类归为 non-repairable `contract`,verify 节点以 `frontend verification unavailable` 硬失败阻断 review/closeout——无真实验证命令的 run 无法走完绿灯链。
|
|
16
29
|
- frontend-test `passed` 回执链改按 dispatch 序号判序(B-HASH-01):`summarizePlaywrightCliReceipts` 先按 `sequence` 排序再判 open→find→cleanup 连续链;并行 `find`/`screenshot` 按完成时间落盘导致的乱序不再误杀执行已通过的 case(Wave 1 实测 4 条 passed 被改判 `browser-command-evidence-missing`)。真 gap / 重复序号仍 fail-closed。
|
package/dist/build-stamp.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.39.0-beta.
|
|
4
|
-
"gitSha": "
|
|
5
|
-
"builtAt": "2026-08-
|
|
3
|
+
"version": "0.39.0-beta.8",
|
|
4
|
+
"gitSha": "b698721e82e0ff98c85484a5b2864d9b28e968f8",
|
|
5
|
+
"builtAt": "2026-08-24T06:32:44.509Z"
|
|
6
6
|
}
|
|
@@ -35,6 +35,7 @@ import { runDagReconcileTasks } from "../commands/dag-reconcile-tasks.js";
|
|
|
35
35
|
import { runDagReconcileRun } from "../commands/dag-reconcile-run.js";
|
|
36
36
|
import { runDagRequestInterrupt } from "../commands/dag-request-interrupt.js";
|
|
37
37
|
import { runDagRerunTask } from "../commands/dag-rerun-task.js";
|
|
38
|
+
import { runDagFollowUp } from "../commands/dag-follow-up.js";
|
|
38
39
|
import { runDagRerun } from "../commands/dag-rerun.js";
|
|
39
40
|
import { runDagResume } from "../commands/dag-resume.js";
|
|
40
41
|
import { runDagDecisionInspect, runDagDecisionValidate, } from "../workflows/dag/decision-envelope.js";
|
|
@@ -135,6 +136,7 @@ const DAG_SUBCOMMANDS = [
|
|
|
135
136
|
"request-interrupt",
|
|
136
137
|
"rerun",
|
|
137
138
|
"rerun-task",
|
|
139
|
+
"follow-up",
|
|
138
140
|
"reconcile-tasks",
|
|
139
141
|
"final-verification",
|
|
140
142
|
"decision",
|
|
@@ -577,6 +579,11 @@ export const COMMAND_DEFINITIONS = [
|
|
|
577
579
|
await runDagRerunTask(repoRoot, rerunTaskArgs);
|
|
578
580
|
return;
|
|
579
581
|
}
|
|
582
|
+
if (subcommand === "follow-up") {
|
|
583
|
+
const followUpArgs = rest.filter((arg) => Boolean(arg));
|
|
584
|
+
await runDagFollowUp(repoRoot, followUpArgs);
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
580
587
|
if (subcommand === "reconcile-tasks") {
|
|
581
588
|
const reconcileArgs = rest.filter((arg) => Boolean(arg));
|
|
582
589
|
await runDagReconcileTasks(repoRoot, reconcileArgs);
|
package/dist/cli/program.js
CHANGED
|
@@ -13,6 +13,7 @@ import { runDagReconcileTasks } from "../commands/dag-reconcile-tasks.js";
|
|
|
13
13
|
import { runDagReconcileRun } from "../commands/dag-reconcile-run.js";
|
|
14
14
|
import { runDagRequestInterrupt } from "../commands/dag-request-interrupt.js";
|
|
15
15
|
import { runDagRerunTask } from "../commands/dag-rerun-task.js";
|
|
16
|
+
import { runDagFollowUp } from "../commands/dag-follow-up.js";
|
|
16
17
|
import { runDagRerun } from "../commands/dag-rerun.js";
|
|
17
18
|
import { runDagReject } from "../commands/dag-reject.js";
|
|
18
19
|
import { runDagCloseoutDraft, runDagReport } from "../commands/dag-report.js";
|
|
@@ -352,6 +353,9 @@ async function runDagAction(repoRoot, subcommand, rest) {
|
|
|
352
353
|
case "rerun-task":
|
|
353
354
|
await runDagRerunTask(repoRoot, args);
|
|
354
355
|
return;
|
|
356
|
+
case "follow-up":
|
|
357
|
+
await runDagFollowUp(repoRoot, args);
|
|
358
|
+
return;
|
|
355
359
|
case "reconcile-tasks":
|
|
356
360
|
await runDagReconcileTasks(repoRoot, args);
|
|
357
361
|
return;
|
|
@@ -511,6 +515,7 @@ function addDagCommand(program, defaultRepoRoot) {
|
|
|
511
515
|
"request-interrupt",
|
|
512
516
|
"rerun",
|
|
513
517
|
"rerun-task",
|
|
518
|
+
"follow-up",
|
|
514
519
|
"reconcile-tasks",
|
|
515
520
|
"final-verification",
|
|
516
521
|
], defaultRepoRoot);
|
|
@@ -1356,12 +1356,15 @@ export async function runClientRecovery(input) {
|
|
|
1356
1356
|
}
|
|
1357
1357
|
const plugin = await installProjectOpenCodePlugin({
|
|
1358
1358
|
repoRoot: input.repoRoot,
|
|
1359
|
+
onlyIfMissing: true,
|
|
1359
1360
|
});
|
|
1360
1361
|
const overflowPlugin = await installProjectOpenCodeOverflowCompactPlugin({
|
|
1361
1362
|
repoRoot: input.repoRoot,
|
|
1363
|
+
onlyIfMissing: true,
|
|
1362
1364
|
});
|
|
1363
1365
|
const piExtension = await installProjectPiContextOverflowExtension({
|
|
1364
1366
|
repoRoot: input.repoRoot,
|
|
1367
|
+
onlyIfMissing: true,
|
|
1365
1368
|
});
|
|
1366
1369
|
const pi = await applyProjectPiSettingsMerge({ repoRoot: input.repoRoot });
|
|
1367
1370
|
if (mode !== "user") {
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { FRONTEND_REVIEW_FINDINGS_PRD_REL, readFrontendReviewFindingsPrd, } from "../workflows/dag/frontend-review-findings.js";
|
|
4
|
+
import { locateDagRun } from "../workflows/dag/lifecycle.js";
|
|
5
|
+
import { createTask, getTaskPaths } from "../task/runtime.js";
|
|
6
|
+
import { importPrdDocument } from "../task/source-references.js";
|
|
7
|
+
function parseFlagValue(rawArgs, flag) {
|
|
8
|
+
const index = rawArgs.indexOf(flag);
|
|
9
|
+
if (index < 0 || index + 1 >= rawArgs.length)
|
|
10
|
+
return undefined;
|
|
11
|
+
return rawArgs[index + 1];
|
|
12
|
+
}
|
|
13
|
+
export function parseDagFollowUpArgs(rawArgs) {
|
|
14
|
+
const positional = rawArgs.filter((arg) => !arg.startsWith("-"));
|
|
15
|
+
const parentRunId = positional[0];
|
|
16
|
+
if (!parentRunId) {
|
|
17
|
+
throw new Error("usage: dag follow-up <parent-run-id> [--task-id <id>] [--execute] [--profile <profile>]");
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
parentRunId,
|
|
21
|
+
taskId: parseFlagValue(rawArgs, "--task-id"),
|
|
22
|
+
execute: rawArgs.includes("--execute"),
|
|
23
|
+
profile: parseFlagValue(rawArgs, "--profile"),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function deriveFollowUpTaskId(parentRunId) {
|
|
27
|
+
// Deterministic, valid task id derived from the parent run id.
|
|
28
|
+
return `${parentRunId}-followup`;
|
|
29
|
+
}
|
|
30
|
+
/** CLI entry: parse raw args and run the follow-up flow. */
|
|
31
|
+
export async function runDagFollowUp(repoRoot, rawArgs) {
|
|
32
|
+
const parsed = parseDagFollowUpArgs(rawArgs);
|
|
33
|
+
const result = await runDagFollowUpInternal(repoRoot, parsed.parentRunId, {
|
|
34
|
+
taskId: parsed.taskId,
|
|
35
|
+
execute: parsed.execute,
|
|
36
|
+
profile: parsed.profile,
|
|
37
|
+
});
|
|
38
|
+
console.log(JSON.stringify(result, null, 2));
|
|
39
|
+
if (!result.ok) {
|
|
40
|
+
process.exitCode = result.mode === "blocked" ? 2 : 1;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export async function runDagFollowUpInternal(repoRoot, parentRunId, options = {}) {
|
|
44
|
+
const located = await locateDagRun(repoRoot, parentRunId);
|
|
45
|
+
if (!located) {
|
|
46
|
+
return {
|
|
47
|
+
ok: false,
|
|
48
|
+
mode: "invalid",
|
|
49
|
+
reason: `dag run not found: ${parentRunId}`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const prd = await readFrontendReviewFindingsPrd(located.runDir);
|
|
53
|
+
if (!prd) {
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
mode: "invalid",
|
|
57
|
+
reason: `no frontend review findings PRD found in run ${parentRunId} (${FRONTEND_REVIEW_FINDINGS_PRD_REL}); the review must have committed request_review_changes`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const taskId = options.taskId ?? deriveFollowUpTaskId(parentRunId);
|
|
61
|
+
try {
|
|
62
|
+
await createTask(repoRoot, taskId, `修复 review 发现(parent ${parentRunId})`);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
mode: "blocked",
|
|
68
|
+
reason: `failed to create task ${taskId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// Materialize the PRD draft into the task's source directory so the task
|
|
72
|
+
// lifecycle can import it as its requirement.
|
|
73
|
+
const prdMaterialized = path.join(getTaskPaths(repoRoot, taskId).sourceDir, "review-findings-prd.md");
|
|
74
|
+
try {
|
|
75
|
+
await mkdir(path.dirname(prdMaterialized), { recursive: true });
|
|
76
|
+
await importPrdDocument({
|
|
77
|
+
repoRoot,
|
|
78
|
+
taskId,
|
|
79
|
+
filePath: path.join(located.runDir, FRONTEND_REVIEW_FINDINGS_PRD_REL),
|
|
80
|
+
name: "review-findings-prd",
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
return {
|
|
85
|
+
ok: false,
|
|
86
|
+
mode: "blocked",
|
|
87
|
+
reason: `failed to import PRD into task ${taskId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (!options.execute) {
|
|
91
|
+
return {
|
|
92
|
+
ok: true,
|
|
93
|
+
mode: "created",
|
|
94
|
+
taskId,
|
|
95
|
+
prdPath: prdMaterialized,
|
|
96
|
+
next: {
|
|
97
|
+
kind: "advance",
|
|
98
|
+
command: `loop-agent task advance ${taskId} --task-kind frontend-implementation --json`,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
// Execute: advance through the frontend DAG.
|
|
103
|
+
const { advanceTaskLifecycle } = await import("../application/task-lifecycle/advance.js");
|
|
104
|
+
const profile = options.profile === "auto" ||
|
|
105
|
+
options.profile === "minimal" ||
|
|
106
|
+
options.profile === "standard" ||
|
|
107
|
+
options.profile === "reviewed" ||
|
|
108
|
+
options.profile === "supervised"
|
|
109
|
+
? options.profile
|
|
110
|
+
: undefined;
|
|
111
|
+
const result = await advanceTaskLifecycle({
|
|
112
|
+
repoRoot,
|
|
113
|
+
taskId,
|
|
114
|
+
taskKind: "frontend-implementation",
|
|
115
|
+
taskKindExplicit: true,
|
|
116
|
+
...(profile ? { profile, profileExplicit: true } : {}),
|
|
117
|
+
observer: undefined,
|
|
118
|
+
});
|
|
119
|
+
const ok = result.lifecycleState === "run-succeeded" ||
|
|
120
|
+
result.lifecycleState === "awaiting-write-set-approval";
|
|
121
|
+
return {
|
|
122
|
+
ok,
|
|
123
|
+
mode: ok ? "created" : "blocked",
|
|
124
|
+
taskId,
|
|
125
|
+
prdPath: prdMaterialized,
|
|
126
|
+
reason: ok
|
|
127
|
+
? undefined
|
|
128
|
+
: `advance lifecycleState: ${result.lifecycleState}${result.blockers.length > 0
|
|
129
|
+
? `; blockers: ${result.blockers.map((b) => b.code).join(", ")}`
|
|
130
|
+
: ""}`,
|
|
131
|
+
next: ok
|
|
132
|
+
? undefined
|
|
133
|
+
: {
|
|
134
|
+
kind: "advance",
|
|
135
|
+
command: `loop-agent task advance ${taskId} --task-kind frontend-implementation --json`,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
@@ -355,6 +355,25 @@ export async function runTaskAdvance(repoRoot, args) {
|
|
|
355
355
|
intervalMs: options.progressIntervalMs,
|
|
356
356
|
});
|
|
357
357
|
}
|
|
358
|
+
// Idempotent client-recovery surface install before any DAG run: the Pi
|
|
359
|
+
// context-overflow extension / OpenCode plugins / project .pi settings
|
|
360
|
+
// are normally installed by `loop-agent init`, but an upgraded or
|
|
361
|
+
// non-init-created project may lack them. Installing here (missing-only)
|
|
362
|
+
// ensures the overflow compact+retry recovery path is always present,
|
|
363
|
+
// independent of how the project was created. Best-effort: a failure to
|
|
364
|
+
// install never blocks the run.
|
|
365
|
+
if (options.approveGate && !options.dryRun) {
|
|
366
|
+
try {
|
|
367
|
+
const { runClientRecovery } = await import("./client-recovery.js");
|
|
368
|
+
// mode "project" installs only project-owned surfaces
|
|
369
|
+
// (.pi/extensions + .opencode/plugins + .pi/settings.json),
|
|
370
|
+
// never the user home; installs are missing-only.
|
|
371
|
+
await runClientRecovery({ repoRoot, mode: "project" });
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
// best-effort; do not fail the run over a recovery-surface install
|
|
375
|
+
}
|
|
376
|
+
}
|
|
358
377
|
const result = await advanceTaskLifecycle(toUseCaseInput(repoRoot, taskId, options, progress?.observer));
|
|
359
378
|
const outcome = mapOutcome(result);
|
|
360
379
|
const gateStop = result.lifecycleState === "awaiting-write-set-approval" &&
|
|
@@ -26,6 +26,15 @@ import { redactSecrets, truncateUtf8Preview } from "../shared/preview.js";
|
|
|
26
26
|
* recoverable partial-write-set (incomplete-write-set) upgrade.
|
|
27
27
|
*/
|
|
28
28
|
export const WRITER_THINKING_EXHAUSTED_CATEGORY = "writer-thinking-exhausted";
|
|
29
|
+
/**
|
|
30
|
+
* The writer session burned an excessive token budget (a read-edit-test loop
|
|
31
|
+
* that never converged) and still failed. Distinct from empty-output so the
|
|
32
|
+
* report shows the real cause and recovery recommends a fresh compacted run.
|
|
33
|
+
* Not auto-retried by default; operators may rerun after a model switch.
|
|
34
|
+
*/
|
|
35
|
+
export const WRITER_BUDGET_EXHAUSTED_CATEGORY = "writer-budget-exhausted";
|
|
36
|
+
/** Token ceiling for a single writer node before it is judged budget-exhausted. */
|
|
37
|
+
export const WRITER_TOKEN_BUDGET = 2_000_000;
|
|
29
38
|
function readWriterThinkingExhaustionEvidence(result) {
|
|
30
39
|
const wider = result;
|
|
31
40
|
return {
|
|
@@ -2681,6 +2690,12 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep, wr
|
|
|
2681
2690
|
changeManifestChangedFiles !== undefined &&
|
|
2682
2691
|
changeManifestChangedFiles.length === 0 &&
|
|
2683
2692
|
writeToolCallCount === 0;
|
|
2693
|
+
// Budget-exhausted: the provider session consumed an extreme amount of
|
|
2694
|
+
// tokens (e.g. an unresolved read-edit-test loop) and still failed. Only
|
|
2695
|
+
// applies on a non-ok writer result; a successful write is never re-labeled.
|
|
2696
|
+
const writerBudgetExhausted = !mapped.ok &&
|
|
2697
|
+
typeof mapped.tokensUsed === "number" &&
|
|
2698
|
+
mapped.tokensUsed > WRITER_TOKEN_BUDGET;
|
|
2684
2699
|
const targetCleanTransport = !mapped.ok &&
|
|
2685
2700
|
isTargetTemplateTransientRetryNode(input.task) &&
|
|
2686
2701
|
rawFailureCategory !== undefined &&
|
|
@@ -2724,17 +2739,35 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep, wr
|
|
|
2724
2739
|
mapped.failureCategory === "invalid-output" ||
|
|
2725
2740
|
mapped.failureCategory === WRITER_EMPTY_DIFF_RETRY_CATEGORY)
|
|
2726
2741
|
? INCOMPLETE_WRITE_SET_RETRY_CATEGORY
|
|
2727
|
-
: //
|
|
2728
|
-
//
|
|
2729
|
-
//
|
|
2730
|
-
// the
|
|
2731
|
-
//
|
|
2732
|
-
//
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2742
|
+
: // partial-success-with-context-overflow: the writer hit a context
|
|
2743
|
+
// overflow on its final provider call but had already persisted
|
|
2744
|
+
// real file changes (change-manifest non-empty). Classify it
|
|
2745
|
+
// distinctly so the report shows "code largely done, verification
|
|
2746
|
+
// incomplete" instead of a misleading empty-output / spec issue,
|
|
2747
|
+
// and recovery can rerun from verify/implement with a compacted
|
|
2748
|
+
// session rather than asking the operator for spec clarification.
|
|
2749
|
+
rawFailureCategory === "context-overflow" &&
|
|
2750
|
+
(changeManifestChangedFiles?.length ?? 0) > 0
|
|
2751
|
+
? "partial-success-with-context-overflow"
|
|
2752
|
+
: // writer-thinking-exhausted: a length-stopped thinking-only attempt with
|
|
2753
|
+
// zero write tool calls and zero attributed diff is terminal and
|
|
2754
|
+
// non-retryable; recommend a model switch + fresh run. Only applies on
|
|
2755
|
+
// the empty-output base category so provider/transport failures keep
|
|
2756
|
+
// their original category, and only when the completeness gate did not
|
|
2757
|
+
// upgrade to incomplete-write-set above.
|
|
2758
|
+
writerThinkingExhausted
|
|
2759
|
+
? WRITER_THINKING_EXHAUSTED_CATEGORY
|
|
2760
|
+
: writerCleanTimeout
|
|
2761
|
+
? WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY
|
|
2762
|
+
: // writer-budget-exhausted: the provider session consumed an
|
|
2763
|
+
// excessive amount of tokens (a read-edit-test loop that never
|
|
2764
|
+
// converged) and still failed. Classify distinctly so the
|
|
2765
|
+
// report says the run burned its budget instead of a generic
|
|
2766
|
+
// empty-output, and recovery recommends a fresh compacted
|
|
2767
|
+
// run rather than spec-clarification.
|
|
2768
|
+
writerBudgetExhausted
|
|
2769
|
+
? WRITER_BUDGET_EXHAUSTED_CATEGORY
|
|
2770
|
+
: mapped.failureCategory,
|
|
2738
2771
|
durationMs: mapped.durationMs || Date.now() - started,
|
|
2739
2772
|
};
|
|
2740
2773
|
}
|
|
@@ -1012,6 +1012,13 @@ export function classifyPiFailure(input) {
|
|
|
1012
1012
|
if (input.exitCode === 0 && input.assistantText.trim())
|
|
1013
1013
|
return "success";
|
|
1014
1014
|
const combined = `${input.stderr}\n${input.stdout}`.toLowerCase();
|
|
1015
|
+
// Context overflow (400 request-too-large / context window exceeded).
|
|
1016
|
+
// Must be detected BEFORE the generic rate-limit/quota phrases: an overflow
|
|
1017
|
+
// error often contains "too many tokens" / "maximum context" which would
|
|
1018
|
+
// otherwise be misread as rate-limit or quota. Kept in sync with
|
|
1019
|
+
// CONTEXT_OVERFLOW_PHRASES in commands/client-recovery.ts.
|
|
1020
|
+
if (/请求上下文过大|context_length_exceeded|context overflow|context length overflow|too many tokens|maximum context|prompt is too long|request_too_large|context window/.test(combined))
|
|
1021
|
+
return "context-overflow";
|
|
1015
1022
|
// Prefer explicit rate-limit / HTTP 429 before generic "limit" quota phrases.
|
|
1016
1023
|
// Chinese gateways often say "请求频率已达到限制" without English "rate limit".
|
|
1017
1024
|
if (/rate.?limit|too many requests|\b429\b|请求频率|频率.*限制|rpm|tpm/.test(combined))
|
|
@@ -1068,7 +1068,7 @@ async function executeBackendTestPipeline(input, meta) {
|
|
|
1068
1068
|
else if (pipeline === "markdown-traceability") {
|
|
1069
1069
|
let report;
|
|
1070
1070
|
try {
|
|
1071
|
-
report = await validateBackendMarkdownTraceability(input.cwd);
|
|
1071
|
+
report = await validateBackendMarkdownTraceability(input.cwd, backendLayout);
|
|
1072
1072
|
}
|
|
1073
1073
|
catch (error) {
|
|
1074
1074
|
report = `# Backend Test Traceability\n\n## Status\n\nFAIL\n\n## Findings\n\n- Traceability validation could not complete: ${error instanceof Error ? error.message : String(error)}\n`;
|
|
@@ -47,10 +47,22 @@ const RAW_TO_NORMALIZED = {
|
|
|
47
47
|
"verify-failure": "validation",
|
|
48
48
|
"test-failure": "validation",
|
|
49
49
|
"verification-plan-stale": "validation",
|
|
50
|
+
"read-burst": "validation",
|
|
51
|
+
// Writer persisted real file changes but the final provider call hit a
|
|
52
|
+
// context overflow; verification never ran. Distinct from empty-output so
|
|
53
|
+
// the report shows partial success instead of a spec issue.
|
|
54
|
+
"partial-success-with-context-overflow": "validation",
|
|
55
|
+
// Writer burned an excessive token budget (unconverged read-edit-test loop)
|
|
56
|
+
// and still failed; distinct from empty-output for accurate reporting.
|
|
57
|
+
"writer-budget-exhausted": "validation",
|
|
50
58
|
network: "executor",
|
|
51
59
|
quota: "executor",
|
|
52
60
|
"rate-limit": "executor",
|
|
53
61
|
unavailable: "executor",
|
|
62
|
+
// Context window overflow (400 request-too-large): the session needs
|
|
63
|
+
// compaction / fresh context before retrying — an executor-side
|
|
64
|
+
// environment condition, not a plan-quality defect.
|
|
65
|
+
"context-overflow": "executor",
|
|
54
66
|
// Deterministic capability/policy refusal: missing verified launcher JS
|
|
55
67
|
// entry, capability allowlist rejection, or missing bounded write tool
|
|
56
68
|
// profile. These are environment/config defects the operator repairs
|
|
@@ -219,6 +219,21 @@ const taskConfigObjectSchema = z.object({
|
|
|
219
219
|
maxFixLoops: z.number().int().min(0).optional().default(2),
|
|
220
220
|
/** Supervised DAG convergence is opt-in until runtime smoke evidence is stronger. */
|
|
221
221
|
convergence: convergenceConfigSchema.optional().default({ enabled: false }),
|
|
222
|
+
/** Frontend recovery continuation quota. Bounds how many times a failed
|
|
223
|
+
* frontend run may auto-recover via M6 recovery operations (1 = current
|
|
224
|
+
* default; raise to 2-3 for more tolerance of transient verify failures). */
|
|
225
|
+
frontendRecovery: z
|
|
226
|
+
.object({
|
|
227
|
+
maxContinuations: z
|
|
228
|
+
.number()
|
|
229
|
+
.int()
|
|
230
|
+
.min(1)
|
|
231
|
+
.max(5)
|
|
232
|
+
.optional()
|
|
233
|
+
.default(1),
|
|
234
|
+
})
|
|
235
|
+
.strict()
|
|
236
|
+
.optional(),
|
|
222
237
|
/** Outer loop auto mode never writes by default; DAG execute requires this policy plus path/writeSet gates. */
|
|
223
238
|
loopAutoExecutionPolicy: loopAutoExecutionPolicySchema
|
|
224
239
|
.optional()
|
|
@@ -133,9 +133,28 @@ export async function listOpenspecDiscovery(repoRoot, options = {}) {
|
|
|
133
133
|
.filter(Boolean);
|
|
134
134
|
const collected = [];
|
|
135
135
|
let excludedGovernanceCount = 0;
|
|
136
|
+
// Metadata / bookkeeping directories that are not normative design specs:
|
|
137
|
+
// changelogs, ADR/history archives, proposals under review, and scratch
|
|
138
|
+
// state. Reading them inflates the candidate list (and the plan prompt)
|
|
139
|
+
// with files no component selection should cite. A task that genuinely
|
|
140
|
+
// needs one can still reference it explicitly — the prewrite gate
|
|
141
|
+
// candidates come from this discovery, but explicit task-source citations
|
|
142
|
+
// flow through openspecMandatoryPaths independently.
|
|
143
|
+
const DEFAULT_EXCLUDED_SPEC_PREFIXES = [
|
|
144
|
+
"openspec/changes",
|
|
145
|
+
"openspec/project-specs/changes",
|
|
146
|
+
"openspec/project-specs/changelog",
|
|
147
|
+
"openspec/project-specs/history",
|
|
148
|
+
"openspec/project-specs/archive",
|
|
149
|
+
"openspec/project-specs/decisions",
|
|
150
|
+
"openspec/project-specs/proposals",
|
|
151
|
+
];
|
|
136
152
|
const isExcluded = (rel) => {
|
|
137
153
|
if (isGovernanceSubtreePath(rel, governanceRoot))
|
|
138
154
|
return true;
|
|
155
|
+
if (DEFAULT_EXCLUDED_SPEC_PREFIXES.some((prefix) => rel === prefix || rel.startsWith(prefix + "/"))) {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
139
158
|
return extraExcludedPrefixes.some((prefix) => rel === prefix || rel.startsWith(prefix + "/"));
|
|
140
159
|
};
|
|
141
160
|
for (const specDir of options.specRoots ?? [...OPENSPEC_SPEC_DIRS]) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-
|
|
1
|
+
import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-Bw9nFjCJ.js";import{p as f}from"./chunk-JWPE2WC7-Dt75ZdBD.js";import{_ as n,l as o}from"./mermaid.core-BlvpGVG8.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-eTOts9L1.js";import"./index-DBh_x_Ek.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-
|
|
1
|
+
import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-BlvpGVG8.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
|