@xenonbyte/da-vinci-workflow 0.2.5 → 0.2.7

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.
@@ -0,0 +1,101 @@
1
+ # Maintainer Bootstrap 指南
2
+
3
+ 这份文档是“维护 Da Vinci 仓库本身”的 canonical 入口。
4
+
5
+ ## 先划边界
6
+
7
+ - 仓库维护者 bootstrap:准备修改这个仓库。
8
+ - 下游项目 bootstrap:`da-vinci bootstrap-project` 用于给其他项目初始化 `.da-vinci/` 工件。
9
+ - 安装状态:`da-vinci status` / `da-vinci verify-install` 只校验安装置信度,不等于仓库就绪度。
10
+ - operator 工作流执行:下游项目操作请看 operator 文档,不要把它当维护者 bootstrap 入口。
11
+
12
+ ## Clone 后前 10 分钟
13
+
14
+ 1. 安装依赖:`npm install`
15
+ 2. 校验打包资产:`npm run validate-assets`
16
+ 3. 显式校验安装范围(推荐):`da-vinci verify-install --platform codex`
17
+ 4. 运行维护者 canonical 就绪检查:`da-vinci maintainer-readiness --platform codex`
18
+
19
+ 如果你维护多个环境,请显式传入多个平台:
20
+
21
+ - `da-vinci verify-install --platform codex,claude`
22
+ - `da-vinci maintainer-readiness --platform codex,claude`
23
+
24
+ 这些命令应在你要维护的仓库根目录执行。需要时可用 `--project <path>` 显式指定目标仓库。
25
+
26
+ ## 入口面分层
27
+
28
+ - Canonical 入口:
29
+ - 本文档 + `da-vinci maintainer-readiness`
30
+ - Secondary/advanced 维护者面:
31
+ - `npm run quality:ci:contracts`
32
+ - `npm run quality:ci:e2e`
33
+ - `npm run quality:ci`
34
+ - 针对回归的 `scripts/test-*.js`
35
+ - 下游项目面(非仓库维护 bootstrap):
36
+ - `da-vinci bootstrap-project`
37
+ - 项目 `.da-vinci/` 工件工作流
38
+ - bootstrap 相邻的安装面:
39
+ - `da-vinci status`
40
+ - `da-vinci verify-install`
41
+
42
+ ## Bootstrap 与 Diagnosis
43
+
44
+ - Bootstrap/setup 回答的是:“我如何准备好在这里工作?”
45
+ - Diagnosis/readiness 回答的是:“当前仓库 + 本地环境是否足够健康以推进本次改动?”
46
+
47
+ Canonical diagnosis 面:
48
+
49
+ - `da-vinci maintainer-readiness`
50
+
51
+ 该命令刻意比 full CI 更窄,并提供可执行的下一步。默认包含:
52
+
53
+ - 打包资产校验
54
+ - selected-platform 的 verify-install 置信度
55
+ - 聚焦质量 lane:`npm run quality:ci:contracts`
56
+
57
+ ## Selected Platform 语义
58
+
59
+ - 显式 `--platform`:只有选中的平台是阻断项。
60
+ - 未选平台仍可见,但会标为 `out_of_scope` / degraded coverage。
61
+ - 未传 `--platform`:按 unknown scope 报告 degraded coverage,不会硬失败。
62
+
63
+ ## Use This / Do Not Use This
64
+
65
+ - Use this:
66
+ - `da-vinci verify-install --platform codex`,然后 `da-vinci maintainer-readiness --platform codex`
67
+ - Do not use this:
68
+ - 只跑 `da-vinci status` 就判断仓库就绪
69
+ - Use this:
70
+ - 用本指南做仓库维护者入门
71
+ - Do not use this:
72
+ - 把 `da-vinci bootstrap-project` 当成这个仓库的维护者 bootstrap
73
+ - Use this:
74
+ - 只有在下游项目操作时才看 operator 文档
75
+ - Do not use this:
76
+ - 把 operator 工作流文档当成维护者首入口
77
+
78
+ ## 常见排错
79
+
80
+ - 现象:`verify-install` 是 WARN 且 scope unknown。
81
+ - 处理:补 `--platform <value>` 显式声明 selected scope。
82
+ - 现象:`maintainer-readiness` 在 verify-install 上 BLOCK。
83
+ - 处理:先修 selected-platform 安装问题,再重跑 readiness。
84
+ - 现象:安装检查都通过,但对改动仍缺乏信心。
85
+ - 处理:升级跑更深的 lanes(`quality:ci:e2e` 或 `quality:ci`)和目标脚本。
86
+ - 现象:混淆仓库 bootstrap 与下游项目 bootstrap。
87
+ - 处理:仓库维护按本指南,下游仓库才使用 `bootstrap-project`。
88
+
89
+ ## 更深层验证 Lane
90
+
91
+ 高风险或范围较大的改动前建议执行:
92
+
93
+ - `npm run quality:ci:e2e`
94
+ - `npm run quality:ci`
95
+
96
+ ## Operator 工作流参考
97
+
98
+ operator 文档与维护者 bootstrap 保持分离:
99
+
100
+ - [skill-usage.md](./skill-usage.md)
101
+ - [workflow-overview.md](./workflow-overview.md)
@@ -12,7 +12,7 @@
12
12
  - screenshot review
13
13
  - runtime gate 和 filesystem audit
14
14
 
15
- 如果你想看 `design`、`tasks`、`build`、`verify` 这些 `dv:` 路由之间怎么衔接,以及 `verify` 出问题后退回哪一层,请看 [dv-command-reference.md](/Users/xubo/x-skills/da-vinci/docs/zh-CN/dv-command-reference.md)。
15
+ 如果你想看 `design`、`tasks`、`build`、`verify` 这些 `dv:` 路由之间怎么衔接,以及 `verify` 出问题后退回哪一层,请看 [dv-command-reference.md](./dv-command-reference.md)。
16
16
 
17
17
  ## 渲染真相模型
18
18
 
@@ -2,6 +2,9 @@
2
2
 
3
3
  这份文档面向真正操作 Da Vinci 的人,而不是底层命令参考。
4
4
 
5
+ 如果你是在维护 Da Vinci 仓库本身,请先看
6
+ [maintainer-bootstrap.md](./maintainer-bootstrap.md),而不是这份偏 operator 的流程文档。
7
+
5
8
  重点讲四件事:
6
9
 
7
10
  - 第一次怎么进入 Da Vinci 工作流
@@ -18,6 +21,7 @@ Da Vinci 应该根据工件真相恢复,而不是根据聊天记忆恢复。
18
21
  - `spec.md` 以及相关需求工件是行为真相
19
22
  - 项目内 `.pen` 是设计真相
20
23
  - `tasks.md`、`verification.md` 和 execution signals 负责说明哪些东西已经实现、哪些还没实现
24
+ - workflow memory 是 `.da-vinci/state/` 下的工件级本地状态,不是跨会话聊天记忆
21
25
 
22
26
  ## 第一次进入时怎么选入口
23
27
 
@@ -79,14 +83,20 @@ $da-vinci use continue for <existing workflow state>
79
83
 
80
84
  - `da-vinci workflow-status`
81
85
  - 先确认当前阶段和 blocker
86
+ - task-group focus 会综合 checklist 进度和最新 `task-execution` / `task-review` 证据
82
87
  - `da-vinci next-step`
83
88
  - 再确认下一步到底应该是 `design`、`tasks` 还是 `build`
89
+ - 当你需要 runtime-aware 的首要聚焦点时,优先看它而不是只看下一条未勾选 checklist
84
90
  - `da-vinci lint-spec`
85
91
  - 当运行时 spec 质量还不够确定时使用
92
+ - `--json` 中重点看 `gates.principleInheritance`、`gates.clarify`、`gates.scenarioQuality`
93
+ - 当 bounded 元数据完整时,把 `gates.clarify.bounded` 视为“可见上下文”;bounded-only clarify 默认不阻断
86
94
  - `da-vinci scope-check`
87
95
  - 当规划工件之间页面/状态传播关系还不清楚时使用
96
+ - `--json` 中重点看 `gates.analyze` 的跨工件一致性阻断项
88
97
  - `da-vinci lint-tasks`
89
98
  - 校验 task groups 是否包含 discipline markers、明确文件落点、执行意图与 verification 命令
99
+ - `--json` 中重点看 `gates.taskCheckpoint` 的 anchor/checkpoint 就绪度
90
100
  - `da-vinci lint-bindings`
91
101
  - 当 `pencil-design.md` 与 `pencil-bindings.md` 同时存在时运行,确保实现落点证据可解析
92
102
  - `da-vinci worktree-preflight`
@@ -112,6 +122,13 @@ da-vinci lint-tasks
112
122
 
113
123
  同一 task group 只有在 `spec` review 已是 `PASS` 时,`quality` review 才允许通过。
114
124
 
125
+ 兼容性说明:
126
+
127
+ - `workflow-status --json` 仍保留扁平 task-group 字段:`status`、`completion`、`nextAction`、`resumeCursor`
128
+ - `completion` 仍表示 checklist 推导的进度百分比
129
+ - 当 implementer/review 证据更新更晚时,`status`、`nextAction`、`resumeCursor` 可能切换到 runtime-aware 值
130
+ - 当规划 gate 阻断晋级时,`workflow-status --json` 与 `next-step --json` 会输出 `blockingGate`
131
+
115
132
  ## 中途退出后,下次怎么恢复
116
133
 
117
134
  最稳妥的恢复顺序是:
@@ -124,12 +141,25 @@ da-vinci lint-tasks
124
141
  6. 在终态前,跑 `da-vinci verify-bindings` 和 `da-vinci verify-coverage`
125
142
  7. 如果要做 completion 表述,确认 `verify-coverage` 的 freshness 仍然有效,并运行 `da-vinci audit --mode completion --change <id> <project-path>`
126
143
 
144
+ 严格性控制:
145
+
146
+ - lint 命令严格性:使用 `--strict`
147
+ - workflow 晋级严格性:使用 `DA_VINCI_DISCIPLINE_STRICT_PROMOTION`
148
+ - clarify/analyze/task-checkpoint 不新增独立 strict 开关
149
+ - 在 integrity audit 中,即使 `lint-spec` signal 顶层是 `PASS`,bounded-only clarify 上下文也应保持可见
150
+
127
151
  恢复时应该遵循工件,而不是旧聊天上下文:
128
152
 
129
153
  - 如果设计工件已经有了,但 `tasks.md` 还没有,下一步应该回到 `tasks`
130
154
  - 如果 `tasks.md` 已存在,但设计 gate 还没过,不要直接进 `build`
131
155
  - 只有实现就绪度已经明确时,才把 `build` 当主恢复路径
132
156
 
157
+ 当 task 执行或评审证据与 checklist 进度冲突时:
158
+
159
+ - 以 `workflow-status` / `next-step` 的路由建议为准
160
+ - 把 task-group 的 `nextAction` 视为有效续跑焦点
161
+ - 把 checklist `completion` 视为进度可见性,不把它当成 blocker/review debt 已清空的证明
162
+
133
163
  ## `change-id` 到底是什么
134
164
 
135
165
  `change-id` 就是 `.da-vinci/changes/<change-id>/` 这个目录名。
@@ -12,11 +12,12 @@
12
12
  - 页面到设计的绑定
13
13
  - 实现与验证
14
14
 
15
- 如果你想看 Pencil 渲染、持久化、门禁和审计的专门说明,请看 [pencil-rendering-workflow.md](/Users/xubo/x-skills/da-vinci/docs/zh-CN/pencil-rendering-workflow.md)。
16
- 如果你想看逐个 `dv:` 命令的职责、下一步推荐和 `verify` 回退规则,请看 [dv-command-reference.md](/Users/xubo/x-skills/da-vinci/docs/zh-CN/dv-command-reference.md)。
17
- 如果你想看约束文件总览(哪些字段是硬门禁、哪些是指导项),请看 [constraint-files.md](/Users/xubo/x-skills/da-vinci/docs/zh-CN/constraint-files.md)。
18
- 如果你想看 sidecar、enforcement、persisted-state 回退和 scaffold 约束,请看 [execution-chain-migration.md](/Users/xubo/x-skills/da-vinci/docs/zh-CN/execution-chain-migration.md)。
19
- 如果你想看更偏操作手册的说明,比如第一次怎么进、暂停后怎么续跑、TUI 怎么用,请看 [skill-usage.md](/Users/xubo/x-skills/da-vinci/docs/zh-CN/skill-usage.md)。
15
+ 如果你想看 Pencil 渲染、持久化、门禁和审计的专门说明,请看 [pencil-rendering-workflow.md](./pencil-rendering-workflow.md)。
16
+ 如果你想看逐个 `dv:` 命令的职责、下一步推荐和 `verify` 回退规则,请看 [dv-command-reference.md](./dv-command-reference.md)。
17
+ 如果你想看约束文件总览(哪些字段是硬门禁、哪些是指导项),请看 [constraint-files.md](./constraint-files.md)。
18
+ 如果你想看 sidecar、enforcement、persisted-state 回退和 scaffold 约束,请看 [execution-chain-migration.md](./execution-chain-migration.md)。
19
+ 如果你想看更偏操作手册的说明,比如第一次怎么进、暂停后怎么续跑、TUI 怎么用,请看 [skill-usage.md](./skill-usage.md)。
20
+ 如果你是在维护仓库本身(不是下游项目操作),请从 [maintainer-bootstrap.md](./maintainer-bootstrap.md) 进入。
20
21
 
21
22
  ## 核心契约
22
23
 
@@ -35,6 +36,9 @@ Da Vinci 围绕一个固定契约工作:
35
36
  真相与编排边界:
36
37
 
37
38
  - 选路和 completion 真相来自工件、checkpoint、execution signals 与 `audit`
39
+ - workflow memory 是工件驱动的本地状态,不是聊天派生记忆
40
+ - `.da-vinci/state/workflow.json` 是 persisted route cache,不是 task-group runtime state 的 canonical owner
41
+ - `.da-vinci/state/task-groups/<change>.json` 是 workflow snapshot 写入后的 task-group runtime state canonical owner
38
42
  - execution profile、worktree preflight 等编排信息始终是 advisory 指导,不替代真相面
39
43
  - 编排提示不能覆盖来自工件、checkpoint 或 completion audit 的明确 `BLOCK`
40
44
 
@@ -56,14 +60,23 @@ Da Vinci 围绕一个固定契约工作:
56
60
 
57
61
  - `da-vinci workflow-status --project <path> [--change <id>] [--json]`
58
62
  - 从工件真相和部分 audit 可见证据推导阶段、blocker、handoff gate 与路由建议
63
+ - 输出 canonical task-group runtime state,并同时保留兼容字段与 planned / implementer / review / effective 分层
59
64
  - `da-vinci next-step --project <path> [--change <id>] [--json]`
60
65
  - 基于同一 workflow-state 模型,给出优先续跑动作
66
+ - 与 `workflow-status` 共用同一 task-group runtime-state 模型,不再是 checklist-only 解释
67
+ - 当 clarify/analyze/task-checkpoint 阻断晋级时,`--json` 会输出 `blockingGate`
61
68
  - `da-vinci lint-spec --project <path> [--change <id>] [--strict] [--json]`
62
69
  - 在进入实现规划或实现前,校验运行时 `spec.md` 结构质量
70
+ - `--json` 中输出机器可读 gate:`gates.principleInheritance`、`gates.clarify`、`gates.scenarioQuality`
71
+ - 当 bounded 元数据完整时,clarify 的 bounded 歧义会保留在 `gates.clarify.bounded` 与 notes 中,默认不作为阻断
63
72
  - 默认 advisory;显式 `--strict` 才会把发现升级为阻断
64
73
  - `da-vinci scope-check --project <path> [--change <id>] [--strict] [--json]`
65
74
  - 校验 proposal、page-map、运行时 spec、pencil-design、tasks 之间页面/状态传播是否一致
75
+ - `--json` 中输出机器可读 gate:`gates.analyze`
66
76
  - 输出页面和状态的机器可读覆盖矩阵,供后续 verify/status 消费
77
+ - `da-vinci lint-tasks --project <path> [--change <id>] [--strict] [--json]`
78
+ - 校验 task-group 规划纪律以及 task-anchor 到上游规划证据的可追溯性
79
+ - `--json` 中输出机器可读 gate:`gates.taskCheckpoint`
67
80
  - `da-vinci generate-sidecars --project <path> [--change <id>] [--json]`
68
81
  - 显式生成确定性的 planning sidecars,供 diff 和下游工具消费
69
82
  - `da-vinci verify-bindings|verify-implementation|verify-structure|verify-coverage --project <path> [--change <id>] [--strict] [--json]`
@@ -79,6 +92,24 @@ Da Vinci 围绕一个固定契约工作:
79
92
 
80
93
  这些命令不替代 `da-vinci audit --mode integrity|completion`。
81
94
  `audit` 仍是 integrity/completion 的正式真相面。
95
+ - 在 integrity 模式下,即使 `lint-spec` 顶层 signal 是 `PASS`,planning-signal notes 也会保留 bounded clarify 上下文
96
+
97
+ persisted-state trust 规则:
98
+
99
+ - 只要 governing artifact 内容 digest 仍匹配,即使 snapshot 很旧,persisted workflow snapshot 仍可用
100
+ - snapshot 年龄仅是 advisory,不再单独导致 persisted route truth 失效
101
+ - execution signals、audits、discipline、verification freshness 等 live overlay 每次读取都会重算
102
+
103
+ planning-gate 严格性控制面:
104
+
105
+ - 命令级 lint 严格性使用已有 `--strict`
106
+ - workflow 晋级严格性使用 `DA_VINCI_DISCIPLINE_STRICT_PROMOTION`
107
+ - clarify/analyze/task-checkpoint gate 不引入新的 strict flag 家族
108
+
109
+ spec-kit 借鉴边界:
110
+
111
+ - 只借鉴 clarify/analyze/gate 这类方法论,不搬运整套工具链模型。
112
+ - 不采用 spec-kit 的 constitution/init/branch/toolchain 工作流。
82
113
 
83
114
  ## 标准工件骨架
84
115
 
@@ -204,6 +235,8 @@ mapping 通过后:
204
235
  - 在大范围实现前,从 `workflow-status` 或 `next-step --json` 查看 `executionProfile`
205
236
  - 如果建议 bounded parallel,先跑 `da-vinci worktree-preflight --project <path> [--change <id>]`;隔离未就绪时降级为串行
206
237
  - 通过 `task-execution` 与有序 `task-review` 记录每个 task group 的执行与审查证据
238
+ - 当 live evidence 比 checklist 更新时,优先按 task-group `nextAction` / `resumeCursor` 读取 runtime-aware 聚焦点
239
+ - 保持把扁平 `completion` 解释为 checklist 进度百分比,而不是 runtime 健康度
207
240
  - 验证需求漂移和设计漂移
208
241
 
209
242
  ### 8. 终态完成
package/lib/audit.js CHANGED
@@ -145,6 +145,18 @@ function pushUnique(targetList, message) {
145
145
  }
146
146
  }
147
147
 
148
+ function listClarifyBoundedFindings(signal) {
149
+ const gates = signal && signal.details && signal.details.gates;
150
+ if (!gates || typeof gates !== "object") {
151
+ return [];
152
+ }
153
+ const clarify = gates.clarify;
154
+ if (!clarify || typeof clarify !== "object" || !Array.isArray(clarify.bounded)) {
155
+ return [];
156
+ }
157
+ return clarify.bounded.map((item) => String(item || "").trim()).filter(Boolean);
158
+ }
159
+
148
160
  function appendTraversalWarnings(projectRoot, rootDir, scan, warnings) {
149
161
  if (!scan) {
150
162
  return;
@@ -834,6 +846,13 @@ function auditProject(projectPathInput, options = {}) {
834
846
  } else if (signal.status === "WARN") {
835
847
  pushUnique(notes, `Planning signal ${surface} is WARN.`);
836
848
  }
849
+
850
+ if (surface === "lint-spec") {
851
+ const boundedClarifyFindings = listClarifyBoundedFindings(signal);
852
+ for (const finding of boundedClarifyFindings) {
853
+ pushUnique(notes, `Planning signal lint-spec clarify bounded context: ${finding}`);
854
+ }
855
+ }
837
856
  }
838
857
 
839
858
  if (mode === "completion" && VERIFICATION_SIGNAL_SURFACES.has(surface)) {
@@ -1,4 +1,5 @@
1
1
  const { writeExecutionSignal } = require("../execution-signals");
2
+ const { STATUS } = require("../workflow-contract");
2
3
 
3
4
  function emitOrThrowOnStatus(status, blockedStatuses, output, continueOnError) {
4
5
  if (!Array.isArray(blockedStatuses) || !blockedStatuses.includes(status)) {
@@ -12,16 +13,76 @@ function emitOrThrowOnStatus(status, blockedStatuses, output, continueOnError) {
12
13
  }
13
14
 
14
15
  function persistExecutionSignal(projectPath, changeId, surface, result, strict = false) {
16
+ const normalizeStatus = (status) => {
17
+ const normalized = String(status || "").trim().toUpperCase();
18
+ if (normalized === STATUS.BLOCK || normalized === STATUS.WARN || normalized === STATUS.PASS) {
19
+ return normalized;
20
+ }
21
+ return "";
22
+ };
23
+ const statusRank = (status) => {
24
+ if (status === STATUS.BLOCK) {
25
+ return 2;
26
+ }
27
+ if (status === STATUS.WARN) {
28
+ return 1;
29
+ }
30
+ if (status === STATUS.PASS) {
31
+ return 0;
32
+ }
33
+ return -1;
34
+ };
35
+ const worseStatus = (left, right) => {
36
+ const l = normalizeStatus(left);
37
+ const r = normalizeStatus(right);
38
+ if (!l && !r) {
39
+ return STATUS.PASS;
40
+ }
41
+ if (!l) {
42
+ return r;
43
+ }
44
+ if (!r) {
45
+ return l;
46
+ }
47
+ return statusRank(l) >= statusRank(r) ? l : r;
48
+ };
49
+ const deriveGateStatus = (gates) => {
50
+ if (!gates || typeof gates !== "object") {
51
+ return STATUS.PASS;
52
+ }
53
+ let worst = STATUS.PASS;
54
+ for (const gate of Object.values(gates)) {
55
+ worst = worseStatus(worst, gate && gate.status);
56
+ if (worst === STATUS.BLOCK) {
57
+ break;
58
+ }
59
+ }
60
+ return worst;
61
+ };
62
+
63
+ const details = {};
64
+ if (result && result.gates && typeof result.gates === "object") {
65
+ details.gates = result.gates;
66
+ }
67
+ if (result && result.summary && typeof result.summary === "object") {
68
+ details.summary = result.summary;
69
+ }
70
+ const persistedStatus = worseStatus(
71
+ result && result.status ? result.status : STATUS.PASS,
72
+ deriveGateStatus(details.gates)
73
+ );
74
+
15
75
  try {
16
76
  writeExecutionSignal(projectPath, {
17
77
  changeId: changeId || "global",
18
78
  surface,
19
- status: result.status,
79
+ status: persistedStatus,
20
80
  advisory: strict ? false : true,
21
81
  strict,
22
82
  failures: result.failures || [],
23
83
  warnings: result.warnings || [],
24
- notes: result.notes || []
84
+ notes: result.notes || [],
85
+ details: Object.keys(details).length > 0 ? details : null
25
86
  });
26
87
  } catch (error) {
27
88
  // Signals are advisory metadata and should not break command execution.
package/lib/cli.js CHANGED
@@ -4,6 +4,7 @@ const {
4
4
  installPlatforms,
5
5
  uninstallPlatforms,
6
6
  getStatus,
7
+ verifyInstall,
7
8
  validateAssets
8
9
  } = require("./install");
9
10
  const { auditProject, formatAuditReport } = require("./audit");
@@ -94,6 +95,10 @@ const {
94
95
  formatWorktreePreflightReport
95
96
  } = require("./worktree-preflight");
96
97
  const { formatTuiHelp, launchTui } = require("../tui");
98
+ const {
99
+ runMaintainerReadinessCheck,
100
+ formatMaintainerReadinessReport
101
+ } = require("./maintainer-readiness");
97
102
 
98
103
  const DEFAULT_MAX_PREFLIGHT_STDIN_BYTES = 1024 * 1024;
99
104
  const DEFAULT_MAX_STDIN_TRANSIENT_RETRIES = 2000;
@@ -120,8 +125,10 @@ const OPTION_FLAGS_WITH_VALUES = new Set([
120
125
  "--task-group",
121
126
  "--changed-files",
122
127
  "--test-evidence",
128
+ "--pending-test-evidence",
123
129
  "--concerns",
124
130
  "--blockers",
131
+ "--out-of-scope-writes",
125
132
  "--issues",
126
133
  "--reviewer",
127
134
  "--source",
@@ -194,8 +201,21 @@ const HELP_OPTION_SPECS = [
194
201
  description: "comma-separated changed files for verify-implementation/verify-structure/verify-coverage/task-execution"
195
202
  },
196
203
  { flag: "--test-evidence <csv>", description: "comma-separated test evidence commands for task-execution" },
204
+ {
205
+ flag: "--pending-test-evidence <csv>",
206
+ description: "comma-separated planned-but-not-executed test commands for task-execution"
207
+ },
208
+ {
209
+ flag: "--confirm-test-evidence-executed",
210
+ description: "required when providing --test-evidence; confirms listed commands actually ran"
211
+ },
197
212
  { flag: "--concerns <csv>", description: "comma-separated concern text for task-execution" },
198
213
  { flag: "--blockers <csv>", description: "comma-separated blocker text for task-execution" },
214
+ {
215
+ flag: "--out-of-scope-writes <csv>",
216
+ description: "comma-separated out-of-scope write paths for task-execution safety visibility"
217
+ },
218
+ { flag: "--partial", description: "mark task-execution payload as non-final progress evidence" },
199
219
  { flag: "--issues <csv>", description: "comma-separated issue text for task-review" },
200
220
  { flag: "--reviewer <name>", description: "reviewer identifier for task-review" },
201
221
  { flag: "--write-verification", description: "append task-review evidence into verification.md" },
@@ -459,6 +479,60 @@ function formatStatus(status) {
459
479
  return lines.join("\n");
460
480
  }
461
481
 
482
+ function formatVerifyInstallReport(result) {
483
+ const scopeLabel = result.scope && result.scope.known
484
+ ? result.scope.selectedPlatforms.join(", ")
485
+ : "unknown";
486
+ const lines = [
487
+ "Da Vinci verify-install",
488
+ `Status: ${result.status}`,
489
+ `Home: ${result.homeDir}`,
490
+ "Surface: install verification (bootstrap-adjacent), not full repository readiness",
491
+ `Selected platform scope: ${scopeLabel}`,
492
+ "Platform coverage:"
493
+ ];
494
+
495
+ for (const platform of result.scope.supportedPlatforms || []) {
496
+ const coverage = result.platformCoverage && result.platformCoverage[platform];
497
+ if (!coverage) {
498
+ continue;
499
+ }
500
+ const checkSummary = Object.entries(coverage.checks || {})
501
+ .map(([name, ok]) => `${name}=${ok ? "yes" : "no"}`)
502
+ .join(" ");
503
+ lines.push(
504
+ `- ${platform}: scope=${coverage.scope} healthy=${coverage.healthy ? "yes" : "no"}${checkSummary ? ` ${checkSummary}` : ""}`
505
+ );
506
+ }
507
+
508
+ if (Array.isArray(result.failures) && result.failures.length > 0) {
509
+ lines.push("Failures:");
510
+ for (const message of result.failures) {
511
+ lines.push(`- ${message}`);
512
+ }
513
+ }
514
+ if (Array.isArray(result.warnings) && result.warnings.length > 0) {
515
+ lines.push("Warnings:");
516
+ for (const message of result.warnings) {
517
+ lines.push(`- ${message}`);
518
+ }
519
+ }
520
+ if (Array.isArray(result.notes) && result.notes.length > 0) {
521
+ lines.push("Notes:");
522
+ for (const message of result.notes) {
523
+ lines.push(`- ${message}`);
524
+ }
525
+ }
526
+ if (Array.isArray(result.nextSteps) && result.nextSteps.length > 0) {
527
+ lines.push("Next steps:");
528
+ for (const step of result.nextSteps) {
529
+ lines.push(`- ${step}`);
530
+ }
531
+ }
532
+
533
+ return lines.join("\n");
534
+ }
535
+
462
536
  function appendStatusIssues(lines, label, missing = [], mismatched = [], unreadable = []) {
463
537
  if (missing.length > 0) {
464
538
  lines.push(` ${label} missing: ${missing.join(", ")}`);
@@ -485,6 +559,10 @@ function printHelp() {
485
559
  " da-vinci install --platform codex,claude,gemini",
486
560
  " da-vinci uninstall --platform codex,claude,gemini",
487
561
  " da-vinci status",
562
+ " da-vinci verify-install [--platform <value>] [--json]",
563
+ " (bootstrap-adjacent install verification for selected maintainer platforms)",
564
+ " da-vinci maintainer-readiness [--platform <value>] [--project <path>] [--json]",
565
+ " (canonical repository maintainer diagnosis/readiness; not downstream bootstrap)",
488
566
  " da-vinci tui [--project <path>] [--change <id>] [--lang en|zh] [--strict] [--json] [--continue-on-error] [--tui-width <cols>] [--alt-screen|--no-alt-screen]",
489
567
  " da-vinci workflow-status [--project <path>] [--change <id>] [--json]",
490
568
  " da-vinci next-step [--project <path>] [--change <id>] [--json]",
@@ -497,7 +575,7 @@ function printHelp() {
497
575
  " da-vinci verify-implementation [--project <path>] [--change <id>] [--changed-files <csv>] [--strict] [--json]",
498
576
  " da-vinci verify-structure [--project <path>] [--change <id>] [--changed-files <csv>] [--strict] [--json]",
499
577
  " da-vinci verify-coverage [--project <path>] [--change <id>] [--changed-files <csv>] [--strict] [--json]",
500
- " da-vinci task-execution --project <path> --change <id> --task-group <id> --status <DONE|DONE_WITH_CONCERNS|NEEDS_CONTEXT|BLOCKED> --summary <text> [--changed-files <csv>] [--test-evidence <csv>] [--concerns <csv>] [--blockers <csv>] [--json]",
578
+ " da-vinci task-execution --project <path> --change <id> --task-group <id> --status <DONE|DONE_WITH_CONCERNS|NEEDS_CONTEXT|BLOCKED> --summary <text> [--changed-files <csv>] [--test-evidence <csv> --confirm-test-evidence-executed] [--pending-test-evidence <csv>] [--concerns <csv>] [--blockers <csv>] [--out-of-scope-writes <csv>] [--partial] [--json]",
501
579
  " da-vinci task-review --project <path> --change <id> --task-group <id> --stage <spec|quality> --status <PASS|WARN|BLOCK> --summary <text> [--issues <csv>] [--reviewer <name>] [--write-verification] [--json]",
502
580
  " da-vinci worktree-preflight --project <path> [--change <id>] [--json]",
503
581
  " da-vinci diff-spec [--project <path>] [--change <id>] [--from <sidecars-dir>] [--json]",
@@ -955,6 +1033,40 @@ async function runCli(argv) {
955
1033
  return;
956
1034
  }
957
1035
 
1036
+ if (command === "verify-install") {
1037
+ const platformValue = getOption(argv, "--platform") || "";
1038
+ const result = verifyInstall({
1039
+ homeDir,
1040
+ platforms: platformValue
1041
+ });
1042
+ const output = argv.includes("--json")
1043
+ ? JSON.stringify(result, null, 2)
1044
+ : formatVerifyInstallReport(result);
1045
+ if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
1046
+ return;
1047
+ }
1048
+ console.log(output);
1049
+ return;
1050
+ }
1051
+
1052
+ if (command === "maintainer-readiness") {
1053
+ const platformValue = getOption(argv, "--platform") || "";
1054
+ const repoRoot = getOption(argv, "--project") || process.cwd();
1055
+ const result = runMaintainerReadinessCheck({
1056
+ repoRoot,
1057
+ homeDir,
1058
+ platforms: platformValue
1059
+ });
1060
+ const output = argv.includes("--json")
1061
+ ? JSON.stringify(result, null, 2)
1062
+ : formatMaintainerReadinessReport(result);
1063
+ if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
1064
+ return;
1065
+ }
1066
+ console.log(output);
1067
+ return;
1068
+ }
1069
+
958
1070
  if (command === "tui") {
959
1071
  const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
960
1072
  const changeId = getOption(argv, "--change");
@@ -1008,6 +1120,7 @@ async function runCli(argv) {
1008
1120
  stage: result.stage,
1009
1121
  checkpointState: result.checkpointState,
1010
1122
  nextStep: result.nextStep || null,
1123
+ blockingGate: result.blockingGate || null,
1011
1124
  discipline: result.discipline || null,
1012
1125
  executionProfile: result.executionProfile || null,
1013
1126
  worktreePreflight: result.worktreePreflight || null,
@@ -1091,8 +1204,12 @@ async function runCli(argv) {
1091
1204
  summary: getOption(argv, "--summary"),
1092
1205
  changedFiles: getCommaSeparatedOptionValues(argv, "--changed-files"),
1093
1206
  testEvidence: getCommaSeparatedOptionValues(argv, "--test-evidence"),
1207
+ pendingTestEvidence: getCommaSeparatedOptionValues(argv, "--pending-test-evidence"),
1208
+ confirmTestEvidenceExecuted: argv.includes("--confirm-test-evidence-executed"),
1094
1209
  concerns: getCommaSeparatedOptionValues(argv, "--concerns"),
1095
- blockers: getCommaSeparatedOptionValues(argv, "--blockers")
1210
+ blockers: getCommaSeparatedOptionValues(argv, "--blockers"),
1211
+ outOfScopeWrites: getCommaSeparatedOptionValues(argv, "--out-of-scope-writes"),
1212
+ partial: argv.includes("--partial")
1096
1213
  });
1097
1214
  const useJson = argv.includes("--json");
1098
1215
  const output = useJson ? JSON.stringify(result, null, 2) : formatTaskExecutionReport(result);
@@ -0,0 +1,56 @@
1
+ const { STATUS } = require("./workflow-contract");
2
+ const { unique } = require("./planning-parsers");
3
+
4
+ function dedupe(values) {
5
+ return unique(values);
6
+ }
7
+
8
+ function buildGateEnvelope(id, options = {}) {
9
+ const gate = {
10
+ id,
11
+ status: STATUS.PASS,
12
+ blocking: [],
13
+ advisory: [],
14
+ compatibility: [],
15
+ evidence: []
16
+ };
17
+ if (options.includeBounded) {
18
+ gate.bounded = [];
19
+ }
20
+ return gate;
21
+ }
22
+
23
+ function finalizeGateEnvelope(gate, options = {}) {
24
+ const strict = options.strict === true;
25
+ const includeBounded = options.includeBounded === true;
26
+ const warnOnBounded = options.warnOnBounded === true;
27
+ const warnOnCompatibility = options.warnOnCompatibility !== false;
28
+ const normalized = {
29
+ ...gate,
30
+ blocking: dedupe(gate && gate.blocking),
31
+ advisory: dedupe(gate && gate.advisory),
32
+ compatibility: dedupe(gate && gate.compatibility),
33
+ evidence: dedupe(gate && gate.evidence)
34
+ };
35
+ if (includeBounded || Array.isArray(gate && gate.bounded)) {
36
+ normalized.bounded = dedupe(gate && gate.bounded);
37
+ }
38
+
39
+ if (normalized.blocking.length > 0) {
40
+ normalized.status = strict ? STATUS.BLOCK : STATUS.WARN;
41
+ return normalized;
42
+ }
43
+
44
+ const hasWarnLevelFindings =
45
+ normalized.advisory.length > 0 ||
46
+ (warnOnCompatibility && normalized.compatibility.length > 0) ||
47
+ (warnOnBounded && Array.isArray(normalized.bounded) && normalized.bounded.length > 0);
48
+ normalized.status = hasWarnLevelFindings ? STATUS.WARN : STATUS.PASS;
49
+ return normalized;
50
+ }
51
+
52
+ module.exports = {
53
+ dedupe,
54
+ buildGateEnvelope,
55
+ finalizeGateEnvelope
56
+ };