add-coder 0.3.25 → 0.3.27

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 (132) hide show
  1. package/README.md +36 -10
  2. package/dist/index.js +481 -209
  3. package/package.json +14 -4
  4. package/templates/.add-coder-src-hash.json +129 -106
  5. package/templates/adapters/claude/hooks/doc-format-guard.sh +114 -33
  6. package/templates/adapters/claude/hooks/lib/common.sh +46 -87
  7. package/templates/adapters/claude/hooks/lib/context-inject.sh +2 -2
  8. package/templates/adapters/claude/hooks/lib/notify.sh +6 -4
  9. package/templates/adapters/claude/hooks/lib/preload-templates.sh +27 -2
  10. package/templates/adapters/claude/hooks/lib/state-detect.sh +8 -112
  11. package/templates/adapters/claude/hooks/lib/vocabulary.sh +13 -7
  12. package/templates/adapters/claude/hooks/prompt-submit.sh +1 -1
  13. package/templates/adapters/claude/hooks/stop-check.sh +9 -0
  14. package/templates/adapters/codex/config.toml.example +12 -0
  15. package/templates/adapters/codex/hooks/doc-format-guard.sh +123 -41
  16. package/templates/adapters/codex/hooks/lib/common.sh +47 -99
  17. package/templates/adapters/codex/hooks/lib/context-inject.sh +4 -4
  18. package/templates/adapters/codex/hooks/lib/notify.sh +6 -4
  19. package/templates/adapters/codex/hooks/lib/preload-templates.sh +27 -6
  20. package/templates/adapters/codex/hooks/lib/state-detect.sh +5 -114
  21. package/templates/adapters/codex/hooks/lib/vocabulary.sh +3 -8
  22. package/templates/adapters/codex/hooks/notification.sh +3 -18
  23. package/templates/adapters/codex/hooks/post-tool-use.sh +24 -119
  24. package/templates/adapters/codex/hooks/pre-tool-use.sh +97 -184
  25. package/templates/adapters/codex/hooks/prompt-submit.sh +8 -3
  26. package/templates/adapters/codex/hooks/session-start.sh +19 -9
  27. package/templates/adapters/codex/hooks/stop-check.sh +44 -18
  28. package/templates/adapters/codex/hooks.json +67 -1
  29. package/templates/adapters/qoder/hooks/doc-format-guard.sh +121 -40
  30. package/templates/adapters/qoder/hooks/lib/common.sh +49 -87
  31. package/templates/adapters/qoder/hooks/lib/context-inject.sh +2 -2
  32. package/templates/adapters/qoder/hooks/lib/notify.sh +6 -4
  33. package/templates/adapters/qoder/hooks/lib/preload-templates.sh +27 -2
  34. package/templates/adapters/qoder/hooks/lib/state-detect.sh +8 -112
  35. package/templates/adapters/qoder/hooks/lib/vocabulary.sh +13 -7
  36. package/templates/adapters/qoder/hooks/prompt-submit.sh +1 -1
  37. package/templates/adapters/qoder/hooks/session-start.sh +1 -1
  38. package/templates/adapters/qoder/hooks/stop-check.sh +11 -0
  39. package/templates/adapters/trae/hooks/doc-format-guard.sh +122 -40
  40. package/templates/adapters/trae/hooks/lib/common.sh +46 -87
  41. package/templates/adapters/trae/hooks/lib/context-inject.sh +2 -2
  42. package/templates/adapters/trae/hooks/lib/notify.sh +5 -3
  43. package/templates/adapters/trae/hooks/lib/preload-templates.sh +27 -2
  44. package/templates/adapters/trae/hooks/lib/state-detect.sh +8 -115
  45. package/templates/adapters/trae/hooks/lib/vocabulary.sh +13 -7
  46. package/templates/adapters/trae/hooks/pre-tool-use.sh +14 -8
  47. package/templates/adapters/trae/hooks/stop-check.sh +9 -0
  48. package/templates/adapters/vscode/hooks/doc-format-guard.sh +114 -33
  49. package/templates/adapters/vscode/hooks/lib/common.sh +46 -87
  50. package/templates/adapters/vscode/hooks/lib/context-inject.sh +2 -2
  51. package/templates/adapters/vscode/hooks/lib/notify.sh +6 -4
  52. package/templates/adapters/vscode/hooks/lib/preload-templates.sh +27 -2
  53. package/templates/adapters/vscode/hooks/lib/state-detect.sh +8 -112
  54. package/templates/adapters/vscode/hooks/lib/vocabulary.sh +13 -7
  55. package/templates/adapters/vscode/hooks/notification.sh +3 -5
  56. package/templates/adapters/vscode/hooks/prompt-submit.sh +1 -1
  57. package/templates/adapters/vscode/hooks/stop-check.sh +9 -0
  58. package/templates/core/docs/ADD-governance-claude-code.md +1 -1
  59. package/templates/core/docs/ADD-governance-codex.md +1 -1
  60. package/templates/core/docs/ADD-governance-qoder-cn.md +1 -1
  61. package/templates/core/docs/ADD-governance-trae.md +1 -1
  62. package/templates/core/docs/ADD-governance-vscode-copilot.md +1 -1
  63. package/templates/core/hooks/doc-format-guard.sh +122 -40
  64. package/templates/core/hooks/lib/common.sh +46 -87
  65. package/templates/core/hooks/lib/context-inject.sh +2 -2
  66. package/templates/core/hooks/lib/notify.sh +5 -3
  67. package/templates/core/hooks/lib/preload-templates.sh +27 -2
  68. package/templates/core/hooks/lib/state-detect.sh +8 -115
  69. package/templates/core/hooks/lib/vocabulary.sh +13 -7
  70. package/templates/core/hooks/pre-tool-use.sh +14 -8
  71. package/templates/core/hooks/stop-check.sh +9 -0
  72. package/templates/core/prisma/add.prisma +140 -82
  73. package/templates/core/rules/project_rules.md +27 -24
  74. package/templates/core/scripts/db-ensure.sh +8 -0
  75. package/templates/core/scripts/gen-plan-index.sh +4 -15
  76. package/templates/core/scripts/mcp-server/notifications/hitl.ts +9 -1
  77. package/templates/core/scripts/mcp-server/notifications/hook.ts +25 -9
  78. package/templates/core/scripts/mcp-server/resources/hitl-approval-widget.ts +34 -0
  79. package/templates/core/scripts/mcp-server/resources/hook-events-report.ts +14 -2
  80. package/templates/core/scripts/mcp-server/resources/index.ts +2 -0
  81. package/templates/core/scripts/mcp-server/sampling/review.ts +25 -10
  82. package/templates/core/scripts/mcp-server/shared/db-types.ts +33 -8
  83. package/templates/core/scripts/mcp-server/shared/dps-scoring.strategy.ts +1 -1
  84. package/templates/core/scripts/mcp-server/shared/env.ts +28 -8
  85. package/templates/core/scripts/mcp-server/shared/fs.ts +3 -0
  86. package/templates/core/scripts/mcp-server/shared/hitl-interaction.strategy.ts +2 -3
  87. package/templates/core/scripts/mcp-server/shared/hitl-lifecycle-mutation.ts +129 -0
  88. package/templates/core/scripts/mcp-server/shared/hitl-proposal-mutation.ts +98 -0
  89. package/templates/core/scripts/mcp-server/shared/hitl-ui.ts +3 -0
  90. package/templates/core/scripts/mcp-server/shared/plan-lifecycle-events.ts +62 -0
  91. package/templates/core/scripts/mcp-server/shared/plan-lifecycle-mutation.ts +78 -0
  92. package/templates/core/scripts/mcp-server/shared/plan-lifecycle-subscriber.ts +145 -0
  93. package/templates/core/scripts/mcp-server/shared/plan-lifecycle.ts +153 -0
  94. package/templates/core/scripts/mcp-server/shared/plan-round-events.ts +56 -0
  95. package/templates/core/scripts/mcp-server/shared/plan-round-mutation.ts +143 -0
  96. package/templates/core/scripts/mcp-server/shared/plan-round-store.ts +47 -0
  97. package/templates/core/scripts/mcp-server/shared/plan-round-subscriber.ts +99 -0
  98. package/templates/core/scripts/mcp-server/shared/plan-status-store.ts +37 -0
  99. package/templates/core/scripts/mcp-server/shared/plan-tracking-mutation.ts +71 -0
  100. package/templates/core/scripts/mcp-server/shared/prisma.ts +12 -6
  101. package/templates/core/scripts/mcp-server/shared/project-root-strategy.ts +1 -2
  102. package/templates/core/scripts/mcp-server/shared/quant-recheck.strategy.ts +16 -0
  103. package/templates/core/scripts/mcp-server/shared/redact.ts +28 -0
  104. package/templates/core/scripts/mcp-server/shared/response.ts +3 -2
  105. package/templates/core/scripts/mcp-server/shared/runtime-context.ts +77 -0
  106. package/templates/core/scripts/mcp-server/shared/schema-topology.ts +159 -0
  107. package/templates/core/scripts/mcp-server/tools/audit.ts +49 -7
  108. package/templates/core/scripts/mcp-server/tools/context.ts +117 -121
  109. package/templates/core/scripts/mcp-server/tools/contract.ts +34 -9
  110. package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_status.ts +50 -3
  111. package/templates/core/scripts/mcp-server/tools/gateway/check_doc_similarity.ts +218 -0
  112. package/templates/core/scripts/mcp-server/tools/gateway/check_dps.ts +22 -7
  113. package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +75 -4
  114. package/templates/core/scripts/mcp-server/tools/gateway/check_spec_sync.ts +19 -6
  115. package/templates/core/scripts/mcp-server/tools/gateway/index.ts +2 -0
  116. package/templates/core/scripts/mcp-server/tools/hitl.ts +160 -75
  117. package/templates/core/scripts/mcp-server/tools/hook-event-report.ts +9 -2
  118. package/templates/core/scripts/mcp-server/tools/index.ts +69 -3
  119. package/templates/core/scripts/mcp-server/tools/plan.ts +140 -15
  120. package/templates/core/scripts/mcp-server/tools/review.ts +44 -15
  121. package/templates/core/scripts/mcp-server.ts +60 -1
  122. package/templates/core/scripts/plan-status-bridge.ts +26 -0
  123. package/templates/core/skills/add-paradigm/SKILL.md +2 -2
  124. package/templates/core/templates/add-route-template.schema.json +24 -56
  125. package/templates/core/templates/collab-contract-template.md +1 -1
  126. package/templates/core/templates/handoff-multi-round-template.schema.json +48 -0
  127. package/templates/core/templates/handoff-single-round-template.schema.json +92 -0
  128. package/templates/core/templates/handoff-single-round.schema.json +1 -1
  129. package/templates/core/templates/hitl-approval-widget.html +222 -37
  130. package/templates/core/templates/spec-template.schema.json +9 -46
  131. package/templates/core/templates/standard-plan-template.schema.json +1 -1
  132. package/templates/core/templates/tasks-template.schema.json +13 -54
@@ -1,15 +1,21 @@
1
1
  #!/bin/bash
2
2
  # vocabulary.sh — 从 vocabulary markdown 表格加载触发词
3
- # 单一数据源: .qoder/vocabulary/add-governance-vocabulary.md §类别 A-F 表格
3
+ # 单一数据源: {MAGIC_DIR}/vocabulary/add-governance-vocabulary.md §类别 A-F 表格
4
+ # 协议层契约(Review P1 #5): 仅当前 magicDir scope;入口注入 MAGIC_DIR 或物理位置推导;
5
+ # 禁止跨端扫描与 .add fallback,禁止 adapter 名称默认值。
4
6
 
5
- # 动态探测 MAGIC_DIR
7
+ # MAGIC_DIR 解析: 入口注入优先;未注入时从脚本物理位置(<magicDir>/hooks/lib/)反推
6
8
  if [ -z "${MAGIC_DIR:-}" ]; then
7
- for m in ".claude" ".qoder" ".vscode" ".add"; do
8
- [ -d "${PROJECT_DIR:-$PWD}/$m" ] && { MAGIC_DIR="$m"; break; }
9
- done
10
- MAGIC_DIR="${MAGIC_DIR:-.add}"
9
+ if [ -n "${HOOK_DIR:-}" ]; then
10
+ MAGIC_DIR="$(basename "$(dirname "$HOOK_DIR")")"
11
+ else
12
+ # 无注入且无物理位置 → 无法判定 → 返回空(调用方 fail-closed,禁止猜测 adapter)
13
+ VOCABULARY_FILE=""
14
+ fi
15
+ fi
16
+ if [ -n "${MAGIC_DIR:-}" ]; then
17
+ VOCABULARY_FILE="${PROJECT_DIR:-$PWD}/$MAGIC_DIR/vocabulary/add-governance-vocabulary.md"
11
18
  fi
12
- VOCABULARY_FILE="${PROJECT_DIR:-$PWD}/$MAGIC_DIR/vocabulary/add-governance-vocabulary.md"
13
19
 
14
20
  # 输出格式: 优先级::触发词正则::响应文本(:: 避免与触发词内的 | 冲突)
15
21
  load_triggers() {
@@ -62,8 +62,9 @@ EOF
62
62
  exit 2
63
63
  fi
64
64
 
65
- # 检测 2: sed -i 原地编辑
66
- if echo "$command" | grep -qE '\bsed\b.*-i'; then
65
+ # 检测 2: sed -i 原地编辑(精确判定:`-i` 必须是 sed 独立 option;不把路径子串如 session-init 误判)
66
+ # 协议层契约(Review P0 #3/命令分类): codex 已实现版本同契约语义
67
+ if echo "$command" | grep -qE '(^|[;&|][[:space:]]*)sed[[:space:]]+([^;&|]*[[:space:]])?(-[[:alpha:]]*i[^[:space:];&|]*|--in-place(=[^[:space:];&|]*)?)([[:space:];&|]|$)'; then
67
68
  _reason="禁止通过 sed -i 直接编辑文件。请使用 SearchReplace 工具。"
68
69
  cat >&2 <<'EOF'
69
70
  ⛔ [ADD PreToolUse §A] 阻断: 禁止通过 sed -i 原地编辑文件。
@@ -136,7 +137,8 @@ if [ "$tool_name" = "Write" ] || [ "$tool_name" = "Edit" ] || [ "$tool_name" = "
136
137
  file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
137
138
  [ -z "$file_path" ] && exit 0
138
139
 
139
- if echo "$file_path" | grep -qE '\.(qoder|claude|add)/(plans|specs|reviews)/'; then
140
+ # §B: 写入守卫按当前 magicDir 限域(Review P1 #5: 禁止多端路径正则)
141
+ if echo "$file_path" | grep -qE "${MAGIC_DIR}/(plans|specs|reviews)/"; then
140
142
  if type detect_active_add >/dev/null 2>&1; then
141
143
  state=$(detect_active_add 2>/dev/null || true)
142
144
  if [ -z "$state" ]; then
@@ -154,8 +156,8 @@ if [ "$tool_name" = "Write" ] || [ "$tool_name" = "Edit" ] || [ "$tool_name" = "
154
156
  exit 2
155
157
  fi
156
158
 
157
- # §C: 模板类型前置注入 — plans/ 写入前提示应选模板
158
- if echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(plans)/'; then
159
+ # §C: 模板类型前置注入 — 仅当前 magicDir plans/ 写入前提示应选模板
160
+ if echo "$file_path" | grep -qE "${MAGIC_DIR}/(plans)/"; then
159
161
  fname=$(basename "$file_path")
160
162
  if echo "$fname" | grep -qE 'plan-v[0-9]'; then
161
163
  echo "💡 [ADD PreToolUse] 写入 Plan → 模板: standard-plan-template.md(标准)或 simple-plan-template.md(≤3文件)" >&2
@@ -177,13 +179,13 @@ if [ "$tool_name" = "Write" ] || [ "$tool_name" = "Edit" ] || [ "$tool_name" = "
177
179
  # implementation/runtime review 不需要 HITL,走 §B 活跃 Plan 检查
178
180
  # handoff 是 Step 8 收敛产物(多轮交接手册),不经 HITL 审批,同样豁免
179
181
  # TODO 应该caijuehub管理
180
- if echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(plans)/'; then
182
+ if echo "$file_path" | grep -qE "${MAGIC_DIR}/(plans)/"; then
181
183
  if echo "$file_path" | grep -qE '\-handoff'; then
182
184
  _do_hitl=false # handoff 不被 HITL 拦截(Step 8 产物,无独立审批)
183
185
  else
184
186
  _do_hitl=true
185
187
  fi
186
- elif echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(reviews)/'; then
188
+ elif echo "$file_path" | grep -qE "${MAGIC_DIR}/(reviews)/"; then
187
189
  if echo "$file_path" | grep -qE '-(implementation|runtime)'; then
188
190
  _do_hitl=false # implementation/runtime review 不被 HITL 拦截
189
191
  else
@@ -194,7 +196,11 @@ if [ "$tool_name" = "Write" ] || [ "$tool_name" = "Edit" ] || [ "$tool_name" = "
194
196
  fi
195
197
 
196
198
  if [ "$_do_hitl" = true ]; then
197
- _relative=$(echo "$file_path" | sed 's|.*/\.\(qoder\|claude\|add\|vscode\|trae\)/\(plans\|reviews\)/||')
199
+ _relative=$(echo "$file_path" | sed "s|.*/\.\(qoder\|claude\|add\|vscode\|trae\)/\(plans\|reviews\)/||")
200
+ # 限域: 仅当前 magicDir 下的 plans/reviews 触发 HITL 门禁
201
+ if ! echo "$file_path" | grep -qE "${MAGIC_DIR}/(plans|reviews)/"; then
202
+ _do_hitl=false
203
+ fi
198
204
  _planName=$(basename "$_relative" .md | sed 's/\.hitl$//;s/-plan-v[0-9]*$//;s/-add-route-v[0-9]*$//;s/-review-v[0-9]*$//;s/-review-implementation$//;s/-review-runtime$//')
199
205
  if [ -n "$_planName" ]; then
200
206
  _tongyi_marker="${PROJECT_DIR}/${MAGIC_DIR}/hitl/.tongyi-${_planName}"
@@ -1,10 +1,12 @@
1
1
  #!/bin/bash
2
2
  # stop-check.sh — Claude Code Stop:四象限分流 + 验收检查(few-shot 上下文注入)
3
3
  # 治理卡位 #7: 验收检查 + devlog + 阻断
4
+ # DB lifecycle 真相源(协议层):DB 不可用时 fail-closed,禁止回退 Handoff/add-route 猜测
4
5
  set -euo pipefail
5
6
 
6
7
  HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
7
8
  export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
9
+ export MAGIC_DIR=$(basename "$(dirname "$HOOK_DIR")")
8
10
  COMMON_LIB="$HOOK_DIR/lib/common.sh"
9
11
  [ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
10
12
 
@@ -13,6 +15,13 @@ export PROJECT_DIR="$PWD"
13
15
  state=$(detect_active_add 2>/dev/null || true)
14
16
  has_dev=$(has_dev_action 2>/dev/null && echo "true" || echo "false")
15
17
 
18
+ # ═══════ Q0: DB 不可用 → fail closed(禁止当“无 Plan”) ═══════
19
+ if [ "${state%%::*}" = "__STATUS_UNAVAILABLE__" ]; then
20
+ reason=$(echo "$state" | awk -F'::' '{print $2}')
21
+ echo "[ADD Stop] ⛔ Plan status 暂不可用(${reason})。未回退 Handoff/add-route 猜测,请恢复数据库或 MCP resolver 后重试。" >&2
22
+ exit "${EXIT_BLOCK:-2}"
23
+ fi
24
+
16
25
  # ═══════ Q1: 无 ADD + 无 dev → 正常停 ═══════
17
26
  if [ -z "$state" ] && [ "$has_dev" != "true" ]; then
18
27
  exit 0
@@ -1,43 +1,54 @@
1
1
  // add.prisma — ADD 治理模型,独立于用户业务表
2
2
 
3
3
  model AddUser {
4
- id String @id @default(cuid())
5
- username String @unique
6
- email String?
7
- devOperations DevOperation[]
8
- auditLogs AuditLog[]
4
+ id String @id @default(cuid())
5
+ username String @unique
6
+ email String?
7
+ devOperations DevOperation[]
8
+ auditLogs AuditLog[]
9
9
  }
10
10
 
11
11
  model DevOperation {
12
- id String @id @default(cuid())
13
- userId String
14
- user AddUser @relation(fields: [userId], references: [id], onDelete: Cascade)
15
- planKeyword String
16
- action String
17
- targetType String
18
- targetId String
19
- beforeState Json?
20
- afterState Json?
21
- reason String?
22
- createdAt DateTime @default(now())
12
+ id String @id @default(cuid())
13
+ userId String
14
+ user AddUser @relation(fields: [userId], references: [id], onDelete: Cascade)
15
+ projectKey String @default("legacy-unknown")
16
+ producerAdapterKey String @default("legacy-unknown")
17
+ contextId String @default("legacy-unknown")
18
+ toolName String @default("record_dev_operation")
19
+ operationKey String @default(cuid())
20
+ planKeyword String
21
+ action String
22
+ targetType String
23
+ targetId String
24
+ beforeState Json?
25
+ afterState Json?
26
+ reason String?
27
+ createdAt DateTime @default(now())
23
28
 
29
+ @@unique([projectKey, producerAdapterKey, toolName, operationKey])
24
30
  @@index([planKeyword])
31
+ @@index([projectKey, producerAdapterKey, planKeyword])
25
32
  }
26
33
 
27
34
  model AuditLog {
28
- id String @id @default(cuid())
29
- userId String
30
- user AddUser @relation(fields: [userId], references: [id], onDelete: Cascade)
31
- action String
32
- targetType String
33
- targetId String
34
- traceId String?
35
- beforeState Json?
36
- afterState Json?
37
- reason String?
38
- createdAt DateTime @default(now())
35
+ id String @id @default(cuid())
36
+ userId String
37
+ user AddUser @relation(fields: [userId], references: [id], onDelete: Cascade)
38
+ projectKey String @default("legacy-unknown")
39
+ producerAdapterKey String @default("legacy-unknown")
40
+ contextId String @default("legacy-unknown")
41
+ action String
42
+ targetType String
43
+ targetId String
44
+ traceId String?
45
+ beforeState Json?
46
+ afterState Json?
47
+ reason String?
48
+ createdAt DateTime @default(now())
39
49
 
40
50
  @@index([traceId])
51
+ @@index([projectKey, producerAdapterKey, contextId])
41
52
  }
42
53
 
43
54
  // ===== HITL 人机审核架构 =====
@@ -61,65 +72,85 @@ enum ReviewType {
61
72
  RUNTIME
62
73
  }
63
74
 
75
+ enum PlanLifecycleStatus {
76
+ DRAFT
77
+ ACTIVE
78
+ BLOCKED
79
+ REJECTED
80
+ CLOSED
81
+ ABANDONED
82
+ }
83
+
64
84
  model HitlRecord {
65
- id String @id @default(cuid())
85
+ id String @id @default(cuid())
86
+ projectKey String @default("legacy-unknown")
87
+ adapterKey String @default("legacy-unknown")
66
88
  planName String
67
- plan PlanRecord @relation(fields: [planName], references: [planName])
68
- round Int @default(1)
69
- type HitlType @default(PLAN)
70
- status HitlStatus @default(DRAFT)
89
+ plan PlanRecord @relation(fields: [projectKey, adapterKey, planName], references: [projectKey, adapterKey, planName], onDelete: Cascade)
90
+ round Int @default(1)
91
+ type HitlType @default(PLAN)
92
+ status HitlStatus @default(DRAFT)
71
93
  proposalPath String?
72
94
  approvedAt DateTime?
73
95
  rejectedAt DateTime?
74
96
  rejectReason String?
75
- createdBy String @default("ai-assistant")
76
- createdAt DateTime @default(now())
77
- updatedAt DateTime @updatedAt
97
+ createdBy String @default("ai-assistant")
98
+ createdAt DateTime @default(now())
99
+ updatedAt DateTime @updatedAt
78
100
 
79
- @@unique([planName, round])
80
- @@index([planName])
101
+ @@unique([projectKey, adapterKey, planName, type, round])
102
+ @@index([projectKey, adapterKey, planName])
81
103
  @@index([status])
82
104
  }
83
105
 
84
106
  model PlanRecord {
85
- id String @id @default(cuid())
86
- planName String @unique
87
- planPath String
88
- specPath String?
89
- tasksPath String?
90
- checklistPath String?
91
- addRoutePath String?
92
- totalTasks Int @default(0)
93
- doneTasks Int @default(0)
94
- checklistT Int @default(0)
95
- checklistTDone Int @default(0)
96
- checklistR Int @default(0)
97
- planKeyword String?
107
+ id String @id @default(cuid())
108
+ projectKey String @default("legacy-unknown")
109
+ adapterKey String @default("legacy-unknown")
110
+ planName String
111
+ lifecycle PlanLifecycleStatus @default(DRAFT)
112
+ revision Int @default(0)
113
+ planPath String
114
+ specPath String?
115
+ tasksPath String?
116
+ checklistPath String?
117
+ addRoutePath String?
118
+ totalTasks Int @default(0)
119
+ doneTasks Int @default(0)
120
+ checklistT Int @default(0)
121
+ checklistTDone Int @default(0)
122
+ checklistR Int @default(0)
123
+ planKeyword String?
98
124
  // 并发协作契约:该 Plan 在契约中的角色(MASTER=总控 / SUB=子 Plan)
99
- contractRole ContractRole?
125
+ contractRole ContractRole?
100
126
  // 所属契约名(与 CollabContract.contractName 对应,反查用)
101
- contractName String?
127
+ contractName String?
102
128
  // 作为总控 Plan 持有的契约(masterPlanName 外键方向)
103
- masterContract CollabContract?
129
+ masterContract CollabContract? @relation("MasterPlan")
130
+ contractBindings CollaborationPlanBinding[]
104
131
  // DPS 四维评分历史(check_dps 调用后回写,供 FFT 自适应权重消费)
105
- dpsSemScore Int? // 语义相关性 0-100
106
- dpsEntropyScore Int? // 信息熵匹配 0-100
107
- dpsCpmScore Int? // CPM 关键路径 0-100
108
- dpsStructScore Int? // 结构完整度 0-100
109
- dpsComposite Int? // 加权 DPS 总分 0-100
110
- hitls HitlRecord[]
111
- reviews ReviewRecord[]
112
- createdAt DateTime @default(now())
113
- updatedAt DateTime @updatedAt
114
-
132
+ dpsSemScore Int? // 语义相关性 0-100
133
+ dpsEntropyScore Int? // 信息熵匹配 0-100
134
+ dpsCpmScore Int? // CPM 关键路径 0-100
135
+ dpsStructScore Int? // 结构完整度 0-100
136
+ dpsComposite Int? // 加权 DPS 总分 0-100
137
+ hitls HitlRecord[]
138
+ reviews ReviewRecord[]
139
+ createdAt DateTime @default(now())
140
+ updatedAt DateTime @updatedAt
141
+
142
+ @@unique([projectKey, adapterKey, planName])
115
143
  @@index([planKeyword])
116
144
  @@index([contractName])
145
+ @@index([projectKey, adapterKey, lifecycle])
117
146
  }
118
147
 
119
148
  model ReviewRecord {
120
149
  id String @id @default(cuid())
150
+ projectKey String @default("legacy-unknown")
151
+ adapterKey String @default("legacy-unknown")
121
152
  planName String
122
- plan PlanRecord @relation(fields: [planName], references: [planName], onDelete: Cascade)
153
+ plan PlanRecord @relation(fields: [projectKey, adapterKey, planName], references: [projectKey, adapterKey, planName], onDelete: Cascade)
123
154
  type ReviewType @default(PLAN_REVIEW)
124
155
  reviewPath String?
125
156
  p0Count Int @default(0)
@@ -128,33 +159,60 @@ model ReviewRecord {
128
159
  createdAt DateTime @default(now())
129
160
  updatedAt DateTime @updatedAt
130
161
 
131
- @@index([planName])
162
+ @@index([projectKey, adapterKey, planName])
132
163
  @@index([type])
133
164
  }
134
165
 
135
166
  enum ContractRole {
136
- MASTER // 总控 Plan(契约持有者)
137
- SUB // 子 Plan(契约实施单元)
167
+ MASTER // 总控 Plan(契约持有者)
168
+ SUB // 子 Plan(契约实施单元)
169
+ }
170
+
171
+ enum CollaborationBindingRole {
172
+ MASTER
173
+ MEMBER
138
174
  }
139
175
 
140
176
  // 并发协作契约(多智能体并行实施规则集,对应 collab-contract 文档)
141
177
  model CollabContract {
142
- id String @id @default(cuid())
143
- contractName String @unique
178
+ id String @id @default(cuid())
179
+ projectKey String @default("legacy-unknown")
180
+ ownerAdapterKey String @default("legacy-unknown")
181
+ contractName String
144
182
  contractPath String
145
183
  // 外键:总控 Plan(契约持有者,ContractRole=MASTER,一对一)
146
- masterPlanName String @unique
147
- masterPlan PlanRecord @relation(fields: [masterPlanName], references: [planName], onDelete: Restrict)
148
- participants Json // [{role, platformEntity, boundPlan, planKeyword, description}]
149
- abilityMatrix Json? // {能力: {专家: boolean}}
150
- stages Json // [{stage, expert, trigger, parallelism}]
151
- dependencyGraph String? // ASCII + 机器可读声明
152
- fileBoundaries Json // [{expert, exclusiveDomain, forbidden, isolationMode}] isolationMode: file/worktree
153
- completionCriteria Json? // {专家: 完成标志}
154
- status String @default("ACTIVE") // ACTIVE / SUPERSEDED
155
- version Int @default(1)
156
- createdAt DateTime @default(now())
157
- updatedAt DateTime @updatedAt
184
+ masterProjectKey String @default("legacy-unknown")
185
+ masterAdapterKey String @default("legacy-unknown")
186
+ masterPlanName String
187
+ masterPlan PlanRecord @relation("MasterPlan", fields: [masterProjectKey, masterAdapterKey, masterPlanName], references: [projectKey, adapterKey, planName], onDelete: Restrict)
188
+ bindings CollaborationPlanBinding[]
189
+ participants Json // [{role, platformEntity, boundPlan, planKeyword, description}]
190
+ abilityMatrix Json? // {能力: {专家: boolean}}
191
+ stages Json // [{stage, expert, trigger, parallelism}]
192
+ dependencyGraph String? // ASCII + 机器可读声明
193
+ fileBoundaries Json // [{expert, exclusiveDomain, forbidden, isolationMode}] isolationMode: file/worktree
194
+ completionCriteria Json? // {专家: 完成标志}
195
+ status String @default("ACTIVE") // ACTIVE / SUPERSEDED
196
+ version Int @default(1)
197
+ createdAt DateTime @default(now())
198
+ updatedAt DateTime @updatedAt
199
+
200
+ @@unique([projectKey, contractName])
201
+ @@unique([masterProjectKey, masterAdapterKey, masterPlanName])
202
+ @@index([projectKey, ownerAdapterKey])
203
+ }
158
204
 
159
- @@index([masterPlanName])
205
+ model CollaborationPlanBinding {
206
+ id String @id @default(cuid())
207
+ contractId String
208
+ contract CollabContract @relation(fields: [contractId], references: [id], onDelete: Cascade)
209
+ projectKey String
210
+ adapterKey String
211
+ planName String
212
+ plan PlanRecord @relation(fields: [projectKey, adapterKey, planName], references: [projectKey, adapterKey, planName], onDelete: Cascade)
213
+ role CollaborationBindingRole @default(MEMBER)
214
+ createdAt DateTime @default(now())
215
+
216
+ @@unique([contractId, projectKey, adapterKey, planName])
217
+ @@index([projectKey, adapterKey, planName])
160
218
  }
@@ -26,8 +26,8 @@
26
26
  | P2 | ADD-13 DPS 上游文档质量闸门 | Plan 概括度 → Review 注意力稀释 → Specs 遗漏 → 实现偏差。`check_dps`(DPS ≥ {{dpsPass}})在 Step 0 末尾量化阻断 | ✅ 已实现 | 2026-06-11 引入 |
27
27
  | P2 | ADD-14 RAHS 下游执行健康度闸门 | 范围保真度 + 类型安全 + 审计完整度 + Spec 合规 + 阶段对称性。`check_rahs`(RAHS ≥ 90)在 Step 4/8 量化阻断 | ✅ 已实现 | 2026-06-11 引入 |
28
28
  | P2 | ADD-15 add-route 闭环自检 | Step 3 代码完成后必须调用 `check_add_route_completeness` 扫描 add-route Step 完成度,防止执行遗漏。返回 complete 方可进入 Step 3.5 | ✅ 已实现 | 2026-06-11 引入 |
29
- | P2 | ADD-17 HITL 磋商临时文件机制 | doc-format-guard 要求 Plan/Review 文件包含完整章节才放行,HITL 第一步只写总览表会被 guard 阻断。用 `{name}.temporary.md` 做磋商(不受 guard 检查),人类拍板后再写正式文件并删除 temporary.md | ✅ 已实现 | 2026-07-23 引入 |
30
- | P2 | ADD-18 HITL 人机审核强制规则 | Plan/Review 必须经过 HITL 审批(`create_hitl` → 人工 tongyi/bohui → `update_hitl` 确认)。无 `.hitl-tongyi-{planName}` 哨兵时 pre-tool-use hook 阻断写入正式文件。提案文件按 hitl-template.md 生成 + schema 校验 | ✅ 已实现 | 2026-07-27 引入 |
29
+ | P2 | ADD-17 HITL 提案载体与状态机 | `create_hitl` 接收 `dimensions`,创建 `HitlRecord(DRAFT)` `*.hitl.md` 提案;人工拍板后由 `update_hitl` 写入终态,正式文档仅在 `status_hitl=TONGYI` 后生成 | ✅ 已实现 | 2026-07-23 引入,2026-08-12 对齐 MCP 实现 |
30
+ | P2 | ADD-18 HITL 人机审核强制规则 | Plan、方案 Review、实现 Review 必须经过 `create_hitl` → 人工 tongyi/bohui → `update_hitl`;`TONGYI` 同步写 DB、提案状态和 `.tongyi-{planName}` 哨兵 | ✅ 已实现 | 2026-07-27 引入 |
31
31
 
32
32
 
33
33
  ### P0 规则:不可跳过
@@ -68,7 +68,7 @@ AI 助手必须先恢复到基线、找到对应的 SKILL 文件并按步骤执
68
68
  | ADD-14 | `add-paradigm` Step 4 末端 + Step 8 收敛 + MCP `check_rahs` | Step 4 合规检查 / Step 8 收敛判定 |
69
69
  | ADD-15 | `add-paradigm` Step 3.6 + MCP `check_add_route_completeness` | Step 3 代码完成后自检 |
70
70
  | ADD-16 | `add-paradigm` Step 9 | runtime-fix plan 收敛后,关闭 gateway.md 运行时发现 |
71
- | ADD-17 | `add-paradigm` 独立能力:生成 Plan / Step 0 | Plan/Review HITL 磋商时(写入 temporary.md 绕过 guard,拍板后写正式文件) |
71
+ | ADD-17 | `add-paradigm` 独立能力:生成 Plan / Step 0 / Step 3.5 | Plan/Review HITL:整理 dimensions create_hitl 生成提案 → update_hitl 拍板 → status_hitl 校验终态 |
72
72
  | ADD-18 | `add-paradigm` 独立能力:生成 Plan + Step 0 + pre-tool-use §C | HITL 审批强制:create_hitl → 人工 tongyi/bohui → update_hitl 确认;hook 哨兵检查 |
73
73
 
74
74
  ---
@@ -783,45 +783,47 @@ ADD-0.1 要求"文档先行"(先改文档再改代码)。ADD-16 是对 ADD-0
783
783
 
784
784
  ---
785
785
 
786
- ## ADD-17:HITL 磋商临时文件机制
786
+ ## ADD-17:HITL 提案载体与状态机
787
787
 
788
- **Plan Review HITL 磋商必须在写入正式文件之前完成,但 doc-format-guard 要求 `.qoder/plans/` `.qoder/reviews/` 下的文件包含完整章节才放行。HITL 第一步只写总览表、不写正文——会被 guard 阻断。**
788
+ **Plan、方案 Review 和实现 Review 在写入正式文件前,必须通过 `create_hitl` 建立结构化审批提案。`*.hitl.md` `HitlRecord` 是同一轮审批的文件/数据库双通道载体;正式文档不得替代审批提案。**
789
789
 
790
790
  ### 流程
791
791
 
792
792
  ```
793
- ① AI {name}.temporary.md(只含 HITL 总览表,放项目根目录,不受 guard 检查)
794
- 人类审阅 HITL 逐行拍板(同意/调整)
795
- AI 根据确认后的 HITL 表生成完整 Plan/Review 写入正式路径(guard 放行)
796
- 删除 temporary.md
793
+ ① AI 读取对应模板并将待拍板发现整理为 dimensions
794
+ create_hitl({ planName, type, dimensions }) HitlRecord(DRAFT) + plans/{YYYY-MM}/{DD}/{planName}.hitl.md
795
+ 人类审阅提案并逐项拍板 update_hitl({ planName, type, status })
796
+ status_hitl 确认 TONGYI → 生成完整 Plan/Review 并写入正式路径
797
+ ⑤ BOHUI → 修订 dimensions 后调用 create_hitl 创建 round+1
797
798
  ```
798
799
 
799
800
  ### 适用范围
800
801
 
801
- | 文档类型 | temporary 磋商 | 正式写入路径 |
802
+ | 文档类型 | HITL type | 正式写入路径 |
802
803
  |---------|:---:|------|
803
- | Plan(标准版/精简版) | 需要 | `.qoder/plans/{YYYY-MM}/{DD}/` |
804
- | Review(方案/实现) | 需要 | `.qoder/reviews/` |
805
- | Spec / Tasks / Checklist | 不需要 | 直接写入 `.qoder/specs/` |
806
- | Handoff | 不需要 | 直接写入 `.qoder/plans/` |
804
+ | Plan(标准版/精简版) | `PLAN` | `{{magicDir}}/plans/{YYYY-MM}/{DD}/` |
805
+ | Review(方案/实现) | `PLAN_REVIEW` | `{{magicDir}}/reviews/` |
806
+ | Runtime Review | 不需要 | `[T]` 全部通过后直接写入 `{{magicDir}}/reviews/` |
807
+ | Spec / Tasks / Checklist | 不需要 | 直接写入 `{{magicDir}}/specs/` |
808
+ | Handoff | 不需要 | 直接写入 `{{magicDir}}/plans/` |
807
809
 
808
810
  ### 与 ADD-0.1 的关系
809
811
 
810
- ADD-0.1 要求"文档先行"——HITL 磋商本身就是文档先行的一种形式(先对齐方向再产出文档)。temporary.md 机制确保 HITL 的"先拍板再展开"哲学不因 guard 的"完整章节校验"而被破坏。
812
+ ADD-0.1 要求“文档先行”。`create_hitl` 先持久化决策维度与审批状态,再展开正式文档,使“先拍板、后展开”具备可查询、可追溯的事实源。
811
813
 
812
814
  ---
813
815
 
814
816
  ## ADD-18:HITL 人机审核强制规则
815
817
 
816
- **Plan PLAN_REVIEW 类型 Review 在写入正式文件前,必须经过 HITL 审批。** 审批通过后生成 `.hitl-tongyi-{planName}` 哨兵文件,pre-tool-use hook 以此判断是否允许写入。
818
+ **Plan、方案 Review 和实现 Review 在写入正式文件前,必须经过 HITL 审批。** 审批通过后,`update_hitl` 更新数据库与 `*.hitl.md` 状态,并在 `{{magicDir}}/hitl/` 生成 `.tongyi-{planName}` 哨兵。pre-tool-use hook 对 Plan/方案 Review 执行哨兵门禁;实现 Review 由 `add-paradigm` Step 3.5 与 `status_hitl` 门禁约束。
817
819
 
818
820
  ### 强制流程
819
821
 
820
822
  ```
821
- ① create_hitl({ planName, type }) → HitlRecord DRAFT,生成 hitl.md 提案文件
822
- ② 人类审阅 hitl.md → 逐行拍板
823
- ③ update_hitl({ planName, status: "TONGYI" }) → .hitl-tongyi-{planName} 哨兵
824
- 或 update_hitl({ planName, status: "BOHUI", reason }) → 提案驳回
823
+ ① create_hitl({ planName, type, dimensions }) → HitlRecord DRAFT,生成 *.hitl.md 提案文件
824
+ ② 人类审阅 *.hitl.md → 逐行拍板
825
+ ③ update_hitl({ planName, type, status: "TONGYI" }) → 更新 DB/提案并写 .tongyi-{planName} 哨兵
826
+ 或 update_hitl({ planName, type, status: "BOHUI", reason }) → 更新 DB/提案并写 .bohui-{planName} 哨兵
825
827
  ④ TONGYI 后:正式写入 Plan/Review 文件(hook §C 放行)
826
828
  ⑤ BOHUI 后:create_hitl 新建 round+1,重新审批
827
829
  ```
@@ -830,15 +832,16 @@ ADD-0.1 要求"文档先行"——HITL 磋商本身就是文档先行的一种
830
832
 
831
833
  | 文档类型 | HITL 审批 | 依据 |
832
834
  |---------|:--------:|------|
833
- | Plan | ✅ 必须 | pre-tool-use §C 拦截 plans/ 目录 |
834
- | Review(PLAN_REVIEW) | ✅ 必须 | pre-tool-use §C 拦截 reviews/ 目录(不含 implementation/runtime) |
835
- | Review(implementation/runtime) | 不需要 | §B 活跃 Plan 检查 |
835
+ | Plan | ✅ 必须 | pre-tool-use §C 哨兵门禁 |
836
+ | 方案 Review | ✅ 必须 | pre-tool-use §C 哨兵门禁 |
837
+ | Implementation Review | 必须 | `add-paradigm` Step 3.5 + `status_hitl` |
838
+ | Runtime Review | ❌ 不需要 | `[T]` 全部通过后直接生成 |
836
839
  | Spec / Tasks / Checklist | ❌ 不需要 | 直接写 |
837
840
  | Handoff | ❌ 不需要 | 直接写 |
838
841
 
839
842
  ### 与 ADD-17 的关系
840
843
 
841
- ADD-17 定义 HITL 如何在 guard 约束下运作(temporary.md 绕过 guard 完成磋商)。ADD-18 定义 HITL 审批本身是强制的——不经过 create_hitl→update_hitl 流程,hook 阻止写入。两者互补:ADD-17 是"怎么做 HITL",ADD-18 是"必须做 HITL"。
844
+ ADD-17 定义审批提案的载体、维度和状态流转;ADD-18 定义哪些正式文档必须先取得 `TONGYI`。两者共同以 `create_hitl update_hitl status_hitl` 为事实链,文件、数据库和哨兵相互校验。
842
845
 
843
846
  ---
844
847
 
@@ -156,6 +156,14 @@ apply_atlas_diff() {
156
156
  # ⑦ Atlas 同步主流程(探测 → dev-url → 目标 → baseline → diff → apply)
157
157
  atlas_sync() {
158
158
  [ "$ENGINE" = "sqlite" ] && return 0
159
+ # 迁移锁(进程层契约 v2 §6):多 IDE 并发 init 时仅一个进程执行迁移
160
+ # 非阻塞拿锁(pg_try_advisory_lock),会话级锁——脚本退出连接断开自动释放
161
+ LOCK_KEY="0xADD001"
162
+ LOCKED="$(podman exec "${PROJECT_NAME:-add-project}-postgres" psql -U "${DATABASE_USER:-admin}" -d "${PROJECT_NAME:-add-project}" -tAc "SELECT pg_try_advisory_lock(${LOCK_KEY});" 2>/dev/null || true)"
163
+ if [ "$LOCKED" != "t" ]; then
164
+ echo "!!! 另一个进程正在迁移,请稍后重试"
165
+ return 1
166
+ fi
159
167
  if ! atlas_cmd version > /dev/null 2>&1; then
160
168
  echo "!!! Atlas 不可用。add-coder sync --patch 可自动安装 @ariga/atlas;或手动: pnpm add -D @ariga/atlas"
161
169
  echo " 降级路径: prisma-diff(免 shadow)→ db-push + 强制备份;文档: README「Atlas 数据库同步能力」"
@@ -1,24 +1,13 @@
1
1
  #!/bin/bash
2
2
  # 生成 {MAGIC_DIR}/plans/index.md 总览清单 — 按日期分层,每日自动更新
3
- # 自动探测 MAGIC_DIR(.qoder / .claude / .vscode / .trae / .codex / .add)
3
+ # MAGIC_DIR 由脚本自身的物理目录确定,各 adapter 自治。
4
4
  set -e
5
5
 
6
- # 探测 MAGIC_DIR:在当前目录向上查找包含 plans/ 的隐藏目录
7
6
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
8
- CURRENT="$SCRIPT_DIR"
9
- MAGIC_DIR=""
10
- while [ "$CURRENT" != "/" ]; do
11
- for d in "$CURRENT"/.qoder "$CURRENT"/.claude "$CURRENT"/.vscode "$CURRENT"/.trae "$CURRENT"/.codex "$CURRENT"/.add; do
12
- if [ -d "$d/plans" ]; then
13
- MAGIC_DIR="$d"
14
- break 2
15
- fi
16
- done
17
- CURRENT="$(dirname "$CURRENT")"
18
- done
7
+ MAGIC_DIR="$(dirname "$SCRIPT_DIR")"
19
8
 
20
- if [ -z "$MAGIC_DIR" ]; then
21
- echo "❌ 未找到 MAGIC_DIR(.qoder / .claude / .vscode / .trae / .codex / .add 均无 plans/ 子目录)" >&2
9
+ if [ ! -d "$MAGIC_DIR/plans" ]; then
10
+ echo "❌ 当前 adapter 不存在 plans/: $MAGIC_DIR" >&2
22
11
  exit 1
23
12
  fi
24
13
 
@@ -3,8 +3,10 @@ import { join } from "path"
3
3
  import { existsSync } from "fs"
4
4
  import { readFileSafe, readdirRecursive, PROJECT_ROOT, MAGIC_DIR } from "../shared/fs.js"
5
5
  import { prisma } from "../shared/prisma.js"
6
+ import { getRuntimeContext } from "../shared/env.js"
6
7
 
7
8
  export function registerHitlNotifications(server: McpServer) {
9
+ const runtimeContext = getRuntimeContext()
8
10
  // ── HITL 表扫描 ──
9
11
  const scanHitl = async () => {
10
12
  const plansDir = join(PROJECT_ROOT, MAGIC_DIR, "plans")
@@ -29,7 +31,13 @@ export function registerHitlNotifications(server: McpServer) {
29
31
  const ops = prisma.devOperation as Record<string, (...a: unknown[]) => unknown>
30
32
  const since = new Date(Date.now() - 24 * 60 * 60 * 1000)
31
33
  const count = await ops.count({
32
- where: { action: "HOOK_INTERCEPT", planKeyword: "no-active-plan", createdAt: { gte: since } },
34
+ where: {
35
+ action: "HOOK_INTERCEPT",
36
+ planKeyword: "no-active-plan",
37
+ projectKey: runtimeContext.projectKey,
38
+ producerAdapterKey: runtimeContext.adapterKey,
39
+ createdAt: { gte: since },
40
+ },
33
41
  }) as number
34
42
  if (count >= 10 && count !== lastWarnedCount) {
35
43
  lastWarnedCount = count