shipwright-cli 3.1.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 (283) 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 +22 -8
  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/config/defaults.json +25 -2
  17. package/config/policy.json +1 -1
  18. package/dashboard/middleware/auth.ts +134 -0
  19. package/dashboard/middleware/constants.ts +21 -0
  20. package/dashboard/public/index.html +8 -6
  21. package/dashboard/public/styles.css +176 -97
  22. package/dashboard/routes/auth.ts +38 -0
  23. package/dashboard/server.ts +117 -25
  24. package/dashboard/services/config.ts +26 -0
  25. package/dashboard/services/db.ts +118 -0
  26. package/dashboard/src/canvas/pixel-agent.ts +298 -0
  27. package/dashboard/src/canvas/pixel-sprites.ts +440 -0
  28. package/dashboard/src/canvas/shipyard-effects.ts +367 -0
  29. package/dashboard/src/canvas/shipyard-scene.ts +616 -0
  30. package/dashboard/src/canvas/submarine-layout.ts +267 -0
  31. package/dashboard/src/components/header.ts +8 -7
  32. package/dashboard/src/core/api.ts +5 -0
  33. package/dashboard/src/core/router.ts +1 -0
  34. package/dashboard/src/design/submarine-theme.ts +253 -0
  35. package/dashboard/src/main.ts +2 -0
  36. package/dashboard/src/types/api.ts +12 -1
  37. package/dashboard/src/views/activity.ts +2 -1
  38. package/dashboard/src/views/metrics.ts +69 -1
  39. package/dashboard/src/views/shipyard.ts +39 -0
  40. package/dashboard/types/index.ts +166 -0
  41. package/docs/plans/2026-02-28-compound-audit-and-shipyard-design.md +186 -0
  42. package/docs/plans/2026-02-28-skipper-shipwright-implementation-plan.md +1182 -0
  43. package/docs/plans/2026-02-28-skipper-shipwright-integration-design.md +531 -0
  44. package/docs/plans/2026-03-01-ai-powered-skill-injection-design.md +298 -0
  45. package/docs/plans/2026-03-01-ai-powered-skill-injection-plan.md +1109 -0
  46. package/docs/plans/2026-03-01-capabilities-cleanup-plan.md +658 -0
  47. package/docs/plans/2026-03-01-clean-architecture-plan.md +924 -0
  48. package/docs/plans/2026-03-01-compound-audit-cascade-design.md +191 -0
  49. package/docs/plans/2026-03-01-compound-audit-cascade-plan.md +921 -0
  50. package/docs/plans/2026-03-01-deep-integration-plan.md +851 -0
  51. package/docs/plans/2026-03-01-pipeline-audit-trail-design.md +145 -0
  52. package/docs/plans/2026-03-01-pipeline-audit-trail-plan.md +770 -0
  53. package/docs/plans/2026-03-01-refined-depths-brand-design.md +382 -0
  54. package/docs/plans/2026-03-01-refined-depths-implementation.md +599 -0
  55. package/docs/plans/2026-03-01-skipper-kernel-integration-design.md +203 -0
  56. package/docs/plans/2026-03-01-unified-platform-design.md +272 -0
  57. package/docs/plans/2026-03-07-claude-code-feature-integration-design.md +189 -0
  58. package/docs/plans/2026-03-07-claude-code-feature-integration-plan.md +1165 -0
  59. package/docs/research/BACKLOG_QUICK_REFERENCE.md +352 -0
  60. package/docs/research/CUTTING_EDGE_RESEARCH_2026.md +546 -0
  61. package/docs/research/RESEARCH_INDEX.md +439 -0
  62. package/docs/research/RESEARCH_SOURCES.md +440 -0
  63. package/docs/research/RESEARCH_SUMMARY.txt +275 -0
  64. package/docs/superpowers/specs/2026-03-10-pipeline-quality-revolution-design.md +341 -0
  65. package/package.json +2 -2
  66. package/scripts/lib/adaptive-model.sh +427 -0
  67. package/scripts/lib/adaptive-timeout.sh +316 -0
  68. package/scripts/lib/audit-trail.sh +309 -0
  69. package/scripts/lib/auto-recovery.sh +471 -0
  70. package/scripts/lib/bandit-selector.sh +431 -0
  71. package/scripts/lib/bootstrap.sh +104 -2
  72. package/scripts/lib/causal-graph.sh +455 -0
  73. package/scripts/lib/compat.sh +126 -0
  74. package/scripts/lib/compound-audit.sh +337 -0
  75. package/scripts/lib/constitutional.sh +454 -0
  76. package/scripts/lib/context-budget.sh +359 -0
  77. package/scripts/lib/convergence.sh +594 -0
  78. package/scripts/lib/cost-optimizer.sh +634 -0
  79. package/scripts/lib/daemon-adaptive.sh +14 -2
  80. package/scripts/lib/daemon-dispatch.sh +106 -17
  81. package/scripts/lib/daemon-failure.sh +34 -4
  82. package/scripts/lib/daemon-patrol.sh +25 -4
  83. package/scripts/lib/daemon-poll-github.sh +361 -0
  84. package/scripts/lib/daemon-poll-health.sh +299 -0
  85. package/scripts/lib/daemon-poll.sh +27 -611
  86. package/scripts/lib/daemon-state.sh +119 -66
  87. package/scripts/lib/daemon-triage.sh +10 -0
  88. package/scripts/lib/dod-scorecard.sh +442 -0
  89. package/scripts/lib/error-actionability.sh +300 -0
  90. package/scripts/lib/formal-spec.sh +461 -0
  91. package/scripts/lib/helpers.sh +180 -5
  92. package/scripts/lib/intent-analysis.sh +409 -0
  93. package/scripts/lib/loop-convergence.sh +350 -0
  94. package/scripts/lib/loop-iteration.sh +682 -0
  95. package/scripts/lib/loop-progress.sh +48 -0
  96. package/scripts/lib/loop-restart.sh +185 -0
  97. package/scripts/lib/memory-effectiveness.sh +506 -0
  98. package/scripts/lib/mutation-executor.sh +352 -0
  99. package/scripts/lib/outcome-feedback.sh +521 -0
  100. package/scripts/lib/pipeline-cli.sh +336 -0
  101. package/scripts/lib/pipeline-commands.sh +1216 -0
  102. package/scripts/lib/pipeline-detection.sh +101 -3
  103. package/scripts/lib/pipeline-execution.sh +897 -0
  104. package/scripts/lib/pipeline-github.sh +28 -3
  105. package/scripts/lib/pipeline-intelligence-compound.sh +431 -0
  106. package/scripts/lib/pipeline-intelligence-scoring.sh +407 -0
  107. package/scripts/lib/pipeline-intelligence-skip.sh +181 -0
  108. package/scripts/lib/pipeline-intelligence.sh +104 -1138
  109. package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
  110. package/scripts/lib/pipeline-quality-checks.sh +17 -711
  111. package/scripts/lib/pipeline-quality-gates.sh +563 -0
  112. package/scripts/lib/pipeline-stages-build.sh +730 -0
  113. package/scripts/lib/pipeline-stages-delivery.sh +965 -0
  114. package/scripts/lib/pipeline-stages-intake.sh +1133 -0
  115. package/scripts/lib/pipeline-stages-monitor.sh +407 -0
  116. package/scripts/lib/pipeline-stages-review.sh +1022 -0
  117. package/scripts/lib/pipeline-stages.sh +161 -2901
  118. package/scripts/lib/pipeline-state.sh +36 -5
  119. package/scripts/lib/pipeline-util.sh +487 -0
  120. package/scripts/lib/policy-learner.sh +438 -0
  121. package/scripts/lib/process-reward.sh +493 -0
  122. package/scripts/lib/project-detect.sh +649 -0
  123. package/scripts/lib/quality-profile.sh +334 -0
  124. package/scripts/lib/recruit-commands.sh +885 -0
  125. package/scripts/lib/recruit-learning.sh +739 -0
  126. package/scripts/lib/recruit-roles.sh +648 -0
  127. package/scripts/lib/reward-aggregator.sh +458 -0
  128. package/scripts/lib/rl-optimizer.sh +362 -0
  129. package/scripts/lib/root-cause.sh +427 -0
  130. package/scripts/lib/scope-enforcement.sh +445 -0
  131. package/scripts/lib/session-restart.sh +493 -0
  132. package/scripts/lib/skill-memory.sh +300 -0
  133. package/scripts/lib/skill-registry.sh +775 -0
  134. package/scripts/lib/spec-driven.sh +476 -0
  135. package/scripts/lib/test-helpers.sh +18 -7
  136. package/scripts/lib/test-holdout.sh +429 -0
  137. package/scripts/lib/test-optimizer.sh +511 -0
  138. package/scripts/shipwright-file-suggest.sh +45 -0
  139. package/scripts/skills/adversarial-quality.md +61 -0
  140. package/scripts/skills/api-design.md +44 -0
  141. package/scripts/skills/architecture-design.md +50 -0
  142. package/scripts/skills/brainstorming.md +43 -0
  143. package/scripts/skills/data-pipeline.md +44 -0
  144. package/scripts/skills/deploy-safety.md +64 -0
  145. package/scripts/skills/documentation.md +38 -0
  146. package/scripts/skills/frontend-design.md +45 -0
  147. package/scripts/skills/generated/.gitkeep +0 -0
  148. package/scripts/skills/generated/_refinements/.gitkeep +0 -0
  149. package/scripts/skills/generated/_refinements/adversarial-quality.patch.md +3 -0
  150. package/scripts/skills/generated/_refinements/architecture-design.patch.md +3 -0
  151. package/scripts/skills/generated/_refinements/brainstorming.patch.md +3 -0
  152. package/scripts/skills/generated/cli-version-management.md +29 -0
  153. package/scripts/skills/generated/collection-system-validation.md +99 -0
  154. package/scripts/skills/generated/large-scale-c-refactoring-coordination.md +97 -0
  155. package/scripts/skills/generated/pattern-matching-similarity-scoring.md +195 -0
  156. package/scripts/skills/generated/test-parallelization-detection.md +65 -0
  157. package/scripts/skills/observability.md +79 -0
  158. package/scripts/skills/performance.md +48 -0
  159. package/scripts/skills/pr-quality.md +49 -0
  160. package/scripts/skills/product-thinking.md +43 -0
  161. package/scripts/skills/security-audit.md +49 -0
  162. package/scripts/skills/systematic-debugging.md +40 -0
  163. package/scripts/skills/testing-strategy.md +47 -0
  164. package/scripts/skills/two-stage-review.md +52 -0
  165. package/scripts/skills/validation-thoroughness.md +55 -0
  166. package/scripts/sw +9 -3
  167. package/scripts/sw-activity.sh +9 -8
  168. package/scripts/sw-adaptive.sh +8 -7
  169. package/scripts/sw-adversarial.sh +2 -1
  170. package/scripts/sw-architecture-enforcer.sh +3 -1
  171. package/scripts/sw-auth.sh +12 -2
  172. package/scripts/sw-autonomous.sh +5 -1
  173. package/scripts/sw-changelog.sh +4 -1
  174. package/scripts/sw-checkpoint.sh +2 -1
  175. package/scripts/sw-ci.sh +15 -6
  176. package/scripts/sw-cleanup.sh +4 -26
  177. package/scripts/sw-code-review.sh +45 -20
  178. package/scripts/sw-connect.sh +2 -1
  179. package/scripts/sw-context.sh +2 -1
  180. package/scripts/sw-cost.sh +107 -5
  181. package/scripts/sw-daemon.sh +71 -11
  182. package/scripts/sw-dashboard.sh +3 -1
  183. package/scripts/sw-db.sh +71 -20
  184. package/scripts/sw-decide.sh +8 -2
  185. package/scripts/sw-decompose.sh +360 -17
  186. package/scripts/sw-deps.sh +4 -1
  187. package/scripts/sw-developer-simulation.sh +4 -1
  188. package/scripts/sw-discovery.sh +378 -5
  189. package/scripts/sw-doc-fleet.sh +4 -1
  190. package/scripts/sw-docs-agent.sh +3 -1
  191. package/scripts/sw-docs.sh +2 -1
  192. package/scripts/sw-doctor.sh +453 -2
  193. package/scripts/sw-dora.sh +4 -1
  194. package/scripts/sw-durable.sh +12 -7
  195. package/scripts/sw-e2e-orchestrator.sh +17 -16
  196. package/scripts/sw-eventbus.sh +13 -4
  197. package/scripts/sw-evidence.sh +364 -12
  198. package/scripts/sw-feedback.sh +550 -9
  199. package/scripts/sw-fix.sh +20 -1
  200. package/scripts/sw-fleet-discover.sh +6 -2
  201. package/scripts/sw-fleet-viz.sh +9 -4
  202. package/scripts/sw-fleet.sh +5 -1
  203. package/scripts/sw-github-app.sh +18 -4
  204. package/scripts/sw-github-checks.sh +3 -2
  205. package/scripts/sw-github-deploy.sh +3 -2
  206. package/scripts/sw-github-graphql.sh +18 -7
  207. package/scripts/sw-guild.sh +5 -1
  208. package/scripts/sw-heartbeat.sh +5 -30
  209. package/scripts/sw-hello.sh +67 -0
  210. package/scripts/sw-hygiene.sh +10 -3
  211. package/scripts/sw-incident.sh +273 -5
  212. package/scripts/sw-init.sh +18 -2
  213. package/scripts/sw-instrument.sh +10 -2
  214. package/scripts/sw-intelligence.sh +44 -7
  215. package/scripts/sw-jira.sh +5 -1
  216. package/scripts/sw-launchd.sh +2 -1
  217. package/scripts/sw-linear.sh +4 -1
  218. package/scripts/sw-logs.sh +4 -1
  219. package/scripts/sw-loop.sh +436 -1076
  220. package/scripts/sw-memory.sh +357 -3
  221. package/scripts/sw-mission-control.sh +6 -1
  222. package/scripts/sw-model-router.sh +483 -27
  223. package/scripts/sw-otel.sh +15 -4
  224. package/scripts/sw-oversight.sh +14 -5
  225. package/scripts/sw-patrol-meta.sh +334 -0
  226. package/scripts/sw-pipeline-composer.sh +7 -1
  227. package/scripts/sw-pipeline-vitals.sh +12 -6
  228. package/scripts/sw-pipeline.sh +54 -2653
  229. package/scripts/sw-pm.sh +16 -8
  230. package/scripts/sw-pr-lifecycle.sh +2 -1
  231. package/scripts/sw-predictive.sh +17 -5
  232. package/scripts/sw-prep.sh +185 -2
  233. package/scripts/sw-ps.sh +5 -25
  234. package/scripts/sw-public-dashboard.sh +17 -4
  235. package/scripts/sw-quality.sh +14 -6
  236. package/scripts/sw-reaper.sh +8 -25
  237. package/scripts/sw-recruit.sh +156 -2303
  238. package/scripts/sw-regression.sh +19 -12
  239. package/scripts/sw-release-manager.sh +3 -1
  240. package/scripts/sw-release.sh +4 -1
  241. package/scripts/sw-remote.sh +3 -1
  242. package/scripts/sw-replay.sh +7 -1
  243. package/scripts/sw-retro.sh +158 -1
  244. package/scripts/sw-review-rerun.sh +3 -1
  245. package/scripts/sw-scale.sh +14 -5
  246. package/scripts/sw-security-audit.sh +6 -1
  247. package/scripts/sw-self-optimize.sh +173 -6
  248. package/scripts/sw-session.sh +9 -3
  249. package/scripts/sw-setup.sh +3 -1
  250. package/scripts/sw-stall-detector.sh +406 -0
  251. package/scripts/sw-standup.sh +15 -7
  252. package/scripts/sw-status.sh +3 -1
  253. package/scripts/sw-strategic.sh +14 -6
  254. package/scripts/sw-stream.sh +13 -4
  255. package/scripts/sw-swarm.sh +20 -7
  256. package/scripts/sw-team-stages.sh +13 -6
  257. package/scripts/sw-templates.sh +7 -31
  258. package/scripts/sw-testgen.sh +17 -6
  259. package/scripts/sw-tmux-pipeline.sh +4 -1
  260. package/scripts/sw-tmux-role-color.sh +2 -0
  261. package/scripts/sw-tmux-status.sh +1 -1
  262. package/scripts/sw-tmux.sh +37 -1
  263. package/scripts/sw-trace.sh +3 -1
  264. package/scripts/sw-tracker-github.sh +3 -0
  265. package/scripts/sw-tracker-jira.sh +3 -0
  266. package/scripts/sw-tracker-linear.sh +3 -0
  267. package/scripts/sw-tracker.sh +3 -1
  268. package/scripts/sw-triage.sh +3 -2
  269. package/scripts/sw-upgrade.sh +3 -1
  270. package/scripts/sw-ux.sh +5 -2
  271. package/scripts/sw-webhook.sh +5 -2
  272. package/scripts/sw-widgets.sh +9 -4
  273. package/scripts/sw-worktree.sh +15 -3
  274. package/scripts/test-skill-injection.sh +1233 -0
  275. package/templates/pipelines/autonomous.json +27 -3
  276. package/templates/pipelines/cost-aware.json +34 -8
  277. package/templates/pipelines/deployed.json +12 -0
  278. package/templates/pipelines/enterprise.json +12 -0
  279. package/templates/pipelines/fast.json +6 -0
  280. package/templates/pipelines/full.json +27 -3
  281. package/templates/pipelines/hotfix.json +6 -0
  282. package/templates/pipelines/standard.json +12 -0
  283. package/templates/pipelines/tdd.json +12 -0
@@ -0,0 +1,350 @@
1
+ #!/usr/bin/env bash
2
+ # Module guard - prevent double-sourcing
3
+ [[ -n "${_LOOP_CONVERGENCE_LOADED:-}" ]] && return 0
4
+ _LOOP_CONVERGENCE_LOADED=1
5
+
6
+ # ─── Auto-Recovery Integration ───────────────────────────────────────────────
7
+ # Source the autonomous recovery system for pre-circuit-breaker recovery attempts.
8
+ _CONVERGENCE_SCRIPT_DIR="${_CONVERGENCE_SCRIPT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"
9
+ [[ -f "${_CONVERGENCE_SCRIPT_DIR}/auto-recovery.sh" ]] && source "${_CONVERGENCE_SCRIPT_DIR}/auto-recovery.sh"
10
+
11
+ # ─── Convergence Detection ────────────────────────────────────────────────────
12
+
13
+ track_iteration_velocity() {
14
+ local changes
15
+ changes="$(git -C "$PROJECT_ROOT" diff --stat HEAD~1 2>/dev/null | tail -1 || echo "")"
16
+ local insertions
17
+ insertions="$(echo "$changes" | grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+' || echo 0)"
18
+ ITERATION_LINES_CHANGED="${insertions:-0}"
19
+ if [[ -n "$VELOCITY_HISTORY" ]]; then
20
+ VELOCITY_HISTORY="${VELOCITY_HISTORY},${ITERATION_LINES_CHANGED}"
21
+ else
22
+ VELOCITY_HISTORY="${ITERATION_LINES_CHANGED}"
23
+ fi
24
+ }
25
+
26
+ # Compute average lines/iteration from recent history
27
+ compute_velocity_avg() {
28
+ if [[ -z "$VELOCITY_HISTORY" ]]; then
29
+ echo "0"
30
+ return 0
31
+ fi
32
+ local total=0 count=0
33
+ local IFS=','
34
+ local val
35
+ for val in $VELOCITY_HISTORY; do
36
+ total=$((total + val))
37
+ count=$((count + 1))
38
+ done
39
+ if [[ "$count" -gt 0 ]]; then
40
+ echo $((total / count))
41
+ else
42
+ echo "0"
43
+ fi
44
+ }
45
+
46
+ check_progress() {
47
+ local changes
48
+ # Exclude loop bookkeeping files — only count real code changes as progress
49
+ changes="$(git -C "$PROJECT_ROOT" diff --stat HEAD~1 \
50
+ -- . ':!.claude/loop-state.md' ':!.claude/pipeline-state.md' \
51
+ ':!**/progress.md' ':!**/error-summary.json' \
52
+ 2>/dev/null | tail -1 || echo "")"
53
+ local insertions
54
+ insertions="$(echo "$changes" | grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+' || echo 0)"
55
+ if [[ "${insertions:-0}" -lt "$MIN_PROGRESS_LINES" ]]; then
56
+ return 1 # No meaningful progress
57
+ fi
58
+ return 0
59
+ }
60
+
61
+ check_completion() {
62
+ local log_file="$1"
63
+ grep -q "LOOP_COMPLETE" "$log_file" 2>/dev/null
64
+ }
65
+
66
+ check_circuit_breaker() {
67
+ # Vitals-driven circuit breaker (preferred over static threshold)
68
+ if type pipeline_compute_vitals >/dev/null 2>&1 && type pipeline_health_verdict >/dev/null 2>&1; then
69
+ local _vitals_json _verdict
70
+ local _loop_state="${STATE_FILE:-}"
71
+ local _loop_artifacts="${ARTIFACTS_DIR:-}"
72
+ local _loop_issue="${ISSUE_NUMBER:-}"
73
+ _vitals_json=$(pipeline_compute_vitals "$_loop_state" "$_loop_artifacts" "$_loop_issue" 2>/dev/null) || true
74
+ if [[ -n "$_vitals_json" && "$_vitals_json" != "{}" ]]; then
75
+ _verdict=$(echo "$_vitals_json" | jq -r '.verdict // "continue"' 2>/dev/null || echo "continue")
76
+ if [[ "$_verdict" == "abort" ]]; then
77
+ local _health_score
78
+ _health_score=$(echo "$_vitals_json" | jq -r '.health_score // 0' 2>/dev/null || echo "0")
79
+ error "Vitals circuit breaker: health score ${_health_score}/100 — aborting (${CONSECUTIVE_FAILURES} stagnant iterations)"
80
+ STATUS="circuit_breaker"
81
+ return 1
82
+ fi
83
+ # Vitals say continue/warn/intervene — don't trip circuit breaker yet
84
+ if [[ "$_verdict" == "continue" || "$_verdict" == "warn" ]]; then
85
+ return 0
86
+ fi
87
+ fi
88
+ fi
89
+
90
+ # ─── Auto-Recovery: attempt fix before aborting ────────────────────────
91
+ if [[ "$CONSECUTIVE_FAILURES" -ge "$CIRCUIT_BREAKER_THRESHOLD" ]]; then
92
+ if type recovery_before_circuit_breaker >/dev/null 2>&1; then
93
+ local error_log="${ARTIFACTS_DIR:-${PROJECT_ROOT:-.}/.claude/pipeline-artifacts}/error-log.jsonl"
94
+ if recovery_before_circuit_breaker "$error_log" "${PROJECT_ROOT:-.}" "${TEST_CMD:-}"; then
95
+ info "Auto-recovery succeeded — resetting circuit breaker"
96
+ CONSECUTIVE_FAILURES=0
97
+ return 0
98
+ fi
99
+ fi
100
+ error "Circuit breaker tripped: ${CIRCUIT_BREAKER_THRESHOLD} consecutive iterations with no meaningful progress."
101
+ STATUS="circuit_breaker"
102
+ return 1
103
+ fi
104
+ return 0
105
+ }
106
+
107
+ check_max_iterations() {
108
+ if [[ "$ITERATION" -le "$MAX_ITERATIONS" ]]; then
109
+ return 0
110
+ fi
111
+
112
+ # Hit the cap — check if we should auto-extend
113
+ if ! $AUTO_EXTEND || [[ "$EXTENSION_COUNT" -ge "$MAX_EXTENSIONS" ]]; then
114
+ if [[ "$EXTENSION_COUNT" -ge "$MAX_EXTENSIONS" ]]; then
115
+ warn "Hard cap reached: ${EXTENSION_COUNT} extensions applied (max ${MAX_EXTENSIONS})."
116
+ fi
117
+ warn "Max iterations ($MAX_ITERATIONS) reached."
118
+ STATUS="max_iterations"
119
+ return 1
120
+ fi
121
+
122
+ # Checkpoint audit: is there meaningful progress worth extending for?
123
+ echo -e "\n ${CYAN}${BOLD}▸ Checkpoint${RESET} — max iterations ($MAX_ITERATIONS) reached, evaluating progress..."
124
+
125
+ local should_extend=false
126
+ local extension_reason=""
127
+
128
+ # Check 1: recent meaningful progress (not stuck)
129
+ if [[ "${CONSECUTIVE_FAILURES:-0}" -lt 2 ]]; then
130
+ # Check 2: agent hasn't signaled completion (if it did, guard_completion handles it)
131
+ local last_log="$LOG_DIR/iteration-$(( ITERATION - 1 )).log"
132
+ if [[ -f "$last_log" ]] && ! grep -q "LOOP_COMPLETE" "$last_log" 2>/dev/null; then
133
+ should_extend=true
134
+ extension_reason="work in progress with recent progress"
135
+ fi
136
+ fi
137
+
138
+ # Check 3: if quality gates or tests are failing, extend to let agent fix them
139
+ if [[ "$TEST_PASSED" == "false" ]] || ! $QUALITY_GATE_PASSED; then
140
+ should_extend=true
141
+ extension_reason="quality gates or tests not yet passing"
142
+ fi
143
+
144
+ if $should_extend; then
145
+ # Scale extension size by velocity — good progress earns more iterations
146
+ local velocity_avg
147
+ velocity_avg="$(compute_velocity_avg)"
148
+ local effective_extension="$EXTENSION_SIZE"
149
+ if [[ "$velocity_avg" -gt 20 ]]; then
150
+ # High velocity: grant more iterations
151
+ effective_extension=$(( EXTENSION_SIZE + 3 ))
152
+ elif [[ "$velocity_avg" -lt 5 ]]; then
153
+ # Low velocity: grant fewer iterations
154
+ effective_extension=$(( EXTENSION_SIZE > 2 ? EXTENSION_SIZE - 2 : 1 ))
155
+ fi
156
+ EXTENSION_COUNT=$(( EXTENSION_COUNT + 1 ))
157
+ MAX_ITERATIONS=$(( MAX_ITERATIONS + effective_extension ))
158
+ echo -e " ${GREEN}✓${RESET} Auto-extending: +${effective_extension} iterations (now ${MAX_ITERATIONS} max, extension ${EXTENSION_COUNT}/${MAX_EXTENSIONS})"
159
+ echo -e " ${DIM}Reason: ${extension_reason} | velocity: ~${velocity_avg} lines/iter${RESET}"
160
+ return 0
161
+ fi
162
+
163
+ warn "Max iterations reached — no recent progress detected."
164
+ STATUS="max_iterations"
165
+ return 1
166
+ }
167
+
168
+ record_iteration_stuckness_data() {
169
+ local exit_code="${1:-0}"
170
+ [[ -z "$LOG_DIR" ]] && return 0
171
+ local tracking_file="${STUCKNESS_TRACKING_FILE:-$LOG_DIR/stuckness-tracking.txt}"
172
+ local diff_hash error_hash
173
+ diff_hash=$(git -C "${PROJECT_ROOT:-.}" diff HEAD 2>/dev/null | (md5 -q 2>/dev/null || md5sum 2>/dev/null | cut -d' ' -f1) || echo "none")
174
+ local error_log="${ARTIFACTS_DIR:-${STATE_DIR:-${PROJECT_ROOT:-.}/.claude}/pipeline-artifacts}/error-log.jsonl"
175
+ if [[ -f "$error_log" ]]; then
176
+ error_hash=$(tail -5 "$error_log" 2>/dev/null | sort -u | (md5 -q 2>/dev/null || md5sum 2>/dev/null | cut -d' ' -f1) || echo "none")
177
+ else
178
+ error_hash="none"
179
+ fi
180
+ echo "${diff_hash}|${error_hash}|${exit_code}" >> "$tracking_file"
181
+ }
182
+
183
+ detect_stuckness() {
184
+ STUCKNESS_HINT=""
185
+ local iteration="${ITERATION:-0}"
186
+ local stuckness_signals=0
187
+ local stuckness_reasons=()
188
+ local tracking_file="${STUCKNESS_TRACKING_FILE:-$LOG_DIR/stuckness-tracking.txt}"
189
+ local tracking_lines
190
+ tracking_lines=$(wc -l < "$tracking_file" 2>/dev/null || true)
191
+ tracking_lines="${tracking_lines:-0}"
192
+
193
+ # Signal 1: Text overlap (existing logic) — compare last 2 iteration logs
194
+ if [[ "$iteration" -ge 3 ]]; then
195
+ local log1="$LOG_DIR/iteration-$(( iteration - 1 )).log"
196
+ local log2="$LOG_DIR/iteration-$(( iteration - 2 )).log"
197
+ local log3="$LOG_DIR/iteration-$(( iteration - 3 )).log"
198
+
199
+ if [[ -f "$log1" && -f "$log2" ]]; then
200
+ local lines1 lines2 common total overlap_pct
201
+ lines1=$(tail -50 "$log1" 2>/dev/null | grep -v '^$' | sort || true)
202
+ lines2=$(tail -50 "$log2" 2>/dev/null | grep -v '^$' | sort || true)
203
+
204
+ if [[ -n "$lines1" && -n "$lines2" ]]; then
205
+ total=$(echo "$lines1" | wc -l | tr -d ' ')
206
+ common=$(comm -12 <(echo "$lines1") <(echo "$lines2") 2>/dev/null | wc -l | tr -d ' ' || true)
207
+ common="${common:-0}"
208
+ if [[ "$total" -gt 0 ]]; then
209
+ overlap_pct=$(( common * 100 / total ))
210
+ else
211
+ overlap_pct=0
212
+ fi
213
+ if [[ "${overlap_pct:-0}" -ge 90 ]]; then
214
+ stuckness_signals=$((stuckness_signals + 1))
215
+ stuckness_reasons+=("high text overlap (${overlap_pct}%) between iterations")
216
+ fi
217
+ fi
218
+ fi
219
+ fi
220
+
221
+ # Signal 2: Git diff hash — last 3 iterations produced zero or identical diffs
222
+ if [[ -f "$tracking_file" ]] && [[ "$tracking_lines" -ge 3 ]]; then
223
+ local last_three
224
+ last_three=$(tail -3 "$tracking_file" 2>/dev/null | cut -d'|' -f1 || true)
225
+ local unique_hashes
226
+ unique_hashes=$(echo "$last_three" | sort -u | grep -v '^$' | wc -l | tr -d ' ')
227
+ if [[ "$unique_hashes" -le 1 ]] && [[ -n "$last_three" ]]; then
228
+ stuckness_signals=$((stuckness_signals + 1))
229
+ stuckness_reasons+=("identical or zero git diffs in last 3 iterations")
230
+ fi
231
+ fi
232
+
233
+ # Signal 3: Error repetition — same error hash in last 3 iterations
234
+ if [[ -f "$tracking_file" ]] && [[ "$tracking_lines" -ge 3 ]]; then
235
+ local last_three_errors
236
+ last_three_errors=$(tail -3 "$tracking_file" 2>/dev/null | cut -d'|' -f2 || true)
237
+ local unique_error_hashes
238
+ unique_error_hashes=$(echo "$last_three_errors" | sort -u | grep -v '^none$' | grep -v '^$' | wc -l | tr -d ' ')
239
+ if [[ "$unique_error_hashes" -eq 1 ]] && [[ -n "$(echo "$last_three_errors" | grep -v '^none$')" ]]; then
240
+ stuckness_signals=$((stuckness_signals + 1))
241
+ stuckness_reasons+=("same error in last 3 iterations")
242
+ fi
243
+ fi
244
+
245
+ # Signal 4: Same error repeating 3+ times (legacy check on error-log content)
246
+ local error_log
247
+ error_log="${ARTIFACTS_DIR:-$PROJECT_ROOT/.claude/pipeline-artifacts}/error-log.jsonl"
248
+ if [[ -f "$error_log" ]]; then
249
+ local last_errors
250
+ last_errors=$(tail -5 "$error_log" 2>/dev/null | jq -r '.error // .message // .error_hash // empty' 2>/dev/null | sort | uniq -c | sort -rn | head -1 || true)
251
+ local repeat_count
252
+ repeat_count=$(echo "$last_errors" | awk '{print $1}' 2>/dev/null || echo "0")
253
+ if [[ "${repeat_count:-0}" -ge 3 ]]; then
254
+ stuckness_signals=$((stuckness_signals + 1))
255
+ stuckness_reasons+=("same error repeated ${repeat_count} times")
256
+ fi
257
+ fi
258
+
259
+ # Signal 5: Exit code pattern — last 3 iterations had same non-zero exit code
260
+ if [[ -f "$tracking_file" ]] && [[ "$tracking_lines" -ge 3 ]]; then
261
+ local last_three_exits
262
+ last_three_exits=$(tail -3 "$tracking_file" 2>/dev/null | cut -d'|' -f3 || true)
263
+ local first_exit
264
+ first_exit=$(echo "$last_three_exits" | head -1)
265
+ if [[ "$first_exit" =~ ^[0-9]+$ ]] && [[ "$first_exit" -ne 0 ]]; then
266
+ local all_same=true
267
+ while IFS= read -r ex; do
268
+ [[ "$ex" != "$first_exit" ]] && all_same=false
269
+ done <<< "$last_three_exits"
270
+ if [[ "$all_same" == true ]]; then
271
+ stuckness_signals=$((stuckness_signals + 1))
272
+ stuckness_reasons+=("same non-zero exit code (${first_exit}) in last 3 iterations")
273
+ fi
274
+ fi
275
+ fi
276
+
277
+ # Signal 6: Git diff size — no or minimal code changes (existing)
278
+ local diff_lines
279
+ diff_lines=$(git -C "${PROJECT_ROOT:-.}" diff HEAD 2>/dev/null | wc -l | tr -d ' ' || true)
280
+ diff_lines="${diff_lines:-0}"
281
+ if [[ "${diff_lines:-0}" -lt 5 ]] && [[ "$iteration" -gt 2 ]]; then
282
+ stuckness_signals=$((stuckness_signals + 1))
283
+ stuckness_reasons+=("no code changes in last iteration")
284
+ fi
285
+
286
+ # Signal 7: Iteration budget — used >70% without passing tests
287
+ local max_iter="${MAX_ITERATIONS:-20}"
288
+ local progress_pct=0
289
+ if [[ "$max_iter" -gt 0 ]]; then
290
+ progress_pct=$(( iteration * 100 / max_iter ))
291
+ fi
292
+ if [[ "$progress_pct" -gt 70 ]] && [[ "${TEST_PASSED:-false}" != "true" ]]; then
293
+ stuckness_signals=$((stuckness_signals + 1))
294
+ stuckness_reasons+=("used ${progress_pct}% of iteration budget without passing tests")
295
+ fi
296
+
297
+ # Gate-aware dampening: if tests pass and the agent has made progress overall,
298
+ # reduce stuckness signal count. The "no code changes" and "identical diffs" signals
299
+ # fire when code is already complete and the agent is fighting evaluator quirks —
300
+ # that's not genuine stuckness, it's "done but gates disagree."
301
+ if [[ "${TEST_PASSED:-}" == "true" ]] && [[ "$stuckness_signals" -ge 2 ]]; then
302
+ # If at least one quality signal is positive, dampen by 1
303
+ if [[ "${AUDIT_RESULT:-}" == "pass" ]] || $QUALITY_GATE_PASSED 2>/dev/null; then
304
+ stuckness_signals=$((stuckness_signals - 1))
305
+ fi
306
+ fi
307
+
308
+ # Decision: 2+ signals = stuck
309
+ if [[ "$stuckness_signals" -ge 2 ]]; then
310
+ STUCKNESS_COUNT=$(( STUCKNESS_COUNT + 1 ))
311
+ STUCKNESS_DIAGNOSIS="${stuckness_reasons[*]}"
312
+ if type emit_event >/dev/null 2>&1; then
313
+ emit_event "loop.stuckness_detected" "signals=$stuckness_signals" "count=$STUCKNESS_COUNT" "iteration=$iteration" "reasons=${stuckness_reasons[*]}"
314
+ fi
315
+ STUCKNESS_HINT="IMPORTANT: The loop appears stuck. Previous approaches have not worked. You MUST try a fundamentally different strategy. Reasons: ${stuckness_reasons[*]}"
316
+ warn "Stuckness detected (${stuckness_signals} signals, count ${STUCKNESS_COUNT}): ${stuckness_reasons[*]}"
317
+
318
+ local diff_summary=""
319
+ local log1="$LOG_DIR/iteration-$(( iteration - 1 )).log"
320
+ local log3="$LOG_DIR/iteration-$(( iteration - 3 )).log"
321
+ if [[ -f "$log3" && -f "$log1" ]]; then
322
+ diff_summary=$(diff <(tail -30 "$log3" 2>/dev/null) <(tail -30 "$log1" 2>/dev/null) 2>/dev/null | head -10 || true)
323
+ fi
324
+
325
+ local alternatives=""
326
+ if type memory_inject_context >/dev/null 2>&1; then
327
+ alternatives=$(memory_inject_context "build" 2>/dev/null | grep -i "fix:" | head -3 || true)
328
+ fi
329
+
330
+ cat <<STUCK_SECTION
331
+ ## Stuckness Detected
332
+ ${STUCKNESS_HINT}
333
+
334
+ ${diff_summary:+Changes between recent iterations:
335
+ $diff_summary
336
+ }
337
+ ${alternatives:+Consider these alternative approaches from past fixes:
338
+ $alternatives
339
+ }
340
+ Try a fundamentally different approach:
341
+ - Break the problem into smaller steps
342
+ - Look for an entirely different implementation strategy
343
+ - Check if there's a dependency or configuration issue blocking progress
344
+ - Read error messages more carefully — the root cause may differ from your assumption
345
+ STUCK_SECTION
346
+ return 0
347
+ fi
348
+
349
+ return 1
350
+ }