@xenonbyte/da-vinci-workflow 0.2.3 → 0.2.5
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 +32 -0
- package/README.md +32 -7
- package/README.zh-CN.md +151 -7
- package/SKILL.md +45 -704
- package/commands/claude/dv/build.md +5 -0
- package/commands/claude/dv/continue.md +4 -0
- package/commands/claude/dv/tasks.md +6 -0
- package/commands/claude/dv/verify.md +2 -0
- package/commands/codex/prompts/dv-build.md +5 -0
- package/commands/codex/prompts/dv-continue.md +4 -0
- package/commands/codex/prompts/dv-tasks.md +6 -0
- package/commands/codex/prompts/dv-verify.md +2 -0
- package/commands/gemini/dv/build.toml +5 -0
- package/commands/gemini/dv/continue.toml +4 -0
- package/commands/gemini/dv/tasks.toml +6 -0
- package/commands/gemini/dv/verify.toml +2 -0
- package/commands/templates/dv-continue.shared.md +4 -0
- package/docs/discipline-and-orchestration-upgrade.md +83 -0
- package/docs/dv-command-reference.md +33 -5
- package/docs/execution-chain-migration.md +23 -0
- package/docs/prompt-entrypoints.md +6 -0
- package/docs/skill-contract-maintenance.md +14 -0
- package/docs/skill-usage.md +16 -0
- package/docs/workflow-overview.md +17 -0
- package/docs/zh-CN/dv-command-reference.md +31 -5
- package/docs/zh-CN/execution-chain-migration.md +23 -0
- package/docs/zh-CN/prompt-entrypoints.md +6 -0
- package/docs/zh-CN/skill-usage.md +16 -0
- package/docs/zh-CN/workflow-overview.md +17 -0
- package/lib/audit-parsers.js +148 -1
- package/lib/cli/helpers.js +43 -0
- package/lib/cli/lint-family.js +56 -0
- package/lib/cli/verify-family.js +79 -0
- package/lib/cli.js +123 -145
- package/lib/execution-profile.js +143 -0
- package/lib/execution-signals.js +19 -1
- package/lib/lint-tasks.js +86 -2
- package/lib/planning-parsers.js +263 -19
- package/lib/scaffold.js +454 -23
- package/lib/supervisor-review.js +2 -1
- package/lib/task-execution.js +160 -0
- package/lib/task-review.js +197 -0
- package/lib/utils.js +19 -0
- package/lib/verify.js +1308 -85
- package/lib/workflow-state.js +452 -30
- package/lib/worktree-preflight.js +214 -0
- package/package.json +1 -1
- package/references/artifact-templates.md +56 -6
- package/references/skill-workflow-detail.md +66 -0
|
@@ -45,10 +45,11 @@ Da Vinci 期望它们遵循工作流状态。
|
|
|
45
45
|
- 当真正难记的是命令面而不是流程本身时,优先用它
|
|
46
46
|
- `da-vinci workflow-status --project <path> [--change <id>] [--json]`
|
|
47
47
|
- 从工件和 checkpoint 真相推导当前 workflow 阶段
|
|
48
|
-
- 报告 blocker、warning、handoff gate
|
|
48
|
+
- 报告 blocker、warning、handoff gate、discipline marker、execution profile 提示与 verification freshness
|
|
49
49
|
- 它和 `audit` 职责不同:它负责选路,不是终态审计真相
|
|
50
50
|
- `da-vinci next-step --project <path> [--change <id>] [--json]`
|
|
51
51
|
- 基于同一套 workflow-state 推导,给出 route-first 的续跑建议
|
|
52
|
+
- JSON 输出包含 `nextStep`、`executionProfile`、`worktreePreflight` 与 discipline/freshness 元数据
|
|
52
53
|
- 在自由扫描工件之前,优先把它作为第一路由信号
|
|
53
54
|
- `da-vinci lint-spec --project <path> [--change <id>] [--strict] [--json]`
|
|
54
55
|
- 校验 Da Vinci 运行时 `spec.md` 的核心章节(`Behavior`、`States`、`Inputs`、`Outputs`、`Acceptance`、`Edge Cases`)
|
|
@@ -59,7 +60,7 @@ Da Vinci 期望它们遵循工作流状态。
|
|
|
59
60
|
- 输出页面与状态两条线的机器可读覆盖矩阵
|
|
60
61
|
- 默认 advisory(有发现给 `WARN` 且不阻断);显式 `--strict` 才升级为阻断
|
|
61
62
|
- `da-vinci lint-tasks --project <path> [--change <id>] [--strict] [--json]`
|
|
62
|
-
- 校验顶层 task groups、编号顺序、verification
|
|
63
|
+
- 校验顶层 task groups、编号顺序、discipline markers、执行模式提示、文件目标、verification 命令与 behavior 覆盖提示
|
|
63
64
|
- 默认 advisory(有发现给 `WARN` 且不阻断);显式 `--strict` 才升级为阻断
|
|
64
65
|
- `da-vinci lint-bindings --project <path> [--change <id>] [--strict] [--json]`
|
|
65
66
|
- 校验实现到 Pencil 的映射是否可解析、source 形态是否合理、实现落点是否可定位
|
|
@@ -67,9 +68,27 @@ Da Vinci 期望它们遵循工作流状态。
|
|
|
67
68
|
- `da-vinci generate-sidecars --project <path> [--change <id>] [--json]`
|
|
68
69
|
- 显式生成确定性的 planning sidecars:`spec.index.json`、`tasks.index.json`、`page-map.index.json`、`bindings.index.json`
|
|
69
70
|
- sidecar 只允许通过这个命令写入;lint/status/verify 不应静默重写
|
|
70
|
-
- `da-vinci verify-bindings
|
|
71
|
-
-
|
|
72
|
-
|
|
71
|
+
- `da-vinci verify-bindings --project <path> [--change <id>] [--strict] [--json]`
|
|
72
|
+
- 校验 `pencil-bindings.md` 的实现落点能否被正确解析
|
|
73
|
+
- `da-vinci verify-implementation --project <path> [--change <id>] [--changed-files <csv>] [--strict] [--json]`
|
|
74
|
+
- 以每个检查项的 `mode` + `confidence` 证据模型校验状态/任务组实现覆盖
|
|
75
|
+
- JS/TS 会走语法感知检查,不把“仅注释命中”或“仅字符串字面量命中”当作正常覆盖
|
|
76
|
+
- 使用 `--changed-files` 时会进入显式增量模式(partial 语义),并输出 selected/scanned/filtered 统计
|
|
77
|
+
- `da-vinci verify-structure --project <path> [--change <id>] [--changed-files <csv>] [--strict] [--json]`
|
|
78
|
+
- 校验 bindings 驱动的结构一致性,并显式报告 `markup`/`heuristic` 置信度
|
|
79
|
+
- 使用 `--changed-files` 时,不相关条目会被显式报告,不会静默扩展成全量扫描
|
|
80
|
+
- `da-vinci verify-coverage --project <path> [--change <id>] [--changed-files <csv>] [--strict] [--json]`
|
|
81
|
+
- 聚合上游 verify surface,并在上游是增量验证时明确标记 partial freshness
|
|
82
|
+
- `da-vinci scaffold --project <path> [--change <id>] [--output <path>] [--json]`
|
|
83
|
+
- 生成 framework-aware 的 TODO 可审查骨架(`next`/`react`/`vue`/`svelte`/`html`)
|
|
84
|
+
- 若已存在明确实现落点,会优先保留该落点的扩展名与路由形状
|
|
85
|
+
- 框架未知或冲突时显式告警并回退 HTML;同时继续严格执行 traversal/output-root 安全约束
|
|
86
|
+
- `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]`
|
|
87
|
+
- 持久化结构化 implementer 执行结果包,作为 task 级执行证据
|
|
88
|
+
- `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]`
|
|
89
|
+
- 持久化有序两阶段 task review 证据(`spec` 在前,`quality` 在后)
|
|
90
|
+
- `da-vinci worktree-preflight --project <path> [--change <id>] [--json]`
|
|
91
|
+
- 运行 advisory worktree 隔离预检(目录 ignore 安全、工作区脏状态、baseline 检查启发)
|
|
73
92
|
- `da-vinci diff-spec --project <path> [--change <id>] [--from <sidecars-dir>] [--json]`
|
|
74
93
|
- 比较规范化 planning sidecars,报告新增/删除/修改的规划项
|
|
75
94
|
- 在同一 surface 下提供 spec 差异以及 tasks/page-map/bindings 摘要差异
|
|
@@ -255,6 +274,10 @@ Da Vinci 期望它们遵循工作流状态。
|
|
|
255
274
|
- 跑 `da-vinci scope-check --project <path> [--change <id>]`
|
|
256
275
|
- 如果 `tasks.md` 已存在,跑 `da-vinci lint-tasks --project <path> [--change <id>]`
|
|
257
276
|
- 如果 `pencil-design.md` 和 `pencil-bindings.md` 都存在,跑 `da-vinci lint-bindings --project <path> [--change <id>]`
|
|
277
|
+
- 查看 `da-vinci next-step --project <path> [--change <id>] --json` 的 `executionProfile`
|
|
278
|
+
- 若 profile 为 `bounded_parallel`,先跑 `da-vinci worktree-preflight --project <path> [--change <id>]`;隔离未就绪则降级串行
|
|
279
|
+
- 用 `da-vinci task-execution ...` 持久化每个 task group 的 implementer 结果包,确保 concern/blocker 可追踪
|
|
280
|
+
- 按顺序执行 task review:先 `da-vinci task-review ... --stage spec ...`,再 `da-vinci task-review ... --stage quality ...`
|
|
258
281
|
- 把命令结果当成门禁,不要把 `BLOCK` 或非零退出码降级成软提示
|
|
259
282
|
- 只要还有 `BLOCK` 或缺少必要 planning/design 工件,就不要进入大范围实现
|
|
260
283
|
|
|
@@ -262,6 +285,7 @@ Da Vinci 期望它们遵循工作流状态。
|
|
|
262
285
|
|
|
263
286
|
- `BUILD SUCCESSFUL` 只代表可编译,不代表 workflow 完成
|
|
264
287
|
- 只要 in-scope task groups 还没做完,就不能宣告 `design complete` 或 `workflow complete`
|
|
288
|
+
- 终态 completion 表述前必须具备 fresh verification evidence
|
|
265
289
|
- 任何终态声明前都要运行 `da-vinci audit --mode completion --change <change-id> <project-path>` 并通过
|
|
266
290
|
|
|
267
291
|
### `/dv:verify`
|
|
@@ -286,6 +310,8 @@ Da Vinci 期望它们遵循工作流状态。
|
|
|
286
310
|
- 跑 `da-vinci verify-coverage --project <path> [--change <id>]`
|
|
287
311
|
- 把命令结果当成门禁,不要把 `BLOCK` 或 `FAIL` 降级成软提示
|
|
288
312
|
- 只要任何验证面仍然是 `BLOCK` 或 `FAIL`,就继续停留在验证阶段并记录 drift,而不是宣称成功
|
|
313
|
+
- 终态表述前要求 `verify-coverage` 和 `workflow-status` 中的验证证据 freshness 为最新
|
|
314
|
+
- 如果启用了 task-review 证据,存在未解决 `WARN/BLOCK` 评审阶段时不得关闭对应 task group
|
|
289
315
|
- 如果要声称终态完成,先要求 `da-vinci audit --mode completion --change <change-id> <project-path>` 通过
|
|
290
316
|
|
|
291
317
|
## 基于状态的下一步规则
|
|
@@ -44,3 +44,26 @@ Audit 集成:
|
|
|
44
44
|
- MVP 不做多框架扩展
|
|
45
45
|
- scaffold 边界来自 `pencil-bindings.md` 映射
|
|
46
46
|
- 接受前需跑 `verify-bindings`、`verify-implementation`、`verify-structure`
|
|
47
|
+
|
|
48
|
+
## 5. 下一阶段规划升级:Discipline And Orchestration
|
|
49
|
+
|
|
50
|
+
execution-chain 能力落地后,下一项规划中的升级是:
|
|
51
|
+
|
|
52
|
+
- `openspec/changes/discipline-and-orchestration-upgrade/`
|
|
53
|
+
|
|
54
|
+
该规划变更会补充:
|
|
55
|
+
|
|
56
|
+
- 实施前交接纪律的强化
|
|
57
|
+
- `tasks.md` 执行就绪质量检查强化
|
|
58
|
+
- 与 workflow state 绑定的有边界编排提示
|
|
59
|
+
- 结构化 task 执行与审查证据
|
|
60
|
+
- 基于 fresh verification evidence 的 completion 表述约束
|
|
61
|
+
- 可选 worktree preflight 隔离建议
|
|
62
|
+
|
|
63
|
+
重要边界:
|
|
64
|
+
|
|
65
|
+
- 该升级不会替代 artifact truth、checkpoint truth 或 completion-audit 权威
|
|
66
|
+
|
|
67
|
+
参考:
|
|
68
|
+
|
|
69
|
+
- `docs/discipline-and-orchestration-upgrade.md`
|
|
@@ -67,13 +67,18 @@
|
|
|
67
67
|
|
|
68
68
|
- 有 shell 能力时,先运行 `da-vinci workflow-status --project <path> [--change <id>] --json`
|
|
69
69
|
- 再用 `da-vinci next-step --project <path> [--change <id>]` 作为第一续跑路由信号
|
|
70
|
+
- 如可用,优先读取 `da-vinci next-step --project <path> [--change <id>] --json`,显式获取 discipline marker、execution-profile 与 verification freshness 信息
|
|
70
71
|
- 如果运行时 spec 质量还不确定,进入 `build` 前先运行 `da-vinci lint-spec --project <path> [--change <id>]`
|
|
71
72
|
- 如果页面或状态在规划工件中的传播关系不确定,进入 `build` 前先运行 `da-vinci scope-check --project <path> [--change <id>]`
|
|
73
|
+
- 如果任务拆解质量不确定,进入 `build` 前先运行 `da-vinci lint-tasks --project <path> [--change <id>]`
|
|
72
74
|
- 进入终态前先运行 `da-vinci verify-bindings --project <path> [--change <id>]` 与 `da-vinci verify-coverage --project <path> [--change <id>]`
|
|
75
|
+
- 若在 `verify-implementation`/`verify-structure`/`verify-coverage` 使用 `--changed-files`,要把结果视为显式增量 partial 证据,而不是全量 fresh 验证
|
|
73
76
|
- 当规划切片有改动且需要恢复判断时,运行 `da-vinci diff-spec --project <path> [--change <id>]`
|
|
74
77
|
- 先根据工件和 checkpoint 真相决定路由
|
|
75
78
|
- Context Delta 只用于恢复和解释,不用于覆盖选路
|
|
76
79
|
- 如果 Context Delta 与当前工件冲突,选路时忽略冲突内容并标记冲突
|
|
80
|
+
- 如果 design approval discipline marker 缺失、格式错误或过期,路由应停留在 `design` 或 `tasks`,不能直接推进到 `build`
|
|
81
|
+
- 如果提示了 bounded parallel 执行,先运行 `da-vinci worktree-preflight --project <path> [--change <id>]`,再决定是否输出并行 build continuation prompt
|
|
77
82
|
|
|
78
83
|
## 默认推荐流程
|
|
79
84
|
|
|
@@ -94,6 +99,7 @@
|
|
|
94
99
|
|
|
95
100
|
- 如果设计已经完成,但 `tasks.md` 还不存在,下一步主推荐通常应该是 `tasks`,而不是 `build`
|
|
96
101
|
- 只有任务和实现就绪度都已经明确时,才把 `build` 作为主推荐下一步
|
|
102
|
+
- 在 fresh verification evidence 缺失时,不能输出终态 completion 表述;应明确要求 completion audit
|
|
97
103
|
|
|
98
104
|
## 平台语法
|
|
99
105
|
|
|
@@ -85,6 +85,12 @@ $da-vinci use continue for <existing workflow state>
|
|
|
85
85
|
- 当运行时 spec 质量还不够确定时使用
|
|
86
86
|
- `da-vinci scope-check`
|
|
87
87
|
- 当规划工件之间页面/状态传播关系还不清楚时使用
|
|
88
|
+
- `da-vinci lint-tasks`
|
|
89
|
+
- 校验 task groups 是否包含 discipline markers、明确文件落点、执行意图与 verification 命令
|
|
90
|
+
- `da-vinci lint-bindings`
|
|
91
|
+
- 当 `pencil-design.md` 与 `pencil-bindings.md` 同时存在时运行,确保实现落点证据可解析
|
|
92
|
+
- `da-vinci worktree-preflight`
|
|
93
|
+
- 准备 bounded parallel 执行前先跑,用于判断是否建议启用本地 worktree 隔离
|
|
88
94
|
|
|
89
95
|
如果你已经在项目根目录里,`--project` 通常可以省略,因为 CLI 默认会回退到当前目录。
|
|
90
96
|
|
|
@@ -95,8 +101,17 @@ da-vinci workflow-status
|
|
|
95
101
|
da-vinci next-step
|
|
96
102
|
da-vinci lint-spec
|
|
97
103
|
da-vinci scope-check
|
|
104
|
+
da-vinci lint-tasks
|
|
98
105
|
```
|
|
99
106
|
|
|
107
|
+
实现阶段不要只靠聊天总结,建议同步写入机器可读证据:
|
|
108
|
+
|
|
109
|
+
- `da-vinci task-execution --project <path> --change <id> --task-group <id> --status <DONE|DONE_WITH_CONCERNS|NEEDS_CONTEXT|BLOCKED> --summary <text> ...`
|
|
110
|
+
- `da-vinci task-review --project <path> --change <id> --task-group <id> --stage spec ...`
|
|
111
|
+
- `da-vinci task-review --project <path> --change <id> --task-group <id> --stage quality ...`
|
|
112
|
+
|
|
113
|
+
同一 task group 只有在 `spec` review 已是 `PASS` 时,`quality` review 才允许通过。
|
|
114
|
+
|
|
100
115
|
## 中途退出后,下次怎么恢复
|
|
101
116
|
|
|
102
117
|
最稳妥的恢复顺序是:
|
|
@@ -107,6 +122,7 @@ da-vinci scope-check
|
|
|
107
122
|
4. 如果规划传播关系不确定,跑 `da-vinci scope-check`
|
|
108
123
|
5. 如果 planning 工件相对上次稳定状态有变化,跑 `da-vinci generate-sidecars` 和 `da-vinci diff-spec`
|
|
109
124
|
6. 在终态前,跑 `da-vinci verify-bindings` 和 `da-vinci verify-coverage`
|
|
125
|
+
7. 如果要做 completion 表述,确认 `verify-coverage` 的 freshness 仍然有效,并运行 `da-vinci audit --mode completion --change <id> <project-path>`
|
|
110
126
|
|
|
111
127
|
恢复时应该遵循工件,而不是旧聊天上下文:
|
|
112
128
|
|
|
@@ -32,6 +32,12 @@ Da Vinci 围绕一个固定契约工作:
|
|
|
32
32
|
- 项目内 `.pen` 是设计真相源
|
|
33
33
|
- 实现必须能追溯到这两层
|
|
34
34
|
|
|
35
|
+
真相与编排边界:
|
|
36
|
+
|
|
37
|
+
- 选路和 completion 真相来自工件、checkpoint、execution signals 与 `audit`
|
|
38
|
+
- execution profile、worktree preflight 等编排信息始终是 advisory 指导,不替代真相面
|
|
39
|
+
- 编排提示不能覆盖来自工件、checkpoint 或 completion audit 的明确 `BLOCK`
|
|
40
|
+
|
|
35
41
|
## 主流程
|
|
36
42
|
|
|
37
43
|
1. 选择正确的 mode。
|
|
@@ -62,6 +68,12 @@ Da Vinci 围绕一个固定契约工作:
|
|
|
62
68
|
- 显式生成确定性的 planning sidecars,供 diff 和下游工具消费
|
|
63
69
|
- `da-vinci verify-bindings|verify-implementation|verify-structure|verify-coverage --project <path> [--change <id>] [--strict] [--json]`
|
|
64
70
|
- 校验从 bindings 到实现与结构覆盖的执行链证据
|
|
71
|
+
- `da-vinci task-execution --project <path> --change <id> --task-group <id> --status <DONE|DONE_WITH_CONCERNS|NEEDS_CONTEXT|BLOCKED> --summary <text> ...`
|
|
72
|
+
- 按 task group 持久化 implementer 结果包,便于恢复时读取未解决 blocker/concern
|
|
73
|
+
- `da-vinci task-review --project <path> --change <id> --task-group <id> --stage <spec|quality> --status <PASS|WARN|BLOCK> --summary <text> ...`
|
|
74
|
+
- 持久化有序两阶段 review 证据(先 `spec` 再 `quality`),并可写回 `verification.md`
|
|
75
|
+
- `da-vinci worktree-preflight --project <path> [--change <id>] [--json]`
|
|
76
|
+
- 在准备 bounded-parallel 执行前提供 advisory 隔离预检
|
|
65
77
|
- `da-vinci diff-spec --project <path> [--change <id>] [--from <sidecars-dir>] [--json]`
|
|
66
78
|
- 报告 spec/tasks/page-map/bindings sidecars 的规范化差异,支持安全续跑
|
|
67
79
|
|
|
@@ -187,7 +199,11 @@ anchor 通过后,再抽 shared primitives,然后再扩更多页面。
|
|
|
187
199
|
mapping 通过后:
|
|
188
200
|
|
|
189
201
|
- 生成 `tasks.md`
|
|
202
|
+
- 保持 task group 可解析:包含 discipline markers、文件目标、执行意图、review 意图和 verification 命令
|
|
190
203
|
- 基于 requirements 和 Pencil 数据实现
|
|
204
|
+
- 在大范围实现前,从 `workflow-status` 或 `next-step --json` 查看 `executionProfile`
|
|
205
|
+
- 如果建议 bounded parallel,先跑 `da-vinci worktree-preflight --project <path> [--change <id>]`;隔离未就绪时降级为串行
|
|
206
|
+
- 通过 `task-execution` 与有序 `task-review` 记录每个 task group 的执行与审查证据
|
|
191
207
|
- 验证需求漂移和设计漂移
|
|
192
208
|
|
|
193
209
|
### 8. 终态完成
|
|
@@ -197,6 +213,7 @@ mapping 通过后:
|
|
|
197
213
|
- design checkpoint 不再阻塞
|
|
198
214
|
- design-source checkpoint 至少 `PASS`
|
|
199
215
|
- 如果用了 Pencil MCP,runtime gate 结果可接受
|
|
216
|
+
- in-scope 的实现和覆盖结论具备 fresh verification evidence
|
|
200
217
|
- `da-vinci audit --mode completion --change <change-id> <project-path>` 通过
|
|
201
218
|
|
|
202
219
|
补充说明:
|
package/lib/audit-parsers.js
CHANGED
|
@@ -760,6 +760,149 @@ function inspectDesignSupervisorReview(pencilDesignText) {
|
|
|
760
760
|
};
|
|
761
761
|
}
|
|
762
762
|
|
|
763
|
+
const DISCIPLINE_MARKER_NAMES = Object.freeze({
|
|
764
|
+
designApproval: "design_approval",
|
|
765
|
+
planSelfReview: "plan_self_review",
|
|
766
|
+
operatorReviewAck: "operator_review_ack"
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
const DISCIPLINE_MARKER_ALIASES = Object.freeze({
|
|
770
|
+
[DISCIPLINE_MARKER_NAMES.designApproval]: [
|
|
771
|
+
"design approval",
|
|
772
|
+
"design-approved",
|
|
773
|
+
"design approved",
|
|
774
|
+
"design_approval",
|
|
775
|
+
"design-approval"
|
|
776
|
+
],
|
|
777
|
+
[DISCIPLINE_MARKER_NAMES.planSelfReview]: [
|
|
778
|
+
"plan self review",
|
|
779
|
+
"plan_self_review",
|
|
780
|
+
"plan-self-review",
|
|
781
|
+
"plan review",
|
|
782
|
+
"plan-review"
|
|
783
|
+
],
|
|
784
|
+
[DISCIPLINE_MARKER_NAMES.operatorReviewAck]: [
|
|
785
|
+
"operator review ack",
|
|
786
|
+
"operator review acknowledgement",
|
|
787
|
+
"operator review acknowledgment",
|
|
788
|
+
"operator_ack",
|
|
789
|
+
"operator-review-ack",
|
|
790
|
+
"operator-review-acknowledgement",
|
|
791
|
+
"operator-review-acknowledgment"
|
|
792
|
+
]
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
const DISCIPLINE_MARKER_KEYWORDS = Object.freeze(
|
|
796
|
+
Object.values(DISCIPLINE_MARKER_ALIASES)
|
|
797
|
+
.flat()
|
|
798
|
+
.map((token) => String(token).replace(/[_-]+/g, " ").toLowerCase())
|
|
799
|
+
);
|
|
800
|
+
|
|
801
|
+
function normalizeDisciplineMarkerName(value) {
|
|
802
|
+
const normalized = String(value || "")
|
|
803
|
+
.toLowerCase()
|
|
804
|
+
.replace(/[`*_~]/g, "")
|
|
805
|
+
.replace(/[_-]+/g, " ")
|
|
806
|
+
.replace(/\s+/g, " ")
|
|
807
|
+
.trim();
|
|
808
|
+
if (!normalized) {
|
|
809
|
+
return "";
|
|
810
|
+
}
|
|
811
|
+
for (const [canonicalName, aliases] of Object.entries(DISCIPLINE_MARKER_ALIASES)) {
|
|
812
|
+
if (aliases.some((alias) => normalized === String(alias).replace(/[_-]+/g, " ").toLowerCase())) {
|
|
813
|
+
return canonicalName;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
return "";
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function normalizeDisciplineMarkerStatus(value) {
|
|
820
|
+
return String(value || "")
|
|
821
|
+
.toUpperCase()
|
|
822
|
+
.replace(/[`*_~]/g, "")
|
|
823
|
+
.replace(/[()]/g, "")
|
|
824
|
+
.replace(/\s+/g, "_")
|
|
825
|
+
.replace(/[^A-Z0-9_]+/g, "_")
|
|
826
|
+
.replace(/^_+|_+$/g, "");
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
function parseDisciplineMarkers(markdownText) {
|
|
830
|
+
const markers = {};
|
|
831
|
+
const ordered = [];
|
|
832
|
+
const malformed = [];
|
|
833
|
+
const lines = String(markdownText || "").replace(/\r\n?/g, "\n").split("\n");
|
|
834
|
+
const markerPattern =
|
|
835
|
+
/^\s*-\s*`?([A-Za-z][A-Za-z0-9 _-]{1,80})`?\s*:\s*`?([A-Za-z0-9 _-]{1,80})`?(?:\s*@\s*`?([^`]+?)`?)?\s*$/;
|
|
836
|
+
|
|
837
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
838
|
+
const rawLine = lines[index];
|
|
839
|
+
const match = rawLine.match(markerPattern);
|
|
840
|
+
if (!match) {
|
|
841
|
+
const normalizedLine = String(rawLine || "")
|
|
842
|
+
.toLowerCase()
|
|
843
|
+
.replace(/[_-]+/g, " ")
|
|
844
|
+
.replace(/\s+/g, " ")
|
|
845
|
+
.trim();
|
|
846
|
+
if (!normalizedLine.startsWith("-")) {
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
if (DISCIPLINE_MARKER_KEYWORDS.some((keyword) => normalizedLine.includes(keyword))) {
|
|
850
|
+
malformed.push({
|
|
851
|
+
line: index + 1,
|
|
852
|
+
reason: "Malformed discipline marker syntax.",
|
|
853
|
+
raw: rawLine
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
continue;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
const markerName = normalizeDisciplineMarkerName(match[1]);
|
|
860
|
+
if (!markerName) {
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
const status = normalizeDisciplineMarkerStatus(match[2]);
|
|
865
|
+
if (!status) {
|
|
866
|
+
malformed.push({
|
|
867
|
+
line: index + 1,
|
|
868
|
+
reason: "Missing marker status token.",
|
|
869
|
+
raw: rawLine
|
|
870
|
+
});
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
const rawTime = String(match[3] || "").trim();
|
|
875
|
+
let time = "";
|
|
876
|
+
if (rawTime) {
|
|
877
|
+
time = normalizeTimeToken(rawTime);
|
|
878
|
+
if (!time) {
|
|
879
|
+
malformed.push({
|
|
880
|
+
line: index + 1,
|
|
881
|
+
reason: "Invalid marker timestamp.",
|
|
882
|
+
raw: rawLine
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
const record = {
|
|
888
|
+
name: markerName,
|
|
889
|
+
status,
|
|
890
|
+
time,
|
|
891
|
+
rawTime,
|
|
892
|
+
line: index + 1,
|
|
893
|
+
raw: rawLine
|
|
894
|
+
};
|
|
895
|
+
ordered.push(record);
|
|
896
|
+
markers[markerName] = record;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
return {
|
|
900
|
+
markers,
|
|
901
|
+
ordered,
|
|
902
|
+
malformed
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
|
|
763
906
|
module.exports = {
|
|
764
907
|
hasMarkdownHeading,
|
|
765
908
|
getMarkdownSection,
|
|
@@ -775,5 +918,9 @@ module.exports = {
|
|
|
775
918
|
getConfiguredDesignSupervisorReviewers,
|
|
776
919
|
hasConfiguredDesignSupervisorReview,
|
|
777
920
|
isDesignSupervisorReviewRequired,
|
|
778
|
-
inspectDesignSupervisorReview
|
|
921
|
+
inspectDesignSupervisorReview,
|
|
922
|
+
DISCIPLINE_MARKER_NAMES,
|
|
923
|
+
normalizeDisciplineMarkerName,
|
|
924
|
+
normalizeDisciplineMarkerStatus,
|
|
925
|
+
parseDisciplineMarkers
|
|
779
926
|
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const { writeExecutionSignal } = require("../execution-signals");
|
|
2
|
+
|
|
3
|
+
function emitOrThrowOnStatus(status, blockedStatuses, output, continueOnError) {
|
|
4
|
+
if (!Array.isArray(blockedStatuses) || !blockedStatuses.includes(status)) {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
if (continueOnError) {
|
|
8
|
+
console.log(output);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
throw new Error(output);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function persistExecutionSignal(projectPath, changeId, surface, result, strict = false) {
|
|
15
|
+
try {
|
|
16
|
+
writeExecutionSignal(projectPath, {
|
|
17
|
+
changeId: changeId || "global",
|
|
18
|
+
surface,
|
|
19
|
+
status: result.status,
|
|
20
|
+
advisory: strict ? false : true,
|
|
21
|
+
strict,
|
|
22
|
+
failures: result.failures || [],
|
|
23
|
+
warnings: result.warnings || [],
|
|
24
|
+
notes: result.notes || []
|
|
25
|
+
});
|
|
26
|
+
} catch (error) {
|
|
27
|
+
// Signals are advisory metadata and should not break command execution.
|
|
28
|
+
const code = error && error.code ? String(error.code).toUpperCase() : "";
|
|
29
|
+
if (code === "EACCES" || code === "ENOSPC") {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const message = error && error.message ? error.message : String(error);
|
|
34
|
+
console.error(
|
|
35
|
+
`Warning: failed to persist execution signal (${surface}) for change ${changeId || "global"}: ${message}`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
emitOrThrowOnStatus,
|
|
42
|
+
persistExecutionSignal
|
|
43
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
function handleLintFamilyCommand(command, context) {
|
|
2
|
+
const lintCommands = new Set(["lint-spec", "scope-check", "lint-tasks", "lint-bindings"]);
|
|
3
|
+
if (!lintCommands.has(command)) {
|
|
4
|
+
return false;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
argv,
|
|
9
|
+
positionalArgs,
|
|
10
|
+
continueOnError,
|
|
11
|
+
getOption,
|
|
12
|
+
lintRuntimeSpecs,
|
|
13
|
+
formatLintSpecReport,
|
|
14
|
+
runScopeCheck,
|
|
15
|
+
formatScopeCheckReport,
|
|
16
|
+
lintTasks,
|
|
17
|
+
formatLintTasksReport,
|
|
18
|
+
lintBindings,
|
|
19
|
+
formatLintBindingsReport,
|
|
20
|
+
emitOrThrowOnStatus,
|
|
21
|
+
persistExecutionSignal
|
|
22
|
+
} = context;
|
|
23
|
+
|
|
24
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
25
|
+
const changeId = getOption(argv, "--change");
|
|
26
|
+
const strict = argv.includes("--strict");
|
|
27
|
+
const useJson = argv.includes("--json");
|
|
28
|
+
|
|
29
|
+
let result;
|
|
30
|
+
let output = "";
|
|
31
|
+
let surface = command;
|
|
32
|
+
if (command === "lint-spec") {
|
|
33
|
+
result = lintRuntimeSpecs(projectPath, { changeId, strict });
|
|
34
|
+
output = useJson ? JSON.stringify(result, null, 2) : formatLintSpecReport(result);
|
|
35
|
+
} else if (command === "scope-check") {
|
|
36
|
+
result = runScopeCheck(projectPath, { changeId, strict });
|
|
37
|
+
output = useJson ? JSON.stringify(result, null, 2) : formatScopeCheckReport(result);
|
|
38
|
+
} else if (command === "lint-tasks") {
|
|
39
|
+
result = lintTasks(projectPath, { changeId, strict });
|
|
40
|
+
output = useJson ? JSON.stringify(result, null, 2) : formatLintTasksReport(result);
|
|
41
|
+
} else {
|
|
42
|
+
result = lintBindings(projectPath, { changeId, strict });
|
|
43
|
+
output = useJson ? JSON.stringify(result, null, 2) : formatLintBindingsReport(result);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
persistExecutionSignal(projectPath, result.changeId || changeId, surface, result, strict);
|
|
47
|
+
if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
console.log(output);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = {
|
|
55
|
+
handleLintFamilyCommand
|
|
56
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
function handleVerifyFamilyCommand(command, context) {
|
|
2
|
+
const verifyCommands = new Set([
|
|
3
|
+
"verify-bindings",
|
|
4
|
+
"verify-implementation",
|
|
5
|
+
"verify-structure",
|
|
6
|
+
"verify-coverage"
|
|
7
|
+
]);
|
|
8
|
+
if (!verifyCommands.has(command)) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
argv,
|
|
14
|
+
positionalArgs,
|
|
15
|
+
continueOnError,
|
|
16
|
+
getOption,
|
|
17
|
+
getCommaSeparatedOptionValues,
|
|
18
|
+
collectOptionEntries,
|
|
19
|
+
verifyBindings,
|
|
20
|
+
verifyImplementation,
|
|
21
|
+
verifyStructure,
|
|
22
|
+
verifyCoverage,
|
|
23
|
+
formatVerifyReport,
|
|
24
|
+
emitOrThrowOnStatus,
|
|
25
|
+
persistExecutionSignal
|
|
26
|
+
} = context;
|
|
27
|
+
|
|
28
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
29
|
+
const changeId = getOption(argv, "--change");
|
|
30
|
+
const strict = argv.includes("--strict");
|
|
31
|
+
const useJson = argv.includes("--json");
|
|
32
|
+
const changedFilesProvided = collectOptionEntries(argv, "--changed-files").length > 0;
|
|
33
|
+
const changedFiles = changedFilesProvided
|
|
34
|
+
? getCommaSeparatedOptionValues(argv, "--changed-files")
|
|
35
|
+
: undefined;
|
|
36
|
+
|
|
37
|
+
let result;
|
|
38
|
+
let title = "Da Vinci verify";
|
|
39
|
+
if (command === "verify-bindings") {
|
|
40
|
+
result = verifyBindings(projectPath, { changeId, strict });
|
|
41
|
+
title = "Da Vinci verify-bindings";
|
|
42
|
+
} else if (command === "verify-implementation") {
|
|
43
|
+
result = verifyImplementation(projectPath, {
|
|
44
|
+
changeId,
|
|
45
|
+
strict,
|
|
46
|
+
changedFiles,
|
|
47
|
+
changedFilesProvided
|
|
48
|
+
});
|
|
49
|
+
title = "Da Vinci verify-implementation";
|
|
50
|
+
} else if (command === "verify-structure") {
|
|
51
|
+
result = verifyStructure(projectPath, {
|
|
52
|
+
changeId,
|
|
53
|
+
strict,
|
|
54
|
+
changedFiles,
|
|
55
|
+
changedFilesProvided
|
|
56
|
+
});
|
|
57
|
+
title = "Da Vinci verify-structure";
|
|
58
|
+
} else {
|
|
59
|
+
result = verifyCoverage(projectPath, {
|
|
60
|
+
changeId,
|
|
61
|
+
strict,
|
|
62
|
+
changedFiles,
|
|
63
|
+
changedFilesProvided
|
|
64
|
+
});
|
|
65
|
+
title = "Da Vinci verify-coverage";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
persistExecutionSignal(projectPath, result.changeId || changeId, command, result, strict);
|
|
69
|
+
const output = useJson ? JSON.stringify(result, null, 2) : formatVerifyReport(result, title);
|
|
70
|
+
if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
console.log(output);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = {
|
|
78
|
+
handleVerifyFamilyCommand
|
|
79
|
+
};
|