@godv61/dsh-task-engine 0.24.0 → 0.26.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/.acceptance.mjs +101 -39
- package/.assessment-batch1.mjs +32 -12
- package/.e2e-presets.mjs +40 -5
- package/.enforce-test.mjs +139 -0
- package/.evidence-test.mjs +118 -0
- package/.filter-test.mjs +93 -0
- package/.freeze-test.mjs +110 -1
- package/.hook-test.mjs +195 -190
- package/.p0-test.mjs +130 -46
- package/.preset-test.mjs +160 -160
- package/.revision-test.mjs +32 -3
- package/.roundtrip-test.mjs +178 -0
- package/.workflow-test.mjs +117 -4
- package/README.md +10 -8
- package/cordis.patch.yml +10 -10
- package/defaults/eng.json +70 -22
- package/docs/BRIEF-FOR-REVIEW.md +162 -162
- package/docs/CHANGELOG.md +49 -0
- package/docs/README.md +40 -40
- package/docs/configuration.md +14 -11
- package/docs/development.md +1 -1
- package/docs/faq.md +4 -4
- package/docs/listing/godv61__dsh-task-engine.yml +5 -5
- package/docs/listing/submission.md +83 -83
- package/docs/manual.html +31 -26
- package/docs/releases/0.23.2.md +21 -21
- package/docs/roadmap.md +34 -34
- package/docs/testing/0.23.2/R02/344/270/232/345/212/241/346/265/213/350/257/225/346/230/216/347/273/206.md +56 -56
- package/docs/testing/0.23.2/R03/344/270/232/345/212/241/346/265/213/350/257/225/346/230/216/347/273/206.md +38 -38
- package/docs/testing/0.23.2//346/265/213/350/257/225/346/212/245/345/221/212.md +65 -65
- package/hooks/commit-msg +159 -72
- package/lib/client.js +963 -571
- package/lib/client.js.map +3 -3
- package/lib/controller.d.ts +11 -2
- package/lib/controller.js +33 -5
- package/lib/controller.js.map +1 -1
- package/lib/dev-task.js +170 -28
- package/lib/dev-task.js.map +1 -1
- package/lib/engine.d.ts +105 -0
- package/lib/engine.js +93 -5
- package/lib/engine.js.map +1 -1
- package/lib/hook.js +17 -4
- package/lib/hook.js.map +1 -1
- package/lib/skill-audit.d.ts +14 -3
- package/lib/skill-audit.js +66 -3
- package/lib/skill-audit.js.map +1 -1
- package/lib/workflows.d.ts +78 -12
- package/lib/workflows.js +258 -94
- package/lib/workflows.js.map +1 -1
- package/package.json +9 -5
- package/preset/agent.cordis.yml +21 -21
- package/preset/enable.mjs +87 -87
- package/preset/persona.md +4 -4
- package/preset/preset.yml +1 -1
- package/rules/coding-conventions.md +6 -6
- package/rules/commit-conventions.md +6 -6
- package/rules/security-redlines.md +5 -5
- package/scripts/verify-dsh-compat.mjs +144 -144
- package/scripts/verify-package.mjs +1 -1
- package/skills/code-verify/SKILL.md +3 -3
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
# 自动化测试逐项记录
|
|
2
|
-
|
|
3
|
-
来源:r03-business-tests.log;共 32 项。
|
|
4
|
-
|
|
5
|
-
| ID | 用例 | 结果 | 耗时 ms |
|
|
6
|
-
| --- | --- | --- | ---: |
|
|
7
|
-
| 1 | 单元 aggregate:同批次累计、码元升序并输出 defectRate | 通过 | 1.695 |
|
|
8
|
-
| 2 | 单元 aggregate:空数组得到空 batches 与全零 totals | 通过 | 0.2052 |
|
|
9
|
-
| 3 | 单元 aggregate:inspected 为 0 时 defectRate 为 0 | 通过 | 0.1467 |
|
|
10
|
-
| 4 | 单元 aggregate:defectRate 至多两位小数且为数值类型 | 通过 | 0.2306 |
|
|
11
|
-
| 5 | 单元 aggregate:totals.defectRate 基于累计值而非各批次比率平均 | 通过 | 0.111 |
|
|
12
|
-
| 6 | 单元 aggregate:按 batchId 字符串码元升序(大写先于小写) | 通过 | 0.0989 |
|
|
13
|
-
| 7 | 单元 validateRecords:batchId 去除前后空格后作为同一批次 | 通过 | 0.1417 |
|
|
14
|
-
| 8 | 单元 validateRecords:接受非负整数与零值边界 | 通过 | 0.1628 |
|
|
15
|
-
| 9 | 单元 validateRecords:拒绝非数组、非法元素与非法字段 | 通过 | 0.683 |
|
|
16
|
-
| 10 | AC1 同批次累计,stdout 仅一行 JSON,totals 正确 | 通过 | 74.2086 |
|
|
17
|
-
| 11 | AC2 空数组合法 | 通过 | 75.882 |
|
|
18
|
-
| 12 | AC3 零数合法 | 通过 | 85.2254 |
|
|
19
|
-
| 13 | AC4 batchId 去空格归并并按码元升序 | 通过 | 82.9206 |
|
|
20
|
-
| 14 | AC5 非法 JSON 失败且 stdout 为空 | 通过 | 90.9043 |
|
|
21
|
-
| 15 | AC6 顶层非数组失败且 stdout 为空 | 通过 | 379.1634 |
|
|
22
|
-
| 16 | AC7 batchId 为空字符串 → 非零退出、stdout 为空、stderr 具体 | 通过 | 80.0845 |
|
|
23
|
-
| 17 | AC7 batchId 为纯空白 → 非零退出、stdout 为空、stderr 具体 | 通过 | 74.659 |
|
|
24
|
-
| 18 | AC7 batchId 缺失 → 非零退出、stdout 为空、stderr 具体 | 通过 | 78.5134 |
|
|
25
|
-
| 19 | AC8 inspected 为负数 → 非零退出、stdout 为空、stderr 具体 | 通过 | 121.0524 |
|
|
26
|
-
| 20 | AC8 defective 为负数 → 非零退出、stdout 为空、stderr 具体 | 通过 | 77.35 |
|
|
27
|
-
| 21 | AC9 inspected 为小数 → 非零退出、stdout 为空、stderr 具体 | 通过 | 66.7267 |
|
|
28
|
-
| 22 | AC9 defective 为小数 → 非零退出、stdout 为空、stderr 具体 | 通过 | 68.4103 |
|
|
29
|
-
| 23 | AC10 inspected 为字符串数字 → 非零退出、stdout 为空、stderr 具体 | 通过 | 71.1564 |
|
|
30
|
-
| 24 | AC10 defective 为字符串数字 → 非零退出、stdout 为空、stderr 具体 | 通过 | 71.2365 |
|
|
31
|
-
| 25 | AC11 defective 大于 inspected → 非零退出、stdout 为空、stderr 具体 | 通过 | 76.4629 |
|
|
32
|
-
| 26 | 非对象元素 → 非零退出、stdout 为空、stderr 具体 | 通过 | 72.6897 |
|
|
33
|
-
| 27 | AC12 无参数失败并提示用法 | 通过 | 75.6079 |
|
|
34
|
-
| 28 | 多余参数失败并提示用法 | 通过 | 73.248 |
|
|
35
|
-
| 29 | AC13 文件不存在失败且 stdout 为空 | 通过 | 71.4465 |
|
|
36
|
-
| 30 | AC14 --help 退出 0 并输出用法 | 通过 | 141.8224 |
|
|
37
|
-
| 31 | AC14 --help 优先于其他参数 | 通过 | 70.1995 |
|
|
38
|
-
| 32 | AC15 examples/batches.json 可直接运行并得到文档所述结果 | 通过 | 73.5514 |
|
|
1
|
+
# 自动化测试逐项记录
|
|
2
|
+
|
|
3
|
+
来源:r03-business-tests.log;共 32 项。
|
|
4
|
+
|
|
5
|
+
| ID | 用例 | 结果 | 耗时 ms |
|
|
6
|
+
| --- | --- | --- | ---: |
|
|
7
|
+
| 1 | 单元 aggregate:同批次累计、码元升序并输出 defectRate | 通过 | 1.695 |
|
|
8
|
+
| 2 | 单元 aggregate:空数组得到空 batches 与全零 totals | 通过 | 0.2052 |
|
|
9
|
+
| 3 | 单元 aggregate:inspected 为 0 时 defectRate 为 0 | 通过 | 0.1467 |
|
|
10
|
+
| 4 | 单元 aggregate:defectRate 至多两位小数且为数值类型 | 通过 | 0.2306 |
|
|
11
|
+
| 5 | 单元 aggregate:totals.defectRate 基于累计值而非各批次比率平均 | 通过 | 0.111 |
|
|
12
|
+
| 6 | 单元 aggregate:按 batchId 字符串码元升序(大写先于小写) | 通过 | 0.0989 |
|
|
13
|
+
| 7 | 单元 validateRecords:batchId 去除前后空格后作为同一批次 | 通过 | 0.1417 |
|
|
14
|
+
| 8 | 单元 validateRecords:接受非负整数与零值边界 | 通过 | 0.1628 |
|
|
15
|
+
| 9 | 单元 validateRecords:拒绝非数组、非法元素与非法字段 | 通过 | 0.683 |
|
|
16
|
+
| 10 | AC1 同批次累计,stdout 仅一行 JSON,totals 正确 | 通过 | 74.2086 |
|
|
17
|
+
| 11 | AC2 空数组合法 | 通过 | 75.882 |
|
|
18
|
+
| 12 | AC3 零数合法 | 通过 | 85.2254 |
|
|
19
|
+
| 13 | AC4 batchId 去空格归并并按码元升序 | 通过 | 82.9206 |
|
|
20
|
+
| 14 | AC5 非法 JSON 失败且 stdout 为空 | 通过 | 90.9043 |
|
|
21
|
+
| 15 | AC6 顶层非数组失败且 stdout 为空 | 通过 | 379.1634 |
|
|
22
|
+
| 16 | AC7 batchId 为空字符串 → 非零退出、stdout 为空、stderr 具体 | 通过 | 80.0845 |
|
|
23
|
+
| 17 | AC7 batchId 为纯空白 → 非零退出、stdout 为空、stderr 具体 | 通过 | 74.659 |
|
|
24
|
+
| 18 | AC7 batchId 缺失 → 非零退出、stdout 为空、stderr 具体 | 通过 | 78.5134 |
|
|
25
|
+
| 19 | AC8 inspected 为负数 → 非零退出、stdout 为空、stderr 具体 | 通过 | 121.0524 |
|
|
26
|
+
| 20 | AC8 defective 为负数 → 非零退出、stdout 为空、stderr 具体 | 通过 | 77.35 |
|
|
27
|
+
| 21 | AC9 inspected 为小数 → 非零退出、stdout 为空、stderr 具体 | 通过 | 66.7267 |
|
|
28
|
+
| 22 | AC9 defective 为小数 → 非零退出、stdout 为空、stderr 具体 | 通过 | 68.4103 |
|
|
29
|
+
| 23 | AC10 inspected 为字符串数字 → 非零退出、stdout 为空、stderr 具体 | 通过 | 71.1564 |
|
|
30
|
+
| 24 | AC10 defective 为字符串数字 → 非零退出、stdout 为空、stderr 具体 | 通过 | 71.2365 |
|
|
31
|
+
| 25 | AC11 defective 大于 inspected → 非零退出、stdout 为空、stderr 具体 | 通过 | 76.4629 |
|
|
32
|
+
| 26 | 非对象元素 → 非零退出、stdout 为空、stderr 具体 | 通过 | 72.6897 |
|
|
33
|
+
| 27 | AC12 无参数失败并提示用法 | 通过 | 75.6079 |
|
|
34
|
+
| 28 | 多余参数失败并提示用法 | 通过 | 73.248 |
|
|
35
|
+
| 29 | AC13 文件不存在失败且 stdout 为空 | 通过 | 71.4465 |
|
|
36
|
+
| 30 | AC14 --help 退出 0 并输出用法 | 通过 | 141.8224 |
|
|
37
|
+
| 31 | AC14 --help 优先于其他参数 | 通过 | 70.1995 |
|
|
38
|
+
| 32 | AC15 examples/batches.json 可直接运行并得到文档所述结果 | 通过 | 73.5514 |
|
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
# 0.23.2 实机循环回归报告
|
|
2
|
-
|
|
3
|
-
[← 发布说明](../../releases/0.23.2.md)
|
|
4
|
-
|
|
5
|
-
日期:2026-09-17。结论:权限缺陷修复后,两个新会话从同一空业务基线完成标准研发流程、独立验收、本地提交和归档回退;门禁专项通过。验证对象是本机 Harness 上的独立 CLI 项目,不是 QMS 页面、数据库或生产业务。
|
|
6
|
-
|
|
7
|
-
## 环境与方法
|
|
8
|
-
|
|
9
|
-
Harness 提交 `d347e703908d0406b7a7ef80e3a0e594d86b2215`,Windows、Node 22.22.3、web profile,模型 `deepseek-v4.1-flash`,工程化开发引擎预设。独立仓库 `D:\dshtest`,分支 `codex/plugin-live-regression`,基线 `aae3bf8994ef803cf8370f5f53840b508539499c` 仅有 `.gitignore`。
|
|
10
|
-
|
|
11
|
-
冻结需求为零依赖 ESM 质检批次统计 CLI:严格校验 JSON 记录,按 trim 后的 batchId 累计并按码元排序,计算百分数缺陷率,正确区分成功 JSON 与失败 stderr,提供单元测试、真实 CLI 子进程测试、样例及 README。所有业务代码由 DSH 编写;观察者只审阅、执行独立验收、归档及修复插件。每轮使用新会话,不读取前轮实现;QMS 未改动。
|
|
12
|
-
|
|
13
|
-
## 轮次结果
|
|
14
|
-
|
|
15
|
-
| 轮次 | 插件 | 业务测试 / 独立黑盒 | 流程结果 | 本地提交 |
|
|
16
|
-
| --- | --- | --- | --- | --- |
|
|
17
|
-
| R01 | 0.23.1 | 24 / 19 通过 | 插件验证权限缺陷阻塞交付;停止、归档,未冒充完成 | 无 |
|
|
18
|
-
| R02 | 0.23.2-rc.1 | 50 / 19 通过 | 审批、3项实施审核、验证、代码审核、提交回写、完成、归档回退通过 | `15ffd9c230363520a934586107ce9f4023e828ed` |
|
|
19
|
-
| R03 | 0.23.2-rc.3 | 32 / 19 通过 | 设计退回修订、4项实施审核、验证、代码审核、提交回写、完成、归档回退通过 | `4e476a514d3109417bac602e818460b09dd57289` |
|
|
20
|
-
|
|
21
|
-
R02 与 R03 的权限核心构建文件 SHA256 均为 `FD116354D20D870E8981B4C098E9F0BDBE9B213397918D7181FE07E0E1713688`;期间只修正提交规则及文档。rc.2 安装核对时发现新指南误称 status 返回格式字段,已改为实际的 `commit.label` 并重打 rc.3,rc.2 不计作业务轮次。
|
|
22
|
-
|
|
23
|
-
R02 为27项单元、23项CLI子进程测试;R03 为9项单元、23项CLI子进程测试。DSH 的 R03 最终文字误写8+24,本报告按实际测试文件与TAP用例核对为9+23。逐项结果和耗时见[R02明细](R02业务测试明细.md)、[R03明细](R03业务测试明细.md)。独立黑盒19项覆盖主用例、空数组、零值、trim与取整、4类非法顶层输入、7类非法字段、非法JSON、缺参数、缺文件和帮助。
|
|
24
|
-
|
|
25
|
-
## 门禁专项
|
|
26
|
-
|
|
27
|
-
| 场景 | 实际结果 | 结论 |
|
|
28
|
-
| --- | --- | --- |
|
|
29
|
-
| 未加载绑定技能即推进 | 返回 load skill requirement-analysis,未推进 | 通过 |
|
|
30
|
-
| 缺少需求产物与确认即推进 | 返回 requirement_confirmation / artifacts_present 未满足 | 通过 |
|
|
31
|
-
| 使用合法消息提前提交 | 返回当前阶段无提交检查点,revision仍为1 | 通过 |
|
|
32
|
-
| 普通权限运行 node --test | 真实exit1,子进程spawn EPERM,回执mode=workspace-write | 按预期失败 |
|
|
33
|
-
| 拒绝该命令的权限升级 | 工具报rejected,台账逐字节哈希不变 | 通过 |
|
|
34
|
-
| 明确重新授权后批准原命令 | 50项测试通过,exit0,回执mode=danger-full-access | 通过 |
|
|
35
|
-
| 修改唯一范围文件 | status出现验证过期的evidence_blockers,冻结旧回执保留 | 通过 |
|
|
36
|
-
| 同会话后续普通shell调用 | 原命令重新EPERM,未继承单次权限 | 通过 |
|
|
37
|
-
|
|
38
|
-
拒绝前后台账 SHA256 均为 `99F13BB166B4D0C9D7EC4EAE00F0471E7CEABD97541ADD85820A64A0D2008306`。专项任务 `DSH-GATE-PROBE` 停在需求阶段,不计作完整业务轮次。过期提交的检查点路径由自动化回归覆盖;本次专项实机验证到动态过期提示,未声称在该检查点另外执行过过期提交。
|
|
39
|
-
|
|
40
|
-
R03完整流程另通过正式 `dev_task verify` 执行 `npm test`:单次审批后32项通过、exit0、实际模式danger-full-access。R02使用可在受限环境运行的真实文件描述符子进程测试,其workspace-write成功回执不冒充权限修复证据。R03修正后的提交消息首次预检即获批准,随后核对真实Git提交并登记。
|
|
41
|
-
|
|
42
|
-
## 插件回归与缺陷
|
|
43
|
-
|
|
44
|
-
P1:0.23.1在执行验证命令之后才审批写台账,导致命令先运行且未收到批准模式。回归测试先复现“审批服务不可用却已调用一次shell”,修复后拒绝、取消、审批不可用、非法参数均不运行命令或改台账;verify与skill_result批准顺序、请求上下文、单次权限和回执写入不重复审批均受断言保护。
|
|
45
|
-
|
|
46
|
-
P2:随包提交规则要求模块名,而引擎要求任务id,R02实际出现两次格式拒绝。规则及手册改为当前任务id和status.commit.label;R03未再出现该格式错误。
|
|
47
|
-
|
|
48
|
-
源码构建、host/client类型检查、126项P0和37项node:test回归通过;候选包干净安装、入口导入、客户端注册、包内测试及CLI语法验证通过。源码测试明细见[自动化测试明细](自动化测试明细.md)。最终0.23.2的 `npm run verify:package` 由用户在插件源码目录执行并确认通过;此项依据用户执行反馈,观察者没有独立保存该次终端日志。发布版本与远端提交另外核对,不以本报告代替发布成功回执。
|
|
49
|
-
|
|
50
|
-
## 归档与干预
|
|
51
|
-
|
|
52
|
-
两个完成轮次的父提交均为B0,文件分别严格限制为10个、5个;归档前工作区干净,任务均为完成、review=pass,最终登记hash等于HEAD。业务ZIP、任务JSON、会话日志及独立验收记录保存在本地 `live-project-test/regression-20260917`;备份分支为 `codex/archive-dsh-reg-r02` 和 `codex/archive-dsh-reg-r03`。业务分支最终恢复B0,保留被忽略的历史台账。
|
|
53
|
-
|
|
54
|
-
| 归档 | 业务ZIP SHA256 |
|
|
55
|
-
| --- | --- |
|
|
56
|
-
| R02 | `2545B54A201D70D536B85BDB52F2356CE2364310FCAF32E99F759408BFC98D82` |
|
|
57
|
-
| R03 | `F7BB52228137BA0A37722097A26AF6BBE1AEFB09C416792F4EE94EA29E71DD56` |
|
|
58
|
-
|
|
59
|
-
观察者处理了正式需求/设计/权限审批;R03退回已知Node22测试目录入口问题,并要求DSH通过正式审批区分EPERM与入口错误,保留默认测试隔离和真实子进程。观察者未代写业务代码。测试包含这些干预,不能称为完全自主执行。
|
|
60
|
-
|
|
61
|
-
## 能力边界与未覆盖范围
|
|
62
|
-
|
|
63
|
-
`dev_task`对阶段、证据和提交登记实施门禁,通用文件写入不会逐次经过状态机;R03实际在实施项仍为todo时写入文件,随后才逐项审核和标记done。没有安装提交钩子时,不能据此宣称插件能阻止直接shell绕过提交预检。本轮未测试强行篡改台账、直接Git绕过、多人并发、QMS集成、全部Harness/模型版本或生产部署。
|
|
64
|
-
|
|
65
|
-
scope_hash只覆盖任务files的内容,不能证明命令或范围外业务文件未变;观察者已纠正DSH一次过度解读。模型报告也可能出现测试分类计数错误,必须复核实际日志。自动化中的模拟宿主与真实DSH会话分开计证,不互相替代。
|
|
1
|
+
# 0.23.2 实机循环回归报告
|
|
2
|
+
|
|
3
|
+
[← 发布说明](../../releases/0.23.2.md)
|
|
4
|
+
|
|
5
|
+
日期:2026-09-17。结论:权限缺陷修复后,两个新会话从同一空业务基线完成标准研发流程、独立验收、本地提交和归档回退;门禁专项通过。验证对象是本机 Harness 上的独立 CLI 项目,不是 QMS 页面、数据库或生产业务。
|
|
6
|
+
|
|
7
|
+
## 环境与方法
|
|
8
|
+
|
|
9
|
+
Harness 提交 `d347e703908d0406b7a7ef80e3a0e594d86b2215`,Windows、Node 22.22.3、web profile,模型 `deepseek-v4.1-flash`,工程化开发引擎预设。独立仓库 `D:\dshtest`,分支 `codex/plugin-live-regression`,基线 `aae3bf8994ef803cf8370f5f53840b508539499c` 仅有 `.gitignore`。
|
|
10
|
+
|
|
11
|
+
冻结需求为零依赖 ESM 质检批次统计 CLI:严格校验 JSON 记录,按 trim 后的 batchId 累计并按码元排序,计算百分数缺陷率,正确区分成功 JSON 与失败 stderr,提供单元测试、真实 CLI 子进程测试、样例及 README。所有业务代码由 DSH 编写;观察者只审阅、执行独立验收、归档及修复插件。每轮使用新会话,不读取前轮实现;QMS 未改动。
|
|
12
|
+
|
|
13
|
+
## 轮次结果
|
|
14
|
+
|
|
15
|
+
| 轮次 | 插件 | 业务测试 / 独立黑盒 | 流程结果 | 本地提交 |
|
|
16
|
+
| --- | --- | --- | --- | --- |
|
|
17
|
+
| R01 | 0.23.1 | 24 / 19 通过 | 插件验证权限缺陷阻塞交付;停止、归档,未冒充完成 | 无 |
|
|
18
|
+
| R02 | 0.23.2-rc.1 | 50 / 19 通过 | 审批、3项实施审核、验证、代码审核、提交回写、完成、归档回退通过 | `15ffd9c230363520a934586107ce9f4023e828ed` |
|
|
19
|
+
| R03 | 0.23.2-rc.3 | 32 / 19 通过 | 设计退回修订、4项实施审核、验证、代码审核、提交回写、完成、归档回退通过 | `4e476a514d3109417bac602e818460b09dd57289` |
|
|
20
|
+
|
|
21
|
+
R02 与 R03 的权限核心构建文件 SHA256 均为 `FD116354D20D870E8981B4C098E9F0BDBE9B213397918D7181FE07E0E1713688`;期间只修正提交规则及文档。rc.2 安装核对时发现新指南误称 status 返回格式字段,已改为实际的 `commit.label` 并重打 rc.3,rc.2 不计作业务轮次。
|
|
22
|
+
|
|
23
|
+
R02 为27项单元、23项CLI子进程测试;R03 为9项单元、23项CLI子进程测试。DSH 的 R03 最终文字误写8+24,本报告按实际测试文件与TAP用例核对为9+23。逐项结果和耗时见[R02明细](R02业务测试明细.md)、[R03明细](R03业务测试明细.md)。独立黑盒19项覆盖主用例、空数组、零值、trim与取整、4类非法顶层输入、7类非法字段、非法JSON、缺参数、缺文件和帮助。
|
|
24
|
+
|
|
25
|
+
## 门禁专项
|
|
26
|
+
|
|
27
|
+
| 场景 | 实际结果 | 结论 |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| 未加载绑定技能即推进 | 返回 load skill requirement-analysis,未推进 | 通过 |
|
|
30
|
+
| 缺少需求产物与确认即推进 | 返回 requirement_confirmation / artifacts_present 未满足 | 通过 |
|
|
31
|
+
| 使用合法消息提前提交 | 返回当前阶段无提交检查点,revision仍为1 | 通过 |
|
|
32
|
+
| 普通权限运行 node --test | 真实exit1,子进程spawn EPERM,回执mode=workspace-write | 按预期失败 |
|
|
33
|
+
| 拒绝该命令的权限升级 | 工具报rejected,台账逐字节哈希不变 | 通过 |
|
|
34
|
+
| 明确重新授权后批准原命令 | 50项测试通过,exit0,回执mode=danger-full-access | 通过 |
|
|
35
|
+
| 修改唯一范围文件 | status出现验证过期的evidence_blockers,冻结旧回执保留 | 通过 |
|
|
36
|
+
| 同会话后续普通shell调用 | 原命令重新EPERM,未继承单次权限 | 通过 |
|
|
37
|
+
|
|
38
|
+
拒绝前后台账 SHA256 均为 `99F13BB166B4D0C9D7EC4EAE00F0471E7CEABD97541ADD85820A64A0D2008306`。专项任务 `DSH-GATE-PROBE` 停在需求阶段,不计作完整业务轮次。过期提交的检查点路径由自动化回归覆盖;本次专项实机验证到动态过期提示,未声称在该检查点另外执行过过期提交。
|
|
39
|
+
|
|
40
|
+
R03完整流程另通过正式 `dev_task verify` 执行 `npm test`:单次审批后32项通过、exit0、实际模式danger-full-access。R02使用可在受限环境运行的真实文件描述符子进程测试,其workspace-write成功回执不冒充权限修复证据。R03修正后的提交消息首次预检即获批准,随后核对真实Git提交并登记。
|
|
41
|
+
|
|
42
|
+
## 插件回归与缺陷
|
|
43
|
+
|
|
44
|
+
P1:0.23.1在执行验证命令之后才审批写台账,导致命令先运行且未收到批准模式。回归测试先复现“审批服务不可用却已调用一次shell”,修复后拒绝、取消、审批不可用、非法参数均不运行命令或改台账;verify与skill_result批准顺序、请求上下文、单次权限和回执写入不重复审批均受断言保护。
|
|
45
|
+
|
|
46
|
+
P2:随包提交规则要求模块名,而引擎要求任务id,R02实际出现两次格式拒绝。规则及手册改为当前任务id和status.commit.label;R03未再出现该格式错误。
|
|
47
|
+
|
|
48
|
+
源码构建、host/client类型检查、126项P0和37项node:test回归通过;候选包干净安装、入口导入、客户端注册、包内测试及CLI语法验证通过。源码测试明细见[自动化测试明细](自动化测试明细.md)。最终0.23.2的 `npm run verify:package` 由用户在插件源码目录执行并确认通过;此项依据用户执行反馈,观察者没有独立保存该次终端日志。发布版本与远端提交另外核对,不以本报告代替发布成功回执。
|
|
49
|
+
|
|
50
|
+
## 归档与干预
|
|
51
|
+
|
|
52
|
+
两个完成轮次的父提交均为B0,文件分别严格限制为10个、5个;归档前工作区干净,任务均为完成、review=pass,最终登记hash等于HEAD。业务ZIP、任务JSON、会话日志及独立验收记录保存在本地 `live-project-test/regression-20260917`;备份分支为 `codex/archive-dsh-reg-r02` 和 `codex/archive-dsh-reg-r03`。业务分支最终恢复B0,保留被忽略的历史台账。
|
|
53
|
+
|
|
54
|
+
| 归档 | 业务ZIP SHA256 |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| R02 | `2545B54A201D70D536B85BDB52F2356CE2364310FCAF32E99F759408BFC98D82` |
|
|
57
|
+
| R03 | `F7BB52228137BA0A37722097A26AF6BBE1AEFB09C416792F4EE94EA29E71DD56` |
|
|
58
|
+
|
|
59
|
+
观察者处理了正式需求/设计/权限审批;R03退回已知Node22测试目录入口问题,并要求DSH通过正式审批区分EPERM与入口错误,保留默认测试隔离和真实子进程。观察者未代写业务代码。测试包含这些干预,不能称为完全自主执行。
|
|
60
|
+
|
|
61
|
+
## 能力边界与未覆盖范围
|
|
62
|
+
|
|
63
|
+
`dev_task`对阶段、证据和提交登记实施门禁,通用文件写入不会逐次经过状态机;R03实际在实施项仍为todo时写入文件,随后才逐项审核和标记done。没有安装提交钩子时,不能据此宣称插件能阻止直接shell绕过提交预检。本轮未测试强行篡改台账、直接Git绕过、多人并发、QMS集成、全部Harness/模型版本或生产部署。
|
|
64
|
+
|
|
65
|
+
scope_hash只覆盖任务files的内容,不能证明命令或范围外业务文件未变;观察者已纠正DSH一次过度解读。模型报告也可能出现测试分类计数错误,必须复核实际日志。自动化中的模拟宿主与真实DSH会话分开计证,不互相替代。
|
package/hooks/commit-msg
CHANGED
|
@@ -172,19 +172,23 @@ var STANDARD = {
|
|
|
172
172
|
{ from: "\u4EA4\u4ED8", to: "\u4EE3\u7801\u5BA1\u6838", requires: ["verified"] },
|
|
173
173
|
{ from: "\u4EE3\u7801\u5BA1\u6838", to: "\u5B8C\u6210", requires: ["review_passed", "artifacts_present"] }
|
|
174
174
|
],
|
|
175
|
+
// No artifacts, no commit policy, no skills: the skeleton carries none of them.
|
|
176
|
+
artifacts: [],
|
|
177
|
+
commit: { policy: "task", message_pattern: "", message_hint: "\u65E0\u683C\u5F0F\u8981\u6C42", checkpoints: ["\u4EE3\u7801\u5BA1\u6838"], file_scope: true },
|
|
178
|
+
high_risk_requires_verification: true
|
|
179
|
+
};
|
|
180
|
+
var STANDARD_RECOMMENDATION = {
|
|
181
|
+
commit: {
|
|
182
|
+
// Only the TEXT conventions. Policy, checkpoints and scope stay on the
|
|
183
|
+
// skeleton: they decide when a gate fires, not how a message reads.
|
|
184
|
+
message_pattern: "^\u3010(\\S+)\u3011\u3010(?:TASK|T\\d+)\u3011.+",
|
|
185
|
+
message_hint: "\u3010<task_id>\u3011\u3010TASK/T1\u3011\u8BF4\u660E \u2014\u2014 \u7B2C\u4E00\u6BB5\u586B\u672C\u4EFB\u52A1 id\uFF08\u5982 GREET-001\uFF09\uFF0C\u5199\u7ED3\u679C\u4E0D\u5199\u7A7A\u6CDB\u52A8\u4F5C"
|
|
186
|
+
},
|
|
175
187
|
artifacts: [
|
|
176
188
|
{ stage: "\u9700\u6C42\u8BC4\u5BA1", id: "requirement", name: "\u9700\u6C42\u8BF4\u660E", fields: ["scope", "acceptance_criteria"] },
|
|
177
189
|
{ stage: "\u8BBE\u8BA1", id: "design", name: "\u8BBE\u8BA1\u6587\u6863", fields: ["approach", "risks", "impact"] },
|
|
178
190
|
{ stage: "\u4EE3\u7801\u5BA1\u6838", id: "review", name: "\u8BC4\u5BA1\u8BB0\u5F55", fields: ["conclusion", "issues"] }
|
|
179
191
|
],
|
|
180
|
-
commit: {
|
|
181
|
-
policy: "task",
|
|
182
|
-
message_pattern: "^\u3010(\\S+)\u3011\u3010(?:TASK|T\\d+)\u3011.+",
|
|
183
|
-
message_hint: "\u3010<task_id>\u3011\u3010TASK/T1\u3011\u8BF4\u660E \u2014\u2014 \u7B2C\u4E00\u6BB5\u586B\u672C\u4EFB\u52A1 id\uFF08\u5982 GREET-001\uFF09\uFF0C\u5199\u7ED3\u679C\u4E0D\u5199\u7A7A\u6CDB\u52A8\u4F5C",
|
|
184
|
-
checkpoints: ["\u4EE3\u7801\u5BA1\u6838"],
|
|
185
|
-
file_scope: true
|
|
186
|
-
},
|
|
187
|
-
high_risk_requires_verification: true,
|
|
188
192
|
stage_bindings: {
|
|
189
193
|
"\u9700\u6C42\u8BC4\u5BA1": { skills: [bundled("requirement-analysis", "security-redlines")] },
|
|
190
194
|
"\u8BBE\u8BA1": { skills: [bundled("solution-design")] },
|
|
@@ -204,30 +208,38 @@ var AGILE = {
|
|
|
204
208
|
transitions: [
|
|
205
209
|
{ from: "\u9700\u6C42", to: "\u5F00\u53D1", requires: ["requirement_confirmation"] },
|
|
206
210
|
{ from: "\u5F00\u53D1", to: "\u4EA4\u4ED8", requires: ["todos_done"] },
|
|
211
|
+
// Entering 审查 is unconditional. A review guard on this edge would be
|
|
212
|
+
// circular — it would demand the verdict BEFORE the stage that produces it —
|
|
213
|
+
// and it would also block the commit at 交付, since a commit requires its
|
|
214
|
+
// stage's outgoing guards. What 审查 requires is declared as completion_guards
|
|
215
|
+
// instead, because a terminal stage has no outgoing edge to carry it.
|
|
207
216
|
{ from: "\u4EA4\u4ED8", to: "\u5BA1\u67E5", requires: [] }
|
|
208
217
|
],
|
|
218
|
+
artifacts: [],
|
|
219
|
+
commit: { policy: "item", message_pattern: "", message_hint: "\u65E0\u683C\u5F0F\u8981\u6C42", checkpoints: ["\u4EA4\u4ED8"], file_scope: true },
|
|
220
|
+
high_risk_requires_verification: false,
|
|
221
|
+
// 审查 is terminal and IS the review, so there is no outgoing edge to carry
|
|
222
|
+
// the guard. Declaring it here is what makes a blocked review prevent
|
|
223
|
+
// completion; without it the stage could be reached and finished with the
|
|
224
|
+
// review failing, which is what the assessment found.
|
|
225
|
+
completion_guards: ["review_passed"]
|
|
226
|
+
};
|
|
227
|
+
var AGILE_RECOMMENDATION = {
|
|
228
|
+
commit: {
|
|
229
|
+
// `item` policy emits two label shapes: `T<n>` mid-flow and `TASK` at the
|
|
230
|
+
// closing checkpoint. The pattern accepts BOTH, so the label the engine hands
|
|
231
|
+
// the model always satisfies the rule that validates it.
|
|
232
|
+
message_pattern: "^\u3010(\\S+)\u3011\u3010(?:TASK|T\\d+)\u3011.+",
|
|
233
|
+
message_hint: "\u3010<task_id>\u3011\u3010TASK/T1\u3011\u8BF4\u660E \u2014\u2014 \u7B2C\u4E00\u6BB5\u586B\u672C\u4EFB\u52A1 id\uFF1B\u5B9E\u65BD\u9879\u63D0\u4EA4\u7528 T1\u3001T2\uFF0C\u6536\u5C3E\u63D0\u4EA4\u7528 TASK"
|
|
234
|
+
},
|
|
209
235
|
artifacts: [
|
|
210
236
|
{ stage: "\u9700\u6C42", id: "requirement", name: "\u9700\u6C42\u8BF4\u660E", fields: ["scope"] },
|
|
211
237
|
// The 审查 stage binds code-review, whose instructions write this artifact with
|
|
212
|
-
// `record artifact=review`. Declaring it
|
|
213
|
-
//
|
|
214
|
-
//
|
|
238
|
+
// `record artifact=review`. Declaring it keeps that binding executable: the
|
|
239
|
+
// engine refuses an undeclared artifact, so a bound skill pointing at one would
|
|
240
|
+
// be a contract the recommendation made impossible to satisfy.
|
|
215
241
|
{ stage: "\u5BA1\u67E5", id: "review", name: "\u8BC4\u5BA1\u8BB0\u5F55", fields: ["conclusion", "issues"] }
|
|
216
242
|
],
|
|
217
|
-
commit: {
|
|
218
|
-
// `item` policy emits two label shapes: `T<n>` for a mid-flow item commit and
|
|
219
|
-
// `TASK` for the closing commit at the checkpoint. The pattern below accepts
|
|
220
|
-
// BOTH, so the label the engine hands the model always satisfies the preset's
|
|
221
|
-
// own rule. Previously it accepted only `T\d+` while the checkpoint stage
|
|
222
|
-
// returned `TASK`, which made the closing commit impossible to phrase: the
|
|
223
|
-
// status line and the validator disagreed about the contract.
|
|
224
|
-
policy: "item",
|
|
225
|
-
message_pattern: "^\u3010(\\S+)\u3011\u3010(?:TASK|T\\d+)\u3011.+",
|
|
226
|
-
message_hint: "\u3010<task_id>\u3011\u3010TASK/T1\u3011\u8BF4\u660E \u2014\u2014 \u7B2C\u4E00\u6BB5\u586B\u672C\u4EFB\u52A1 id\uFF1B\u5B9E\u65BD\u9879\u63D0\u4EA4\u7528 T1\u3001T2\uFF0C\u6536\u5C3E\u63D0\u4EA4\u7528 TASK",
|
|
227
|
-
checkpoints: ["\u4EA4\u4ED8"],
|
|
228
|
-
file_scope: true
|
|
229
|
-
},
|
|
230
|
-
high_risk_requires_verification: false,
|
|
231
243
|
stage_bindings: {
|
|
232
244
|
"\u9700\u6C42": { skills: [bundled("requirement-analysis", "security-redlines")] },
|
|
233
245
|
"\u5F00\u53D1": { skills: [bundled("code-implement", "coding-conventions", "security-redlines")] },
|
|
@@ -242,18 +254,13 @@ var MINIMAL = {
|
|
|
242
254
|
{ from: "\u5F00\u53D1", to: "\u4EA4\u4ED8", requires: ["todos_done"] }
|
|
243
255
|
],
|
|
244
256
|
artifacts: [],
|
|
245
|
-
commit: {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
checkpoints: ["\u4EA4\u4ED8"],
|
|
250
|
-
file_scope: false
|
|
251
|
-
},
|
|
252
|
-
high_risk_requires_verification: false,
|
|
257
|
+
commit: { policy: "task", message_pattern: "", message_hint: "\u65E0\u683C\u5F0F\u8981\u6C42", checkpoints: ["\u4EA4\u4ED8"], file_scope: false },
|
|
258
|
+
high_risk_requires_verification: false
|
|
259
|
+
};
|
|
260
|
+
var MINIMAL_RECOMMENDATION = {
|
|
253
261
|
// A fast-change flow checks each change once, not twice under two headings a
|
|
254
262
|
// short change rarely distinguishes. The item still has to be reviewed — what
|
|
255
|
-
// drops is the duplicated verdict, which is where the weight actually was
|
|
256
|
-
// flow used to carry the same per-item audit burden as the full one.
|
|
263
|
+
// drops is the duplicated verdict, which is where the weight actually was.
|
|
257
264
|
review_depth: "single",
|
|
258
265
|
stage_bindings: {
|
|
259
266
|
"\u5F00\u53D1": { skills: [bundled("code-implement", "coding-conventions", "security-redlines")] },
|
|
@@ -269,54 +276,126 @@ function deriveCapabilities(config2) {
|
|
|
269
276
|
if (config2.commit.file_scope) caps.push("file_scope");
|
|
270
277
|
return caps;
|
|
271
278
|
}
|
|
272
|
-
function preset(id, version, label, description, config2) {
|
|
273
|
-
return {
|
|
279
|
+
function preset(id, version, label, description, config2, recommendation) {
|
|
280
|
+
return {
|
|
281
|
+
id,
|
|
282
|
+
version,
|
|
283
|
+
label,
|
|
284
|
+
description,
|
|
285
|
+
capabilities: deriveCapabilities(config2),
|
|
286
|
+
config: config2,
|
|
287
|
+
// exactOptionalPropertyTypes: omit rather than assign undefined.
|
|
288
|
+
...recommendation !== void 0 ? { recommendation } : {}
|
|
289
|
+
};
|
|
274
290
|
}
|
|
275
291
|
var FLOW_PRESETS = {
|
|
276
|
-
standard: preset("standard",
|
|
277
|
-
// Version
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
-
|
|
281
|
-
// Version
|
|
282
|
-
//
|
|
283
|
-
//
|
|
284
|
-
//
|
|
285
|
-
|
|
292
|
+
standard: preset("standard", 3, "\u5B8C\u6574\u7814\u53D1", "\u65B0\u529F\u80FD\u3001\u67B6\u6784\u6216\u8DE8\u6A21\u5757\u6539\u52A8\u3001\u9AD8\u98CE\u9669\u4EFB\u52A1\uFF1A\u9700\u6C42\u786E\u8BA4 \u2192 \u65B9\u6848\u786E\u8BA4 \u2192 \u5B9E\u73B0 \u2192 \u9A8C\u8BC1 \u2192 \u5BA1\u6838 \u2192 \u63D0\u4EA4", STANDARD, STANDARD_RECOMMENDATION),
|
|
293
|
+
// Version 4 for agile: 交付 → 审查 now requires review_passed, where it previously
|
|
294
|
+
// required nothing. That is a gate-semantics change, so a task created before it
|
|
295
|
+
// keeps its frozen version 3 config and behaves exactly as it did.
|
|
296
|
+
//
|
|
297
|
+
// Version 3 across all three: the preset is now a bare skeleton and its former
|
|
298
|
+
// built-in bindings, commit rule and artifacts are a separate recommendation a
|
|
299
|
+
// user adopts explicitly. Tasks created before this keep their frozen config and
|
|
300
|
+
// are unaffected; a project's existing config is likewise left exactly as it is.
|
|
301
|
+
agile: preset("agile", 4, "\u65E5\u5E38\u8FED\u4EE3", "\u76EE\u6807\u660E\u786E\u7684\u5E38\u89C4\u529F\u80FD\u4E0E\u7F3A\u9677\u4FEE\u590D\uFF1A\u76EE\u6807\u4E0E\u9A8C\u6536 \u2192 \u5B9E\u73B0 \u2192 \u9A8C\u6536\u4E0E\u5BA1\u67E5 \u2192 \u63D0\u4EA4", AGILE, AGILE_RECOMMENDATION),
|
|
302
|
+
minimal: preset("minimal", 3, "\u5FEB\u901F\u4FEE\u6539", "\u5C40\u90E8\u3001\u4F4E\u98CE\u9669\u3001\u65B9\u6848\u660E\u786E\u7684\u6539\u52A8\uFF1A\u4FEE\u6539 \u2192 \u68C0\u67E5\u4E0E\u63D0\u4EA4\uFF0C\u6BCF\u9879\u4E00\u6B21\u68C0\u67E5", MINIMAL, MINIMAL_RECOMMENDATION)
|
|
286
303
|
};
|
|
287
304
|
function dedupe(names) {
|
|
288
305
|
return [...new Set(names)];
|
|
289
306
|
}
|
|
290
|
-
function
|
|
291
|
-
if (
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const
|
|
296
|
-
const
|
|
297
|
-
const
|
|
298
|
-
for (const
|
|
299
|
-
const
|
|
300
|
-
if (
|
|
301
|
-
|
|
302
|
-
|
|
307
|
+
function applyProjectConfig(base, project) {
|
|
308
|
+
if (project === void 0) return base;
|
|
309
|
+
const next = { ...base };
|
|
310
|
+
if (project.stage_bindings !== void 0) {
|
|
311
|
+
const profiles = { ...project.skill_profiles ?? {} };
|
|
312
|
+
const explicitProfiles = new Set(Object.keys(project.skill_profiles ?? {}));
|
|
313
|
+
const problems = [];
|
|
314
|
+
const bindings = {};
|
|
315
|
+
for (const [stage, binding] of Object.entries(project.stage_bindings)) {
|
|
316
|
+
const entry = {};
|
|
317
|
+
if (binding === null || typeof binding !== "object") {
|
|
318
|
+
problems.push(`stage_bindings "${stage}" must be an object`);
|
|
319
|
+
bindings[stage] = entry;
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (binding.skill_refs !== void 0 && binding.skills !== void 0) {
|
|
323
|
+
problems.push(`stage_bindings "${stage}" has both skill_refs and legacy skills`);
|
|
324
|
+
}
|
|
325
|
+
if (binding.skill_refs !== void 0) {
|
|
326
|
+
if (!Array.isArray(binding.skill_refs)) {
|
|
327
|
+
problems.push(`stage_bindings "${stage}" skill_refs must be an array`);
|
|
328
|
+
}
|
|
329
|
+
entry.skills = (Array.isArray(binding.skill_refs) ? binding.skill_refs : []).flatMap((ref) => {
|
|
330
|
+
if (ref === null || typeof ref !== "object") {
|
|
331
|
+
problems.push(`stage_bindings "${stage}" has an invalid skill reference`);
|
|
332
|
+
return [];
|
|
333
|
+
}
|
|
334
|
+
const key = formatResourceRef(ref);
|
|
335
|
+
const profile = profiles[key];
|
|
336
|
+
if (profile === void 0) {
|
|
337
|
+
problems.push(`skill profile "${key}" is missing`);
|
|
338
|
+
return [];
|
|
339
|
+
}
|
|
340
|
+
if (profile === null || !Array.isArray(profile.rules)) {
|
|
341
|
+
problems.push(`skill profile "${key}" must declare a rules array`);
|
|
342
|
+
return [];
|
|
343
|
+
}
|
|
344
|
+
return [{ skill: ref, rules: profile.rules, ...profile.evidence !== void 0 ? { evidence: profile.evidence } : {} }];
|
|
345
|
+
});
|
|
346
|
+
} else if (binding.skills !== void 0) {
|
|
347
|
+
if (!Array.isArray(binding.skills)) {
|
|
348
|
+
problems.push(`stage_bindings "${stage}" skills must be an array`);
|
|
349
|
+
}
|
|
350
|
+
entry.skills = (Array.isArray(binding.skills) ? binding.skills : []).flatMap((skill) => {
|
|
351
|
+
if (skill === null || typeof skill !== "object" || skill.skill === null || typeof skill.skill !== "object") {
|
|
352
|
+
problems.push(`stage_bindings "${stage}" has an invalid skill binding`);
|
|
353
|
+
return [];
|
|
354
|
+
}
|
|
355
|
+
const key = formatResourceRef(skill.skill);
|
|
356
|
+
if (explicitProfiles.has(key)) {
|
|
357
|
+
const profile = profiles[key];
|
|
358
|
+
if (profile === null || !Array.isArray(profile?.rules)) {
|
|
359
|
+
problems.push(`skill profile "${key}" must declare a rules array`);
|
|
360
|
+
return [];
|
|
361
|
+
}
|
|
362
|
+
return [{
|
|
363
|
+
skill: skill.skill,
|
|
364
|
+
rules: profile.rules,
|
|
365
|
+
...profile.evidence !== void 0 ? { evidence: profile.evidence } : {}
|
|
366
|
+
}];
|
|
367
|
+
}
|
|
368
|
+
const candidate = { rules: skill.rules ?? [], ...skill.evidence !== void 0 ? { evidence: skill.evidence } : {} };
|
|
369
|
+
const existing = profiles[key];
|
|
370
|
+
if (existing !== void 0 && JSON.stringify(existing) !== JSON.stringify(candidate)) {
|
|
371
|
+
problems.push(`skill "${key}" has different rules or evidence in multiple stages; copy it as a distinct skill`);
|
|
372
|
+
} else profiles[key] = candidate;
|
|
373
|
+
return [skill];
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
const legacy = dedupe(binding.legacy_rules ?? []);
|
|
377
|
+
if (legacy.length > 0) entry.legacy_rules = legacy;
|
|
378
|
+
bindings[stage] = entry;
|
|
303
379
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
if (
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
changed = true;
|
|
380
|
+
next.stage_bindings = bindings;
|
|
381
|
+
next.skill_profiles = profiles;
|
|
382
|
+
if (problems.length > 0) next.configuration_errors = problems;
|
|
383
|
+
} else if (project.skill_profiles !== void 0) {
|
|
384
|
+
next.skill_profiles = project.skill_profiles;
|
|
310
385
|
}
|
|
311
|
-
if (
|
|
312
|
-
|
|
386
|
+
if (project.commit !== void 0) next.commit = project.commit;
|
|
387
|
+
if (project.artifacts !== void 0) next.artifacts = project.artifacts;
|
|
388
|
+
if (project.review_depth !== void 0) next.review_depth = project.review_depth;
|
|
389
|
+
if (project.commit_required !== void 0) next.commit_required = project.commit_required;
|
|
390
|
+
return next;
|
|
313
391
|
}
|
|
314
|
-
function resolveFlow(flow,
|
|
392
|
+
function resolveFlow(flow, project) {
|
|
315
393
|
const preset2 = FLOW_PRESETS[flow];
|
|
316
394
|
if (preset2 === void 0) {
|
|
317
395
|
return { ok: false, code: "UNKNOWN_FLOW", flow, knownFlows: Object.keys(FLOW_PRESETS) };
|
|
318
396
|
}
|
|
319
|
-
|
|
397
|
+
const { flow: _ignored, ...rest } = project ?? { flow };
|
|
398
|
+
return { ok: true, config: applyProjectConfig(preset2.config, { flow, ...rest }), preset: preset2 };
|
|
320
399
|
}
|
|
321
400
|
|
|
322
401
|
// src/snapshot.ts
|
|
@@ -471,8 +550,16 @@ function loadWorkflow(parsed) {
|
|
|
471
550
|
if (typeof flow !== "string" || flow.trim() === "") {
|
|
472
551
|
refuse('\u9879\u76EE .dsh/eng.json \u7F3A "flow" \u5B57\u6BB5\u2014\u2014\u8BBE\u4E3A standard|agile|minimal\uFF0C\u6216\u5220\u9664\u8BE5\u6587\u4EF6');
|
|
473
552
|
}
|
|
474
|
-
const
|
|
475
|
-
|
|
553
|
+
const project = {
|
|
554
|
+
flow,
|
|
555
|
+
...typeof parsed.stage_bindings === "object" && parsed.stage_bindings !== null && !Array.isArray(parsed.stage_bindings) ? { stage_bindings: parsed.stage_bindings } : {},
|
|
556
|
+
...typeof parsed.skill_profiles === "object" && parsed.skill_profiles !== null && !Array.isArray(parsed.skill_profiles) ? { skill_profiles: parsed.skill_profiles } : {},
|
|
557
|
+
...typeof parsed.commit === "object" && parsed.commit !== null && !Array.isArray(parsed.commit) ? { commit: parsed.commit } : {},
|
|
558
|
+
...Array.isArray(parsed.artifacts) ? { artifacts: parsed.artifacts } : {},
|
|
559
|
+
...typeof parsed.review_depth === "string" ? { review_depth: parsed.review_depth } : {},
|
|
560
|
+
...typeof parsed.commit_required === "boolean" ? { commit_required: parsed.commit_required } : {}
|
|
561
|
+
};
|
|
562
|
+
const resolved = resolveFlow(flow, project);
|
|
476
563
|
if (!resolved.ok) {
|
|
477
564
|
refuse(`\u672A\u77E5\u6D41\u7A0B "${resolved.flow}"\uFF08\u5DF2\u77E5\u6D41\u7A0B\uFF1A${resolved.knownFlows.join("\u3001")}\uFF09`);
|
|
478
565
|
}
|