shipwright-cli 3.2.0 → 3.3.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 (279) hide show
  1. package/.claude/agents/code-reviewer.md +2 -0
  2. package/.claude/agents/devops-engineer.md +2 -0
  3. package/.claude/agents/doc-fleet-agent.md +2 -0
  4. package/.claude/agents/pipeline-agent.md +2 -0
  5. package/.claude/agents/shell-script-specialist.md +2 -0
  6. package/.claude/agents/test-specialist.md +2 -0
  7. package/.claude/hooks/agent-crash-capture.sh +32 -0
  8. package/.claude/hooks/post-tool-use.sh +3 -2
  9. package/.claude/hooks/pre-tool-use.sh +35 -3
  10. package/README.md +4 -4
  11. package/claude-code/hooks/config-change.sh +18 -0
  12. package/claude-code/hooks/instructions-reloaded.sh +7 -0
  13. package/claude-code/hooks/worktree-create.sh +25 -0
  14. package/claude-code/hooks/worktree-remove.sh +20 -0
  15. package/config/code-constitution.json +130 -0
  16. package/dashboard/middleware/auth.ts +134 -0
  17. package/dashboard/middleware/constants.ts +21 -0
  18. package/dashboard/public/index.html +2 -6
  19. package/dashboard/public/styles.css +100 -97
  20. package/dashboard/routes/auth.ts +38 -0
  21. package/dashboard/server.ts +66 -25
  22. package/dashboard/services/config.ts +26 -0
  23. package/dashboard/services/db.ts +118 -0
  24. package/dashboard/src/canvas/pixel-agent.ts +298 -0
  25. package/dashboard/src/canvas/pixel-sprites.ts +440 -0
  26. package/dashboard/src/canvas/shipyard-effects.ts +367 -0
  27. package/dashboard/src/canvas/shipyard-scene.ts +616 -0
  28. package/dashboard/src/canvas/submarine-layout.ts +267 -0
  29. package/dashboard/src/components/header.ts +8 -7
  30. package/dashboard/src/core/router.ts +1 -0
  31. package/dashboard/src/design/submarine-theme.ts +253 -0
  32. package/dashboard/src/main.ts +2 -0
  33. package/dashboard/src/types/api.ts +2 -1
  34. package/dashboard/src/views/activity.ts +2 -1
  35. package/dashboard/src/views/shipyard.ts +39 -0
  36. package/dashboard/types/index.ts +166 -0
  37. package/docs/plans/2026-02-28-compound-audit-and-shipyard-design.md +186 -0
  38. package/docs/plans/2026-02-28-skipper-shipwright-implementation-plan.md +1182 -0
  39. package/docs/plans/2026-02-28-skipper-shipwright-integration-design.md +531 -0
  40. package/docs/plans/2026-03-01-ai-powered-skill-injection-design.md +298 -0
  41. package/docs/plans/2026-03-01-ai-powered-skill-injection-plan.md +1109 -0
  42. package/docs/plans/2026-03-01-capabilities-cleanup-plan.md +658 -0
  43. package/docs/plans/2026-03-01-clean-architecture-plan.md +924 -0
  44. package/docs/plans/2026-03-01-compound-audit-cascade-design.md +191 -0
  45. package/docs/plans/2026-03-01-compound-audit-cascade-plan.md +921 -0
  46. package/docs/plans/2026-03-01-deep-integration-plan.md +851 -0
  47. package/docs/plans/2026-03-01-pipeline-audit-trail-design.md +145 -0
  48. package/docs/plans/2026-03-01-pipeline-audit-trail-plan.md +770 -0
  49. package/docs/plans/2026-03-01-refined-depths-brand-design.md +382 -0
  50. package/docs/plans/2026-03-01-refined-depths-implementation.md +599 -0
  51. package/docs/plans/2026-03-01-skipper-kernel-integration-design.md +203 -0
  52. package/docs/plans/2026-03-01-unified-platform-design.md +272 -0
  53. package/docs/plans/2026-03-07-claude-code-feature-integration-design.md +189 -0
  54. package/docs/plans/2026-03-07-claude-code-feature-integration-plan.md +1165 -0
  55. package/docs/research/BACKLOG_QUICK_REFERENCE.md +352 -0
  56. package/docs/research/CUTTING_EDGE_RESEARCH_2026.md +546 -0
  57. package/docs/research/RESEARCH_INDEX.md +439 -0
  58. package/docs/research/RESEARCH_SOURCES.md +440 -0
  59. package/docs/research/RESEARCH_SUMMARY.txt +275 -0
  60. package/docs/superpowers/specs/2026-03-10-pipeline-quality-revolution-design.md +341 -0
  61. package/package.json +2 -2
  62. package/scripts/lib/adaptive-model.sh +427 -0
  63. package/scripts/lib/adaptive-timeout.sh +316 -0
  64. package/scripts/lib/audit-trail.sh +309 -0
  65. package/scripts/lib/auto-recovery.sh +471 -0
  66. package/scripts/lib/bandit-selector.sh +431 -0
  67. package/scripts/lib/bootstrap.sh +104 -2
  68. package/scripts/lib/causal-graph.sh +455 -0
  69. package/scripts/lib/compat.sh +126 -0
  70. package/scripts/lib/compound-audit.sh +337 -0
  71. package/scripts/lib/constitutional.sh +454 -0
  72. package/scripts/lib/context-budget.sh +359 -0
  73. package/scripts/lib/convergence.sh +594 -0
  74. package/scripts/lib/cost-optimizer.sh +634 -0
  75. package/scripts/lib/daemon-adaptive.sh +10 -0
  76. package/scripts/lib/daemon-dispatch.sh +106 -17
  77. package/scripts/lib/daemon-failure.sh +34 -4
  78. package/scripts/lib/daemon-patrol.sh +23 -2
  79. package/scripts/lib/daemon-poll-github.sh +361 -0
  80. package/scripts/lib/daemon-poll-health.sh +299 -0
  81. package/scripts/lib/daemon-poll.sh +27 -611
  82. package/scripts/lib/daemon-state.sh +112 -66
  83. package/scripts/lib/daemon-triage.sh +10 -0
  84. package/scripts/lib/dod-scorecard.sh +442 -0
  85. package/scripts/lib/error-actionability.sh +300 -0
  86. package/scripts/lib/formal-spec.sh +461 -0
  87. package/scripts/lib/helpers.sh +177 -4
  88. package/scripts/lib/intent-analysis.sh +409 -0
  89. package/scripts/lib/loop-convergence.sh +350 -0
  90. package/scripts/lib/loop-iteration.sh +682 -0
  91. package/scripts/lib/loop-progress.sh +48 -0
  92. package/scripts/lib/loop-restart.sh +185 -0
  93. package/scripts/lib/memory-effectiveness.sh +506 -0
  94. package/scripts/lib/mutation-executor.sh +352 -0
  95. package/scripts/lib/outcome-feedback.sh +521 -0
  96. package/scripts/lib/pipeline-cli.sh +336 -0
  97. package/scripts/lib/pipeline-commands.sh +1216 -0
  98. package/scripts/lib/pipeline-detection.sh +100 -2
  99. package/scripts/lib/pipeline-execution.sh +897 -0
  100. package/scripts/lib/pipeline-github.sh +28 -3
  101. package/scripts/lib/pipeline-intelligence-compound.sh +431 -0
  102. package/scripts/lib/pipeline-intelligence-scoring.sh +407 -0
  103. package/scripts/lib/pipeline-intelligence-skip.sh +181 -0
  104. package/scripts/lib/pipeline-intelligence.sh +100 -1136
  105. package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
  106. package/scripts/lib/pipeline-quality-checks.sh +17 -715
  107. package/scripts/lib/pipeline-quality-gates.sh +563 -0
  108. package/scripts/lib/pipeline-stages-build.sh +730 -0
  109. package/scripts/lib/pipeline-stages-delivery.sh +965 -0
  110. package/scripts/lib/pipeline-stages-intake.sh +1133 -0
  111. package/scripts/lib/pipeline-stages-monitor.sh +407 -0
  112. package/scripts/lib/pipeline-stages-review.sh +1022 -0
  113. package/scripts/lib/pipeline-stages.sh +59 -2929
  114. package/scripts/lib/pipeline-state.sh +36 -5
  115. package/scripts/lib/pipeline-util.sh +487 -0
  116. package/scripts/lib/policy-learner.sh +438 -0
  117. package/scripts/lib/process-reward.sh +493 -0
  118. package/scripts/lib/project-detect.sh +649 -0
  119. package/scripts/lib/quality-profile.sh +334 -0
  120. package/scripts/lib/recruit-commands.sh +885 -0
  121. package/scripts/lib/recruit-learning.sh +739 -0
  122. package/scripts/lib/recruit-roles.sh +648 -0
  123. package/scripts/lib/reward-aggregator.sh +458 -0
  124. package/scripts/lib/rl-optimizer.sh +362 -0
  125. package/scripts/lib/root-cause.sh +427 -0
  126. package/scripts/lib/scope-enforcement.sh +445 -0
  127. package/scripts/lib/session-restart.sh +493 -0
  128. package/scripts/lib/skill-memory.sh +300 -0
  129. package/scripts/lib/skill-registry.sh +775 -0
  130. package/scripts/lib/spec-driven.sh +476 -0
  131. package/scripts/lib/test-helpers.sh +18 -7
  132. package/scripts/lib/test-holdout.sh +429 -0
  133. package/scripts/lib/test-optimizer.sh +511 -0
  134. package/scripts/shipwright-file-suggest.sh +45 -0
  135. package/scripts/skills/adversarial-quality.md +61 -0
  136. package/scripts/skills/api-design.md +44 -0
  137. package/scripts/skills/architecture-design.md +50 -0
  138. package/scripts/skills/brainstorming.md +43 -0
  139. package/scripts/skills/data-pipeline.md +44 -0
  140. package/scripts/skills/deploy-safety.md +64 -0
  141. package/scripts/skills/documentation.md +38 -0
  142. package/scripts/skills/frontend-design.md +45 -0
  143. package/scripts/skills/generated/.gitkeep +0 -0
  144. package/scripts/skills/generated/_refinements/.gitkeep +0 -0
  145. package/scripts/skills/generated/_refinements/adversarial-quality.patch.md +3 -0
  146. package/scripts/skills/generated/_refinements/architecture-design.patch.md +3 -0
  147. package/scripts/skills/generated/_refinements/brainstorming.patch.md +3 -0
  148. package/scripts/skills/generated/cli-version-management.md +29 -0
  149. package/scripts/skills/generated/collection-system-validation.md +99 -0
  150. package/scripts/skills/generated/large-scale-c-refactoring-coordination.md +97 -0
  151. package/scripts/skills/generated/pattern-matching-similarity-scoring.md +195 -0
  152. package/scripts/skills/generated/test-parallelization-detection.md +65 -0
  153. package/scripts/skills/observability.md +79 -0
  154. package/scripts/skills/performance.md +48 -0
  155. package/scripts/skills/pr-quality.md +49 -0
  156. package/scripts/skills/product-thinking.md +43 -0
  157. package/scripts/skills/security-audit.md +49 -0
  158. package/scripts/skills/systematic-debugging.md +40 -0
  159. package/scripts/skills/testing-strategy.md +47 -0
  160. package/scripts/skills/two-stage-review.md +52 -0
  161. package/scripts/skills/validation-thoroughness.md +55 -0
  162. package/scripts/sw +9 -3
  163. package/scripts/sw-activity.sh +9 -2
  164. package/scripts/sw-adaptive.sh +2 -1
  165. package/scripts/sw-adversarial.sh +2 -1
  166. package/scripts/sw-architecture-enforcer.sh +3 -1
  167. package/scripts/sw-auth.sh +12 -2
  168. package/scripts/sw-autonomous.sh +5 -1
  169. package/scripts/sw-changelog.sh +4 -1
  170. package/scripts/sw-checkpoint.sh +2 -1
  171. package/scripts/sw-ci.sh +5 -1
  172. package/scripts/sw-cleanup.sh +4 -26
  173. package/scripts/sw-code-review.sh +10 -4
  174. package/scripts/sw-connect.sh +2 -1
  175. package/scripts/sw-context.sh +2 -1
  176. package/scripts/sw-cost.sh +48 -3
  177. package/scripts/sw-daemon.sh +66 -9
  178. package/scripts/sw-dashboard.sh +3 -1
  179. package/scripts/sw-db.sh +59 -16
  180. package/scripts/sw-decide.sh +8 -2
  181. package/scripts/sw-decompose.sh +360 -17
  182. package/scripts/sw-deps.sh +4 -1
  183. package/scripts/sw-developer-simulation.sh +4 -1
  184. package/scripts/sw-discovery.sh +325 -2
  185. package/scripts/sw-doc-fleet.sh +4 -1
  186. package/scripts/sw-docs-agent.sh +3 -1
  187. package/scripts/sw-docs.sh +2 -1
  188. package/scripts/sw-doctor.sh +453 -2
  189. package/scripts/sw-dora.sh +4 -1
  190. package/scripts/sw-durable.sh +4 -3
  191. package/scripts/sw-e2e-orchestrator.sh +17 -16
  192. package/scripts/sw-eventbus.sh +7 -1
  193. package/scripts/sw-evidence.sh +364 -12
  194. package/scripts/sw-feedback.sh +550 -9
  195. package/scripts/sw-fix.sh +20 -1
  196. package/scripts/sw-fleet-discover.sh +6 -2
  197. package/scripts/sw-fleet-viz.sh +4 -1
  198. package/scripts/sw-fleet.sh +5 -1
  199. package/scripts/sw-github-app.sh +16 -3
  200. package/scripts/sw-github-checks.sh +3 -2
  201. package/scripts/sw-github-deploy.sh +3 -2
  202. package/scripts/sw-github-graphql.sh +18 -7
  203. package/scripts/sw-guild.sh +5 -1
  204. package/scripts/sw-heartbeat.sh +5 -30
  205. package/scripts/sw-hello.sh +67 -0
  206. package/scripts/sw-hygiene.sh +6 -1
  207. package/scripts/sw-incident.sh +265 -1
  208. package/scripts/sw-init.sh +18 -2
  209. package/scripts/sw-instrument.sh +10 -2
  210. package/scripts/sw-intelligence.sh +42 -6
  211. package/scripts/sw-jira.sh +5 -1
  212. package/scripts/sw-launchd.sh +2 -1
  213. package/scripts/sw-linear.sh +4 -1
  214. package/scripts/sw-logs.sh +4 -1
  215. package/scripts/sw-loop.sh +432 -1128
  216. package/scripts/sw-memory.sh +356 -2
  217. package/scripts/sw-mission-control.sh +6 -1
  218. package/scripts/sw-model-router.sh +481 -26
  219. package/scripts/sw-otel.sh +13 -4
  220. package/scripts/sw-oversight.sh +14 -5
  221. package/scripts/sw-patrol-meta.sh +334 -0
  222. package/scripts/sw-pipeline-composer.sh +5 -1
  223. package/scripts/sw-pipeline-vitals.sh +2 -1
  224. package/scripts/sw-pipeline.sh +53 -2664
  225. package/scripts/sw-pm.sh +12 -5
  226. package/scripts/sw-pr-lifecycle.sh +2 -1
  227. package/scripts/sw-predictive.sh +7 -1
  228. package/scripts/sw-prep.sh +185 -2
  229. package/scripts/sw-ps.sh +5 -25
  230. package/scripts/sw-public-dashboard.sh +15 -3
  231. package/scripts/sw-quality.sh +2 -1
  232. package/scripts/sw-reaper.sh +8 -25
  233. package/scripts/sw-recruit.sh +156 -2303
  234. package/scripts/sw-regression.sh +19 -12
  235. package/scripts/sw-release-manager.sh +3 -1
  236. package/scripts/sw-release.sh +4 -1
  237. package/scripts/sw-remote.sh +3 -1
  238. package/scripts/sw-replay.sh +7 -1
  239. package/scripts/sw-retro.sh +158 -1
  240. package/scripts/sw-review-rerun.sh +3 -1
  241. package/scripts/sw-scale.sh +10 -3
  242. package/scripts/sw-security-audit.sh +6 -1
  243. package/scripts/sw-self-optimize.sh +6 -3
  244. package/scripts/sw-session.sh +9 -3
  245. package/scripts/sw-setup.sh +3 -1
  246. package/scripts/sw-stall-detector.sh +406 -0
  247. package/scripts/sw-standup.sh +15 -7
  248. package/scripts/sw-status.sh +3 -1
  249. package/scripts/sw-strategic.sh +4 -1
  250. package/scripts/sw-stream.sh +7 -1
  251. package/scripts/sw-swarm.sh +18 -6
  252. package/scripts/sw-team-stages.sh +13 -6
  253. package/scripts/sw-templates.sh +5 -29
  254. package/scripts/sw-testgen.sh +7 -1
  255. package/scripts/sw-tmux-pipeline.sh +4 -1
  256. package/scripts/sw-tmux-role-color.sh +2 -0
  257. package/scripts/sw-tmux-status.sh +1 -1
  258. package/scripts/sw-tmux.sh +3 -1
  259. package/scripts/sw-trace.sh +3 -1
  260. package/scripts/sw-tracker-github.sh +3 -0
  261. package/scripts/sw-tracker-jira.sh +3 -0
  262. package/scripts/sw-tracker-linear.sh +3 -0
  263. package/scripts/sw-tracker.sh +3 -1
  264. package/scripts/sw-triage.sh +2 -1
  265. package/scripts/sw-upgrade.sh +3 -1
  266. package/scripts/sw-ux.sh +5 -2
  267. package/scripts/sw-webhook.sh +3 -1
  268. package/scripts/sw-widgets.sh +3 -1
  269. package/scripts/sw-worktree.sh +15 -3
  270. package/scripts/test-skill-injection.sh +1233 -0
  271. package/templates/pipelines/autonomous.json +27 -3
  272. package/templates/pipelines/cost-aware.json +34 -8
  273. package/templates/pipelines/deployed.json +12 -0
  274. package/templates/pipelines/enterprise.json +12 -0
  275. package/templates/pipelines/fast.json +6 -0
  276. package/templates/pipelines/full.json +27 -3
  277. package/templates/pipelines/hotfix.json +6 -0
  278. package/templates/pipelines/standard.json +12 -0
  279. package/templates/pipelines/tdd.json +12 -0
@@ -0,0 +1,1022 @@
1
+ # pipeline-stages-review.sh — review, compound_quality, audit stages
2
+ # Source from pipeline-stages.sh. Requires all pipeline globals and dependencies.
3
+ [[ -n "${_PIPELINE_STAGES_REVIEW_LOADED:-}" ]] && return 0
4
+ _PIPELINE_STAGES_REVIEW_LOADED=1
5
+
6
+ stage_review() {
7
+ CURRENT_STAGE_ID="review"
8
+ # Consume retry context if this is a retry attempt
9
+ local _retry_ctx="${ARTIFACTS_DIR}/.retry-context-review.md"
10
+ if [[ -s "$_retry_ctx" ]]; then
11
+ local _review_retry_hints
12
+ _review_retry_hints=$(cat "$_retry_ctx" 2>/dev/null || true)
13
+ rm -f "$_retry_ctx"
14
+ fi
15
+ local diff_file="$ARTIFACTS_DIR/review-diff.patch"
16
+ local review_file="$ARTIFACTS_DIR/review.md"
17
+
18
+ _safe_base_diff > "$diff_file" 2>/dev/null || true
19
+
20
+ if [[ ! -s "$diff_file" ]]; then
21
+ warn "No diff found — skipping review"
22
+ return 0
23
+ fi
24
+
25
+ if ! command -v claude >/dev/null 2>&1; then
26
+ warn "Claude CLI not found — skipping AI review"
27
+ return 0
28
+ fi
29
+
30
+ local diff_stats
31
+ diff_stats=$(_safe_base_diff --stat | tail -1 || echo "")
32
+ info "Running AI code review... ${DIM}($diff_stats)${RESET}"
33
+
34
+ # Semantic risk scoring when intelligence is enabled
35
+ if type intelligence_search_memory >/dev/null 2>&1 && command -v claude >/dev/null 2>&1; then
36
+ local diff_files
37
+ diff_files=$(_safe_base_diff --name-only || true)
38
+ local risk_score="low"
39
+ # Fast heuristic: flag high-risk file patterns
40
+ if echo "$diff_files" | grep -qiE 'migration|schema|auth|crypto|security|password|token|secret|\.env'; then
41
+ risk_score="high"
42
+ elif echo "$diff_files" | grep -qiE 'api|route|controller|middleware|hook'; then
43
+ risk_score="medium"
44
+ fi
45
+ emit_event "review.risk_assessed" \
46
+ "issue=${ISSUE_NUMBER:-0}" \
47
+ "risk=$risk_score" \
48
+ "files_changed=$(echo "$diff_files" | wc -l | xargs)"
49
+ if [[ "$risk_score" == "high" ]]; then
50
+ warn "High-risk changes detected (DB schema, auth, crypto, or secrets)"
51
+ fi
52
+ fi
53
+
54
+ local review_model="${MODEL:-opus}"
55
+ # Intelligence model routing (when no explicit CLI --model override)
56
+ if [[ -z "$MODEL" && -n "${CLAUDE_MODEL:-}" ]]; then
57
+ review_model="$CLAUDE_MODEL"
58
+ fi
59
+
60
+ # Build adversarial review prompt with project context
61
+ local review_prompt="You are a SKEPTICAL senior engineer reviewing code for production.
62
+ Your job is to FIND PROBLEMS, not confirm quality.
63
+
64
+ For each issue found, use this format:
65
+ - **[SEVERITY]** file:line — description
66
+
67
+ Severity levels: Critical, Bug, Security, Warning, Suggestion
68
+
69
+ Mandatory requirements:
70
+ 1. Find at least 3 issues (or explain why code is exceptional)
71
+ 2. Check EVERY acceptance criterion — mark PASS/FAIL with evidence
72
+ 3. Flag every scope creep (unplanned files)
73
+ 4. Verify all never-ship rules are not violated
74
+ 5. Assess all always-require rules are met
75
+
76
+ Focus areas:
77
+ 1. Logic bugs and edge cases
78
+ 2. Security vulnerabilities (injection, XSS, auth bypass, etc.)
79
+ 3. Error handling gaps
80
+ 4. Performance issues
81
+ 5. Missing validation
82
+ 6. Project convention violations (see standards below)
83
+ 7. Architectural constraint violations
84
+ 8. Data validation and sanitization gaps
85
+
86
+ Be thorough and adversarial. Only accept exceptional code without issues.
87
+ "
88
+
89
+ # Dark factory: inject spec compliance check into review
90
+ local spec_file="${ARTIFACTS_DIR}/spec.json"
91
+ if [[ -f "$spec_file" ]] && type spec_diff >/dev/null 2>&1; then
92
+ SPEC_DIR="${ARTIFACTS_DIR}/specs"
93
+ local compliance_report
94
+ compliance_report=$(spec_diff "$spec_file" "${PROJECT_ROOT:-.}" 2>/dev/null || true)
95
+ if [[ -n "$compliance_report" && -f "$compliance_report" ]]; then
96
+ local verdict
97
+ verdict=$(jq -r '.verdict // "unknown"' "$compliance_report" 2>/dev/null || echo "unknown")
98
+ if [[ "$verdict" == "review_needed" ]]; then
99
+ local unmod_files
100
+ unmod_files=$(jq -r '.file_coverage.unmodified_files[]?' "$compliance_report" 2>/dev/null | head -5 || true)
101
+ review_prompt+="
102
+ ## Spec Compliance Warning
103
+ The specification expected changes to files that were NOT modified:
104
+ ${unmod_files}
105
+ Check if these files should have been changed to meet the spec goals.
106
+ "
107
+ fi
108
+ fi
109
+ fi
110
+
111
+ # Inject quality profile standards (never-ship, always-require, focus areas)
112
+ local quality_profile="${PROJECT_ROOT}/.claude/quality-profile.json"
113
+ if [[ -f "$quality_profile" ]]; then
114
+ local never_ship_rules always_require_rules focus_areas learned_rules
115
+
116
+ never_ship_rules=$(jq -r '.quality.never_ship[]? // empty' "$quality_profile" 2>/dev/null | sed 's/^/ - /')
117
+ if [[ -n "$never_ship_rules" ]]; then
118
+ review_prompt+="
119
+ ## Project Standards — NEVER SHIP Rules
120
+ These are absolute violations that must be caught:
121
+ ${never_ship_rules}
122
+ "
123
+ fi
124
+
125
+ always_require_rules=$(jq -r '.quality.always_require[]? // empty' "$quality_profile" 2>/dev/null | sed 's/^/ - /')
126
+ if [[ -n "$always_require_rules" ]]; then
127
+ review_prompt+="
128
+ ## Project Standards — ALWAYS REQUIRE
129
+ These must be present in this PR:
130
+ ${always_require_rules}
131
+ "
132
+ fi
133
+
134
+ focus_areas=$(jq -r '.review.focus_areas[]? // empty' "$quality_profile" 2>/dev/null | sed 's/^/ - /')
135
+ if [[ -n "$focus_areas" ]]; then
136
+ review_prompt+="
137
+ ## Review Focus Areas
138
+ Pay extra attention to these areas for this project:
139
+ ${focus_areas}
140
+ "
141
+ fi
142
+
143
+ learned_rules=$(jq -r '.quality.learned_rules[]? | "\(.rule) (source: \(.source), confidence: \(.confidence))" | @base64' "$quality_profile" 2>/dev/null | while read -r encoded; do
144
+ [[ -z "$encoded" ]] && continue
145
+ echo "$encoded" | base64 -d 2>/dev/null || true
146
+ done | sed 's/^/ - /')
147
+ if [[ -n "$learned_rules" ]]; then
148
+ review_prompt+="
149
+ ## Learned Rules from Previous Reviews
150
+ These patterns were discovered from past code review findings:
151
+ ${learned_rules}
152
+ "
153
+ fi
154
+ fi
155
+
156
+ # Inject acceptance criteria from intake stage
157
+ local acceptance_file="$ARTIFACTS_DIR/acceptance-criteria.json"
158
+ if [[ -f "$acceptance_file" ]]; then
159
+ local ac_list
160
+ ac_list=$(jq -r '.acceptance_criteria[]? // empty' "$acceptance_file" 2>/dev/null | sed 's/^/ - /')
161
+ if [[ -n "$ac_list" ]]; then
162
+ review_prompt+="
163
+ ## Definition of Done (Acceptance Criteria)
164
+ Verify EVERY criterion below is met:
165
+ ${ac_list}
166
+ "
167
+ fi
168
+ fi
169
+
170
+ # Inject scope report (planned vs actual files)
171
+ local scope_file="$ARTIFACTS_DIR/scope-report.json"
172
+ if [[ -f "$scope_file" ]]; then
173
+ local planned_files unplanned_files
174
+ planned_files=$(jq -r '.planned_files[]? // empty' "$scope_file" 2>/dev/null | sed 's/^/ - /')
175
+ unplanned_files=$(jq -r '.unplanned_files[]? // empty' "$scope_file" 2>/dev/null | sed 's/^/ - UNPLANNED: /')
176
+
177
+ if [[ -n "$planned_files" ]]; then
178
+ review_prompt+="
179
+ ## Scope Report
180
+ Planned files to modify:
181
+ ${planned_files}
182
+ "
183
+ fi
184
+ if [[ -n "$unplanned_files" ]]; then
185
+ review_prompt+="
186
+ Unplanned files changed (scope creep?):
187
+ ${unplanned_files}
188
+ "
189
+ fi
190
+ fi
191
+
192
+ # Inject previous review findings and anti-patterns from memory
193
+ if type intelligence_search_memory >/dev/null 2>&1; then
194
+ local review_memory
195
+ review_memory=$(intelligence_search_memory "code review findings anti-patterns for: ${GOAL:-}" "${HOME}/.shipwright/memory" 5 2>/dev/null) || true
196
+ review_memory=$(prune_context_section "memory" "$review_memory" 10000)
197
+ if [[ -n "$review_memory" ]]; then
198
+ review_prompt+="
199
+ ## Known Issues from Previous Reviews
200
+ These anti-patterns and issues have been found in past reviews of this codebase. Flag them if they recur:
201
+ ${review_memory}
202
+ "
203
+ fi
204
+ fi
205
+
206
+ # Inject project conventions if CLAUDE.md exists
207
+ local claudemd="$PROJECT_ROOT/.claude/CLAUDE.md"
208
+ if [[ -f "$claudemd" ]]; then
209
+ local conventions
210
+ conventions=$(grep -A2 'Common Pitfalls\|Shell Standards\|Bash 3.2' "$claudemd" 2>/dev/null | head -20 || true)
211
+ if [[ -n "$conventions" ]]; then
212
+ review_prompt+="
213
+ ## Project Conventions
214
+ ${conventions}
215
+ "
216
+ fi
217
+ fi
218
+
219
+ # ── Constitutional AI: deterministic principle-based checks ──
220
+ if type constitutional_load >/dev/null 2>&1; then
221
+ if constitutional_load 2>/dev/null; then
222
+ local constitution_violations
223
+ constitution_violations=$(constitutional_self_critique "$BASE_BRANCH" 2>/dev/null) || true
224
+ constitution_violations="${constitution_violations:-0}"
225
+ if [[ "$constitution_violations" -gt 0 ]]; then
226
+ local constitution_prompt
227
+ constitution_prompt=$(constitutional_inject_prompt "" "high" 2>/dev/null || true)
228
+ if [[ -n "$constitution_prompt" ]]; then
229
+ review_prompt+="
230
+ ${constitution_prompt}
231
+ "
232
+ fi
233
+ fi
234
+ fi
235
+ fi
236
+
237
+ # Inject CODEOWNERS focus areas for review
238
+ if [[ "${NO_GITHUB:-}" != "true" ]] && type gh_codeowners >/dev/null 2>&1; then
239
+ local review_owners
240
+ review_owners=$(gh_codeowners 2>/dev/null | head -10 || true)
241
+ if [[ -n "$review_owners" ]]; then
242
+ review_prompt+="
243
+ ## Code Owners (focus areas)
244
+ ${review_owners}
245
+ "
246
+ fi
247
+ fi
248
+
249
+ # Inject Definition of Done if present
250
+ local dod_file="$PROJECT_ROOT/.claude/DEFINITION-OF-DONE.md"
251
+ if [[ -f "$dod_file" ]]; then
252
+ review_prompt+="
253
+ ## Definition of Done (verify these)
254
+ $(cat "$dod_file")
255
+ "
256
+ fi
257
+
258
+ # Inject skill prompts for review stage
259
+ # Prefer adaptive selection when available
260
+ if type skill_select_adaptive >/dev/null 2>&1; then
261
+ local _review_skill_files _review_skills
262
+ _review_skill_files=$(skill_select_adaptive "${INTELLIGENCE_ISSUE_TYPE:-backend}" "review" "${ISSUE_BODY:-}" "${INTELLIGENCE_COMPLEXITY:-5}" 2>/dev/null || true)
263
+ if [[ -n "$_review_skill_files" ]]; then
264
+ _review_skills=$(while IFS= read -r _path; do
265
+ [[ -z "$_path" ]] && continue
266
+ [[ -f "$_path" ]] && cat "$_path" 2>/dev/null
267
+ done <<< "$_review_skill_files")
268
+ if [[ -n "$_review_skills" ]]; then
269
+ _review_skills=$(prune_context_section "review-skills" "$_review_skills" 5000)
270
+ review_prompt+="
271
+ ## Review Skill Guidance (${INTELLIGENCE_ISSUE_TYPE:-backend} issue)
272
+ ${_review_skills}
273
+ "
274
+ fi
275
+ fi
276
+ elif type skill_load_prompts >/dev/null 2>&1; then
277
+ # Fallback to static selection
278
+ local _review_skills
279
+ _review_skills=$(skill_load_prompts "${INTELLIGENCE_ISSUE_TYPE:-backend}" "review" 2>/dev/null || true)
280
+ if [[ -n "$_review_skills" ]]; then
281
+ _review_skills=$(prune_context_section "review-skills" "$_review_skills" 5000)
282
+ review_prompt+="
283
+ ## Review Skill Guidance (${INTELLIGENCE_ISSUE_TYPE:-backend} issue)
284
+ ${_review_skills}
285
+ "
286
+ fi
287
+ fi
288
+
289
+ review_prompt+="
290
+ ## Diff to Review
291
+ $(cat "$diff_file")"
292
+
293
+ # Inject skill prompts for review stage
294
+ _skill_prompts=""
295
+ if type skill_load_from_plan >/dev/null 2>&1; then
296
+ _skill_prompts=$(skill_load_from_plan "review" 2>/dev/null || true)
297
+ elif type skill_select_adaptive >/dev/null 2>&1; then
298
+ local _skill_files
299
+ _skill_files=$(skill_select_adaptive "${INTELLIGENCE_ISSUE_TYPE:-backend}" "review" "${ISSUE_BODY:-}" "${INTELLIGENCE_COMPLEXITY:-5}" 2>/dev/null || true)
300
+ if [[ -n "$_skill_files" ]]; then
301
+ _skill_prompts=$(while IFS= read -r _path; do
302
+ [[ -z "$_path" || ! -f "$_path" ]] && continue
303
+ cat "$_path" 2>/dev/null
304
+ done <<< "$_skill_files")
305
+ fi
306
+ elif type skill_load_prompts >/dev/null 2>&1; then
307
+ _skill_prompts=$(skill_load_prompts "${INTELLIGENCE_ISSUE_TYPE:-backend}" "review" 2>/dev/null || true)
308
+ fi
309
+ if [[ -n "$_skill_prompts" ]]; then
310
+ _skill_prompts=$(prune_context_section "skills" "$_skill_prompts" 8000)
311
+ review_prompt="${review_prompt}
312
+ ## Skill Guidance (${INTELLIGENCE_ISSUE_TYPE:-backend} issue, AI-selected)
313
+ ${_skill_prompts}
314
+ "
315
+ fi
316
+
317
+ # Guard total prompt size
318
+ review_prompt=$(guard_prompt_size "review" "$review_prompt")
319
+
320
+ # Skip permissions — pipeline runs headlessly (claude -p) and has no terminal
321
+ # for interactive permission prompts. Same rationale as build stage (line ~1083).
322
+ local review_args=(--print)
323
+ local _review_flags
324
+ _review_flags="$(_pipeline_claude_flags "review" "$review_model")"
325
+ # shellcheck disable=SC2206
326
+ review_args+=($_review_flags --max-turns "$(_smart_int "max_turns.pipeline_stage" 25)" --dangerously-skip-permissions)
327
+
328
+ # ── Two-Stage Review: Pass 1 (Spec Compliance) ──
329
+ local _two_stage=false
330
+ if type skill_has_two_stage_review >/dev/null 2>&1 && skill_has_two_stage_review "${INTELLIGENCE_ISSUE_TYPE:-backend}"; then
331
+ _two_stage=true
332
+ local spec_review_file="$ARTIFACTS_DIR/review-spec.md"
333
+ local plan_file="$ARTIFACTS_DIR/plan.md"
334
+
335
+ if [[ -s "$plan_file" ]]; then
336
+ info "Two-stage review: Pass 1 — Spec compliance"
337
+ local spec_prompt="You are a spec compliance reviewer. Compare the implementation against the plan.
338
+
339
+ ## Plan
340
+ $(cat "$plan_file" 2>/dev/null | head -200)
341
+
342
+ ## Implementation Diff
343
+ $(cat "$diff_file" 2>/dev/null)
344
+
345
+ ## Task
346
+ Compare the diff against the plan:
347
+ 1. Does the code implement every task from the plan's checklist?
348
+ 2. Were all planned files actually modified?
349
+ 3. Is anything from the plan NOT implemented?
350
+ 4. Was anything added that WASN'T in the plan?
351
+
352
+ For each gap found:
353
+ - **[SPEC-GAP]** description — what was planned vs what was implemented
354
+
355
+ If all requirements are met, write: \"Spec compliance: PASS — all planned tasks implemented.\"
356
+ "
357
+ spec_prompt=$(guard_prompt_size "spec-review" "$spec_prompt")
358
+ claude "${review_args[@]}" "$spec_prompt" < /dev/null > "$spec_review_file" 2>"${ARTIFACTS_DIR}/.claude-tokens-spec-review.log" || true
359
+ parse_claude_tokens "${ARTIFACTS_DIR}/.claude-tokens-spec-review.log"
360
+
361
+ if [[ -s "$spec_review_file" ]]; then
362
+ local spec_gaps
363
+ spec_gaps=$(grep -c 'SPEC-GAP' "$spec_review_file" 2>/dev/null || true)
364
+ spec_gaps="${spec_gaps:-0}"
365
+ if [[ "$spec_gaps" -gt 0 ]]; then
366
+ warn "Spec review found $spec_gaps gap(s) — see $spec_review_file"
367
+ else
368
+ success "Spec compliance: PASS"
369
+ fi
370
+ emit_event "review.spec_complete" \
371
+ "issue=${ISSUE_NUMBER:-0}" \
372
+ "gaps=$spec_gaps"
373
+ fi
374
+ info "Two-stage review: Pass 2 — Code quality"
375
+ fi
376
+ fi
377
+
378
+ claude "${review_args[@]}" "$review_prompt" < /dev/null > "$review_file" 2>"${ARTIFACTS_DIR}/.claude-tokens-review.log" || true
379
+ parse_claude_tokens "${ARTIFACTS_DIR}/.claude-tokens-review.log"
380
+
381
+ if [[ ! -s "$review_file" ]]; then
382
+ warn "Review produced no output — check ${ARTIFACTS_DIR}/.claude-tokens-review.log for errors"
383
+ return 0
384
+ fi
385
+
386
+ # Extract severity counts — try JSON structure first, then grep fallback
387
+ local critical_count=0 bug_count=0 warning_count=0
388
+
389
+ # Check if review output is structured JSON (e.g. from structured review tools)
390
+ local json_parsed=false
391
+ if head -1 "$review_file" 2>/dev/null | grep -q '^{' 2>/dev/null; then
392
+ local j_critical j_bug j_warning
393
+ j_critical=$(jq -r '.issues | map(select(.severity == "Critical")) | length' "$review_file" 2>/dev/null || echo "")
394
+ if [[ -n "$j_critical" && "$j_critical" != "null" ]]; then
395
+ critical_count="$j_critical"
396
+ bug_count=$(jq -r '.issues | map(select(.severity == "Bug" or .severity == "Security")) | length' "$review_file" 2>/dev/null || echo "0")
397
+ warning_count=$(jq -r '.issues | map(select(.severity == "Warning" or .severity == "Suggestion")) | length' "$review_file" 2>/dev/null || echo "0")
398
+ json_parsed=true
399
+ fi
400
+ fi
401
+
402
+ # Grep fallback for markdown-formatted review output
403
+ if [[ "$json_parsed" != "true" ]]; then
404
+ critical_count=$(grep -ciE '\*\*\[?Critical\]?\*\*' "$review_file" 2>/dev/null || true)
405
+ critical_count="${critical_count:-0}"
406
+ bug_count=$(grep -ciE '\*\*\[?(Bug|Security)\]?\*\*' "$review_file" 2>/dev/null || true)
407
+ bug_count="${bug_count:-0}"
408
+ warning_count=$(grep -ciE '\*\*\[?(Warning|Suggestion)\]?\*\*' "$review_file" 2>/dev/null || true)
409
+ warning_count="${warning_count:-0}"
410
+ fi
411
+ local total_issues=$((critical_count + bug_count + warning_count))
412
+
413
+ if [[ "$critical_count" -gt 0 ]]; then
414
+ error "Review found ${BOLD}$critical_count critical${RESET} issue(s) — see $review_file"
415
+ elif [[ "$bug_count" -gt 0 ]]; then
416
+ warn "Review found $bug_count bug/security issue(s) — see ${DIM}$review_file${RESET}"
417
+ elif [[ "$total_issues" -gt 0 ]]; then
418
+ info "Review found $total_issues suggestion(s)"
419
+ else
420
+ success "Review clean"
421
+ fi
422
+
423
+ # ── Dark factory: formal spec verification ──
424
+ if type formal_spec_extract >/dev/null 2>&1; then
425
+ local _spec_file="${ARTIFACTS_DIR}/formal-specs.json"
426
+ local _spec_report="${ARTIFACTS_DIR}/formal-spec-report.json"
427
+ local diff_files_for_spec
428
+ diff_files_for_spec=$(_safe_base_diff --name-only 2>/dev/null || true)
429
+ if [[ -n "$diff_files_for_spec" ]]; then
430
+ formal_spec_extract "${PROJECT_ROOT:-.}" "$_spec_file" >/dev/null 2>&1 || true
431
+ if [[ -f "$_spec_file" ]]; then
432
+ local _spec_count
433
+ _spec_count=$(jq -r '.count // 0' "$_spec_file" 2>/dev/null || echo "0")
434
+ if [[ "$_spec_count" -gt 0 ]]; then
435
+ formal_spec_verify "$_spec_file" "${PROJECT_ROOT:-.}" "$_spec_report" >/dev/null 2>&1 || true
436
+ if [[ -f "$_spec_report" ]]; then
437
+ local _spec_violations _spec_pct
438
+ _spec_violations=$(jq -r '.violations // 0' "$_spec_report" 2>/dev/null || echo "0")
439
+ _spec_pct=$(jq -r '.compliance_pct // 100' "$_spec_report" 2>/dev/null || echo "100")
440
+ if [[ "$_spec_violations" -gt 0 ]]; then
441
+ warn "Formal spec verification: ${_spec_violations} violation(s), ${_spec_pct}% compliance"
442
+ else
443
+ success "Formal spec verification: ${_spec_pct}% compliance"
444
+ fi
445
+ emit_event "review.formal_spec" \
446
+ "issue=${ISSUE_NUMBER:-0}" \
447
+ "violations=$_spec_violations" \
448
+ "compliance_pct=$_spec_pct" 2>/dev/null || true
449
+ fi
450
+ fi
451
+ fi
452
+ fi
453
+ fi
454
+
455
+ # ── Oversight gate: pipeline review/quality stages block on verdict ──
456
+ if [[ -x "$SCRIPT_DIR/sw-oversight.sh" ]] && [[ "${SKIP_GATES:-false}" != "true" ]]; then
457
+ local reject_reason=""
458
+ local _sec_count
459
+ _sec_count=$(grep -ciE '\*\*\[?Security\]?\*\*' "$review_file" 2>/dev/null || true)
460
+ _sec_count="${_sec_count:-0}"
461
+ local _blocking=$((critical_count + _sec_count))
462
+ [[ "$_blocking" -gt 0 ]] && reject_reason="Review found ${_blocking} critical/security issue(s)"
463
+ if ! bash "$SCRIPT_DIR/sw-oversight.sh" gate --diff "$diff_file" --description "${GOAL:-Pipeline review}" --reject-if "$reject_reason" >/dev/null 2>&1; then
464
+ error "Oversight gate rejected — blocking pipeline"
465
+ emit_event "review.oversight_blocked" "issue=${ISSUE_NUMBER:-0}"
466
+ log_stage "review" "BLOCKED: oversight gate rejected"
467
+ return 1
468
+ fi
469
+ fi
470
+
471
+ # ── Review Blocking Gate ──
472
+ # Block pipeline on critical/bug/security issues (bugs now block as per spec)
473
+ local security_count
474
+ security_count=$(grep -ciE '\*\*\[?Security\]?\*\*' "$review_file" 2>/dev/null || true)
475
+ security_count="${security_count:-0}"
476
+
477
+ local blocking_issues=$((critical_count + bug_count + security_count))
478
+
479
+ if [[ "$blocking_issues" -gt 0 ]]; then
480
+ # Check if compound_quality stage is enabled — if so, let it handle issues
481
+ local compound_enabled="false"
482
+ if [[ -n "${PIPELINE_CONFIG:-}" && -f "${PIPELINE_CONFIG:-/dev/null}" ]]; then
483
+ compound_enabled=$(jq -r '.stages[] | select(.id == "compound_quality") | .enabled' "$PIPELINE_CONFIG" 2>/dev/null) || true
484
+ [[ -z "$compound_enabled" || "$compound_enabled" == "null" ]] && compound_enabled="false"
485
+ fi
486
+
487
+ # Check if this is a fast template (don't block fast pipelines)
488
+ local is_fast="false"
489
+ if [[ "${PIPELINE_NAME:-}" == "fast" || "${PIPELINE_NAME:-}" == "hotfix" ]]; then
490
+ is_fast="true"
491
+ fi
492
+
493
+ if [[ "$compound_enabled" == "true" ]]; then
494
+ info "Review found ${blocking_issues} critical/security issue(s) — compound_quality stage will handle"
495
+ elif [[ "$is_fast" == "true" ]]; then
496
+ warn "Review found ${blocking_issues} critical/security issue(s) — fast template, not blocking"
497
+ elif [[ "${SKIP_GATES:-false}" == "true" ]]; then
498
+ warn "Review found ${blocking_issues} critical/security issue(s) — skip-gates mode, not blocking"
499
+ else
500
+ error "Review found ${BOLD}${blocking_issues} critical/security issue(s)${RESET} — blocking pipeline"
501
+ emit_event "review.blocked" \
502
+ "issue=${ISSUE_NUMBER:-0}" \
503
+ "critical=${critical_count}" \
504
+ "security=${security_count}"
505
+
506
+ # Save blocking issues for self-healing context
507
+ grep -iE '\*\*\[?(Critical|Security)\]?\*\*' "$review_file" > "$ARTIFACTS_DIR/review-blockers.md" 2>/dev/null || true
508
+
509
+ # Post review to GitHub before failing
510
+ if [[ -n "$ISSUE_NUMBER" ]]; then
511
+ local review_summary
512
+ review_summary=$(head -40 "$review_file")
513
+ gh_comment_issue "$ISSUE_NUMBER" "## 🔍 Code Review — ❌ Blocked
514
+
515
+ **Stats:** $diff_stats
516
+ **Blocking issues:** ${blocking_issues} (${critical_count} critical, ${security_count} security)
517
+
518
+ <details>
519
+ <summary>Review details</summary>
520
+
521
+ ${review_summary}
522
+
523
+ </details>
524
+
525
+ _Pipeline will attempt self-healing rebuild._"
526
+ fi
527
+
528
+ log_stage "review" "BLOCKED: $blocking_issues critical/security issues found"
529
+ return 1
530
+ fi
531
+ fi
532
+
533
+ # Post review to GitHub issue
534
+ if [[ -n "$ISSUE_NUMBER" ]]; then
535
+ local review_summary
536
+ review_summary=$(head -40 "$review_file")
537
+ gh_comment_issue "$ISSUE_NUMBER" "## 🔍 Code Review
538
+
539
+ **Stats:** $diff_stats
540
+ **Issues found:** $total_issues (${critical_count} critical, ${bug_count} bugs, ${warning_count} suggestions)
541
+
542
+ <details>
543
+ <summary>Review details</summary>
544
+
545
+ ${review_summary}
546
+
547
+ </details>"
548
+ fi
549
+
550
+ log_stage "review" "AI review complete ($total_issues issues: $critical_count critical, $bug_count bugs, $warning_count suggestions)"
551
+ }
552
+
553
+ # ─── Spec Verification Stage (dark factory: spec-driven development) ────────
554
+ # Runs between review and compound_quality. Verifies implementation against
555
+ # the spec by checking each acceptance criterion.
556
+ stage_spec_verification() {
557
+ CURRENT_STAGE_ID="spec_verification"
558
+
559
+ # Check if spec-driven is disabled
560
+ if [[ "${SPEC_DRIVEN_ENABLED:-true}" == "false" ]]; then
561
+ info "Spec-driven development disabled — skipping spec verification"
562
+ return 0
563
+ fi
564
+
565
+ local spec_file="${ARTIFACTS_DIR}/spec.json"
566
+
567
+ if [[ ! -f "$spec_file" ]]; then
568
+ info "No spec found — skipping spec verification"
569
+ log_stage "spec_verification" "Skipped: no spec available"
570
+ return 0
571
+ fi
572
+
573
+ info "Verifying implementation against specification..."
574
+
575
+ local criteria_count
576
+ criteria_count=$(jq '.acceptance_criteria | length' "$spec_file" 2>/dev/null || echo "0")
577
+
578
+ if [[ "$criteria_count" -eq 0 ]]; then
579
+ warn "Spec has no acceptance criteria — skipping verification"
580
+ log_stage "spec_verification" "Skipped: no acceptance criteria in spec"
581
+ return 0
582
+ fi
583
+
584
+ # Build verification results
585
+ local verified=0
586
+ local unverified=0
587
+ local manual_review=0
588
+ local results_json="["
589
+ local first=true
590
+ local idx=0
591
+
592
+ while [[ "$idx" -lt "$criteria_count" ]]; do
593
+ local criterion verification_method testable
594
+ criterion=$(jq -r --argjson i "$idx" '.acceptance_criteria[$i].criterion // ""' "$spec_file" 2>/dev/null)
595
+ verification_method=$(jq -r --argjson i "$idx" '.acceptance_criteria[$i].verification_method // "manual"' "$spec_file" 2>/dev/null)
596
+ testable=$(jq -r --argjson i "$idx" '.acceptance_criteria[$i].testable // false' "$spec_file" 2>/dev/null)
597
+
598
+ local status="unverified"
599
+ local evidence=""
600
+
601
+ case "$verification_method" in
602
+ unit_test|integration_test)
603
+ # Check if tests exist and pass by looking at test results
604
+ local test_results="${ARTIFACTS_DIR}/test-results.log"
605
+ if [[ -f "$test_results" ]]; then
606
+ # If test results exist and contain no failures, mark as verified
607
+ local test_failures
608
+ test_failures=$(grep -ciE 'fail|error|FAIL' "$test_results" 2>/dev/null || true)
609
+ test_failures="${test_failures:-0}"
610
+ if [[ "$test_failures" -eq 0 ]]; then
611
+ status="verified"
612
+ evidence="Tests passed (no failures in test-results.log)"
613
+ verified=$((verified + 1))
614
+ else
615
+ status="unverified"
616
+ evidence="Test failures detected in test-results.log"
617
+ unverified=$((unverified + 1))
618
+ fi
619
+ else
620
+ status="unverified"
621
+ evidence="No test results found"
622
+ unverified=$((unverified + 1))
623
+ fi
624
+ ;;
625
+ static_analysis)
626
+ # Check if constitutional checker ran and passed
627
+ local const_report="${ARTIFACTS_DIR}/constitutional-audit.json"
628
+ if [[ -f "$const_report" ]]; then
629
+ local violations
630
+ violations=$(jq '.total_violations // 0' "$const_report" 2>/dev/null || echo "0")
631
+ if [[ "$violations" -eq 0 ]]; then
632
+ status="verified"
633
+ evidence="Constitutional audit passed (0 violations)"
634
+ verified=$((verified + 1))
635
+ else
636
+ status="unverified"
637
+ evidence="Constitutional audit found $violations violations"
638
+ unverified=$((unverified + 1))
639
+ fi
640
+ else
641
+ status="unverified"
642
+ evidence="No static analysis results found"
643
+ unverified=$((unverified + 1))
644
+ fi
645
+ ;;
646
+ manual)
647
+ status="manual_review"
648
+ evidence="Requires human review"
649
+ manual_review=$((manual_review + 1))
650
+ ;;
651
+ *)
652
+ status="unverified"
653
+ evidence="Unknown verification method: $verification_method"
654
+ unverified=$((unverified + 1))
655
+ ;;
656
+ esac
657
+
658
+ # Build JSON result entry
659
+ local escaped_criterion escaped_evidence
660
+ escaped_criterion=$(printf '%s' "$criterion" | jq -Rs . 2>/dev/null || echo "\"$criterion\"")
661
+ escaped_evidence=$(printf '%s' "$evidence" | jq -Rs . 2>/dev/null || echo "\"$evidence\"")
662
+
663
+ if $first; then
664
+ first=false
665
+ else
666
+ results_json="${results_json},"
667
+ fi
668
+ results_json="${results_json}{\"criterion\":${escaped_criterion},\"verification_method\":\"${verification_method}\",\"status\":\"${status}\",\"evidence\":${escaped_evidence}}"
669
+
670
+ idx=$((idx + 1))
671
+ done
672
+ results_json="${results_json}]"
673
+
674
+ # Compute compliance score
675
+ local total_checkable=$((verified + unverified))
676
+ local compliance_score=0
677
+ if [[ "$total_checkable" -gt 0 ]]; then
678
+ compliance_score=$((verified * 100 / total_checkable))
679
+ elif [[ "$manual_review" -gt 0 && "$unverified" -eq 0 ]]; then
680
+ # All criteria are manual — treat as 100% machine compliance
681
+ compliance_score=100
682
+ fi
683
+
684
+ # Generate verification report
685
+ local report_file="${ARTIFACTS_DIR}/spec-verification-report.json"
686
+ cat > "$report_file" <<REPORTEOF
687
+ {
688
+ "spec_file": "${spec_file}",
689
+ "verified_at": "$(now_iso)",
690
+ "summary": {
691
+ "total_criteria": ${criteria_count},
692
+ "verified": ${verified},
693
+ "unverified": ${unverified},
694
+ "manual_review": ${manual_review},
695
+ "compliance_score": ${compliance_score}
696
+ },
697
+ "results": ${results_json}
698
+ }
699
+ REPORTEOF
700
+
701
+ # Pretty-print if jq available
702
+ if command -v jq >/dev/null 2>&1; then
703
+ local pp
704
+ pp=$(jq '.' "$report_file" 2>/dev/null) || true
705
+ if [[ -n "$pp" ]]; then
706
+ echo "$pp" > "$report_file"
707
+ fi
708
+ fi
709
+
710
+ save_artifact "spec-verification-report.json" "$(cat "$report_file")" || true
711
+
712
+ # Report results
713
+ if [[ "$compliance_score" -lt 80 ]]; then
714
+ warn "Spec compliance: ${compliance_score}% (${verified}/${total_checkable} verified) — below 80% threshold"
715
+ else
716
+ success "Spec compliance: ${compliance_score}% (${verified}/${total_checkable} verified)"
717
+ fi
718
+
719
+ if [[ "$manual_review" -gt 0 ]]; then
720
+ info "${manual_review} criteria flagged for manual review"
721
+ fi
722
+
723
+ emit_event "spec_verification.completed" \
724
+ "issue=${ISSUE_NUMBER:-0}" \
725
+ "compliance=${compliance_score}" \
726
+ "verified=${verified}" \
727
+ "unverified=${unverified}" \
728
+ "manual=${manual_review}"
729
+
730
+ log_stage "spec_verification" "Compliance: ${compliance_score}% — ${verified} verified, ${unverified} unverified, ${manual_review} manual"
731
+ }
732
+
733
+ # ─── Compound Quality (fallback) ────────────────────────────────────────────
734
+ # Machine-verifiable DoD scorecard, then adversarial review, negative testing, e2e checks.
735
+ # If pipeline-intelligence.sh was sourced first, its enhanced version takes priority.
736
+ if ! type stage_compound_quality >/dev/null 2>&1; then
737
+ stage_compound_quality() {
738
+ CURRENT_STAGE_ID="compound_quality"
739
+ # Consume retry context if this is a retry attempt
740
+ local _retry_ctx="${ARTIFACTS_DIR}/.retry-context-compound_quality.md"
741
+ if [[ -s "$_retry_ctx" ]]; then
742
+ local _cq_retry_hints
743
+ _cq_retry_hints=$(cat "$_retry_ctx" 2>/dev/null || true)
744
+ rm -f "$_retry_ctx"
745
+ fi
746
+
747
+ # Source DoD scorecard library
748
+ if [[ -f "$SCRIPT_DIR/lib/dod-scorecard.sh" ]]; then
749
+ # shellcheck disable=SC1090
750
+ source "$SCRIPT_DIR/lib/dod-scorecard.sh"
751
+ fi
752
+
753
+ # ── Machine-Verifiable DoD Scorecard (runs first) ──
754
+ info "Computing machine-verifiable Definition of Done scorecard..."
755
+ local quality_profile="${PROJECT_ROOT}/.claude/quality-profile.json"
756
+ local dod_scorecard_json
757
+ dod_scorecard_json=$(compute_dod_scorecard "${BASE_BRANCH:-main}" "$ARTIFACTS_DIR" "$quality_profile" 2>/dev/null) || true
758
+
759
+ if [[ -n "$dod_scorecard_json" ]]; then
760
+ # Display scorecard
761
+ local scorecard_display
762
+ scorecard_display=$(format_scorecard "$dod_scorecard_json")
763
+ echo "$scorecard_display"
764
+
765
+ # Log scorecard
766
+ log_stage "compound_quality" "DoD Scorecard computed"
767
+
768
+ # If scorecard fails, skip LLM checks and return failure
769
+ if ! scorecard_passed "$dod_scorecard_json"; then
770
+ local blocking_failures
771
+ blocking_failures=$(get_blocking_failures "$dod_scorecard_json")
772
+ error "DoD Scorecard gate failed on: $blocking_failures"
773
+ emit_event "compound_quality.dod_failed" \
774
+ "issue=${ISSUE_NUMBER:-0}" \
775
+ "failures=$blocking_failures"
776
+ return 1
777
+ else
778
+ success "DoD Scorecard gate passed"
779
+ emit_event "compound_quality.dod_passed" "issue=${ISSUE_NUMBER:-0}"
780
+ fi
781
+ fi
782
+
783
+ # Load skill prompts for compound quality (used by adversarial review)
784
+ local _cq_skills=""
785
+ if type skill_load_prompts >/dev/null 2>&1; then
786
+ _cq_skills=$(skill_load_prompts "${INTELLIGENCE_ISSUE_TYPE:-backend}" "compound_quality" 2>/dev/null || true)
787
+ fi
788
+ # Write skill guidance to artifact for sw-adversarial.sh to consume
789
+ if [[ -n "$_cq_skills" ]]; then
790
+ echo "$_cq_skills" > "${ARTIFACTS_DIR}/.compound-quality-skills.md" 2>/dev/null || true
791
+ fi
792
+ if [[ -n "${_cq_retry_hints:-}" ]]; then
793
+ echo "$_cq_retry_hints" >> "${ARTIFACTS_DIR}/.compound-quality-skills.md" 2>/dev/null || true
794
+ fi
795
+
796
+ # Read stage config from pipeline template
797
+ local cfg
798
+ cfg=$(jq -r '.stages[] | select(.id == "compound_quality") | .config // {}' "$PIPELINE_CONFIG" 2>/dev/null) || cfg="{}"
799
+
800
+ local do_adversarial do_negative do_e2e do_dod max_cycles blocking
801
+ do_adversarial=$(echo "$cfg" | jq -r '.adversarial // false')
802
+ do_negative=$(echo "$cfg" | jq -r '.negative // false')
803
+ do_e2e=$(echo "$cfg" | jq -r '.e2e // false')
804
+ do_dod=$(echo "$cfg" | jq -r '.dod_audit // false')
805
+ max_cycles=$(echo "$cfg" | jq -r '.max_cycles // 1')
806
+ blocking=$(echo "$cfg" | jq -r '.compound_quality_blocking // false')
807
+
808
+ local pass_count=0 fail_count=0 total=0
809
+ local compound_log="$ARTIFACTS_DIR/compound-quality.log"
810
+ : > "$compound_log"
811
+
812
+ # ── Adversarial review ──
813
+ if [[ "$do_adversarial" == "true" ]]; then
814
+ total=$((total + 1))
815
+ info "Running adversarial review..."
816
+ if [[ -x "$SCRIPT_DIR/sw-adversarial.sh" ]]; then
817
+ if bash "$SCRIPT_DIR/sw-adversarial.sh" --repo "${REPO_DIR:-.}" >> "$compound_log" 2>&1; then
818
+ pass_count=$((pass_count + 1))
819
+ success "Adversarial review passed"
820
+ else
821
+ fail_count=$((fail_count + 1))
822
+ warn "Adversarial review found issues"
823
+ fi
824
+ else
825
+ warn "sw-adversarial.sh not found, skipping"
826
+ fi
827
+ fi
828
+
829
+ # ── Negative / edge-case testing ──
830
+ if [[ "$do_negative" == "true" ]]; then
831
+ total=$((total + 1))
832
+ info "Running negative test pass..."
833
+ if [[ -n "${TEST_CMD:-}" ]]; then
834
+ if eval "$TEST_CMD" >> "$compound_log" 2>&1; then
835
+ pass_count=$((pass_count + 1))
836
+ success "Negative test pass passed"
837
+ else
838
+ fail_count=$((fail_count + 1))
839
+ warn "Negative test pass found failures"
840
+ fi
841
+ else
842
+ pass_count=$((pass_count + 1))
843
+ info "No test command configured, skipping negative tests"
844
+ fi
845
+ fi
846
+
847
+ # ── E2E checks ──
848
+ if [[ "$do_e2e" == "true" ]]; then
849
+ total=$((total + 1))
850
+ info "Running e2e checks..."
851
+ if [[ -x "$SCRIPT_DIR/sw-e2e-orchestrator.sh" ]]; then
852
+ if bash "$SCRIPT_DIR/sw-e2e-orchestrator.sh" run >> "$compound_log" 2>&1; then
853
+ pass_count=$((pass_count + 1))
854
+ success "E2E checks passed"
855
+ else
856
+ fail_count=$((fail_count + 1))
857
+ warn "E2E checks found issues"
858
+ fi
859
+ else
860
+ pass_count=$((pass_count + 1))
861
+ info "sw-e2e-orchestrator.sh not found, skipping e2e"
862
+ fi
863
+ fi
864
+
865
+ # ── Definition of Done audit ──
866
+ if [[ "$do_dod" == "true" ]]; then
867
+ total=$((total + 1))
868
+ info "Running definition-of-done audit..."
869
+ if [[ -x "$SCRIPT_DIR/sw-quality.sh" ]]; then
870
+ if bash "$SCRIPT_DIR/sw-quality.sh" validate >> "$compound_log" 2>&1; then
871
+ pass_count=$((pass_count + 1))
872
+ success "DoD audit passed"
873
+ else
874
+ fail_count=$((fail_count + 1))
875
+ warn "DoD audit found gaps"
876
+ fi
877
+ else
878
+ pass_count=$((pass_count + 1))
879
+ info "sw-quality.sh not found, skipping DoD audit"
880
+ fi
881
+ fi
882
+
883
+ # ── Summary ──
884
+ log_stage "compound_quality" "Compound quality: $pass_count/$total checks passed, $fail_count failed"
885
+
886
+ if [[ "$fail_count" -gt 0 && "$blocking" == "true" ]]; then
887
+ error "Compound quality gate failed: $fail_count of $total checks failed"
888
+ return 1
889
+ fi
890
+
891
+ return 0
892
+ }
893
+ fi # end fallback stage_compound_quality
894
+
895
+ # ─── Audit Stage ───────────────────────────────────────────────────────────
896
+ # Security and quality audits: secrets scan, file permissions, || true usage,
897
+ # test coverage delta, atomic write checks.
898
+ stage_audit() {
899
+ CURRENT_STAGE_ID="audit"
900
+
901
+ # Read stage config from pipeline template
902
+ local cfg
903
+ cfg=$(jq -r '.stages[] | select(.id == "audit") | .config // {}' "$PIPELINE_CONFIG" 2>/dev/null) || cfg="{}"
904
+
905
+ local do_secret_scan do_perms do_atomic_writes do_coverage blocking
906
+ do_secret_scan=$(echo "$cfg" | jq -r '.secret_scan // true')
907
+ do_perms=$(echo "$cfg" | jq -r '.file_permissions // true')
908
+ do_atomic_writes=$(echo "$cfg" | jq -r '.atomic_writes // true')
909
+ do_coverage=$(echo "$cfg" | jq -r '.coverage_delta // true')
910
+ blocking=$(echo "$cfg" | jq -r '.blocking // false')
911
+
912
+ local audit_log="$ARTIFACTS_DIR/audit.log"
913
+ : > "$audit_log"
914
+
915
+ local issues=0
916
+
917
+ # ── Secret Scanning ──
918
+ if [[ "$do_secret_scan" == "true" ]]; then
919
+ info "Scanning for secrets in changed files..."
920
+ local secret_patterns=(
921
+ "sk-ant-" "ANTHROPIC_API_KEY=" "GITHUB_TOKEN=" "OPENAI_API_KEY="
922
+ "AWS_SECRET_ACCESS_KEY=" "DATABASE_URL=" "PRIVATE_KEY="
923
+ "api_key=" "secret=" "password=" "token="
924
+ )
925
+
926
+ local changed_files
927
+ changed_files=$(git diff --name-only "${BASE_BRANCH:-main}..HEAD" 2>/dev/null || git diff --name-only HEAD~5 2>/dev/null || echo "")
928
+
929
+ for pattern in "${secret_patterns[@]}"; do
930
+ while IFS= read -r file; do
931
+ [[ -z "$file" ]] && continue
932
+ if grep -l "$pattern" "$file" 2>/dev/null | grep -qv node_modules; then
933
+ echo "WARN: Potential secret found in $file: $pattern" >> "$audit_log"
934
+ warn "Found potential secret: $pattern in $file"
935
+ issues=$((issues + 1))
936
+ fi
937
+ done <<< "$changed_files"
938
+ done
939
+ fi
940
+
941
+ # ── File Permission Check ──
942
+ if [[ "$do_perms" == "true" ]]; then
943
+ info "Checking file permissions on sensitive files..."
944
+ local sensitive_patterns=(".env" "secret" "credential" "key" "token" "config")
945
+
946
+ for pattern in "${sensitive_patterns[@]}"; do
947
+ while IFS= read -r file; do
948
+ [[ -z "$file" ]] && continue
949
+ # Check for world-readable files
950
+ local perms
951
+ perms=$(stat -f "%OLp" "$file" 2>/dev/null || stat -c "%a" "$file" 2>/dev/null)
952
+ if [[ "$perms" =~ [4567]$ ]]; then # world-readable
953
+ echo "WARN: World-readable sensitive file: $file ($perms)" >> "$audit_log"
954
+ warn "World-readable file: $file ($perms)"
955
+ issues=$((issues + 1))
956
+ fi
957
+ done < <(find . -name "*${pattern}*" -type f 2>/dev/null | head -20)
958
+ done
959
+ fi
960
+
961
+ # ── || true Count (atomic write pattern) ──
962
+ if [[ "$do_atomic_writes" == "true" ]]; then
963
+ info "Checking for unprotected direct writes (|| true usage)..."
964
+ local baseline_true_count=0
965
+ local current_true_count=0
966
+
967
+ # Baseline (before changes)
968
+ if git rev-parse "${BASE_BRANCH:-main}" >/dev/null 2>&1; then
969
+ baseline_true_count=$(git show "${BASE_BRANCH:-main}:." 2>/dev/null | grep -r "|| true" 2>/dev/null | wc -l)
970
+ fi
971
+
972
+ # Current
973
+ current_true_count=$(grep -r "|| true" --include="*.sh" . 2>/dev/null | wc -l)
974
+
975
+ local true_delta=$((current_true_count - baseline_true_count))
976
+ if [[ $true_delta -gt 0 ]]; then
977
+ echo "WARN: Added $true_delta new '|| true' clauses (may mask errors)" >> "$audit_log"
978
+ warn "Added $true_delta new || true patterns"
979
+ issues=$((issues + 1))
980
+ fi
981
+ fi
982
+
983
+ # ── Test Coverage Delta ──
984
+ if [[ "$do_coverage" == "true" && -n "${COVERAGE_FILE:-}" ]]; then
985
+ info "Comparing test coverage..."
986
+ if [[ -f "$COVERAGE_FILE" ]]; then
987
+ local current_coverage
988
+ current_coverage=$(grep -oP 'Coverage: \K[0-9.]+' "$COVERAGE_FILE" | head -1)
989
+ if [[ -n "$current_coverage" ]]; then
990
+ # Try to get baseline coverage
991
+ local baseline_coverage=0
992
+ if git show "${BASE_BRANCH:-main}:.claude/coverage.txt" >/dev/null 2>&1; then
993
+ baseline_coverage=$(git show "${BASE_BRANCH:-main}:.claude/coverage.txt" 2>/dev/null | \
994
+ grep -oP 'Coverage: \K[0-9.]+' | head -1 || echo "0")
995
+ fi
996
+
997
+ local coverage_delta
998
+ coverage_delta=$(echo "$current_coverage - $baseline_coverage" | bc 2>/dev/null || echo "0")
999
+ if (( $(echo "$coverage_delta < -2" | bc -l 2>/dev/null || echo 0) )); then
1000
+ echo "WARN: Coverage decreased by ${coverage_delta}pp (from ${baseline_coverage}% to ${current_coverage}%)" >> "$audit_log"
1001
+ warn "Coverage delta: ${coverage_delta}pp"
1002
+ issues=$((issues + 1))
1003
+ fi
1004
+ fi
1005
+ fi
1006
+ fi
1007
+
1008
+ log_stage "audit" "Audit complete: $issues issue(s) found"
1009
+
1010
+ if [[ "$issues" -gt 0 && "$blocking" == "true" ]]; then
1011
+ error "Audit gate failed: $issues issue(s) detected"
1012
+ emit_event "pipeline.audit_failed" "issues=$issues"
1013
+ return 1
1014
+ fi
1015
+
1016
+ if [[ "$issues" -gt 0 ]]; then
1017
+ emit_event "pipeline.audit_warnings" "issues=$issues"
1018
+ fi
1019
+
1020
+ return 0
1021
+ }
1022
+