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
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="3.2.0"
9
+ VERSION="3.3.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -29,20 +29,25 @@ fi
29
29
  if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
30
30
  emit_event() {
31
31
  local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
32
- local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
32
+ local payload
33
+ payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
33
34
  while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
34
35
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
35
36
  }
36
37
  fi
37
38
  # ─── Storage Paths ──────────────────────────────────────────────────────────
38
39
  INCIDENTS_FILE="${HOME}/.shipwright/incidents.jsonl"
40
+ MERGE_OUTCOMES_FILE="${HOME}/.shipwright/optimization/merge-outcomes.jsonl"
39
41
  ERROR_THRESHOLD=5 # Create issue if error count >= threshold
42
+ # shellcheck disable=SC2034
40
43
  ERROR_LOG_DIR="${REPO_DIR}/.claude/pipeline-artifacts"
41
44
  ARTIFACTS_DIR="${ARTIFACTS_DIR:-${REPO_DIR}/.claude/pipeline-artifacts}"
45
+ POST_MERGE_MONITORING_FILE="${ARTIFACTS_DIR}/post-merge-monitoring.json"
42
46
 
43
47
  # ─── Initialize directories ────────────────────────────────────────────────
44
48
  ensure_dirs() {
45
49
  mkdir -p "${HOME}/.shipwright"
50
+ mkdir -p "${HOME}/.shipwright/optimization"
46
51
  mkdir -p "$ARTIFACTS_DIR"
47
52
  }
48
53
 
@@ -61,6 +66,7 @@ parse_error_patterns() {
61
66
  local log_file="$1"
62
67
  local error_count=0
63
68
  local error_types=""
69
+ # shellcheck disable=SC2034
64
70
  local stack_traces=""
65
71
 
66
72
  if [[ ! -f "$log_file" ]]; then
@@ -72,24 +78,28 @@ parse_error_patterns() {
72
78
  if [[ "$line" =~ (Error|Exception|panic|fatal).*: ]]; then
73
79
  error_count=$((error_count + 1))
74
80
  # Extract error message
75
- local err_msg=$(echo "$line" | sed -E 's/^.*\[.*\] //; s/:.*//')
81
+ local err_msg
82
+ err_msg=$(echo "$line" | sed -E 's/^.*\[.*\] //; s/:.*//')
76
83
  error_types="${error_types}${err_msg};"
77
84
  fi
78
85
  done < "$log_file"
79
86
 
80
87
  # Output CSV: count|types|first_stack_trace
81
- local first_stack=$(head -50 "$log_file" | tail -20)
88
+ local first_stack
89
+ first_stack=$(head -50 "$log_file" | tail -20)
82
90
  echo "$error_count|$error_types|$first_stack"
83
91
  }
84
92
 
85
93
  # ─── Find commit that likely introduced regression ───────────────────────────
86
94
  find_regression_commit() {
95
+ # shellcheck disable=SC2034
87
96
  local error_pattern="$1"
88
97
  local max_commits="${2:-20}"
89
98
 
90
99
  # Search recent commits for changes that might have introduced the error
91
100
  # Pattern: look for commits touching error-related code
92
101
  local commit_hash
102
+ # shellcheck disable=SC2034
93
103
  commit_hash=$(cd "$REPO_DIR" && git log --all -n "$max_commits" --pretty=format:"%H %s" | \
94
104
  while read -r hash subject; do
95
105
  # Simple heuristic: commits that touched multiple files or had large diffs
@@ -104,7 +114,505 @@ find_regression_commit() {
104
114
  echo "${commit_hash:0:7}"
105
115
  }
106
116
 
107
- # ─── Collect errors from monitor stage output ────────────────────────────────
117
+ # ═══════════════════════════════════════════════════════════════════════════════
118
+ # POST-MERGE FEEDBACK FUNCTIONS
119
+ # ═══════════════════════════════════════════════════════════════════════════════
120
+
121
+ # ─── Monitor production after merge ─────────────────────────────────────────────
122
+ feedback_post_merge_monitor() {
123
+ local merge_sha="${1:-}"
124
+ local environment="${2:-production}"
125
+ local monitoring_window="${3:-1800}" # 30 minutes default
126
+ local poll_interval="${4:-60}" # 60 seconds
127
+ local owner_repo="${5:-}"
128
+
129
+ if [[ -z "$merge_sha" ]]; then
130
+ error "Usage: feedback_post_merge_monitor <merge_sha> [environment] [window_secs] [poll_interval]"
131
+ return 1
132
+ fi
133
+
134
+ info "Starting post-merge production monitoring for: $merge_sha"
135
+ info "Environment: $environment | Window: $((monitoring_window / 60))m | Poll: ${poll_interval}s"
136
+
137
+ ensure_dirs
138
+
139
+ # Initialize monitoring data
140
+ local monitoring_data
141
+ local start_epoch
142
+ start_epoch=$(now_epoch)
143
+ monitoring_data=$(jq -n \
144
+ --arg ts "$(now_iso)" \
145
+ --arg merge_sha "$merge_sha" \
146
+ --arg env "$environment" \
147
+ --arg window "$monitoring_window" \
148
+ --arg start_ep "$start_epoch" \
149
+ '{
150
+ timestamp: $ts,
151
+ merge_sha: $merge_sha,
152
+ environment: $env,
153
+ monitoring_window_secs: ($window | tonumber),
154
+ start_epoch: ($start_ep | tonumber),
155
+ checks: [],
156
+ errors_detected: 0,
157
+ deployment_status: null,
158
+ monitoring_complete: false
159
+ }')
160
+
161
+ local start_time
162
+ start_time=$(now_epoch)
163
+ local elapsed=0
164
+ local iteration=0
165
+
166
+ # Poll for monitoring window duration
167
+ while [[ "$elapsed" -lt "$monitoring_window" ]]; do
168
+ iteration=$((iteration + 1))
169
+ local current_check
170
+
171
+ # Check GitHub deployment status (if NO_GITHUB not set)
172
+ local deploy_status="unknown"
173
+ local deploy_errors=0
174
+ if [[ "${NO_GITHUB:-}" != "true" && "${NO_GITHUB:-}" != "1" ]]; then
175
+ if [[ -z "$owner_repo" ]]; then
176
+ owner_repo=$(get_owner_repo) || true
177
+ fi
178
+ if [[ -n "$owner_repo" ]]; then
179
+ deploy_status=$(gh api "repos/${owner_repo}/deployments" \
180
+ --jq '.[] | select(.ref == "'$merge_sha'") | .statuses[0].state // "unknown"' \
181
+ 2>/dev/null || echo "unknown")
182
+ fi
183
+ fi
184
+
185
+ # Simulate or check for error spikes (look at error log if available)
186
+ if [[ -f "${ARTIFACTS_DIR}/error-log.jsonl" ]]; then
187
+ deploy_errors=$(wc -l < "${ARTIFACTS_DIR}/error-log.jsonl" 2>/dev/null || echo "0")
188
+ fi
189
+
190
+ current_check=$(jq -n \
191
+ --arg ts "$(now_iso)" \
192
+ --argjson iter "$iteration" \
193
+ --arg deploy_st "$deploy_status" \
194
+ --argjson err_count "$deploy_errors" \
195
+ '{
196
+ timestamp: $ts,
197
+ iteration: $iter,
198
+ deployment_status: $deploy_st,
199
+ error_count: $err_count
200
+ }')
201
+
202
+ monitoring_data=$(echo "$monitoring_data" | jq \
203
+ --argjson check "$current_check" \
204
+ --argjson err_count "$deploy_errors" \
205
+ '.checks += [$check] | .errors_detected += ($err_count | tonumber)')
206
+
207
+ # Check for immediate failure
208
+ if [[ "$deploy_status" == "failure" || "$deploy_status" == "error" ]]; then
209
+ warn "Deployment failed detected at iteration $iteration!"
210
+ monitoring_data=$(echo "$monitoring_data" | jq '.deployment_status = "failed"')
211
+ break
212
+ fi
213
+
214
+ elapsed=$(($(now_epoch) - start_time))
215
+ if [[ "$elapsed" -lt "$monitoring_window" ]]; then
216
+ sleep "$poll_interval"
217
+ fi
218
+ done
219
+
220
+ # Mark monitoring complete
221
+ local end_epoch
222
+ end_epoch=$(now_epoch)
223
+ monitoring_data=$(echo "$monitoring_data" | jq \
224
+ --arg ts "$(now_iso)" \
225
+ --arg end_ep "$end_epoch" \
226
+ '.monitoring_complete = true | .end_timestamp = $ts | .end_epoch = ($end_ep | tonumber)')
227
+
228
+ # Write monitoring data atomically
229
+ local tmp_file
230
+ tmp_file=$(mktemp)
231
+ echo "$monitoring_data" > "$tmp_file"
232
+ mv "$tmp_file" "$POST_MERGE_MONITORING_FILE"
233
+
234
+ success "Post-merge monitoring complete: $POST_MERGE_MONITORING_FILE"
235
+ emit_event "feedback_post_merge_monitor" "merge_sha=$merge_sha" "environment=$environment" "errors=$( echo "$monitoring_data" | jq -r '.errors_detected')"
236
+ }
237
+
238
+ # ─── Detect regressions from monitoring data ────────────────────────────────────
239
+ feedback_detect_regression() {
240
+ local monitoring_file="${1:-$POST_MERGE_MONITORING_FILE}"
241
+
242
+ if [[ ! -f "$monitoring_file" ]]; then
243
+ error "Monitoring file not found: $monitoring_file"
244
+ return 1
245
+ fi
246
+
247
+ local merge_sha
248
+ merge_sha=$(jq -r '.merge_sha' "$monitoring_file")
249
+ local errors_detected
250
+ errors_detected=$(jq -r '.errors_detected // 0' "$monitoring_file")
251
+ local deploy_status
252
+ deploy_status=$(jq -r '.deployment_status // "unknown"' "$monitoring_file")
253
+ local error_count_threshold=5
254
+
255
+ local regression="false"
256
+ local regression_type="none"
257
+ local severity="P3"
258
+ local evidence=""
259
+
260
+ # P0: Deployment failed
261
+ if [[ "$deploy_status" == "failed" ]]; then
262
+ regression="true"
263
+ regression_type="deploy_failure"
264
+ severity="P0"
265
+ evidence="Deployment failed after merge"
266
+ # P1: Error spike
267
+ elif [[ "$errors_detected" -ge "$error_count_threshold" ]]; then
268
+ regression="true"
269
+ regression_type="error_spike"
270
+ severity="P1"
271
+ evidence="Error count: $errors_detected (threshold: $error_count_threshold)"
272
+ # P2: Minor errors (below threshold but present)
273
+ elif [[ "$errors_detected" -gt 0 ]]; then
274
+ regression="true"
275
+ regression_type="minor_errors"
276
+ severity="P2"
277
+ evidence="Error count: $errors_detected"
278
+ fi
279
+
280
+ local regression_result
281
+ regression_result=$(jq -n \
282
+ --arg ts "$(now_iso)" \
283
+ --arg sha "$merge_sha" \
284
+ --arg regr "$regression" \
285
+ --arg type "$regression_type" \
286
+ --arg sev "$severity" \
287
+ --arg evid "$evidence" \
288
+ '{
289
+ timestamp: $ts,
290
+ merge_sha: $sha,
291
+ regression: ($regr == "true"),
292
+ type: $type,
293
+ severity: $sev,
294
+ evidence: $evid
295
+ }')
296
+
297
+ echo "$regression_result"
298
+ emit_event "feedback_detect_regression" "merge_sha=$merge_sha" "regression=$regression" "severity=$severity"
299
+ }
300
+
301
+ # ─── Correlate regressions with changed files ───────────────────────────────────
302
+ feedback_correlate_with_changes() {
303
+ local pr_number="${1:-}"
304
+ local regression_json="${2:-}"
305
+
306
+ if [[ -z "$pr_number" ]]; then
307
+ error "Usage: feedback_correlate_with_changes <pr_number> [regression_json]"
308
+ return 1
309
+ fi
310
+
311
+ # Get PR's changed files from git log (if available)
312
+ local changed_files=""
313
+ local culprit_files=""
314
+ local confidence=0
315
+
316
+ # Try to get files from recent commits
317
+ if [[ "${NO_GITHUB:-}" != "true" && "${NO_GITHUB:-}" != "1" ]]; then
318
+ changed_files=$(git log --oneline --all -n 50 --pretty=format:%B 2>/dev/null | \
319
+ grep -i "pr #$pr_number\|merge.*$pr_number" | head -1 || echo "")
320
+ fi
321
+
322
+ # If no GitHub data, fall back to git history
323
+ if [[ -z "$changed_files" ]]; then
324
+ changed_files=$(cd "$REPO_DIR" && git diff HEAD~1..HEAD --name-only 2>/dev/null | head -10 || echo "")
325
+ fi
326
+
327
+ # Score likelihood of files being culprits (common patterns)
328
+ if [[ -n "$changed_files" ]]; then
329
+ # Files commonly correlated with regressions
330
+ while IFS= read -r file; do
331
+ if [[ "$file" =~ (auth|login|session|permission|access) ]]; then
332
+ culprit_files="${culprit_files}${file}:auth "
333
+ confidence=$((confidence + 25))
334
+ elif [[ "$file" =~ (api|endpoint|route|handler|request) ]]; then
335
+ culprit_files="${culprit_files}${file}:api "
336
+ confidence=$((confidence + 20))
337
+ elif [[ "$file" =~ (database|migration|schema|sql) ]]; then
338
+ culprit_files="${culprit_files}${file}:db "
339
+ confidence=$((confidence + 20))
340
+ elif [[ "$file" =~ (util|helper|core|lib) ]]; then
341
+ culprit_files="${culprit_files}${file}:shared "
342
+ confidence=$((confidence + 15))
343
+ fi
344
+ done <<< "$changed_files"
345
+ fi
346
+
347
+ # Cap confidence at 100
348
+ if [[ "$confidence" -gt 100 ]]; then
349
+ confidence=100
350
+ fi
351
+
352
+ local correlation_result
353
+ correlation_result=$(jq -n \
354
+ --arg pr "$pr_number" \
355
+ --arg files "$changed_files" \
356
+ --arg culprits "$culprit_files" \
357
+ --arg conf "$confidence" \
358
+ '{
359
+ pr_number: $pr,
360
+ changed_files: $files,
361
+ culprit_files: $culprits,
362
+ confidence_percent: ($conf | tonumber)
363
+ }')
364
+
365
+ echo "$correlation_result"
366
+ emit_event "feedback_correlate" "pr=$pr_number" "confidence=$confidence"
367
+ }
368
+
369
+ # ─── Auto-respond to regressions based on severity ──────────────────────────────
370
+ feedback_auto_respond() {
371
+ local regression_json="${1:-}"
372
+ local pr_number="${2:-}"
373
+
374
+ if [[ -z "$regression_json" ]]; then
375
+ error "Usage: feedback_auto_respond <regression_json> [pr_number]"
376
+ return 1
377
+ fi
378
+
379
+ local severity
380
+ severity=$(echo "$regression_json" | jq -r '.severity // "P3"')
381
+ local regression_type
382
+ regression_type=$(echo "$regression_json" | jq -r '.type // "unknown"')
383
+ local evidence
384
+ evidence=$(echo "$regression_json" | jq -r '.evidence // ""')
385
+
386
+ info "Auto-responding to regression: $severity / $regression_type"
387
+
388
+ ensure_dirs
389
+
390
+ case "$severity" in
391
+ P0)
392
+ # Deploy failed: trigger rollback + create incident
393
+ info "P0 SEVERITY: Deployment failed — triggering rollback"
394
+ cmd_rollback "production" "Post-merge regression detected: $regression_type"
395
+
396
+ # Create incident issue if GitHub available
397
+ if [[ "${NO_GITHUB:-}" != "true" && "${NO_GITHUB:-}" != "1" ]]; then
398
+ local owner_repo
399
+ owner_repo=$(get_owner_repo) || true
400
+ if [[ -n "$owner_repo" && -n "$pr_number" ]]; then
401
+ if command -v gh >/dev/null 2>&1; then
402
+ gh issue create \
403
+ --repo "$owner_repo" \
404
+ --title "CRITICAL: Post-Merge Deployment Failed - PR #$pr_number" \
405
+ --body "**Severity**: P0 - CRITICAL
406
+
407
+ **Issue**: Deployment failed immediately after PR merge
408
+
409
+ **Type**: $regression_type
410
+ **Evidence**: $evidence
411
+
412
+ **Action Taken**: Rollback initiated
413
+
414
+ **Related PR**: #$pr_number
415
+
416
+ ---
417
+ Auto-created by Shipwright Post-Merge Feedback System" \
418
+ --label "shipwright" \
419
+ --label "incident" \
420
+ --label "p0" \
421
+ 2>&1 | grep -oE 'https://github.com/[^ ]+' | head -1 || true
422
+ fi
423
+ fi
424
+ fi
425
+ ;;
426
+ P1)
427
+ # Error spike: create hotfix issue
428
+ info "P1 SEVERITY: Error spike detected — creating hotfix issue"
429
+ if [[ "${NO_GITHUB:-}" != "true" && "${NO_GITHUB:-}" != "1" ]]; then
430
+ local owner_repo
431
+ owner_repo=$(get_owner_repo) || true
432
+ if [[ -n "$owner_repo" && -n "$pr_number" ]]; then
433
+ if command -v gh >/dev/null 2>&1; then
434
+ gh issue create \
435
+ --repo "$owner_repo" \
436
+ --title "Hotfix Needed: Post-Merge Error Spike - PR #$pr_number" \
437
+ --body "**Severity**: P1 - HIGH
438
+
439
+ **Issue**: Error spike detected in production after PR merge
440
+
441
+ **Type**: $regression_type
442
+ **Evidence**: $evidence
443
+
444
+ **Action Required**: Investigate and create hotfix
445
+
446
+ **Related PR**: #$pr_number
447
+
448
+ ---
449
+ Auto-created by Shipwright Post-Merge Feedback System" \
450
+ --label "shipwright" \
451
+ --label "hotfix" \
452
+ --label "p1" \
453
+ 2>&1 | grep -oE 'https://github.com/[^ ]+' | head -1 || true
454
+ fi
455
+ fi
456
+ fi
457
+ ;;
458
+ P2)
459
+ # Performance or minor regression: schedule for next sprint
460
+ info "P2 SEVERITY: Performance degradation — scheduling for next sprint"
461
+ ;;
462
+ P3|*)
463
+ # Minor: just record in memory
464
+ info "P3 SEVERITY: Minor regression — recorded for awareness"
465
+ ;;
466
+ esac
467
+
468
+ emit_event "feedback_auto_respond" "severity=$severity" "type=$regression_type"
469
+ }
470
+
471
+ # ─── Learn from post-merge outcomes ─────────────────────────────────────────────
472
+ feedback_learn_from_outcome() {
473
+ local pr_number="${1:-}"
474
+ local merge_result="${2:-success}"
475
+ local deploy_result="${3:-unknown}"
476
+ local regression="${4:-false}"
477
+ local regression_type="${5:-none}"
478
+
479
+ if [[ -z "$pr_number" ]]; then
480
+ error "Usage: feedback_learn_from_outcome <pr_number> [merge_result] [deploy_result] [regression] [type]"
481
+ return 1
482
+ fi
483
+
484
+ info "Recording merge outcome for learning: PR #$pr_number"
485
+
486
+ ensure_dirs
487
+
488
+ # Calculate time-to-detect (from merge timestamp if available)
489
+ local time_to_detect=0
490
+ if [[ -f "$POST_MERGE_MONITORING_FILE" ]]; then
491
+ local start_epoch
492
+ start_epoch=$(jq -r '.start_epoch // 0' "$POST_MERGE_MONITORING_FILE")
493
+ local end_epoch
494
+ end_epoch=$(jq -r '.end_epoch // 0' "$POST_MERGE_MONITORING_FILE")
495
+ if [[ "$end_epoch" -gt 0 && "$start_epoch" -gt 0 ]]; then
496
+ time_to_detect=$((end_epoch - start_epoch))
497
+ fi
498
+ fi
499
+
500
+ local outcome_entry
501
+ outcome_entry=$(jq -c -n \
502
+ --arg ts "$(now_iso)" \
503
+ --arg pr "$pr_number" \
504
+ --arg merge_res "$merge_result" \
505
+ --arg deploy_res "$deploy_result" \
506
+ --arg regr "$regression" \
507
+ --arg regr_type "$regression_type" \
508
+ --arg ttd "$time_to_detect" \
509
+ --arg repo "$(basename "$REPO_DIR")" \
510
+ '{
511
+ timestamp: $ts,
512
+ pr_number: $pr,
513
+ repository: $repo,
514
+ merge_result: $merge_res,
515
+ deploy_result: $deploy_res,
516
+ regression_detected: ($regr == "true"),
517
+ regression_type: $regr_type,
518
+ time_to_detect_secs: ($ttd | tonumber)
519
+ }')
520
+
521
+ # Atomic write
522
+ local tmp_file
523
+ tmp_file=$(mktemp)
524
+ echo "$outcome_entry" >> "$tmp_file"
525
+ cat "$MERGE_OUTCOMES_FILE" >> "$tmp_file" 2>/dev/null || true
526
+ mv "$tmp_file" "$MERGE_OUTCOMES_FILE"
527
+
528
+ success "Learned outcome: PR #$pr_number ($merge_result / $deploy_result / regression=$regression)"
529
+ emit_event "feedback_learn_from_outcome" "pr=$pr_number" "merge_result=$merge_result" "regression=$regression"
530
+ }
531
+
532
+ # ─── Generate post-merge health report ──────────────────────────────────────────
533
+ feedback_report() {
534
+ local days="${1:-30}"
535
+
536
+ info "Post-Merge Health Report (last $days days)"
537
+ echo ""
538
+
539
+ if [[ ! -f "$MERGE_OUTCOMES_FILE" ]]; then
540
+ warn "No merge outcomes recorded yet"
541
+ return 0
542
+ fi
543
+
544
+ # Calculate metrics
545
+ local total_merges=0
546
+ local successful_merges=0
547
+ local regressions=0
548
+ local p0_count=0
549
+ local p1_count=0
550
+ local p2_count=0
551
+ local total_detect_time=0
552
+ local avg_detect_time=0
553
+ local common_types=""
554
+
555
+ while IFS= read -r line; do
556
+ local merge_res
557
+ merge_res=$(echo "$line" | jq -r '.merge_result // "unknown"')
558
+ local regression
559
+ regression=$(echo "$line" | jq -r '.regression_detected // false')
560
+ local regr_type
561
+ regr_type=$(echo "$line" | jq -r '.regression_type // "none"')
562
+ local ttd
563
+ ttd=$(echo "$line" | jq -r '.time_to_detect_secs // 0')
564
+
565
+ total_merges=$((total_merges + 1))
566
+ [[ "$merge_res" == "success" ]] && successful_merges=$((successful_merges + 1))
567
+ [[ "$regression" == "true" ]] && regressions=$((regressions + 1))
568
+
569
+ total_detect_time=$((total_detect_time + ttd))
570
+
571
+ # Count severity (inferred from time-to-detect for now)
572
+ if [[ "$ttd" -lt 300 ]]; then
573
+ p0_count=$((p0_count + 1))
574
+ elif [[ "$ttd" -lt 900 ]]; then
575
+ p1_count=$((p1_count + 1))
576
+ else
577
+ p2_count=$((p2_count + 1))
578
+ fi
579
+
580
+ # Track common regression types
581
+ if [[ "$regr_type" != "none" ]]; then
582
+ common_types="${common_types}${regr_type};"
583
+ fi
584
+ done < "$MERGE_OUTCOMES_FILE"
585
+
586
+ if [[ "$total_merges" -gt 0 ]]; then
587
+ avg_detect_time=$((total_detect_time / total_merges))
588
+ fi
589
+
590
+ # Output report
591
+ echo " ${CYAN}Merge Statistics${RESET}"
592
+ echo " Total Merges: $total_merges"
593
+ echo " Success Rate: $((successful_merges * 100 / total_merges))% ($successful_merges/$total_merges)"
594
+ echo " Regressions: $regressions ($((regressions * 100 / total_merges))%)"
595
+ echo ""
596
+ echo " ${CYAN}Detection Metrics${RESET}"
597
+ echo " Avg Detection Time: ${avg_detect_time}s ($((avg_detect_time / 60))m)"
598
+ echo " P0 Incidents: $p0_count"
599
+ echo " P1 Issues: $p1_count"
600
+ echo " P2 Items: $p2_count"
601
+ echo ""
602
+ echo " ${CYAN}Common Regression Types${RESET}"
603
+ if [[ -n "$common_types" ]]; then
604
+ echo "$common_types" | tr ';' '\n' | sort | uniq -c | sort -rn | head -5 | while read -r count type; do
605
+ [[ -n "$type" ]] && echo " $type: $count occurrences"
606
+ done
607
+ else
608
+ echo " (None recorded)"
609
+ fi
610
+ echo ""
611
+ success "Report generated"
612
+ return 0
613
+ }
614
+
615
+ # ─── Collect errors from monitor stage output ────────────────────────────────────
108
616
  cmd_collect() {
109
617
  local log_path="${1:-.}"
110
618
 
@@ -127,6 +635,7 @@ cmd_collect() {
127
635
  while IFS= read -r file; do
128
636
  local result
129
637
  result=$(parse_error_patterns "$file") || continue
638
+ # shellcheck disable=SC2034
130
639
  IFS='|' read -r count types traces <<< "$result"
131
640
  total_errors=$((total_errors + count))
132
641
  error_summary="${error_summary}${types};"
@@ -398,20 +907,37 @@ ${BOLD}SUBCOMMANDS${RESET}
398
907
  ${CYAN}rollback${RESET} [env] [reason] Trigger rollback via Deployments API
399
908
  ${CYAN}learn${RESET} [cause] [fix] Capture incident in memory system
400
909
  ${CYAN}report${RESET} [days] Show recent incidents (default: 7 days)
910
+
911
+ ${CYAN}post-merge${RESET} [sha] [env] Monitor production after merge
912
+ ${CYAN}regressions${RESET} [file] Detect regressions from monitoring data
913
+ ${CYAN}correlate${RESET} [pr] [json] Link regressions to changed files
914
+ ${CYAN}outcomes${RESET} [pr] [...] Record merge outcome for learning
915
+ ${CYAN}health${RESET} [days] Post-merge health report
916
+
401
917
  ${CYAN}help${RESET} Show this help message
402
918
 
403
919
  ${BOLD}EXAMPLES${RESET}
920
+ ${DIM}# Traditional flow${RESET}
404
921
  ${DIM}shipwright feedback collect ./logs${RESET}
405
922
  ${DIM}shipwright feedback analyze${RESET}
406
923
  ${DIM}shipwright feedback create-issue${RESET}
407
924
  ${DIM}shipwright feedback rollback production "Hotfix v1.2.3 regression"${RESET}
408
- ${DIM}shipwright feedback learn "Off-by-one error in pagination" "Fixed in PR #456"${RESET}
925
+ ${DIM}shipwright feedback learn "Off-by-one error" "Fixed in PR #456"${RESET}
409
926
  ${DIM}shipwright feedback report 30${RESET}
410
927
 
928
+ ${DIM}# Post-merge monitoring flow${RESET}
929
+ ${DIM}shipwright feedback post-merge abc1234 production${RESET}
930
+ ${DIM}shipwright feedback regressions${RESET}
931
+ ${DIM}shipwright feedback correlate 42${RESET}
932
+ ${DIM}shipwright feedback outcomes 42 success deployed true error_spike${RESET}
933
+ ${DIM}shipwright feedback health 30${RESET}
934
+
411
935
  ${BOLD}STORAGE${RESET}
412
- Incidents: $INCIDENTS_FILE
413
- Errors: ${ARTIFACTS_DIR}/errors-collected.json
414
- Rollbacks: ${ARTIFACTS_DIR}/rollbacks.jsonl
936
+ Incidents: $INCIDENTS_FILE
937
+ Errors: ${ARTIFACTS_DIR}/errors-collected.json
938
+ Rollbacks: ${ARTIFACTS_DIR}/rollbacks.jsonl
939
+ Merge Outcomes: $MERGE_OUTCOMES_FILE
940
+ Monitoring: $POST_MERGE_MONITORING_FILE
415
941
 
416
942
  ${BOLD}VERSION${RESET}
417
943
  $VERSION
@@ -442,6 +968,21 @@ main() {
442
968
  report)
443
969
  cmd_report "$@"
444
970
  ;;
971
+ post-merge)
972
+ feedback_post_merge_monitor "$@"
973
+ ;;
974
+ regressions)
975
+ feedback_detect_regression "$@"
976
+ ;;
977
+ correlate)
978
+ feedback_correlate_with_changes "$@"
979
+ ;;
980
+ outcomes)
981
+ feedback_learn_from_outcome "$@"
982
+ ;;
983
+ health)
984
+ feedback_report "$@"
985
+ ;;
445
986
  help|-h|--help)
446
987
  show_help
447
988
  ;;