ai-delivery-workflow 0.5.0 → 0.6.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.
Files changed (81) hide show
  1. package/README.md +5 -6
  2. package/bin/ai-delivery.mjs +2 -24
  3. package/docs/CLI-PARAMETER-REFERENCE.zh-CN.md +6 -67
  4. package/docs/DUAL-REPOSITORY-WORKSPACE.zh-CN.md +1 -1
  5. package/docs/FILE-REFERENCE.zh-CN.md +6 -28
  6. package/docs/MAINTENANCE-VERIFICATION.zh-CN.md +2 -2
  7. package/docs/PROJECT-MANUAL.zh-CN.md +23 -38
  8. package/docs/STATE-CLI-MAINTENANCE.zh-CN.md +8 -10
  9. package/docs/STATE-CLI-USER-GUIDE.zh-CN.md +6 -16
  10. package/docs/agents/workflow-manager-acceptance-matrix.md +9 -15
  11. package/docs/agents/workflow-manager-current-state.md +12 -15
  12. package/lib/communication-confirmation.mjs +177 -0
  13. package/lib/delivery-state.mjs +59 -910
  14. package/lib/fs-utils.mjs +0 -16
  15. package/lib/project-bootstrap.mjs +288 -61
  16. package/lib/project-installer.mjs +49 -181
  17. package/lib/project-repair.mjs +217 -0
  18. package/lib/toml-hooks.mjs +2 -4
  19. package/lib/workflow-manager-governance.mjs +3 -47
  20. package/lib/workspace.mjs +2 -3
  21. package/package.json +2 -2
  22. package/skills/ai-delivery-bootstrap/SKILL.md +5 -1
  23. package/skills/ai-delivery-bootstrap/references/bootstrap-contract.md +1 -1
  24. package/skills/ai-delivery-checkpoint-task/SKILL.md +2 -4
  25. package/skills/ai-delivery-checkpoint-task/references/checkpoint-contract.md +4 -2
  26. package/skills/ai-delivery-checkpoint-task/scripts/hook-event.mjs +2 -8
  27. package/skills/ai-delivery-checkpoint-task/scripts/task-state.mjs +26 -46
  28. package/skills/ai-delivery-close-version/SKILL.md +13 -10
  29. package/skills/ai-delivery-close-version/references/version-closeout-contract.md +2 -1
  30. package/skills/ai-delivery-define-product/SKILL.md +6 -3
  31. package/skills/ai-delivery-design-architecture/SKILL.md +10 -5
  32. package/skills/ai-delivery-design-architecture/assets/architecture-template/architecture-gate.yaml +2 -0
  33. package/skills/ai-delivery-design-architecture/assets/architecture-template/prototype-architecture-validation.yaml +2 -0
  34. package/skills/ai-delivery-design-architecture/references/architecture-contract.md +4 -0
  35. package/skills/ai-delivery-design-experience/SKILL.md +8 -6
  36. package/skills/ai-delivery-design-experience/assets/experience-template/product-prototype-reconciliation.yaml +2 -0
  37. package/skills/ai-delivery-design-experience/assets/experience-template/ui-handoff.yaml +3 -0
  38. package/skills/ai-delivery-design-experience/references/experience-contract.md +3 -13
  39. package/skills/ai-delivery-design-experience/references/prototype-management-contract.md +1 -1
  40. package/skills/ai-delivery-design-tests/SKILL.md +4 -0
  41. package/skills/ai-delivery-develop-iteration/SKILL.md +6 -1
  42. package/skills/ai-delivery-develop-iteration/assets/development-template/candidate-manifest.yaml +2 -0
  43. package/skills/ai-delivery-execute-work-package/SKILL.md +4 -2
  44. package/skills/ai-delivery-orchestrate/SKILL.md +7 -7
  45. package/skills/ai-delivery-orchestrate/assets/codebase-template/AGENTS.md +14 -0
  46. package/skills/ai-delivery-orchestrate/assets/codebase-template/CODEBASE_GUIDE.md +124 -0
  47. package/skills/ai-delivery-orchestrate/assets/codebase-template/docs/CODEBASE_INDEX.md +87 -0
  48. package/skills/ai-delivery-orchestrate/assets/communication-template/confirmation-examples.yaml +67 -0
  49. package/skills/ai-delivery-orchestrate/assets/communication-template/confirmation-summary.yaml +42 -0
  50. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/iterations/ITER-0001/02-solution-design/product-impact-assessment.yaml +42 -0
  51. package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/hooks/hook-event.mjs +2 -8
  52. package/skills/ai-delivery-orchestrate/assets/project-template/AGENTS.md +4 -5
  53. package/skills/ai-delivery-orchestrate/references/codebase-document-contract.md +91 -0
  54. package/skills/ai-delivery-orchestrate/references/communication-confirmation-contract.md +52 -0
  55. package/skills/ai-delivery-orchestrate/references/formal-state-contract.md +11 -20
  56. package/skills/ai-delivery-orchestrate/references/product-impact-assessment-contract.md +87 -0
  57. package/skills/ai-delivery-orchestrate/references/workflow-model.md +1 -3
  58. package/skills/ai-delivery-orchestrate/scripts/confirmation-state.mjs +30 -0
  59. package/skills/ai-delivery-orchestrate-release/SKILL.md +3 -1
  60. package/skills/ai-delivery-plan-iteration/SKILL.md +6 -1
  61. package/skills/ai-delivery-prepare-platform/SKILL.md +1 -1
  62. package/skills/ai-delivery-prepare-release/SKILL.md +2 -0
  63. package/skills/ai-delivery-prepare-release/assets/release-template/release-manifest.yaml +0 -2
  64. package/skills/ai-delivery-review-change/SKILL.md +3 -1
  65. package/skills/ai-delivery-validate-artifacts/SKILL.md +10 -7
  66. package/skills/ai-delivery-validate-artifacts/references/artifact-contract.md +2 -1
  67. package/skills/ai-delivery-verify-candidate/SKILL.md +5 -2
  68. package/skills/ai-delivery-verify-candidate/references/candidate-verification-contract.md +1 -1
  69. package/docs/PROTOTYPE-MIGRATION.zh-CN.md +0 -164
  70. package/docs/VIEWER-MAINTENANCE.zh-CN.md +0 -150
  71. package/docs/VIEWER-USER-GUIDE.zh-CN.md +0 -113
  72. package/lib/project-migration.mjs +0 -1193
  73. package/lib/prototype-migration.mjs +0 -713
  74. package/skills/ai-delivery-design-experience/assets/experience-template/LEGACY-COMPATIBILITY.md +0 -16
  75. package/skills/ai-delivery-design-experience/assets/experience-template/canvas-catalog.csv +0 -1
  76. package/skills/ai-delivery-design-experience/assets/experience-template/canvas-pages.csv +0 -1
  77. package/skills/ai-delivery-design-experience/assets/experience-template/page-catalog.csv +0 -1
  78. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-file-terminals.csv +0 -1
  79. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-files.csv +0 -1
  80. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-manifest.yaml +0 -76
  81. package/skills/ai-delivery-design-experience/assets/experience-template/prototype-set.yaml +0 -24
@@ -1,8 +1,8 @@
1
1
  # 正式状态 CLI 维护指南
2
2
 
3
- 当前生产图是 `development-seven-node`:`00-bootstrap -> 01-product-shaping -> 02-solution-design -> 03-delivery-readiness -> 04-implementation -> 05-candidate-assurance -> 06-version-closeout`。旧 13 个研发节点仅保留只读兼容:解析、`inspect`、`verify`、`rebuild` 和显式迁移继续消费历史记录;所有新任务、物料登记、transition、candidate、版本收尾和 Gate mutation 在写入及 Hook dispatch 前拒绝。`R00-release-request` 至 `R10-release-archive` 继续允许写入。
3
+ 当前生产图是 `development-seven-node`:`00-bootstrap -> 01-product-shaping -> 02-solution-design -> 03-delivery-readiness -> 04-implementation -> 05-candidate-assurance -> 06-version-closeout`。`R00-release-request` 至 `R10-release-archive` 继续允许写入。
4
4
 
5
- 正式状态写入会先完整暂存权威事件分片、全局兼容视图和 JSONL 事件索引,持久化 prepared journal,再提交各目标并写入 committed marker;同步异常时恢复原文件,进程中断后由下一次受锁 mutation 或 `rebuild` 幂等恢复。`inspect`、`verify`、Bootstrap 诊断与 Workflow Manager 正式状态读取期间获取与 mutation 共用的 formal-state lock;活动 writer、其他 reader 或未恢复 journal 均使读取 fail-closed,避免检查通过后又读到混合 revision。只读调用方不得接管死锁或执行事务恢复。日常 `verify` 不遍历历史分片,只校验当前视图、JSONL 索引、物料和状态不变量;显式 `verify --history` 还检查全部事件分片的连续性、元数据、记录 checksum、schema 2 checksum 链以及最新 replay 结果与全局视图的一致性。`rebuild` 按 revision replay 每个 stream,并按确定顺序重建 JSONL 索引。
5
+ 正式状态写入会先完整暂存权威事件分片、当前视图和 JSONL 事件索引,持久化 prepared journal,再提交各目标并写入 committed marker;同步异常时恢复原文件,进程中断后由下一次受锁 mutation 或 `rebuild` 幂等恢复。`inspect`、`verify`、Bootstrap 诊断与 Workflow Manager 正式状态读取期间获取与 mutation 共用的 formal-state lock;活动 writer、其他 reader 或未恢复 journal 均使读取 fail-closed,避免检查通过后又读到混合 revision。只读调用方不得接管死锁或执行事务恢复。日常 `verify` 不遍历历史分片,只校验当前视图、JSONL 索引、物料和状态不变量;显式 `verify --history` 还检查全部事件分片的连续性、元数据、记录 checksum、schema 2 checksum 链以及最新 replay 结果与全局视图的一致性。`rebuild` 按 revision replay 每个 stream,并按确定顺序重建 JSONL 索引。
6
6
 
7
7
  ## 1. 权威源码与安装结果
8
8
 
@@ -42,14 +42,14 @@ artifact registry 与 workflow state 的新 mutation 都使用 schema 2 增量
42
42
  每次 mutation:
43
43
 
44
44
  1. 获取 mutation 与只读正式状态快照共用的 formal-state lock,避免并发追加或读取时观察到部分提交;
45
- 2. artifact 读取并比较 `--expected-revision`;workflow 读取并比较 owning scope 的 `--expected-scope-revision`,旧顶层参数只作兼容;
45
+ 2. artifact 读取并比较 `--expected-revision`;workflow 读取并比较 owning scope 的 `--expected-scope-revision`;
46
46
  3. 校验命令输入和跨文件引用;
47
47
  4. 每个受影响 aggregate 的 revision 分别加一;
48
- 5. 生成同一语义动作涉及的全部事件分片、当前 YAML、兼容视图和下一版 JSONL 的完整暂存内容,并对每个原文件和新文件记录 checksum;
48
+ 5. 生成同一语义动作涉及的全部事件分片、当前 YAML 和下一版 JSONL 的完整暂存内容,并对每个原文件和新文件记录 checksum;
49
49
  6. 持久化一个 prepared journal,再替换全部目标;捕获到异常时按 checksum 恢复整个写集合;
50
50
  7. 所有目标替换完成后持久化 committed marker,再清理备份、暂存文件和 journal;
51
51
  8. 进程中断后,下一次受锁 mutation 或 `rebuild` 回滚 prepared journal,或验证并清理 committed journal;锁持有进程仍存活时拒绝接管;
52
- 9. schema 2 事务把前一分片 `record_checksum` 写入 `previous_checksum`,旧分片没有记录 checksum 时使用其规范化记录 checksum 作为链锚;scope start 的 `project.yaml` 兼容字段与 workflow shard、当前视图、JSONL 同事务提交;物料交接的 workflow/artifact 分片共享事务 ID;
52
+ 9. schema 2 事务把前一分片 `record_checksum` 写入 `previous_checksum`;scope start 的项目身份与 workflow shard、当前视图、JSONL 同事务提交;物料交接的 workflow/artifact 分片共享事务 ID;
53
53
  10. 释放锁。
54
54
 
55
55
  不要在新调用方中解析并重写正式 YAML。调用 `executeDeliveryStateCommand()`,或在已安装项目中执行 `.workflow/tools/state/state.mjs`。bootstrap 已按此边界登记诊断报告。
@@ -62,8 +62,6 @@ artifact registry 与 workflow state 的新 mutation 都使用 schema 2 增量
62
62
  - `verify`
63
63
  - `snapshot create`
64
64
  - `artifact register`
65
- - `artifact status`
66
- - `artifact compact`(顶层 `ai-delivery compact --dry-run` 为零写入;apply 先验证压缩归档再移除 loose history)
67
65
  - `artifact prune`(默认零写入 dry-run,显式 `--apply` 才执行保留裁剪)
68
66
  - `transition`
69
67
  - `gate request`
@@ -92,7 +90,7 @@ artifact registry 与 workflow state 的新 mutation 都使用 schema 2 增量
92
90
 
93
91
  日常 `verify` 校验:
94
92
 
95
- - `workflow-state.schema_version` 为 2(兼容读取 1),`artifact-registry.schema_version` 为 1;
93
+ - `workflow-state.schema_version` 为 2,`artifact-registry.schema_version` 为 1;
96
94
  - revision 为非负整数;
97
95
  - revision 1 到当前 revision 的审计事件各出现一次;
98
96
  - 当前正式 YAML checksum 与最后审计事件一致;
@@ -105,7 +103,7 @@ artifact registry 与 workflow state 的新 mutation 都使用 schema 2 增量
105
103
 
106
104
  `snapshot references --iteration-id <id> [--candidate-id <id>]` 是版本归档唯一可信引用投影。它复用正式状态模块的 anchor 校验,在同一读锁内验证 pointer、anchor、快照、scope manifest 与 candidate,并为 Gate decision 生成 canonical checksum。checkpoint 任务脚本只能消费该 JSON 输出,不得复制 anchor 校验逻辑。
107
105
 
108
- 已有锚点时,日常 `verify` 验证锚点与快照 checksum、当前正式视图和 JSONL 后缀,不读取事件分片或解压历史归档。`verify --history --since-anchor` 额外要求锚点后每个 stream 的分片数量与 revision 差一致,后缀 revision 各出现一次,分片元数据与 JSONL 后缀一致,记录 checksum 和 checksum 链连续,并从锚点快照 replay 到当前正式视图。没有 compaction/retention 时,`verify --history` 保持完整历史审计:要求每个 stream 的分片数量与当前 revision 一致,revision 1 到当前 revision 各出现一次,并 replay 全部旧快照与 delta。`artifact compact` 只选择最新可信 anchor 覆盖的 legacy `after_value` shards;dry-run 零写入计算 root digest、coverage 和净释放空间,apply 先写 gzip bundle/manifest、重新校验所有 entry checksum 并 replay 到锚定 registry checksum,验证失败不得移动 loose 文件。成功后 `verify --history` 返回 `compacted-history` 并审计归档包与保留后缀。`artifact prune` 则删除 anchor 前缀并写 `.workflow/control/retention/artifact-registry/<anchor-id>.yaml` receipt;其 `verify --history` 返回 `retained-history`,不能伪称重新审计已删除字节。`rebuild` 以 anchor 原字节快照为基线,不得从 revision 0 replay 或重写仍承担 anchor 边界的 JSONL 索引。
106
+ 已有锚点时,日常 `verify` 验证锚点与快照 checksum、当前正式视图和 JSONL 后缀,不读取事件分片。`verify --history --since-anchor` 额外要求锚点后每个 stream 的分片数量与 revision 差一致,后缀 revision 各出现一次,分片元数据与 JSONL 后缀一致,记录 checksum 和 checksum 链连续,并从锚点快照 replay 到当前正式视图。`verify --history` 要求每个 stream 的分片数量与当前 revision 一致,revision 1 到当前 revision 各出现一次,并 replay 全部事件与 delta。`artifact prune` 删除 anchor 前缀并写 `.workflow/control/retention/artifact-registry/<anchor-id>.yaml` receipt;其 `verify --history` 返回 `retained-history`,不能伪称重新审计已删除字节。`rebuild` 以 anchor 快照为基线并重建 JSONL 索引。
109
107
 
110
108
  JSONL 审计不是外部签名账本。拥有仓库写权限的人可以同时篡改状态和审计文件;高保证环境应把 Git 签名、CI 证明或外部不可变审计存储作为补充,不能宣称本地 checksum 可抵御恶意共同篡改。
111
109
 
@@ -135,7 +133,7 @@ npm run audit:verify
135
133
 
136
134
  ## 9. 完成定义
137
135
 
138
- `workflow-state.yaml` 当前 schema 为 2。修改状态实现时必须保持 schema 1 只读兼容,并分别验证 `development_state` 与 `release_state` 的 scope、局部 revision、节点集合、Gate 集合和节点尝试。流程图不能退化为简单“上一编号节点”判断:平台准备允许并行,体验与产品允许带原因重开,后续迭代允许用成功的冻结基线物料满足未受影响的前置条件。
136
+ `workflow-state.yaml` 当前 schema 为 2。修改状态实现时必须分别验证 `development_state` 与 `release_state` 的 scope、局部 revision、节点集合、Gate 集合和节点尝试。流程图不能退化为简单“上一编号节点”判断:平台准备允许并行,体验与产品允许带原因重开,后续迭代允许用成功的冻结基线物料满足未受影响的前置条件。
139
137
 
140
138
  独立 aggregate 的 writer 仍通过共享提交区串行更新 JSONL 和全局视图,但使用各自的 aggregate revision:最多等待可识别的活动事务 30 秒,获取锁后重新读取并可分别成功。同一 aggregate 的 writer 在等待后必须因 revision conflict fail-closed。未知格式锁和只读入口不等待、不恢复,遇到 live lock 或 journal 立即失败。
141
139
 
@@ -1,10 +1,10 @@
1
1
  # 正式状态 CLI 使用指南
2
2
 
3
- 当前研发生产模型是 `development-seven-node`:`00-bootstrap -> 01-product-shaping -> 02-solution-design -> 03-delivery-readiness -> 04-implementation -> 05-candidate-assurance -> 06-version-closeout`。生产 Skill 为新任务、物料和 transition 使用这些节点 ID。旧 13 个研发节点仅保留只读兼容:`inspect`、`verify`、`rebuild` 和显式迁移可读取历史记录;新任务、物料登记、transition、candidate、版本收尾和 Gate 写入全部拒绝。`R00-release-request` 至 `R10-release-archive` 继续允许写入。
3
+ 当前研发生产模型是 `development-seven-node`:`00-bootstrap -> 01-product-shaping -> 02-solution-design -> 03-delivery-readiness -> 04-implementation -> 05-candidate-assurance -> 06-version-closeout`。生产 Skill 为新任务、物料和 transition 使用这些节点 ID。`R00-release-request` 至 `R10-release-archive` 继续允许写入。
4
4
 
5
5
  ## 可重建视图
6
6
 
7
- 正式状态 mutation 会生成 `.workflow/control/events/<stream>/<revision>-<event-id>.yaml` 权威事件分片,并与 `workflow-state.yaml` 或 `artifact-registry.yaml` 当前视图及 `state-events.jsonl` 索引作为一组提交。workflow 与 artifact mutation 都使用 schema 2 增量事务:事件只保存顶层元数据和受影响的 scope 或物料,不再嵌入完整视图;scope start 还把 `project.yaml` 兼容身份纳入同一事务。带已登记 evidence 的 transition/Gate 会在同一事务中同时生成 workflow delta 和 artifact handoff delta,并向 registry 顶层 `handoffs` 追加独立关系;已登记 artifact revision 的字段保持不变。提交前会在 `.workflow/delivery/runtime/formal-state-locks/` 写入恢复 journal;`inspect`、`verify`、Bootstrap 诊断及 Workflow Manager 正式状态读取期间持有同一把 formal-state lock,遇到并发 mutation 或未恢复 journal 时 fail-closed,不会返回混合 revision。mutation writer 最多等待可识别的活动提交 30 秒,获取锁后重新读取并校验自己的 aggregate revision。下一次 mutation 或 `rebuild` 根据 commit marker 回滚 prepared 事务或清理 committed 事务。旧的完整快照分片保持只读兼容且不会在首次追加新事务时被改写。视图丢失或损坏时执行:
7
+ 正式状态 mutation 会生成 `.workflow/control/events/<stream>/<revision>-<event-id>.yaml` 权威事件分片,并与 `workflow-state.yaml` 或 `artifact-registry.yaml` 当前视图及 `state-events.jsonl` 索引作为一组提交。workflow 与 artifact mutation 都使用 schema 2 增量事务:事件只保存顶层元数据和受影响的 scope 或物料,不再嵌入完整视图;scope start 同时固化项目身份。带已登记 evidence 的 transition/Gate 会在同一事务中同时生成 workflow delta 和 artifact handoff delta,并向 registry 顶层 `handoffs` 追加独立关系;已登记 artifact revision 的字段保持不变。提交前会在 `.workflow/delivery/runtime/formal-state-locks/` 写入恢复 journal;`inspect`、`verify`、Bootstrap 诊断及 Workflow Manager 正式状态读取期间持有同一把 formal-state lock,遇到并发 mutation 或未恢复 journal 时 fail-closed,不会返回混合 revision。mutation writer 最多等待可识别的活动提交 30 秒,获取锁后重新读取并校验自己的 aggregate revision。下一次 mutation 或 `rebuild` 根据 commit marker 回滚 prepared 事务或清理 committed 事务。视图丢失或损坏时执行:
8
8
 
9
9
  ```powershell
10
10
  node .workflow/tools/state/state.mjs rebuild
@@ -153,7 +153,7 @@ node .workflow/tools/state/state.mjs artifact register \
153
153
  --supersedes ART-PRODUCT-001
154
154
  ```
155
155
 
156
- `draft` 和 `review-draft` 不得登记。新登记的 submitted、rejected、approved、frozen、failed 和 superseded revision 都不可原地修改;`artifact status` 只用于把升级前已登记的遗留 draft 携带 evidence 一次性迁移为正式状态。`--path` 必须是项目相对路径,以 `.workflow/` 开头,且真实文件不能通过符号链接逃逸。重复 ID、重复受控版本和正式 revision 重写会被拒绝。成功命令会原子提交 delta 分片、当前 registry 和 JSONL 索引;任一目标无法暂存或替换时立即回滚。若进程在提交窗口中断,`inspect` 和 `verify` 会拒绝读取半提交状态;重试 mutation 或运行 `rebuild` 会先恢复 journal,再继续命令。
156
+ `draft` 和 `review-draft` 不得登记。新登记的 submitted、rejected、approved、frozen、failed 和 superseded revision 都不可原地修改。`--path` 必须是项目相对路径,以 `.workflow/` 开头,且真实文件不能通过符号链接逃逸。重复 ID、重复受控版本和正式 revision 重写会被拒绝。成功命令会原子提交 delta 分片、当前 registry 和 JSONL 索引;任一目标无法暂存或替换时立即回滚。若进程在提交窗口中断,`inspect` 和 `verify` 会拒绝读取半提交状态;重试 mutation 或运行 `rebuild` 会先恢复 journal,再继续命令。
157
157
 
158
158
  可信 history anchor 建立后,可先零写入预览可裁剪的 artifact shard:
159
159
 
@@ -164,15 +164,6 @@ node .workflow/tools/state/state.mjs artifact prune --apply
164
164
 
165
165
  默认命令只返回计划,不创建目录、receipt 或时间戳。只有显式 `--apply` 才删除 anchor 已覆盖的 `.workflow/control/events/artifact-registry/` 分片,并在 `.workflow/control/retention/artifact-registry/<anchor-id>.yaml` 写入一条带 checksum 的保留决策;anchor 后的分片和 JSONL 边界继续保留。裁剪后 `verify --history` 返回 `retained-history`,从最新可信 anchor 验证剩余链,不再声称重新审计了已删除的前缀;`rebuild` 同样从 anchor 快照开始 replay。
166
166
 
167
- legacy full-snapshot history 需要保留可重放原字节时,不使用 prune,而由发行包执行独立 compact:
168
-
169
- ```bash
170
- ai-delivery compact <project-root> --dry-run
171
- ai-delivery compact <project-root>
172
- ```
173
-
174
- dry-run 严格零写入,返回 anchor、revision 覆盖范围、原历史根摘要、loose/archive 字节数和预计净释放空间。apply 把 anchor 覆盖的 legacy `after_value` shards 写入 `.workflow/control/archives/history/artifact-registry/<anchor-id>.json.gz`,manifest 绑定 bundle checksum、每个原文件、coverage 和锚定 registry checksum。CLI 从落盘 bundle 解压并完整 replay,只有 replay checksum 与 anchor 完全一致才移除 loose shards;失败时不移除任何 loose history。压缩后 `verify --history` 返回 `compacted-history` 并重新校验 archive、entry、anchor 与 replay;第一条后续 delta 从 anchor 的记录链头继续。
175
-
176
167
  版本归档前由 `task-state.mjs archive-version` 内部调用以下只读投影;通常不需要人工单独执行:
177
168
 
178
169
  ```bash
@@ -209,7 +200,7 @@ node .workflow/tools/state/state.mjs transition \
209
200
  --production-version-id <version-id>
210
201
  ```
211
202
 
212
- `version-closed` 要求 `06-version-closeout` 已完成,且物料来自该节点并处于 `release-ready`;它更新 `latest_line_version_id`。`release-archived` 要求 `R10-release-archive` 已完成,且物料来自该节点并处于 `released`;它更新 `production_version_id` 和 `production_release_id`。三个字段的权威来源都是 `workflow-state.yaml`,不得直接编辑;`project.yaml` 中的旧字段只用于兼容读取。
203
+ `version-closed` 要求 `06-version-closeout` 已完成,且物料来自该节点并处于 `release-ready`;它更新 `latest_line_version_id`。`release-archived` 要求 `R10-release-archive` 已完成,且物料来自该节点并处于 `released`;它更新 `production_version_id` 和 `production_release_id`。三个字段的权威来源都是 `workflow-state.yaml`,不得直接编辑。
213
204
 
214
205
  Gate 请求:
215
206
 
@@ -236,7 +227,6 @@ node .workflow/tools/state/state.mjs gate decide \
236
227
 
237
228
  决定必须保留请求时的全部 evidence,并明确记录 `actor` 和 `rationale`。AI 文本、Hook、Manager 页面成功提示或命令成功不能替代正式批准凭证。非视觉体验范围把 UX Gate 设为 `not-required` 时,还必须附加来自 `01-product-shaping`、状态成功且 `artifact_type` 包含 `terminal` 的已登记证据;推荐统一使用 `product-terminal-scope`。
238
229
 
239
- `UX-LF` 已从当前契约退役。新的 request 和 decide 会在写入前拒绝;历史记录仍可 inspect、verify 和 rebuild。升级 dry-run 会在 `contract_retirement.affected_iterations` 中列出开放 scope 的 pending `UX-LF`,正式升级由 `system:package-upgrade` 追加 `gate-contract-retired` 事件和 checksum 回执,不创建人工 Gate 决定,也不改写关闭迭代或归档。
240
230
 
241
231
  ## 7. revision 冲突与失败恢复
242
232
 
@@ -253,10 +243,10 @@ node .workflow/tools/state/state.mjs gate decide \
253
243
 
254
244
  ## 8. 生产阶段
255
245
 
256
- 研发与发布使用两个独立状态域:`development_state.scope_id` 由 `iteration-started` 建立,`release_state.scope_id` 由用户明确发布后执行 `release-started` 建立。两者分别保存局部 revision、活动/阻塞/完成节点、Gate、节点尝试次数和选定物料;局部 revision 用于 workflow 乐观并发,顶层 `workflow_state.revision` 只负责审计串行化并保留旧参数兼容。
246
+ 研发与发布使用两个独立状态域:`development_state.scope_id` 由 `iteration-started` 建立,`release_state.scope_id` 由用户明确发布后执行 `release-started` 建立。两者分别保存局部 revision、活动/阻塞/完成节点、Gate、节点尝试次数和选定物料;局部 revision 用于 workflow 乐观并发,顶层 `workflow_state.revision` 只负责审计串行化。
257
247
 
258
248
  `node-started` 会验证流程图前置条件。当前迭代未执行的前置节点,可以用来自该节点且处于成功状态的已登记基线物料作为 evidence;这用于后续迭代只重访受影响基线。重新打开已完成节点必须提供 `--reason`,并产生新的节点尝试,旧 Gate 决定不能批准新一轮输出。
259
249
 
260
- `node-completed` 只接受成功状态物料,并检查当前尝试的必需 Gate:`GATE-A`、`GATE-B`、`UX-VS`、`UX-UI` 和 `PRODUCTION-APPROVAL`。只有确实无视觉交互的体验范围可以依据产品终端证据把活动 UX Gate 决定为 `not-required`;Gate A、Gate B 和生产审批必须为 `approved`。历史 `12-release-preparation` 至 `16-version-archive` 只允许登记和读取审计物料,新的 transition 和 Gate 命令会拒绝这些节点。
250
+ `node-completed` 只接受成功状态物料,并检查当前尝试的必需 Gate:`GATE-A`、`GATE-B`、`UX-VS`、`UX-UI` 和 `PRODUCTION-APPROVAL`。只有确实无视觉交互的体验范围可以依据产品终端证据把活动 UX Gate 决定为 `not-required`;Gate A、Gate B 和生产审批必须为 `approved`。
261
251
 
262
252
  每个节点交接、生产审批和部署前运行日常 `verify`;版本收尾和发布归档还必须运行 `verify --history`。新研发流程使用 `06-version-closeout` 和 `release-ready`;完成该节点后通过 `version-closed` 记录累计研发版本,但不得创建任何 R 节点。独立发布使用 `R00-release-request` 至 `R10-release-archive`;启动 R00 时必须提供节点为 R00、状态成功且 `artifact_type: release-request` 的显式用户请求物料,并固定请求时 `main` 的 `expected_main`。R04 必须以 `release-git.mjs create-candidate` 的 `--expected-main`、精确 `--source-tag/--source-commit`、`--receipt` 和 `--apply` 从该快照创建 release 分支。增量发布只以 `--no-ff` 合入一个精确不可变 `version-ready` Tag;同版本重发在目标 Tag 已包含于快照时不新增合并,候选就是该快照。候选、审批和部署都绑定该精确候选提交。R09 成功结论先写入带 `operation: production-verification-decision` 的机器 JSON,再通过 `release-git.mjs promote --production-verification <R09-json>` 触发受控的精确 `main` 快进;R10 在归档前准备 schema 2 的产品线级 `candidate-inventory.json`,其根部固定 `scope: product-line`、`product_id`、`line_branch` 和 active candidate,每项候选保留自己的 `release_id`、commit/status。以 `sync-release-fix --expected-line --production-verification <R09-json> --candidate-inventory <predecessor-R10-json> --successor-candidate-inventory <new-R10-json> --receipt --apply` 受控同步每个成功的 release-fix 到 `line/<product-id>`;脚本只写入新的不可变 successor 库存,在回执中绑定前代/后代库存,并派生、登记带 release ID 的 `stale_candidate_ids` 与 `candidate_status_updates`。main 漂移、候选不一致或遗漏已成功 fix 都使候选 stale。R10 完成并登记 `released` 归档物料后,才通过 `release-archived` 正式回写生产版本和 release ID。生产审批只发生在 R07,并绑定精确 release ID、main 基线、source Tag/commit、候选 commit/Tag、包 checksum、镜像 digest、配置、迁移、环境和回滚镜像;任一项变化均创建新发布候选并重新审批。
@@ -8,7 +8,7 @@ This matrix is the completion ledger for P0/P1/P2. `covered` means current autom
8
8
 
9
9
  | Requirement | Current implementation | Executable evidence | Status |
10
10
  | --- | --- | --- | --- |
11
- | One installed control surface | Manager runtime, React assets and cross-platform launchers; no Viewer on new install | `verification/workflow-manager.test.mjs`, `verification/project-installer.test.mjs` | covered |
11
+ | One installed control surface | Manager runtime, React assets and cross-platform launchers | `verification/workflow-manager.test.mjs`, `verification/project-installer.test.mjs` | covered |
12
12
  | CLI/HTTP/UI parity | all call `workflow-manager.mjs` resources/actions | package, installed CLI and HTTP equality tests | covered |
13
13
  | Seven-node formal state | P01/P02 derive only from `state inspect.derived_node_states` | delivery-state and browser tests | covered |
14
14
  | Stable context | workspace, project, branch, iteration, revision and checksum fields | Manager inspect/resource tests | covered |
@@ -16,8 +16,8 @@ This matrix is the completion ledger for P0/P1/P2. `covered` means current autom
16
16
  | Governed writes | fixed action allowlist, actor, revision/checksum, idempotency and plan checksum as applicable | governance, prototypes, systems and Mock tests | covered |
17
17
  | Receipts and events | all stateful Manager actions use formal service or write immutable Receipt/Event | module-specific tests | covered |
18
18
  | Desktop visual contract | Chinese Operational Carbon, IBM Plex, Lucide; 1280x720 and 1440x900 | 19 Page browser acceptance | covered |
19
- | Package installation lifecycle | new install, Viewer remove/backup/rollback, installed Manager | installer and Manager tests | covered |
20
- | `0.4.0` candidate and target project | package, isolated install/upgrade/migrate/rollback, tushare read-only preflight | candidate SHA-256, isolated receipts, installed Manager probes and target-project dry-run summaries below | covered |
19
+ | Package installation lifecycle | new install, current-contract upgrade, installed Manager | installer and Manager tests | covered |
20
+ | `0.4.0` candidate and target project | package and isolated current-contract install/doctor | candidate SHA-256 and installed Manager probes | covered |
21
21
 
22
22
  ## Page delivery
23
23
 
@@ -45,25 +45,20 @@ This matrix is the completion ledger for P0/P1/P2. `covered` means current autom
45
45
 
46
46
  `P13` is contextual and absent from fixed navigation. Page identifiers are fixed and unique; new Pages append identities without reusing retired numbers.
47
47
 
48
- ## Legacy convergence
48
+ ## Current contract
49
49
 
50
50
  | Surface | Current evidence | Status |
51
51
  | --- | --- | --- |
52
- | Workflow Viewer | template/runtime/tests removed; new manifest omits Viewer; old Viewer is Receipt-backed `remove` and rollback restore | covered |
52
+ | Workflow Manager | one installed Manager control surface | covered |
53
53
  | User-visible Prototype Review Server | CLI/server/static shell/tests removed; target and Page Receipt internals retained for Manager | covered |
54
54
  | Public documentation | current guides route users and agents to Manager P11/P12/P13 | covered |
55
- | Historical 13-node state | read-only state compatibility remains; Manager exposes only seven-node producer model | covered |
55
+ | Formal state | schema 2 and seven-node producer model | covered |
56
56
 
57
57
  ## `0.4.0` candidate evidence
58
58
 
59
59
  The canonical candidate identity, SHA-256, file count, source commit, and lifecycle report are recorded in the maintenance-repository file `audit/WORKFLOW-MANAGER-P0-P1-P2-CLOSURE.md`. The archive and expanded lifecycle workspace remain in ignored `.tmp/`; the audit record is outside the npm package, so the archive does not self-reference its own checksum.
60
60
 
61
61
  - A fresh isolated project passed Doctor except for the optional CodeGraph warning. The historical r8 candidate returned HTTP 200, health `true`, schema `workflow-manager.v1`, 19 Pages, seven nodes, Prototype `wm-prototype-r24@4452c605`, and 25 discoverable governed actions.
62
- - An isolated `0.3.0` project produced an upgrade plan with zero conflicts, one Manager addition, eight managed replacements, one Viewer removal, and no code-repository write. Applying the upgrade produced Receipt `UPGRADE-20260812T221812525Z-cde0bf7d`.
63
- - Rolling back that Receipt restored manifest version `0.3.0`, restored all seven Viewer files, removed Manager, changed the Receipt status to `rolled_back`, and left both isolated Git worktrees byte-for-byte clean against their pre-upgrade commits. The recorded pre-upgrade Viewer digest remains `5cfb8de36f36ccf8149e6f8047e85e48b573a52ef28e67adf35d675497148369`.
64
- - Against `E:\work\ai\codex\tushare`, `doctor`, `upgrade --dry-run`, and `migrate --dry-run` completed without formal apply. Doctor reported the existing pre-upgrade managed-runtime drift; the upgrade plan has one addition, eight replacements and one removal with zero managed-asset conflicts and `code_repository.write_planned=false`. Each dry-run was bracketed by an identical current-state Git status digest.
65
- - The `tushare` migration preflight deliberately returned `can_apply=false`: one legacy Prototype source checksum conflicts with its manifest and 24 task families require human semantic-grouping decisions across 183 historical tasks. No business state was changed. This is the required fail-closed result, not authorization to apply or force migration.
66
- - The maintainer cancelled the formal `tushare` upgrade/migration on 2026-08-21. The target remains read-only; the failed-closed migration plan is retained as evidence and must not be forced into an apply.
67
62
 
68
63
  The current formal Manager baseline has since been promoted to `wm-prototype-r33@f98e7595` through the independent Prototype-to-Frontend gate. The historical `0.4.0` candidate above remains bound to r24 and is immutable evidence; it does not contain the r33 promotion and cannot be reused for npm publication. A new release candidate is required before publication.
69
64
 
@@ -77,9 +72,9 @@ This addendum covers the destructive v2 Prototype Revision contract. It does not
77
72
  | Fresh tarball installation delivers v2-only Manager/Experience material | `isolated package Candidate Assurance binds v2 Revision facts across installed CLI, HTTP, and P07/P11-P17` in `verification/workflow-manager.test.mjs` runs `npm pack`, consumer install, `init`, and `doctor` | covered |
78
73
  | P07 and P11-P17 use one Revision fact through installed CLI, HTTP, and UI | the same isolated test compares installed CLI/HTTP JSON, verifies each Web browser request and response is `TERM-WEB` scoped, and proves P12 renders the independent `TERM-ADMIN` target at `1280x720` and `1440x900` | covered |
79
74
  | Component Candidate lifecycle write is controlled across Agent and UI service boundaries | the same isolated test runs P11 `candidate -> under-review`, installed CLI `under-review -> registered`, and HTTP `registered -> modified`; each apply emits a Receipt/event and both Agent clients replay idempotently | covered |
80
- | Old catalog input has no compatibility fallback | the same isolated test removes the required `components` catalog and verifies matching CLI/HTTP errors plus the P13 controlled error state | covered |
75
+ | Incomplete catalog input is rejected | the same isolated test removes the required `components` catalog and verifies matching CLI/HTTP errors plus the P13 controlled error state | covered |
81
76
 
82
- `prototype-set.yaml` and its members are retained only as read-only migration inputs. New Experience production uses `prototype-revision-catalog.v2`; no new install, Manager read model, or Candidate Assurance path emits a legacy set.
77
+ New Experience production uses `prototype-revision-catalog.v2`; no install, Manager read model, or Candidate Assurance path emits an obsolete set.
83
78
 
84
79
  ## Release gate
85
80
 
@@ -88,6 +83,5 @@ P0/P1/P2 completes only after all of the following are current and green:
88
83
  1. `node --test verification/workflow-manager*.test.mjs` and the full `npm test` suite.
89
84
  2. `npm run manager:build`, `npm run skills:validate`, `npm run pack:check`, and `git diff --check`.
90
85
  3. Final `index.html` asset references are Git-tracked and present in the tarball.
91
- 4. A `0.4.0` candidate passes isolated new install, upgrade, migrate dry-run, rollback and installed Manager CLI/API/UI checks.
92
- 5. The candidate passes `doctor`, `upgrade --dry-run`, and `migrate --dry-run` against `E:\work\ai\codex\tushare` without formal apply.
86
+ 4. A `0.4.0` candidate passes isolated current-contract install and installed Manager CLI/API/UI checks.
93
87
  6. Gitee P0/P1/P2 Issues contain the same evidence, duplicate tickets are merged or closed, dependencies are corrected, and every write is read back. This is covered: `IK8BA0` through `IK8BA5`, `IK8B9Z`, `IK8B9U`, `IK6HCD`, `IK89SN`, and `IK6HCE` were closed in the Gitee UI after explicit user confirmation and API v5 subsequently returned `state: rejected` for each, which is Gitee's API representation of the UI's “已关闭” status.
@@ -1,13 +1,13 @@
1
1
  # 工作流与工作流管理器阶段现状
2
2
 
3
- 更新时间:2026-08-21
3
+ 更新时间:2026-08-22
4
4
 
5
5
  本文是继续设计、实现或审核 Workflow Manager 时的阶段入口。它区分已经进入正式工作流源码的能力、已经接受但尚未实现的产品决策、仅在隔离原型中验证的交互,以及仍未完成的事项。领域术语以根目录 `CONTEXT.md` 为准,不以本文替代 ADR、正式状态契约或 Issue。
6
6
 
7
7
  ## 1. 当前基线
8
8
 
9
9
  - 当前仓库是 `ai-delivery-workflow` 的维护与发行源码仓库,不是安装了工作流的业务项目。
10
- - 当前实施分支为 `codex/workflow-manager-foundation`;Workflow Manager 实现在 `ae70304`,外部 Issue 收尾在 `e727af1`。`0.4.0` 的最终候选身份、校验和及生命周期证据记录在维护仓库的 `audit/WORKFLOW-MANAGER-P0-P1-P2-CLOSURE.md`;该记录不随安装包分发,候选包和展开验证工作区保留在被忽略的 `.tmp/`。工作树仍包含用户维护的文档和参考资料变更。
10
+ - 当前正式源码候选版本为 `0.6.0`(待完成发布 Gate);上一版 `0.5.1` 的提交、Git tag、npm 包身份及 Workflow Manager 的历史 `0.4.0` 候选身份、校验和和生命周期证据仍记录在维护仓库的 `audit/WORKFLOW-MANAGER-P0-P1-P2-CLOSURE.md`,这些历史记录不可改写。候选包和展开验证工作区保留在被忽略的 `.tmp/`。
11
11
  - 正式维护源码位于 `bin/`、`lib/`、`skills/`、`docs/`、`verification/` 和 `audit/`。
12
12
  - Workflow Manager 设计原型位于 `prototypes/workflow-manager/`,业务项目原型夹具位于 `prototypes/order-ops-business/`。两者是可版本化设计资产,不进入发行包,也不等于正式前端已经实现;运行时依赖、构建产物和一次性验证工作区仍放在 `.tmp/`。
13
13
 
@@ -51,11 +51,11 @@
51
51
  - Delivery Readiness 后使用 `scope freeze` 冻结范围;只有明确人工批准的 `scope amend` 可以改变范围。
52
52
  - Slice 超出逻辑任务数、活跃工时、墙钟时长或阻塞修复轮次预算时,只执行 `scope warn`。其结果固定为 `warn-only` 和 `reslice_performed: false`,不会自动重新切片、重排计划或改变验收。
53
53
  - 任务使用一个稳定任务 ID,并在任务内部记录 attempt 和 phase。过去的 `F/R/T/P/vN` 后缀任务模式已被替代。
54
- - 物料登记、事件分片、可信快照、历史校验、prune 和 compact 已进入正式源码;正式 revision 和审核凭证不可原地覆盖。
54
+ - 物料登记、事件分片、可信快照、历史校验和 prune 已进入正式源码;正式 revision 和审核凭证不可原地覆盖。
55
55
 
56
56
  ### 2.4 原型优先 UI 治理
57
57
 
58
- - 不再使用 UX-LF。`UX-VS` 负责视觉规范确认,`UX-UI` 负责正式原型审核,两者归入 `02-solution-design`。
58
+ - `UX-VS` 负责视觉规范确认,`UX-UI` 负责正式原型审核,两者归入 `02-solution-design`。
59
59
  - 所有用户可见 UI 修改都遵守“先修改隔离 Prototype -> 用户确认 -> 再修改正式前端”,不存在小改、紧急修复或业务逻辑调整例外。
60
60
  - 一个项目只有一个逻辑 Prototype。Page 是稳定路由页面,Review Scene 是 Page 的可审核状态;原型必须列出错误、空态、权限、恢复等正式系统中只在条件满足时出现的状态。
61
61
  - 页面级 Prototype 审核、Revision/checksum 绑定、Review Shell、Page Receipt、正式前端守卫和 Candidate Assurance UI Acceptance 已进入维护源码。
@@ -67,13 +67,13 @@
67
67
 
68
68
  - Microcks 是默认项目级 Mock System。External Dependency Mock 和 Product Backend Mock 分开管理;协议定义和 Mock Scenario Pack 可版本化,运行数据库、流量和日志可丢弃。
69
69
  - CodeGraph 是可选、可降级的代码导航适配器。工作流只管理项目索引,不安装全局 MCP;不可用时降级到 Semble 和 `rg`,其分析结果不能替代正式 Gate 或既定测试集。
70
- - 面向旧项目的 Prototype 迁移、UX-LF 契约退役、升级保护报告、UI Acceptance 迁移和历史压缩均已有正式实现和验证。
70
+ - 当前正式路径只接受 `.workflow/`、schema 2 和 v2 Prototype Revision;旧布局、旧 schema 和旧 catalog 由 CLI、HTTP 与 UI 确定性拒绝。
71
71
 
72
72
  ## 3. 已实现的 Workflow Manager 契约
73
73
 
74
74
  ### 3.1 产品边界
75
75
 
76
- 项目绑定的 Workflow Manager 已取代 Workflow Viewer 和独立的用户可见 Review Server。它负责工作流状态、合法流转、人工决策、项目原型审核、项目系统、Mock、需求、任务、测试、交付成果、事件和设置。
76
+ 项目绑定的 Workflow Manager 负责工作流状态、合法流转、人工决策、项目原型审核、项目系统、Mock、需求、任务、测试、交付成果、事件和设置。
77
77
 
78
78
  Workflow Manager 不是任意命令执行器、文件编辑器或 AI 执行器。它只暴露有契约的读取和变更动作;直接 YAML 修改、任意 shell、任意文件路径和绕过 Gate 均不属于产品能力。
79
79
 
@@ -160,16 +160,13 @@ Workflow Manager 从一开始就是人和 AI 共同使用的控制面。UI 与 W
160
160
 
161
161
  - 当前 P0/P1/P2 候选包的精确路径、SHA-256、文件数和生命周期报告由维护仓库的 `audit/WORKFLOW-MANAGER-P0-P1-P2-CLOSURE.md` 固定登记。审计记录不在 npm 包范围内,因此不会导致候选包自引用;实际 tarball 和展开验证工作区仍保留在 `.tmp/`。
162
162
  - 隔离新安装的 Doctor 除可选 CodeGraph 提示外全部通过;安装后 Manager 的主页、健康检查、`workflow-manager.v1` Schema、19 个 Page、七节点投影、25 个可发现受控动作和 r33 Prototype 投影均已实测。
163
- - 隔离 `0.3.0` 项目的升级、迁移 dry-run、正式升级及回滚已实测。升级 Receipt 为 `UPGRADE-20260812T221812525Z-cde0bf7d`;回滚恢复 `0.3.0`、全部 7 个 Viewer 文件和升级前两个 Git 工作树状态,移除 Manager,并把 Receipt 标记为 `rolled_back`。升级前 Viewer 摘要为 `5cfb8de36f36ccf8149e6f8047e85e48b573a52ef28e67adf35d675497148369`。
164
- - 候选包已对 `E:\work\ai\codex\tushare` 执行 `doctor`、`upgrade --dry-run` 和 `migrate --dry-run`,没有执行正式 apply。Doctor 识别出该旧安装已有的受管 Skill/运行时完整性漂移;升级计划没有受管资产冲突、不写代码仓库,并计划新增 Manager、替换 8 处受管资产、移除 Viewer。每个 dry-run 前后的工作流 Git 状态摘要一致。维护者于 2026-08-21 明确取消 `tushare` 正式升级/迁移;该项目保持只读,不得再次尝试 apply 或 force。
165
- - `tushare` 当前不能直接迁移:一个旧 Prototype 来源文件与清单 checksum 不一致,183 个历史任务中有 24 个任务家族需要人工判断语义分组。迁移正确返回 `can_apply=false` 且没有修改业务状态;不得把该结果改写成迁移已就绪,也不得使用 force 绕过人工决策。
166
163
 
167
164
  ### 4.2 v2 Prototype catalog Candidate Assurance
168
165
 
169
166
  - 新安装交付把 v2 Prototype Revision 的运行时、Manager、Agent Interface、Experience Skill 和全部必需 catalog 模板列为包表面。`doctor` 在包表面缺失时立即失败,不再继续计算会因缺失运行时而崩溃的受管资产摘要。
170
- - 新的生产路径只接受 `prototype-revision-catalog.v2`。Experience Skill、Project Template、安装后的 Manager 与 Candidate Assurance 都从同一 Revision/checksum 读取事实;缺少任一必需 catalog 的旧 Revision 被 CLI、HTTP 和 UI 确定性拒绝。`prototype-set.yaml` 及 member 文件仍保留为只读迁移输入,明确标记 `compatibility_mode: read-only-legacy` 和 `current_flow_output: false`,不得作为新流程输出或兼容回退。
167
+ - 新的生产路径只接受 `prototype-revision-catalog.v2`。Experience Skill、Project Template、安装后的 Manager 与 Candidate Assurance 都从同一 Revision/checksum 读取事实;缺少任一必需 catalog 时由 CLI、HTTP 和 UI 确定性拒绝。
171
168
  - `verification/workflow-manager.test.mjs` 的隔离 Candidate Assurance 用当前工作树实际执行 `npm pack`,在 `.tmp/` 的独立 consumer 安装 tarball,并由安装后的 `ai-delivery init` 与 `doctor` 建立项目。它用多 Logical Terminal 的 v2 fixture 证明安装后的 Manager CLI、HTTP 和 P07/P11-P17 浏览器 UI 读取同一 Prototype Revision 身份和 checksum:每个 Web UI 请求和返回都显式为 `TERM-WEB`,P12 的 `TERM-ADMIN` 路由实际显示 Admin 目标而不回退到 Web。Component Candidate 通过 P11 提交 `candidate -> under-review`、已安装 CLI 提交 `under-review -> registered`、HTTP 提交 `registered -> modified`;每次 apply 均产生 Receipt/Event,CLI 与 HTTP 均重放同一幂等键。初始 CLI/HTTP 计划仍返回相同 plan/successor;删除 v2 `components` catalog 后,三种入口返回相同受控拒绝。测试覆盖 `1280x720` 与 `1440x900` 桌面视口,且有效 Revision 路径没有浏览器 console error/warn。
172
- - 可重建证据和边界记录在 `audit/WORKFLOW-MANAGER-V2-CANDIDATE-ASSURANCE.md`。它不声明新的 npm 发布或替代 `audit/WORKFLOW-MANAGER-P0-P1-P2-CLOSURE.md` 的历史候选身份;生成的 tarball、consumer、缓存和浏览器临时文件都留在被忽略的 `.tmp/` 并在测试结束后删除。
169
+ - 可重建证据和边界记录在 `audit/WORKFLOW-MANAGER-V2-CANDIDATE-ASSURANCE.md`。它不替代 `audit/WORKFLOW-MANAGER-P0-P1-P2-CLOSURE.md` 的历史候选身份;`0.5.0` 发布时的源码已通过完整 `release:check`、npm 2FA Gate 并发布。生成的 tarball、consumer、缓存和浏览器临时文件都留在被忽略的 `.tmp/` 并在测试结束后删除。
173
170
 
174
171
  ## 5. 开放事项
175
172
 
@@ -185,14 +182,14 @@ Workflow Manager 从一开始就是人和 AI 共同使用的控制面。UI 与 W
185
182
  ### 已关闭的历史 Issue
186
183
 
187
184
  - [IK89SN](https://gitee.com/hzr1348/work-flow/issues/IK89SN):P13 已实现 `Ctrl+V` 粘贴图片、持久化、checksum 绑定和 AI 可读附件接口。
188
- - [IK6HCD](https://gitee.com/hzr1348/work-flow/issues/IK6HCD):由 Workflow Manager 收敛旧 Viewer 的状态投影需求,避免继续建设重复产品。
189
- - [IK6HCE](https://gitee.com/hzr1348/work-flow/issues/IK6HCE):`0.4.0` 候选包、隔离升级/迁移/回滚及 tushare dry-run 均已验证;正式 apply 与发布仍属于独立人工 Gate。
185
+ - [IK6HCD](https://gitee.com/hzr1348/work-flow/issues/IK6HCD):Workflow Manager 控制面与 P13 附件能力已收敛。
186
+ - [IK6HCE](https://gitee.com/hzr1348/work-flow/issues/IK6HCE):`0.4.0` 候选包与隔离安装验证已完成;正式发布仍属于独立人工 Gate。
190
187
 
191
188
  ### 下一阶段顺序
192
189
 
193
190
  1. P0/P1/P2 及 Prototype 治理 01–07 已完成;实现提交为 `ae70304`,后续 v2 Candidate Assurance 收尾提交为 `db8abfb`,候选验证与 11 个 Gitee Issue 收尾均已完成。
194
- 2. `tushare` 正式升级/迁移已按维护者指示取消并保持只读;不得把历史 dry-run 改写为 apply 许可。
195
- 3. r33 晋级改变了当前源码候选内容,历史 `0.4.0` r8 候选证据仍不可变且不能复用。npm 发布需在干净且同步的 `main` 上重新生成候选并通过 `release:check`、账号/2FA 与人工发布 Gate。
191
+ 2. 后续变更继续沿当前 `.workflow/`、schema 2 与 v2 Prototype Revision 契约推进。
192
+ 3. r33 晋级改变了当前源码候选内容,历史 `0.4.0` r8 候选证据仍不可变且不能复用;该变更已在 `0.5.0` 中完成发布闭环。后续发布仍需从干净且同步的 `main` 重新生成候选,并通过 `release:check`、账号/2FA 与人工发布 Gate。
196
193
 
197
194
  ## 6. 继续工作时必须读取的来源
198
195
 
@@ -0,0 +1,177 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { parse as parseYaml, stringify as stringifyYaml } from "./yaml-runtime.mjs";
5
+
6
+ export const CONFIRMATION_STATUSES = new Set(["proposed", "confirmed", "rejected", "deferred", "superseded"]);
7
+ export const CONFIRMATION_CATEGORIES = new Set([
8
+ "observed_fact", "business_fact", "business_decision", "domain_term", "business_constraint",
9
+ "architecture_decision", "standard_decision", "risk", "assumption", "open_question",
10
+ "execution_fact", "execution_decision",
11
+ ]);
12
+ export const NEXT_ACTION_OWNERS = new Set(["agent", "user", "external"]);
13
+
14
+ const CLOSED_STATUSES = new Set(["confirmed", "rejected", "superseded"]);
15
+ const ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
16
+
17
+ function text(value) {
18
+ return typeof value === "string" && value.trim().length > 0;
19
+ }
20
+
21
+ function list(value) {
22
+ return Array.isArray(value) ? value : [];
23
+ }
24
+
25
+ function addError(errors, location, message) {
26
+ errors.push(`${location}: ${message}`);
27
+ }
28
+
29
+ function stableValue(value) {
30
+ if (Array.isArray(value)) return value.map(stableValue);
31
+ if (value && typeof value === "object") {
32
+ return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stableValue(value[key])]));
33
+ }
34
+ return value;
35
+ }
36
+
37
+ function sameRecord(left, right) {
38
+ return JSON.stringify(stableValue(left)) === JSON.stringify(stableValue(right));
39
+ }
40
+
41
+ function recordIsClosed(record, byId, visiting = new Set()) {
42
+ if (!record) return false;
43
+ if (record.status !== "superseded") return CLOSED_STATUSES.has(record.status);
44
+ if (!record.superseded_by || visiting.has(record.confirmation_id)) return false;
45
+ visiting.add(record.confirmation_id);
46
+ const replacement = byId.get(record.superseded_by);
47
+ return Boolean(replacement) && recordIsClosed(replacement, byId, visiting);
48
+ }
49
+
50
+ function detectCycles(records, byId, errors) {
51
+ const visiting = new Set();
52
+ const visited = new Set();
53
+ const visit = (record) => {
54
+ if (visited.has(record.confirmation_id)) return;
55
+ if (visiting.has(record.confirmation_id)) {
56
+ addError(errors, `records.${record.confirmation_id}`, "dependency cycle detected");
57
+ return;
58
+ }
59
+ visiting.add(record.confirmation_id);
60
+ for (const dependencyId of list(record.depends_on)) {
61
+ const dependency = byId.get(dependencyId);
62
+ if (dependency) visit(dependency);
63
+ }
64
+ visiting.delete(record.confirmation_id);
65
+ visited.add(record.confirmation_id);
66
+ };
67
+ records.forEach(visit);
68
+ }
69
+
70
+ export function validateConfirmationSummary(summary, options = {}) {
71
+ const errors = [];
72
+ const warnings = [];
73
+ if (!summary || typeof summary !== "object" || Array.isArray(summary)) {
74
+ return { valid: false, errors: ["summary: must be a mapping"], warnings: [], frontier: [], blocked_proposals: [] };
75
+ }
76
+ if (summary.schema_version !== 1) addError(errors, "schema_version", "must equal 1");
77
+ for (const field of ["summary_id", "node", "iteration_id", "owner", "source_revision", "updated_at"]) {
78
+ if (!text(summary[field])) addError(errors, field, "must be a non-empty string");
79
+ }
80
+ if (!ID_PATTERN.test(String(summary.summary_id || ""))) addError(errors, "summary_id", "has invalid format");
81
+ if (!Number.isInteger(summary.version) || summary.version < 1) addError(errors, "version", "must be a positive integer");
82
+ if (!Number.isInteger(summary.frontier_round) || summary.frontier_round < 1) addError(errors, "frontier_round", "must be a positive integer");
83
+ if (!Array.isArray(summary.records)) addError(errors, "records", "must be an array");
84
+ const records = list(summary.records);
85
+ const byId = new Map();
86
+ records.forEach((record, index) => {
87
+ const prefix = `records[${index}]`;
88
+ if (!record || typeof record !== "object" || Array.isArray(record)) {
89
+ addError(errors, prefix, "must be a mapping");
90
+ return;
91
+ }
92
+ if (!text(record.confirmation_id) || !ID_PATTERN.test(record.confirmation_id || "")) addError(errors, `${prefix}.confirmation_id`, "must be a stable ID");
93
+ if (byId.has(record.confirmation_id)) addError(errors, `${prefix}.confirmation_id`, "must be unique");
94
+ else byId.set(record.confirmation_id, record);
95
+ if (!text(record.question_id) || !ID_PATTERN.test(record.question_id || "")) addError(errors, `${prefix}.question_id`, "must be a stable ID");
96
+ if (!CONFIRMATION_CATEGORIES.has(record.category)) addError(errors, `${prefix}.category`, "is not an allowed category");
97
+ if (!CONFIRMATION_STATUSES.has(record.status)) addError(errors, `${prefix}.status`, "is not an allowed status");
98
+ if (!Number.isInteger(record.frontier_round) || record.frontier_round < 1) addError(errors, `${prefix}.frontier_round`, "must be a positive integer");
99
+ if (!text(record.question)) addError(errors, `${prefix}.question`, "must be provided");
100
+ if (!text(record.recommendation)) addError(errors, `${prefix}.recommendation`, "must be provided");
101
+ if (!Array.isArray(record.depends_on)) addError(errors, `${prefix}.depends_on`, "must be an array");
102
+ if (!Array.isArray(record.evidence_refs)) addError(errors, `${prefix}.evidence_refs`, "must be an array");
103
+ if (!Array.isArray(record.authority_refs)) addError(errors, `${prefix}.authority_refs`, "must be an array");
104
+ if (!NEXT_ACTION_OWNERS.has(record.next_action_owner)) addError(errors, `${prefix}.next_action_owner`, "must be agent, user, or external");
105
+ if (!text(record.next_action)) addError(errors, `${prefix}.next_action`, "must be explicit and idempotent");
106
+ if (record.status === "proposed" && record.decision !== null && record.decision !== undefined) addError(errors, `${prefix}.decision`, "must be null while proposed");
107
+ if (["confirmed", "rejected"].includes(record.status)) {
108
+ if (!text(record.decision)) addError(errors, `${prefix}.decision`, "is required after a decision");
109
+ if (!text(record.actor)) addError(errors, `${prefix}.actor`, "is required after a decision");
110
+ if (!text(record.confirmed_at)) addError(errors, `${prefix}.confirmed_at`, "is required after a decision");
111
+ if (!list(record.authority_refs).length && !options.allowUnlinkedConfirmed) addError(errors, `${prefix}.authority_refs`, "must point to the owning artifact or handoff");
112
+ }
113
+ if (["observed_fact", "execution_fact"].includes(record.category) && !list(record.evidence_refs).length) addError(errors, `${prefix}.evidence_refs`, "is required for an observed fact");
114
+ if (record.status === "deferred" && record.next_action_owner === "agent") addError(errors, `${prefix}.next_action_owner`, "deferred decisions require user or external ownership");
115
+ if (record.status === "superseded" && !text(record.supersedes) && !text(record.superseded_by)) addError(errors, prefix, "must point to a replacement or predecessor");
116
+ });
117
+
118
+ for (const record of records) {
119
+ for (const dependencyId of list(record.depends_on)) {
120
+ if (!byId.has(dependencyId)) addError(errors, `records.${record.confirmation_id}.depends_on`, `unknown dependency ${dependencyId}`);
121
+ if (dependencyId === record.confirmation_id) addError(errors, `records.${record.confirmation_id}.depends_on`, "cannot depend on itself");
122
+ }
123
+ if (record.supersedes && !byId.has(record.supersedes)) addError(errors, `records.${record.confirmation_id}.supersedes`, `unknown record ${record.supersedes}`);
124
+ if (record.superseded_by && !byId.has(record.superseded_by)) addError(errors, `records.${record.confirmation_id}.superseded_by`, `unknown record ${record.superseded_by}`);
125
+ }
126
+ detectCycles(records, byId, errors);
127
+ const frontier = records.filter((record) => record.status === "proposed"
128
+ && list(record.depends_on).every((dependencyId) => recordIsClosed(byId.get(dependencyId), byId)));
129
+ const blockedProposals = records.filter((record) => record.status === "proposed" && !frontier.includes(record));
130
+ if (summary.shared_understanding === true && frontier.length) addError(errors, "shared_understanding", "cannot be true while frontier is non-empty");
131
+ if (summary.shared_understanding === true && blockedProposals.length) addError(errors, "shared_understanding", "cannot be true while a proposed decision is blocked");
132
+ if (summary.shared_understanding === true) {
133
+ if (!text(summary.shared_understanding_actor)) addError(errors, "shared_understanding_actor", "is required after user confirmation");
134
+ if (!text(summary.shared_understanding_confirmed_at)) addError(errors, "shared_understanding_confirmed_at", "is required after user confirmation");
135
+ }
136
+ return {
137
+ valid: errors.length === 0,
138
+ errors,
139
+ warnings,
140
+ frontier: frontier.map((record) => record.confirmation_id),
141
+ blocked_proposals: blockedProposals.map((record) => record.confirmation_id),
142
+ };
143
+ }
144
+
145
+ export function readConfirmationSummary(file, options = {}) {
146
+ const absolute = path.resolve(file);
147
+ const bytes = fs.readFileSync(absolute);
148
+ const summary = parseYaml(bytes.toString("utf8"));
149
+ return {
150
+ file: absolute,
151
+ checksum: crypto.createHash("sha256").update(bytes).digest("hex"),
152
+ summary,
153
+ validation: validateConfirmationSummary(summary, options),
154
+ };
155
+ }
156
+
157
+ export function writeConfirmationSummary(file, summary) {
158
+ const validation = validateConfirmationSummary(summary);
159
+ if (!validation.valid) throw new Error(`Invalid confirmation summary:\n${validation.errors.join("\n")}`);
160
+ const absolute = path.resolve(file);
161
+ if (fs.existsSync(absolute)) {
162
+ const existing = readConfirmationSummary(absolute, { allowUnlinkedConfirmed: false });
163
+ if (existing.summary.summary_id !== summary.summary_id) throw new Error("summary_id cannot change for an existing confirmation summary");
164
+ if (summary.version < existing.summary.version) throw new Error("confirmation summary version cannot move backwards");
165
+ const incoming = new Map(list(summary.records).map((record) => [record.confirmation_id, record]));
166
+ for (const record of list(existing.summary.records)) {
167
+ if (CLOSED_STATUSES.has(record.status) && incoming.has(record.confirmation_id)
168
+ && !sameRecord(record, incoming.get(record.confirmation_id))) {
169
+ throw new Error(`closed confirmation ${record.confirmation_id} is immutable; append a superseding record`);
170
+ }
171
+ }
172
+ if (summary.version === existing.summary.version && sameRecord(existing.summary, summary)) return validation;
173
+ }
174
+ fs.mkdirSync(path.dirname(absolute), { recursive: true });
175
+ fs.writeFileSync(absolute, stringifyYaml(summary), "utf8");
176
+ return validation;
177
+ }