dev-playbooks-cn 1.0.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 (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +466 -0
  3. package/bin/devbooks.js +987 -0
  4. package/package.json +43 -0
  5. package/skills/Skills/344/275/277/347/224/250/350/257/264/346/230/216.md +446 -0
  6. package/skills/Skill/345/274/200/345/217/221/346/214/207/345/215/227.md +248 -0
  7. package/skills/_shared/context-detection-template.md +315 -0
  8. package/skills/_shared/mcp-enhancement-template.md +144 -0
  9. package/skills/_shared/references//351/200/232/347/224/250/345/256/210/351/227/250/345/215/217/350/256/256.md +114 -0
  10. package/skills/_template/config-discovery-template.md +126 -0
  11. package/skills/devbooks-brownfield-bootstrap/SKILL.md +167 -0
  12. package/skills/devbooks-brownfield-bootstrap/references//344/273/243/347/240/201/345/257/274/350/210/252/347/255/226/347/225/245.md +203 -0
  13. package/skills/devbooks-brownfield-bootstrap/references//345/255/230/351/207/217/351/241/271/347/233/256/345/210/235/345/247/213/345/214/226.md +96 -0
  14. package/skills/devbooks-brownfield-bootstrap/references//345/255/230/351/207/217/351/241/271/347/233/256/345/210/235/345/247/213/345/214/226/346/217/220/347/244/272/350/257/215.md +115 -0
  15. package/skills/devbooks-brownfield-bootstrap/references//346/234/257/350/257/255/350/241/250/346/250/241/346/235/277.md +42 -0
  16. package/skills/devbooks-brownfield-bootstrap/scripts/cod-update.sh +357 -0
  17. package/skills/devbooks-brownfield-bootstrap/templates/project-profile-template.md +172 -0
  18. package/skills/devbooks-c4-map/SKILL.md +151 -0
  19. package/skills/devbooks-c4-map/references/C4/346/236/266/346/236/204/345/234/260/345/233/276/346/217/220/347/244/272/350/257/215.md +33 -0
  20. package/skills/devbooks-c4-map/references//345/210/206/345/261/202/347/272/246/346/235/237/346/243/200/346/237/245/346/270/205/345/215/225.md +185 -0
  21. package/skills/devbooks-code-review/SKILL.md +175 -0
  22. package/skills/devbooks-code-review/references/PR/346/250/241/346/235/277/344/270/216/346/214/207/345/215/227.md +321 -0
  23. package/skills/devbooks-code-review/references//344/273/243/347/240/201/350/257/204/345/256/241/346/217/220/347/244/272/350/257/215.md +100 -0
  24. package/skills/devbooks-code-review/references//345/235/217/345/221/263/351/201/223/351/200/237/346/237/245/350/241/250.md +495 -0
  25. package/skills/devbooks-code-review/references//350/265/204/346/272/220/347/256/241/347/220/206/345/256/241/346/237/245/346/270/205/345/215/225.md +311 -0
  26. package/skills/devbooks-coder/SKILL.md +219 -0
  27. package/skills/devbooks-coder/references//344/273/243/347/240/201/345/256/236/347/216/260/346/217/220/347/244/272/350/257/215.md +70 -0
  28. package/skills/devbooks-coder/references//344/275/216/351/243/216/351/231/251/346/224/271/345/212/250/346/212/200/346/234/257.md +275 -0
  29. package/skills/devbooks-coder/references//346/227/245/345/277/227/350/247/204/350/214/203.md +329 -0
  30. package/skills/devbooks-coder/references//347/274/226/347/240/201/351/243/216/346/240/274/347/273/206/345/210/231.md +351 -0
  31. package/skills/devbooks-coder/references//351/224/231/350/257/257/347/240/201/350/247/204/350/214/203.md +463 -0
  32. package/skills/devbooks-delivery-workflow/SKILL.md +217 -0
  33. package/skills/devbooks-delivery-workflow/references//344/272/244/344/273/230/351/252/214/346/224/266/345/267/245/344/275/234/346/265/201.md +256 -0
  34. package/skills/devbooks-delivery-workflow/references//345/216/237/345/236/213-/347/224/237/344/272/247/345/217/214/350/275/250/346/250/241/345/274/217.md +168 -0
  35. package/skills/devbooks-delivery-workflow/references//345/217/230/346/233/264/351/252/214/350/257/201/344/270/216/350/277/275/346/272/257/346/250/241/346/235/277.md +133 -0
  36. package/skills/devbooks-delivery-workflow/scripts/ac-trace-check.sh +330 -0
  37. package/skills/devbooks-delivery-workflow/scripts/audit-scope.sh +262 -0
  38. package/skills/devbooks-delivery-workflow/scripts/change-check.sh +1040 -0
  39. package/skills/devbooks-delivery-workflow/scripts/change-codemod-scaffold.sh +135 -0
  40. package/skills/devbooks-delivery-workflow/scripts/change-evidence.sh +152 -0
  41. package/skills/devbooks-delivery-workflow/scripts/change-scaffold.sh +442 -0
  42. package/skills/devbooks-delivery-workflow/scripts/change-spec-delta-scaffold.sh +136 -0
  43. package/skills/devbooks-delivery-workflow/scripts/constitution-check.sh +237 -0
  44. package/skills/devbooks-delivery-workflow/scripts/env-match-check.sh +128 -0
  45. package/skills/devbooks-delivery-workflow/scripts/fitness-check.sh +387 -0
  46. package/skills/devbooks-delivery-workflow/scripts/guardrail-check.sh +519 -0
  47. package/skills/devbooks-delivery-workflow/scripts/handoff-check.sh +141 -0
  48. package/skills/devbooks-delivery-workflow/scripts/hygiene-check.sh +340 -0
  49. package/skills/devbooks-delivery-workflow/scripts/migrate-from-openspec.sh +385 -0
  50. package/skills/devbooks-delivery-workflow/scripts/migrate-to-v2-gates.sh +202 -0
  51. package/skills/devbooks-delivery-workflow/scripts/progress-dashboard.sh +319 -0
  52. package/skills/devbooks-delivery-workflow/scripts/prototype-promote.sh +341 -0
  53. package/skills/devbooks-delivery-workflow/scripts/spec-preview.sh +203 -0
  54. package/skills/devbooks-delivery-workflow/scripts/spec-promote.sh +118 -0
  55. package/skills/devbooks-delivery-workflow/scripts/spec-rollback.sh +124 -0
  56. package/skills/devbooks-delivery-workflow/scripts/spec-stage.sh +117 -0
  57. package/skills/devbooks-delivery-workflow/scripts/verify-all.sh +78 -0
  58. package/skills/devbooks-delivery-workflow/scripts/verify-npm-package.sh +123 -0
  59. package/skills/devbooks-delivery-workflow/scripts/verify-openspec-free.sh +81 -0
  60. package/skills/devbooks-delivery-workflow/scripts/verify-slash-commands.sh +146 -0
  61. package/skills/devbooks-delivery-workflow/templates/handoff.md +50 -0
  62. package/skills/devbooks-design-backport/SKILL.md +73 -0
  63. package/skills/devbooks-design-backport/references//345/233/236/345/206/231/350/256/276/350/256/241/346/226/207/346/241/243/346/217/220/347/244/272/350/257/215.md +196 -0
  64. package/skills/devbooks-design-doc/SKILL.md +121 -0
  65. package/skills/devbooks-design-doc/references//345/276/256/346/234/215/345/212/241/350/256/276/350/256/241/346/270/205/345/215/225.md +149 -0
  66. package/skills/devbooks-design-doc/references//350/256/276/350/256/241/346/226/207/346/241/243/346/217/220/347/244/272/350/257/215.md +189 -0
  67. package/skills/devbooks-design-doc/references//351/232/220/347/247/201/345/220/210/350/247/204/346/243/200/346/237/245/346/270/205/345/215/225.md +240 -0
  68. package/skills/devbooks-entropy-monitor/SKILL.md +188 -0
  69. package/skills/devbooks-entropy-monitor/references//347/206/265/345/272/246/351/207/217/346/226/271/346/263/225/350/256/272.md +223 -0
  70. package/skills/devbooks-entropy-monitor/scripts/entropy-measure.sh +449 -0
  71. package/skills/devbooks-entropy-monitor/scripts/entropy-report.sh +303 -0
  72. package/skills/devbooks-entropy-monitor/templates/thresholds.json +99 -0
  73. package/skills/devbooks-federation/SKILL.md +264 -0
  74. package/skills/devbooks-federation/scripts/federation-check.sh +144 -0
  75. package/skills/devbooks-federation/templates/federation.yaml +89 -0
  76. package/skills/devbooks-impact-analysis/SKILL.md +135 -0
  77. package/skills/devbooks-impact-analysis/references//345/275/261/345/223/215/345/210/206/346/236/220/346/217/220/347/244/272/350/257/215.md +82 -0
  78. package/skills/devbooks-impact-analysis/scripts/graph-cache.sh +214 -0
  79. package/skills/devbooks-implementation-plan/SKILL.md +83 -0
  80. package/skills/devbooks-implementation-plan/references//347/274/226/347/240/201/350/256/241/345/210/222/346/217/220/347/244/272/350/257/215.md +99 -0
  81. package/skills/devbooks-index-bootstrap/SKILL.md +240 -0
  82. package/skills/devbooks-proposal-author/SKILL.md +83 -0
  83. package/skills/devbooks-proposal-author/references//346/217/220/346/241/210/346/222/260/345/206/231/346/217/220/347/244/272/350/257/215.md +66 -0
  84. package/skills/devbooks-proposal-challenger/SKILL.md +86 -0
  85. package/skills/devbooks-proposal-challenger/references//344/274/246/347/220/206/344/270/216/345/220/210/350/247/204/346/243/200/346/237/245/346/270/205/345/215/225.md +176 -0
  86. package/skills/devbooks-proposal-challenger/references//346/217/220/346/241/210/350/264/250/347/226/221/346/217/220/347/244/272/350/257/215.md +57 -0
  87. package/skills/devbooks-proposal-debate-workflow/SKILL.md +78 -0
  88. package/skills/devbooks-proposal-debate-workflow/references//346/217/220/346/241/210/345/257/271/350/276/251/345/267/245/344/275/234/346/265/201.md +24 -0
  89. package/skills/devbooks-proposal-debate-workflow/references//346/217/220/346/241/210/345/257/271/350/276/251/346/250/241/346/235/277.md +35 -0
  90. package/skills/devbooks-proposal-debate-workflow/scripts/proposal-debate-check.sh +102 -0
  91. package/skills/devbooks-proposal-judge/SKILL.md +78 -0
  92. package/skills/devbooks-proposal-judge/references//346/217/220/346/241/210/350/243/201/345/206/263/346/217/220/347/244/272/350/257/215.md +37 -0
  93. package/skills/devbooks-router/SKILL.md +346 -0
  94. package/skills/devbooks-spec-contract/SKILL.md +191 -0
  95. package/skills/devbooks-spec-contract/references/API/350/256/276/350/256/241/346/214/207/345/215/227.md +349 -0
  96. package/skills/devbooks-spec-contract/references//345/245/221/347/272/246/344/270/216/346/225/260/346/215/256/345/256/232/344/271/211/346/217/220/347/244/272/350/257/215.md +85 -0
  97. package/skills/devbooks-spec-contract/references//350/247/204/346/240/274/345/217/230/346/233/264/346/217/220/347/244/272/350/257/215.md +63 -0
  98. package/skills/devbooks-spec-contract/references//351/232/220/345/274/217/345/217/230/346/233/264/346/243/200/346/265/213/346/217/220/347/244/272/350/257/215.md +183 -0
  99. package/skills/devbooks-spec-contract/scripts/implicit-change-detect.sh +378 -0
  100. package/skills/devbooks-spec-gardener/SKILL.md +72 -0
  101. package/skills/devbooks-spec-gardener/references//350/247/204/346/240/274/345/233/255/344/270/201/346/217/220/347/244/272/350/257/215.md +41 -0
  102. package/skills/devbooks-test-owner/SKILL.md +172 -0
  103. package/skills/devbooks-test-owner/references//345/217/230/346/233/264/351/252/214/350/257/201/344/270/216/350/277/275/346/272/257/346/250/241/346/235/277.md +228 -0
  104. package/skills/devbooks-test-owner/references//345/274/202/346/255/245/347/263/273/347/273/237/346/265/213/350/257/225/347/255/226/347/225/245.md +316 -0
  105. package/skills/devbooks-test-owner/references//346/265/213/350/257/225/344/273/243/347/240/201/346/217/220/347/244/272/350/257/215.md +208 -0
  106. package/skills/devbooks-test-owner/references//346/265/213/350/257/225/345/210/206/345/261/202/347/255/226/347/225/245.md +281 -0
  107. package/skills/devbooks-test-owner/references//346/265/213/350/257/225/351/251/261/345/212/250.md +394 -0
  108. package/skills/devbooks-test-owner/references//350/247/243/344/276/235/350/265/226/346/212/200/346/234/257/351/200/237/346/237/245/350/241/250.md +432 -0
  109. package/skills/devbooks-test-reviewer/SKILL.md +189 -0
  110. package/templates/.devbooks/config.yaml +88 -0
  111. package/templates/claude-commands/devbooks/apply.md +38 -0
  112. package/templates/claude-commands/devbooks/archive.md +33 -0
  113. package/templates/claude-commands/devbooks/backport.md +19 -0
  114. package/templates/claude-commands/devbooks/bootstrap.md +19 -0
  115. package/templates/claude-commands/devbooks/c4.md +19 -0
  116. package/templates/claude-commands/devbooks/challenger.md +19 -0
  117. package/templates/claude-commands/devbooks/code.md +19 -0
  118. package/templates/claude-commands/devbooks/debate.md +19 -0
  119. package/templates/claude-commands/devbooks/delivery.md +19 -0
  120. package/templates/claude-commands/devbooks/design.md +19 -0
  121. package/templates/claude-commands/devbooks/entropy.md +19 -0
  122. package/templates/claude-commands/devbooks/federation.md +19 -0
  123. package/templates/claude-commands/devbooks/gardener.md +19 -0
  124. package/templates/claude-commands/devbooks/impact.md +19 -0
  125. package/templates/claude-commands/devbooks/index.md +19 -0
  126. package/templates/claude-commands/devbooks/judge.md +19 -0
  127. package/templates/claude-commands/devbooks/plan.md +19 -0
  128. package/templates/claude-commands/devbooks/proposal.md +19 -0
  129. package/templates/claude-commands/devbooks/quick.md +42 -0
  130. package/templates/claude-commands/devbooks/review.md +19 -0
  131. package/templates/claude-commands/devbooks/router.md +19 -0
  132. package/templates/claude-commands/devbooks/spec.md +19 -0
  133. package/templates/claude-commands/devbooks/test-review.md +19 -0
  134. package/templates/claude-commands/devbooks/test.md +19 -0
  135. package/templates/dev-playbooks/README.md +458 -0
  136. package/templates/dev-playbooks/changes/.gitkeep +1 -0
  137. package/templates/dev-playbooks/constitution.md +116 -0
  138. package/templates/dev-playbooks/project.md +96 -0
  139. package/templates/dev-playbooks/scripts/.gitkeep +1 -0
  140. package/templates/dev-playbooks/specs/_meta/anti-patterns/.gitkeep +2 -0
  141. package/templates/dev-playbooks/specs/_meta/glossary.md +47 -0
  142. package/templates/dev-playbooks/specs/_meta/project-profile.md +79 -0
  143. package/templates/dev-playbooks/specs/architecture/fitness-rules.md +95 -0
@@ -0,0 +1,519 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ usage() {
5
+ cat >&2 <<EOF
6
+ usage: guardrail-check.sh <change-id> [options]
7
+
8
+ Options:
9
+ --project-root <dir> Project root directory (default: current dir)
10
+ --change-root <dir> Change root directory (default: changes)
11
+ --truth-root <dir> Truth root directory for architecture constraints
12
+ --role <role> Role to check permissions for (coder|test-owner|reviewer)
13
+ --check-lockfile Check if lockfile changes require explicit declaration
14
+ --check-engineering Check if engineering system changes require approval
15
+ --check-layers Check layering constraints (dependency guard)
16
+ --check-cycles Check for circular dependencies
17
+ --check-hotspots Warn if changes touch high-risk hotspots
18
+ -h, --help Show this help message
19
+
20
+ Exit codes:
21
+ 0 - All checks passed
22
+ 1 - Guardrail violation detected
23
+ 2 - Invalid arguments
24
+ 3 - Missing required files
25
+ EOF
26
+ }
27
+
28
+ if [[ $# -eq 0 ]]; then
29
+ usage
30
+ exit 2
31
+ fi
32
+
33
+ if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
34
+ usage
35
+ exit 0
36
+ fi
37
+
38
+ change_id="$1"
39
+ shift
40
+
41
+ project_root="${DEVBOOKS_PROJECT_ROOT:-$(pwd)}"
42
+ change_root="${DEVBOOKS_CHANGE_ROOT:-changes}"
43
+ truth_root="${DEVBOOKS_TRUTH_ROOT:-}"
44
+ role=""
45
+ check_lockfile=false
46
+ check_engineering=false
47
+ check_layers=false
48
+ check_cycles=false
49
+ check_hotspots=false
50
+
51
+ while [[ $# -gt 0 ]]; do
52
+ case "$1" in
53
+ -h|--help)
54
+ usage
55
+ exit 0
56
+ ;;
57
+ --project-root)
58
+ project_root="${2:-}"
59
+ shift 2
60
+ ;;
61
+ --change-root)
62
+ change_root="${2:-}"
63
+ shift 2
64
+ ;;
65
+ --truth-root)
66
+ truth_root="${2:-}"
67
+ shift 2
68
+ ;;
69
+ --role)
70
+ role="${2:-}"
71
+ shift 2
72
+ ;;
73
+ --check-lockfile)
74
+ check_lockfile=true
75
+ shift
76
+ ;;
77
+ --check-engineering)
78
+ check_engineering=true
79
+ shift
80
+ ;;
81
+ --check-layers)
82
+ check_layers=true
83
+ shift
84
+ ;;
85
+ --check-cycles)
86
+ check_cycles=true
87
+ shift
88
+ ;;
89
+ --check-hotspots)
90
+ check_hotspots=true
91
+ shift
92
+ ;;
93
+ *)
94
+ usage
95
+ exit 2
96
+ ;;
97
+ esac
98
+ done
99
+
100
+ if [[ -z "$project_root" || -z "$change_root" ]]; then
101
+ usage
102
+ exit 2
103
+ fi
104
+
105
+ if ! command -v rg >/dev/null 2>&1; then
106
+ echo "error: missing dependency: rg (ripgrep)" >&2
107
+ exit 2
108
+ fi
109
+
110
+ if [[ "$change_root" = /* ]]; then
111
+ file="${change_root}/${change_id}/verification.md"
112
+ else
113
+ file="${project_root}/${change_root}/${change_id}/verification.md"
114
+ fi
115
+
116
+
117
+ if [[ ! -f "$file" ]]; then
118
+ echo "error: missing ${file}" >&2
119
+ exit 2
120
+ fi
121
+
122
+ # Check if guardrail section exists - if not, skip (guardrail review not applicable)
123
+ if ! rg -n "^F\\) Structural Quality Gate Record|^## F\\) Structural Quality Gate" "$file" >/dev/null 2>&1; then
124
+ echo "ok: guardrail section not present (not applicable for ${change_id})"
125
+ exit 0
126
+ fi
127
+
128
+ decision_line=$(rg -n "^- Decision and Authorization:" "$file" || true)
129
+ if [[ -z "$decision_line" ]]; then
130
+ echo "error: guardrail section exists but missing '- Decision and Authorization:' line in ${file}" >&2
131
+ exit 1
132
+ fi
133
+
134
+ value="$(echo "$decision_line" | sed -E 's/^[0-9]+:- Decision and Authorization: *//')"
135
+
136
+ if [[ -z "$value" || "$value" == "<"* || "$value" == "TBD"* ]]; then
137
+ echo "error: unresolved guardrail decision in ${file}" >&2
138
+ exit 1
139
+ fi
140
+
141
+ echo "ok: guardrail decision present for ${change_id}"
142
+
143
+ # =============================================================================
144
+ # Role Permission Checks (inspired by VS Code role permission separation)
145
+ # =============================================================================
146
+
147
+ # Define file patterns forbidden for each role
148
+ declare -A ROLE_FORBIDDEN_PATTERNS
149
+ ROLE_FORBIDDEN_PATTERNS[coder]="tests/|test/|\.test\.|\.spec\.|__tests__|verification\.md"
150
+ ROLE_FORBIDDEN_PATTERNS[test-owner]="" # test-owner can modify test files
151
+ ROLE_FORBIDDEN_PATTERNS[reviewer]=".*" # reviewer should not modify any files
152
+
153
+ # Define sensitive files forbidden for all roles (similar to VS Code engineering system protection)
154
+ SENSITIVE_PATTERNS="\.devbooks/|\.github/workflows/|build/|package-lock\.json|yarn\.lock|pnpm-lock\.yaml|Cargo\.lock"
155
+
156
+ check_role_permissions() {
157
+ local role="$1"
158
+ local change_path="$2"
159
+
160
+ if [[ -z "$role" ]]; then
161
+ return 0
162
+ fi
163
+
164
+ echo "info: checking role permissions for '${role}'..."
165
+
166
+ # Get list of changed files (from git diff or change package record)
167
+ local changed_files=""
168
+ if [[ -d "${project_root}/.git" ]]; then
169
+ changed_files=$(cd "$project_root" && git diff --name-only HEAD~1 2>/dev/null || true)
170
+ fi
171
+
172
+ # If no git, try reading from proposal.md Impact section
173
+ if [[ -z "$changed_files" && -f "${change_path}/proposal.md" ]]; then
174
+ changed_files=$(grep -A 100 "^## Impact" "${change_path}/proposal.md" | grep -E "^\s*-\s+\`" | sed 's/.*`\([^`]*\)`.*/\1/' || true)
175
+ fi
176
+
177
+ if [[ -z "$changed_files" ]]; then
178
+ echo "warn: cannot determine changed files, skipping role permission check"
179
+ return 0
180
+ fi
181
+
182
+ local forbidden="${ROLE_FORBIDDEN_PATTERNS[$role]:-}"
183
+ local violations=""
184
+
185
+ while IFS= read -r file; do
186
+ [[ -z "$file" ]] && continue
187
+
188
+ # Check role-specific forbidden patterns
189
+ if [[ -n "$forbidden" ]] && echo "$file" | grep -qE "$forbidden"; then
190
+ violations="${violations}\n - ${file} (forbidden for role '${role}')"
191
+ fi
192
+
193
+ # Check sensitive files (forbidden for all roles unless explicitly declared)
194
+ if echo "$file" | grep -qE "$SENSITIVE_PATTERNS"; then
195
+ # Check if proposal.md has engineering-system-change tag
196
+ if ! grep -q "engineering-system-change" "${change_path}/proposal.md" 2>/dev/null; then
197
+ violations="${violations}\n - ${file} (sensitive file requires 'engineering-system-change' tag in proposal.md)"
198
+ fi
199
+ fi
200
+ done <<< "$changed_files"
201
+
202
+ if [[ -n "$violations" ]]; then
203
+ echo -e "error: role permission violations detected:${violations}" >&2
204
+ return 1
205
+ fi
206
+
207
+ echo "ok: role permissions check passed for '${role}'"
208
+ return 0
209
+ }
210
+
211
+ # =============================================================================
212
+ # Lockfile Idempotency Check (inspired by VS Code no-package-lock-changes.yml)
213
+ # =============================================================================
214
+
215
+ check_lockfile_changes() {
216
+ local change_path="$1"
217
+
218
+ echo "info: checking lockfile changes..."
219
+
220
+ local lockfiles="package-lock.json yarn.lock pnpm-lock.yaml Cargo.lock Gemfile.lock poetry.lock"
221
+ local changed_lockfiles=""
222
+
223
+ if [[ -d "${project_root}/.git" ]]; then
224
+ for lockfile in $lockfiles; do
225
+ if cd "$project_root" && git diff --name-only HEAD~1 2>/dev/null | grep -q "^${lockfile}$"; then
226
+ changed_lockfiles="${changed_lockfiles} ${lockfile}"
227
+ fi
228
+ done
229
+ fi
230
+
231
+ if [[ -n "$changed_lockfiles" ]]; then
232
+ # Check if proposal.md explicitly declares dependency changes
233
+ if ! grep -qE "(dependency|deps|upgrade|update.*package)" "${change_path}/proposal.md" 2>/dev/null; then
234
+ echo "error: lockfile changes detected (${changed_lockfiles}) but proposal.md does not declare dependency changes" >&2
235
+ echo "hint: add dependency change description to proposal.md or use '--check-lockfile=false'" >&2
236
+ return 1
237
+ fi
238
+ fi
239
+
240
+ echo "ok: lockfile check passed"
241
+ return 0
242
+ }
243
+
244
+ # =============================================================================
245
+ # Engineering System Change Check
246
+ # =============================================================================
247
+
248
+ check_engineering_changes() {
249
+ local change_path="$1"
250
+
251
+ echo "info: checking engineering system changes..."
252
+
253
+ local eng_patterns="\.devbooks/|\.github/|build/|scripts/|Makefile|gulpfile|webpack\.config|vite\.config|tsconfig|eslint\.config|\.eslintrc"
254
+ local eng_changes=""
255
+
256
+ if [[ -d "${project_root}/.git" ]]; then
257
+ eng_changes=$(cd "$project_root" && git diff --name-only HEAD~1 2>/dev/null | grep -E "$eng_patterns" || true)
258
+ fi
259
+
260
+ if [[ -n "$eng_changes" ]]; then
261
+ # Check if proposal.md has engineering-system-change tag
262
+ if ! grep -q "engineering-system-change" "${change_path}/proposal.md" 2>/dev/null; then
263
+ echo "error: engineering system changes detected but proposal.md missing 'engineering-system-change' tag:" >&2
264
+ echo "$eng_changes" | sed 's/^/ - /' >&2
265
+ return 1
266
+ fi
267
+ fi
268
+
269
+ echo "ok: engineering system check passed"
270
+ return 0
271
+ }
272
+
273
+ # =============================================================================
274
+ # Layering Constraints Check (Dependency Guard)
275
+ # Prevent dependency direction violations (upper layer cannot directly depend on lower layer implementation details)
276
+ # =============================================================================
277
+
278
+ check_layering_constraints() {
279
+ local change_path="$1"
280
+ local constraints_file="${truth_root}/architecture/c4.md"
281
+
282
+ echo "info: checking layering constraints (dependency guard)..."
283
+
284
+ # Skip if no truth_root or constraints file doesn't exist
285
+ if [[ -z "$truth_root" ]]; then
286
+ echo "warn: --truth-root not specified, skipping layering check"
287
+ return 0
288
+ fi
289
+
290
+ if [[ ! -f "$constraints_file" ]]; then
291
+ echo "warn: no layering constraints file found at ${constraints_file}, skipping"
292
+ return 0
293
+ fi
294
+
295
+ # Get changed files
296
+ local changed_files=""
297
+ if [[ -d "${project_root}/.git" ]]; then
298
+ changed_files=$(cd "$project_root" && git diff --name-only HEAD~1 2>/dev/null || true)
299
+ fi
300
+
301
+ if [[ -z "$changed_files" ]]; then
302
+ echo "warn: cannot determine changed files, skipping layering check"
303
+ return 0
304
+ fi
305
+
306
+ local violations=""
307
+
308
+ # Parse layering rules from constraints file
309
+ # Format: | base | src/base/ | ... | (none) | platform, domain, ... |
310
+
311
+ # Common layering violation checks
312
+ while IFS= read -r file; do
313
+ [[ -z "$file" ]] && continue
314
+ [[ ! "$file" =~ \.(ts|tsx|js|jsx|py|go|java|rs)$ ]] && continue
315
+
316
+ local file_path="${project_root}/${file}"
317
+ [[ ! -f "$file_path" ]] && continue
318
+
319
+ # Check if base layer imports platform/domain/application/ui
320
+ if [[ "$file" =~ ^src/base/ ]] || [[ "$file" =~ /base/ ]]; then
321
+ if rg -q "from ['\"].*(platform|domain|application|app|ui)/" "$file_path" 2>/dev/null; then
322
+ violations="${violations}\n - ${file}: base layer imports upper layer"
323
+ fi
324
+ fi
325
+
326
+ # Check if common layer imports browser/node specific code
327
+ if [[ "$file" =~ /common/ ]]; then
328
+ if rg -q "from ['\"].*(browser|node)/" "$file_path" 2>/dev/null; then
329
+ violations="${violations}\n - ${file}: common layer imports platform-specific code"
330
+ fi
331
+ # Check if using DOM API
332
+ if rg -q "(document\.|window\.|navigator\.)" "$file_path" 2>/dev/null; then
333
+ violations="${violations}\n - ${file}: common layer uses DOM API"
334
+ fi
335
+ fi
336
+
337
+ # Check if core imports contrib
338
+ if [[ "$file" =~ /core/ ]] || [[ "$file" =~ /services/ ]]; then
339
+ if rg -q "from ['\"].*contrib/" "$file_path" 2>/dev/null; then
340
+ violations="${violations}\n - ${file}: core imports contrib (violates extension point design)"
341
+ fi
342
+ fi
343
+
344
+ done <<< "$changed_files"
345
+
346
+ if [[ -n "$violations" ]]; then
347
+ echo -e "error: layering constraint violations detected:${violations}" >&2
348
+ echo "hint: see ${constraints_file} for allowed dependencies" >&2
349
+ return 1
350
+ fi
351
+
352
+ echo "ok: layering constraints check passed"
353
+ return 0
354
+ }
355
+
356
+ # =============================================================================
357
+ # Circular Dependency Check
358
+ # =============================================================================
359
+
360
+ check_circular_dependencies() {
361
+ echo "info: checking for circular dependencies..."
362
+
363
+ # Check if madge tool is available
364
+ if command -v madge >/dev/null 2>&1; then
365
+ local circular=""
366
+ circular=$(cd "$project_root" && madge --circular --warning src/ 2>/dev/null | grep -E "^\s+[a-zA-Z]" || true)
367
+
368
+ if [[ -n "$circular" ]]; then
369
+ echo "error: circular dependencies detected:" >&2
370
+ echo "$circular" | sed 's/^/ /' >&2
371
+ return 1
372
+ fi
373
+ else
374
+ # Fallback: use simple grep to detect common circular patterns
375
+ echo "info: madge not available, using basic circular detection"
376
+
377
+ # Check if files both import each other (simple heuristic)
378
+ if [[ -d "${project_root}/.git" ]]; then
379
+ local changed_files
380
+ changed_files=$(cd "$project_root" && git diff --name-only HEAD~1 2>/dev/null | grep -E '\.(ts|js|tsx|jsx)$' || true)
381
+
382
+ while IFS= read -r file; do
383
+ [[ -z "$file" ]] && continue
384
+ local file_path="${project_root}/${file}"
385
+ [[ ! -f "$file_path" ]] && continue
386
+
387
+ # Get file's imports
388
+ local imports
389
+ imports=$(rg "^import .* from ['\"]\./" "$file_path" 2>/dev/null | sed "s/.*from ['\"]\\([^'\"]*\\)['\"].*/\\1/" || true)
390
+
391
+ # Check if imported files import current file back
392
+ local file_base
393
+ file_base=$(basename "$file" | sed 's/\.[^.]*$//')
394
+
395
+ while IFS= read -r imported; do
396
+ [[ -z "$imported" ]] && continue
397
+ local imported_path="${project_root}/$(dirname "$file")/${imported}"
398
+ [[ "$imported_path" =~ \.ts$ ]] || imported_path="${imported_path}.ts"
399
+
400
+ if [[ -f "$imported_path" ]] && rg -q "from ['\"].*${file_base}['\"]" "$imported_path" 2>/dev/null; then
401
+ echo "warn: potential circular dependency: ${file} <-> ${imported}" >&2
402
+ fi
403
+ done <<< "$imports"
404
+ done <<< "$changed_files"
405
+ fi
406
+ fi
407
+
408
+ echo "ok: circular dependency check passed"
409
+ return 0
410
+ }
411
+
412
+ # =============================================================================
413
+ # Hotspot Warning Check
414
+ # Hotspot = High change frequency x High complexity
415
+ # =============================================================================
416
+
417
+ check_hotspot_changes() {
418
+ local change_path="$1"
419
+ local hotspots_file="${truth_root}/architecture/hotspots.md"
420
+
421
+ echo "info: checking if changes touch hotspots..."
422
+
423
+ # If hotspots file exists, read hotspot list from it
424
+ local hotspot_files=""
425
+ if [[ -n "$truth_root" && -f "$hotspots_file" ]]; then
426
+ hotspot_files=$(grep -E "^\| " "$hotspots_file" | grep -v "File\|---" | awk -F'|' '{print $2}' | tr -d ' ' || true)
427
+ fi
428
+
429
+ # If no hotspots file, try computing from git history
430
+ if [[ -z "$hotspot_files" && -d "${project_root}/.git" ]]; then
431
+ echo "info: no hotspots.md found, computing from git history (top 10 churn files)..."
432
+ hotspot_files=$(cd "$project_root" && git log --oneline --name-only --since="30 days ago" 2>/dev/null | \
433
+ grep -E '\.(ts|tsx|js|jsx|py|go|java|rs)$' | \
434
+ sort | uniq -c | sort -rn | head -10 | awk '{print $2}' || true)
435
+ fi
436
+
437
+ if [[ -z "$hotspot_files" ]]; then
438
+ echo "info: no hotspot data available, skipping"
439
+ return 0
440
+ fi
441
+
442
+ # Get changed files
443
+ local changed_files=""
444
+ if [[ -d "${project_root}/.git" ]]; then
445
+ changed_files=$(cd "$project_root" && git diff --name-only HEAD~1 2>/dev/null || true)
446
+ fi
447
+
448
+ # Check if changed files touch hotspots
449
+ local hotspot_hits=""
450
+ while IFS= read -r changed; do
451
+ [[ -z "$changed" ]] && continue
452
+ if echo "$hotspot_files" | grep -qF "$changed"; then
453
+ hotspot_hits="${hotspot_hits}\n - ${changed}"
454
+ fi
455
+ done <<< "$changed_files"
456
+
457
+ if [[ -n "$hotspot_hits" ]]; then
458
+ echo -e "warn: changes touch high-risk hotspots (high churn x complexity):${hotspot_hits}" >&2
459
+ echo "hint: consider extra review and testing for these files" >&2
460
+ # This is a warning, not an error, does not block merge
461
+ fi
462
+
463
+ echo "ok: hotspot check completed"
464
+ return 0
465
+ }
466
+
467
+ # =============================================================================
468
+ # Run Additional Checks
469
+ # =============================================================================
470
+
471
+ exit_code=0
472
+
473
+ # Role permission check
474
+ if [[ -n "$role" ]]; then
475
+ change_path=$(dirname "$file")
476
+ if ! check_role_permissions "$role" "$change_path"; then
477
+ exit_code=1
478
+ fi
479
+ fi
480
+
481
+ # Lockfile check
482
+ if [[ "$check_lockfile" == "true" ]]; then
483
+ change_path=$(dirname "$file")
484
+ if ! check_lockfile_changes "$change_path"; then
485
+ exit_code=1
486
+ fi
487
+ fi
488
+
489
+ # Engineering system change check
490
+ if [[ "$check_engineering" == "true" ]]; then
491
+ change_path=$(dirname "$file")
492
+ if ! check_engineering_changes "$change_path"; then
493
+ exit_code=1
494
+ fi
495
+ fi
496
+
497
+ # Layering constraint check (Dependency Guard)
498
+ if [[ "$check_layers" == "true" ]]; then
499
+ change_path=$(dirname "$file")
500
+ if ! check_layering_constraints "$change_path"; then
501
+ exit_code=1
502
+ fi
503
+ fi
504
+
505
+ # Circular dependency check
506
+ if [[ "$check_cycles" == "true" ]]; then
507
+ if ! check_circular_dependencies; then
508
+ exit_code=1
509
+ fi
510
+ fi
511
+
512
+ # Hotspot warning check (warning only, does not affect exit code)
513
+ if [[ "$check_hotspots" == "true" ]]; then
514
+ change_path=$(dirname "$file")
515
+ check_hotspot_changes "$change_path"
516
+ # Hotspots are just warnings, do not affect exit_code
517
+ fi
518
+
519
+ exit $exit_code
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env bash
2
+ # handoff-check.sh - Verify role handoff has proper confirmation
3
+ #
4
+ # This script checks that handoff.md exists and has proper confirmation
5
+ # signatures from both roles involved in the handoff.
6
+ #
7
+ # Reference: harden-devbooks-quality-gates design.md AC-004
8
+
9
+ set -euo pipefail
10
+
11
+ usage() {
12
+ cat <<'EOF' >&2
13
+ usage: handoff-check.sh <change-id> [options]
14
+
15
+ Verify role handoff has proper confirmation:
16
+ 1. Checks handoff.md exists
17
+ 2. Verifies all parties have confirmed (default behavior)
18
+ 3. Returns exit code based on verification status
19
+
20
+ Options:
21
+ --project-root <dir> Project root directory (default: pwd)
22
+ --change-root <dir> Change packages root (default: changes)
23
+ --allow-partial Allow partial confirmation (at least one [x])
24
+ -h, --help Show this help message
25
+
26
+ Exit Codes:
27
+ 0 - All checks passed
28
+ 1 - Check failed
29
+ 2 - Usage error
30
+
31
+ Examples:
32
+ handoff-check.sh my-change-001
33
+ handoff-check.sh my-change-001 --change-root dev-playbooks/changes
34
+ handoff-check.sh my-change-001 --allow-partial
35
+ EOF
36
+ }
37
+
38
+ if [[ $# -eq 0 ]]; then
39
+ usage
40
+ exit 2
41
+ fi
42
+
43
+ if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
44
+ usage
45
+ exit 0
46
+ fi
47
+
48
+ change_id="$1"
49
+ shift
50
+
51
+ project_root="${DEVBOOKS_PROJECT_ROOT:-$(pwd)}"
52
+ change_root="${DEVBOOKS_CHANGE_ROOT:-changes}"
53
+ allow_partial=false
54
+
55
+ while [[ $# -gt 0 ]]; do
56
+ case "$1" in
57
+ -h|--help)
58
+ usage
59
+ exit 0
60
+ ;;
61
+ --project-root)
62
+ project_root="${2:-}"
63
+ shift 2
64
+ ;;
65
+ --change-root)
66
+ change_root="${2:-}"
67
+ shift 2
68
+ ;;
69
+ --allow-partial)
70
+ allow_partial=true
71
+ shift
72
+ ;;
73
+ *)
74
+ echo "error: unknown option: $1" >&2
75
+ usage
76
+ exit 2
77
+ ;;
78
+ esac
79
+ done
80
+
81
+ # Validate change-id
82
+ if [[ -z "$change_id" || "$change_id" == "-"* || "$change_id" =~ [[:space:]] ]]; then
83
+ echo "error: invalid change-id: '$change_id'" >&2
84
+ exit 2
85
+ fi
86
+
87
+ # Build paths
88
+ project_root="${project_root%/}"
89
+ change_root="${change_root%/}"
90
+
91
+ if [[ "$change_root" = /* ]]; then
92
+ change_dir="${change_root}/${change_id}"
93
+ else
94
+ change_dir="${project_root}/${change_root}/${change_id}"
95
+ fi
96
+
97
+ handoff_file="${change_dir}/handoff.md"
98
+
99
+ echo "handoff-check: checking '${change_id}'"
100
+ echo " change-dir: ${change_dir}"
101
+
102
+ # Check change directory exists
103
+ if [[ ! -d "$change_dir" ]]; then
104
+ echo "error: missing change directory: ${change_dir}" >&2
105
+ exit 1
106
+ fi
107
+
108
+ # Check handoff.md exists
109
+ if [[ ! -f "$handoff_file" ]]; then
110
+ echo "error: missing handoff.md: ${handoff_file}" >&2
111
+ exit 1
112
+ fi
113
+
114
+ # Check for confirmation section (supports both Chinese and English)
115
+ if ! grep -qE "Confirmation Signatures|Confirmation|Confirm|确认签名|确认|签名|交接" "$handoff_file" 2>/dev/null; then
116
+ echo "error: handoff.md missing confirmation section" >&2
117
+ exit 1
118
+ fi
119
+
120
+ # Count confirmed checkboxes (lines with [x] or [X])
121
+ confirmed_count=$(grep -cE "^- \[[xX]\]" "$handoff_file" 2>/dev/null) || confirmed_count=0
122
+ unconfirmed_count=$(grep -cE "^- \[ \]" "$handoff_file" 2>/dev/null) || unconfirmed_count=0
123
+ total_count=$((confirmed_count + unconfirmed_count))
124
+
125
+ echo " signatures: ${confirmed_count}/${total_count} confirmed"
126
+
127
+ if [[ "$confirmed_count" -eq 0 ]]; then
128
+ echo "error: no confirmed signatures in handoff.md (need at least one [x])" >&2
129
+ exit 1
130
+ fi
131
+
132
+ # Default: require all parties to confirm
133
+ if [[ "$allow_partial" != true ]]; then
134
+ if [[ "$unconfirmed_count" -gt 0 ]]; then
135
+ echo "error: incomplete signatures - all parties must confirm (${confirmed_count}/${total_count})" >&2
136
+ exit 1
137
+ fi
138
+ fi
139
+
140
+ echo "ok: handoff verification passed"
141
+ exit 0