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,511 @@
1
+ #!/usr/bin/env bash
2
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
3
+ # ║ test-optimizer — Test execution optimization: parallel, affected-first, ║
4
+ # ║ fast-fail, with historical data and learning ║
5
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
6
+ #
7
+ # Functions:
8
+ # testopt_init Initialize test discovery and history loading
9
+ # testopt_select_affected Select tests affected by changed files
10
+ # testopt_prioritize Order tests by likelihood to fail
11
+ # testopt_run_with_fast_fail Execute with stop-on-first-fail
12
+ # testopt_run_parallel Execute independent tests in parallel
13
+ # testopt_record_history Record results for learning
14
+ # testopt_report Print optimization stats
15
+ #
16
+ # Usage:
17
+ # source scripts/lib/test-optimizer.sh
18
+ # testopt_init <project_root>
19
+ # testopt_record_history "test_file" "pass/fail" "duration" "changed_files"
20
+ # testopt_report
21
+ #
22
+ set -euo pipefail
23
+
24
+ # Module guard
25
+ [[ -n "${_TEST_OPTIMIZER_LOADED:-}" ]] && return 0; _TEST_OPTIMIZER_LOADED=1
26
+
27
+ # ─── Defaults ──────────────────────────────────────────────────────────────
28
+ SCRIPT_DIR="${SCRIPT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"
29
+
30
+ # State: discovered test files, historical data, changed files
31
+ declare -a DISCOVERED_TESTS=()
32
+ declare -a AFFECTED_TESTS=()
33
+ declare -a TEST_HISTORY=()
34
+ declare -a CHANGED_FILES=()
35
+
36
+ TESTOPT_HISTORY_FILE="${HOME}/.shipwright/optimization/test-history.jsonl"
37
+ TESTOPT_PROJECT_ROOT=""
38
+ TESTOPT_STATS_TESTS_RUN=0
39
+ TESTOPT_STATS_TESTS_SKIPPED=0
40
+ TESTOPT_STATS_TIME_SAVED=0
41
+ TESTOPT_STATS_FAIL_EARLY="false"
42
+
43
+ # Ensure helpers
44
+ [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
45
+ [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
46
+ [[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
47
+ [[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
48
+ [[ "$(type -t emit_event 2>/dev/null)" == "function" ]] || emit_event() { true; }
49
+
50
+ # ─── Test Discovery ────────────────────────────────────────────────────────
51
+
52
+ # Discover all test files in a project
53
+ # testopt_discover_tests <project_root>
54
+ testopt_discover_tests() {
55
+ local project_root="${1:-.}"
56
+ [[ ! -d "$project_root" ]] && { error "Project root not found: $project_root"; return 1; }
57
+
58
+ DISCOVERED_TESTS=()
59
+
60
+ # Pattern 1: *-test.sh
61
+ while IFS= read -r test_file; do
62
+ [[ -f "$test_file" ]] && DISCOVERED_TESTS+=("$test_file")
63
+ done < <(find "$project_root" -name "*-test.sh" -type f 2>/dev/null || true)
64
+
65
+ # Pattern 2: *_test.sh
66
+ while IFS= read -r test_file; do
67
+ [[ -f "$test_file" ]] && DISCOVERED_TESTS+=("$test_file")
68
+ done < <(find "$project_root" -name "*_test.sh" -type f 2>/dev/null || true)
69
+
70
+ # Pattern 3: test_*.sh
71
+ while IFS= read -r test_file; do
72
+ [[ -f "$test_file" ]] && DISCOVERED_TESTS+=("$test_file")
73
+ done < <(find "$project_root" -name "test_*.sh" -type f 2>/dev/null || true)
74
+
75
+ # Deduplicate
76
+ local IFS=$'\n'
77
+ DISCOVERED_TESTS=($(sort -u <<<"${DISCOVERED_TESTS[*]}" 2>/dev/null || true))
78
+ }
79
+
80
+ # ─── History Loading ──────────────────────────────────────────────────────
81
+
82
+ # Load historical test data
83
+ testopt_load_history() {
84
+ TEST_HISTORY=()
85
+ if [[ ! -f "$TESTOPT_HISTORY_FILE" ]]; then
86
+ return 0
87
+ fi
88
+
89
+ while IFS= read -r line; do
90
+ [[ -z "$line" ]] && continue
91
+ TEST_HISTORY+=("$line")
92
+ done < "$TESTOPT_HISTORY_FILE"
93
+ }
94
+
95
+ # Query history for a test file: returns duration, or 0 if not found
96
+ testopt_get_historical_duration() {
97
+ local test_file="$1"
98
+ for entry in "${TEST_HISTORY[@]:-}"; do
99
+ local file
100
+ file=$(echo "$entry" | jq -r '.test_file // empty' 2>/dev/null || true)
101
+ if [[ "$file" == "$test_file" ]]; then
102
+ echo "$entry" | jq -r '.duration_s // 0' 2>/dev/null || echo 0
103
+ return 0
104
+ fi
105
+ done
106
+ echo 0
107
+ }
108
+
109
+ # Query history for fail rate (0.0-1.0)
110
+ testopt_get_fail_rate() {
111
+ local test_file="$1"
112
+ local pass_count=0
113
+ local fail_count=0
114
+
115
+ for entry in "${TEST_HISTORY[@]:-}"; do
116
+ local file result
117
+ file=$(echo "$entry" | jq -r '.test_file // empty' 2>/dev/null || true)
118
+ result=$(echo "$entry" | jq -r '.result // empty' 2>/dev/null || true)
119
+ if [[ "$file" == "$test_file" ]]; then
120
+ if [[ "$result" == "pass" ]]; then
121
+ pass_count=$((pass_count + 1))
122
+ elif [[ "$result" == "fail" ]]; then
123
+ fail_count=$((fail_count + 1))
124
+ fi
125
+ fi
126
+ done
127
+
128
+ local total=$((pass_count + fail_count))
129
+ if [[ "$total" -eq 0 ]]; then
130
+ echo "0.0"
131
+ else
132
+ # Return fail_count/total as float (bash approximation)
133
+ echo "$fail_count" | awk -v total="$total" '{ printf "%.2f", $1 / total }'
134
+ fi
135
+ }
136
+
137
+ # ─── Affected Test Selection ───────────────────────────────────────────────
138
+
139
+ # Detect which files changed between revisions
140
+ # testopt_get_changed_files [<from_ref> <to_ref>]
141
+ testopt_get_changed_files() {
142
+ local from_ref="${1:-HEAD~1}"
143
+ local to_ref="${2:-HEAD}"
144
+
145
+ CHANGED_FILES=()
146
+
147
+ # Try git diff first
148
+ if command -v git >/dev/null 2>&1; then
149
+ while IFS= read -r file; do
150
+ [[ -n "$file" ]] && CHANGED_FILES+=("$file")
151
+ done < <(git diff --name-only "$from_ref" "$to_ref" 2>/dev/null || true)
152
+ fi
153
+ }
154
+
155
+ # Map changed files to affected test files
156
+ # Returns: test files that import/source changed files or are in same directory
157
+ testopt_select_affected() {
158
+ local -a changed_files=("$@")
159
+ AFFECTED_TESTS=()
160
+
161
+ if [[ ${#changed_files[@]} -eq 0 ]]; then
162
+ # No changes detected, return all tests
163
+ AFFECTED_TESTS=("${DISCOVERED_TESTS[@]}")
164
+ return 0
165
+ fi
166
+
167
+ # Extract directories from changed files
168
+ declare -a changed_dirs=()
169
+ for file in "${changed_files[@]}"; do
170
+ local dir
171
+ dir=$(dirname "$file")
172
+ changed_dirs+=("$dir")
173
+ done
174
+
175
+ # For each discovered test, check if it's affected
176
+ for test_file in "${DISCOVERED_TESTS[@]}"; do
177
+ local test_dir
178
+ test_dir=$(dirname "$test_file")
179
+
180
+ local is_affected=0
181
+
182
+ # Check 1: Test in same directory as changed file
183
+ for dir in "${changed_dirs[@]}"; do
184
+ if [[ "$test_dir" == "$dir" ]]; then
185
+ is_affected=1
186
+ break
187
+ fi
188
+ done
189
+
190
+ # Check 2: Test sources/imports changed file
191
+ if [[ "$is_affected" -eq 0 ]]; then
192
+ for changed_file in "${changed_files[@]}"; do
193
+ # Check if test file sources the changed file
194
+ if grep -qF "source.*$changed_file\|source.*./$(basename "$changed_file")" "$test_file" 2>/dev/null || true; then
195
+ is_affected=1
196
+ break
197
+ fi
198
+ # Check by pattern (lib imports)
199
+ local changed_base
200
+ changed_base=$(basename "$changed_file")
201
+ if grep -qF "source.*$changed_base" "$test_file" 2>/dev/null || true; then
202
+ is_affected=1
203
+ break
204
+ fi
205
+ done
206
+ fi
207
+
208
+ if [[ "$is_affected" -eq 1 ]]; then
209
+ AFFECTED_TESTS+=("$test_file")
210
+ fi
211
+ done
212
+
213
+ # Fallback: if no affected tests found, use all tests
214
+ if [[ ${#AFFECTED_TESTS[@]} -eq 0 ]]; then
215
+ AFFECTED_TESTS=("${DISCOVERED_TESTS[@]}")
216
+ fi
217
+ }
218
+
219
+ # ─── Test Prioritization ──────────────────────────────────────────────────
220
+
221
+ # Prioritize tests by: fail rate, historical duration, then name
222
+ # Returns: space-separated test list (stdout)
223
+ testopt_prioritize() {
224
+ local -a tests_to_sort=("$@")
225
+
226
+ if [[ ${#tests_to_sort[@]} -eq 0 ]]; then
227
+ tests_to_sort=("${AFFECTED_TESTS[@]}")
228
+ fi
229
+
230
+ # Build temp file with scoring
231
+ local tmp_score_file
232
+ tmp_score_file=$(mktemp)
233
+ trap "rm -f '$tmp_score_file'" RETURN
234
+
235
+ for test_file in "${tests_to_sort[@]}"; do
236
+ local fail_rate duration
237
+ fail_rate=$(testopt_get_fail_rate "$test_file")
238
+ duration=$(testopt_get_historical_duration "$test_file")
239
+
240
+ # Score: fail_rate (0-100) * 100 + duration (so high-fail tests run first, then fast ones)
241
+ local fail_score
242
+ fail_score=$(echo "$fail_rate" | awk '{ printf "%.0f", $1 * 100 }')
243
+ local score=$((fail_score * 100 - duration))
244
+
245
+ echo "$score $test_file" >> "$tmp_score_file"
246
+ done
247
+
248
+ # Sort by score descending, output just test files
249
+ sort -rn "$tmp_score_file" 2>/dev/null | awk '{ print $2 }' || echo "${tests_to_sort[@]}"
250
+ }
251
+
252
+ # ─── Test Execution ────────────────────────────────────────────────────────
253
+
254
+ # Run tests with fast-fail: stop on first failure
255
+ # testopt_run_with_fast_fail [--continue-on-fail] <test1> [test2] ...
256
+ # Returns: 0 on all pass, 1 on first fail
257
+ testopt_run_with_fast_fail() {
258
+ local continue_on_fail=false
259
+ [[ "$1" == "--continue-on-fail" ]] && { continue_on_fail=true; shift; }
260
+
261
+ local -a tests=("$@")
262
+ [[ ${#tests[@]} -eq 0 ]] && tests=("${AFFECTED_TESTS[@]}")
263
+
264
+ local failed_test=""
265
+ local all_passed=true
266
+ local tmp_results
267
+ tmp_results=$(mktemp)
268
+ trap "rm -f '$tmp_results'" RETURN
269
+
270
+ info "Running ${#tests[@]} test(s) with fast-fail..."
271
+
272
+ for test_file in "${tests[@]}"; do
273
+ [[ ! -f "$test_file" ]] && continue
274
+
275
+ local start_ts exit_code=0
276
+ start_ts=$(date +%s)
277
+
278
+ # Run the test
279
+ bash "$test_file" > /dev/null 2>&1 || exit_code=$?
280
+
281
+ local duration=$(($(date +%s) - start_ts))
282
+
283
+ if [[ "$exit_code" -ne 0 ]]; then
284
+ all_passed=false
285
+ failed_test="$test_file"
286
+ local result="fail"
287
+ TESTOPT_STATS_FAIL_EARLY=true
288
+ TESTOPT_STATS_TESTS_RUN=$((TESTOPT_STATS_TESTS_RUN + 1))
289
+
290
+ # Record this failure
291
+ {
292
+ echo "{"
293
+ echo " \"test_file\": \"$test_file\","
294
+ echo " \"result\": \"$result\","
295
+ echo " \"duration_s\": $duration,"
296
+ echo " \"ts\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\""
297
+ echo "}"
298
+ } >> "$tmp_results"
299
+
300
+ error "Test failed: $test_file (${duration}s)"
301
+ emit_event "testopt.fail_fast" "test=$test_file" "duration=$duration"
302
+
303
+ if [[ "$continue_on_fail" == false ]]; then
304
+ break
305
+ fi
306
+ else
307
+ TESTOPT_STATS_TESTS_RUN=$((TESTOPT_STATS_TESTS_RUN + 1))
308
+ {
309
+ echo "{"
310
+ echo " \"test_file\": \"$test_file\","
311
+ echo " \"result\": \"pass\","
312
+ echo " \"duration_s\": $duration,"
313
+ echo " \"ts\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\""
314
+ echo "}"
315
+ } >> "$tmp_results" 2>/dev/null || true
316
+ success "Test passed: $test_file (${duration}s)"
317
+ fi
318
+ done
319
+
320
+ # Return failed test name in stdout for caller to process
321
+ [[ -n "$failed_test" ]] && echo "$failed_test"
322
+
323
+ [[ "$all_passed" == "true" ]] && return 0 || return 1
324
+ }
325
+
326
+ # Run tests in parallel (grouped by directory)
327
+ # testopt_run_parallel [--max-workers=N] <test1> [test2] ...
328
+ # Returns: 0 on all pass, 1 on any fail
329
+ testopt_run_parallel() {
330
+ local max_workers=4
331
+ [[ "$1" == --max-workers=* ]] && { max_workers="${1#--max-workers=}"; shift; }
332
+
333
+ local -a tests=("$@")
334
+ [[ ${#tests[@]} -eq 0 ]] && tests=("${AFFECTED_TESTS[@]}")
335
+
336
+ info "Running ${#tests[@]} test(s) in parallel (max ${max_workers} workers)..."
337
+
338
+ # Group tests by directory for better cache locality
339
+ declare -a test_groups=()
340
+ declare -a current_group=()
341
+ local current_dir=""
342
+
343
+ for test_file in "${tests[@]}"; do
344
+ local test_dir
345
+ test_dir=$(dirname "$test_file")
346
+ if [[ "$test_dir" != "$current_dir" ]] && [[ ${#current_group[@]} -gt 0 ]]; then
347
+ test_groups+=("${current_group[*]}")
348
+ current_group=()
349
+ fi
350
+ current_dir="$test_dir"
351
+ current_group+=("$test_file")
352
+ done
353
+ [[ ${#current_group[@]} -gt 0 ]] && test_groups+=("${current_group[*]}")
354
+
355
+ # Run groups in parallel
356
+ local tmp_results
357
+ tmp_results=$(mktemp)
358
+ trap "rm -f '$tmp_results'" RETURN
359
+
360
+ local all_passed=true
361
+ local job_count=0
362
+
363
+ for group in "${test_groups[@]:-}"; do
364
+ # Wait for a worker slot
365
+ while [[ $(jobs -r | wc -l) -ge "$max_workers" ]]; do
366
+ sleep 0.1
367
+ done
368
+
369
+ # Run this group in background
370
+ {
371
+ for test_file in $group; do
372
+ [[ ! -f "$test_file" ]] && continue
373
+ local start_ts exit_code=0
374
+ start_ts=$(date +%s)
375
+ bash "$test_file" > /dev/null 2>&1 || exit_code=$?
376
+ local duration=$(($(date +%s) - start_ts))
377
+
378
+ if [[ "$exit_code" -ne 0 ]]; then
379
+ all_passed=false
380
+ echo "$test_file FAIL $duration" >> "$tmp_results"
381
+ else
382
+ echo "$test_file PASS $duration" >> "$tmp_results"
383
+ fi
384
+ done
385
+ } &
386
+
387
+ job_count=$((job_count + 1))
388
+ done
389
+
390
+ # Wait for all background jobs
391
+ wait
392
+ TESTOPT_STATS_TESTS_RUN=$((TESTOPT_STATS_TESTS_RUN + ${#tests[@]}))
393
+
394
+ # Report results
395
+ if [[ -f "$tmp_results" ]]; then
396
+ while IFS= read -r line; do
397
+ local test_file status duration
398
+ test_file=$(echo "$line" | awk '{ print $1 }')
399
+ status=$(echo "$line" | awk '{ print $2 }')
400
+ duration=$(echo "$line" | awk '{ print $3 }')
401
+
402
+ if [[ "$status" == "PASS" ]]; then
403
+ success "Test passed: $test_file (${duration}s)"
404
+ else
405
+ error "Test failed: $test_file (${duration}s)"
406
+ fi
407
+ done < "$tmp_results"
408
+ fi
409
+
410
+ [[ "$all_passed" == true ]] && return 0 || return 1
411
+ }
412
+
413
+ # ─── History Recording ─────────────────────────────────────────────────────
414
+
415
+ # Record a test execution result for future prioritization
416
+ # testopt_record_history <test_file> <result> <duration> [changed_files...]
417
+ testopt_record_history() {
418
+ local test_file="$1"
419
+ local result="${2:-unknown}" # pass or fail
420
+ local duration="${3:-0}"
421
+ shift 3 || true
422
+ local changed_files=("$@")
423
+
424
+ [[ -z "$test_file" ]] && return 1
425
+
426
+ # Ensure history directory exists
427
+ mkdir -p "$(dirname "$TESTOPT_HISTORY_FILE")"
428
+
429
+ # Atomic write: temp file + move
430
+ local tmp_history
431
+ tmp_history=$(mktemp)
432
+ trap "rm -f '$tmp_history'" RETURN
433
+
434
+ # Build JSON entry (single line JSONL format)
435
+ local changed_files_json="[]"
436
+ if [[ ${#changed_files[@]} -gt 0 ]]; then
437
+ changed_files_json="[$(printf '"%s",' "${changed_files[@]}" | sed 's/,$//')]"
438
+ fi
439
+
440
+ local json_line
441
+ if command -v jq >/dev/null 2>&1; then
442
+ json_line=$(jq -c -n \
443
+ --arg test_file "$test_file" \
444
+ --arg result "$result" \
445
+ --argjson duration "$duration" \
446
+ --argjson changed_files "$changed_files_json" \
447
+ --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
448
+ '{test_file: $test_file, result: $result, duration_s: $duration, changed_files: $changed_files, ts: $ts}' 2>/dev/null)
449
+ else
450
+ json_line="{\"test_file\": \"$test_file\", \"result\": \"$result\", \"duration_s\": $duration, \"changed_files\": [], \"ts\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"
451
+ fi
452
+
453
+ echo "$json_line" >> "$tmp_history"
454
+
455
+ # Append to history (use >> to append, not overwrite)
456
+ cat "$tmp_history" >> "$TESTOPT_HISTORY_FILE" 2>/dev/null || true
457
+
458
+ emit_event "testopt.recorded" "test=$test_file" "result=$result" "duration=$duration"
459
+ }
460
+
461
+ # ─── Initialization ────────────────────────────────────────────────────────
462
+
463
+ # Initialize test optimizer for a pipeline run
464
+ # testopt_init <project_root>
465
+ testopt_init() {
466
+ local project_root="${1:-.}"
467
+ TESTOPT_PROJECT_ROOT="$project_root"
468
+
469
+ info "Initializing test optimizer..."
470
+
471
+ # Discover tests
472
+ testopt_discover_tests "$project_root"
473
+ [[ ${#DISCOVERED_TESTS[@]} -eq 0 ]] && { warn "No test files discovered"; return 0; }
474
+ info "Discovered ${#DISCOVERED_TESTS[@]} test file(s)"
475
+
476
+ # Load historical data
477
+ testopt_load_history
478
+ [[ ${#TEST_HISTORY[@]} -eq 0 ]] && { info "No historical test data found"; } || { info "Loaded ${#TEST_HISTORY[@]} historical record(s)"; }
479
+
480
+ # Get changed files (assume standard git workflow)
481
+ testopt_get_changed_files "HEAD~1" "HEAD" 2>/dev/null || testopt_get_changed_files
482
+
483
+ if [[ ${#CHANGED_FILES[@]} -gt 0 ]]; then
484
+ info "Detected ${#CHANGED_FILES[@]} changed file(s)"
485
+ testopt_select_affected
486
+ info "Selected ${#AFFECTED_TESTS[@]} affected test(s)"
487
+ else
488
+ AFFECTED_TESTS=("${DISCOVERED_TESTS[@]}")
489
+ fi
490
+ }
491
+
492
+ # ─── Reporting ────────────────────────────────────────────────────────────
493
+
494
+ # Print test optimization statistics
495
+ testopt_report() {
496
+ local test_saved=0
497
+ [[ "$TESTOPT_STATS_FAIL_EARLY" == true ]] && test_saved=$((${#DISCOVERED_TESTS[@]} - TESTOPT_STATS_TESTS_RUN))
498
+
499
+ echo ""
500
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
501
+ echo "Test Execution Optimization Report"
502
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
503
+ echo ""
504
+ echo " Discovered tests: ${#DISCOVERED_TESTS[@]}"
505
+ echo " Affected tests: ${#AFFECTED_TESTS[@]}"
506
+ echo " Tests run: $TESTOPT_STATS_TESTS_RUN"
507
+ echo " Tests skipped: $TESTOPT_STATS_TESTS_SKIPPED"
508
+ [[ "$TESTOPT_STATS_FAIL_EARLY" == true ]] && echo " Fast-fail: Yes (stopped at first failure, saved $test_saved tests)"
509
+ echo ""
510
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
511
+ }
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env bash
2
+ # Custom file suggestion for Claude Code @ autocomplete
3
+ # Surfaces Shipwright-specific files for quick access
4
+ set -euo pipefail
5
+
6
+ PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || echo ".")
7
+
8
+ # Core config files
9
+ for f in \
10
+ ".claude/pipeline-state.md" \
11
+ ".claude/daemon-config.json" \
12
+ ".claude/fleet-config.json" \
13
+ ".claude/loop-state.md" \
14
+ ".claude/managed-mcp.json" \
15
+ ".claude/settings.json" \
16
+ ".claude/CLAUDE.md" \
17
+ "CLAUDE.md" \
18
+ "CHANGELOG.md"; do
19
+ [[ -f "$PROJECT_ROOT/$f" ]] && echo "$f"
20
+ done
21
+
22
+ # Agent definitions
23
+ for f in "$PROJECT_ROOT"/.claude/agents/*.md; do
24
+ [[ -f "$f" ]] && echo ".claude/agents/$(basename "$f")"
25
+ done
26
+
27
+ # Schemas
28
+ for f in "$PROJECT_ROOT"/schemas/*.json; do
29
+ [[ -f "$f" ]] && echo "schemas/$(basename "$f")"
30
+ done
31
+
32
+ # Pipeline artifacts (most recent)
33
+ if [[ -d "$PROJECT_ROOT/.claude/pipeline-artifacts" ]]; then
34
+ for f in plan.md design.md composed-pipeline.json; do
35
+ [[ -f "$PROJECT_ROOT/.claude/pipeline-artifacts/$f" ]] && echo ".claude/pipeline-artifacts/$f"
36
+ done
37
+ fi
38
+
39
+ # Loop logs (latest iteration)
40
+ if [[ -d "$PROJECT_ROOT/.claude/loop-logs" ]]; then
41
+ # shellcheck disable=SC2012
42
+ ls -t "$PROJECT_ROOT/.claude/loop-logs"/iteration-*.log 2>/dev/null | head -3 | while read -r f; do
43
+ echo ".claude/loop-logs/$(basename "$f")"
44
+ done
45
+ fi
@@ -0,0 +1,61 @@
1
+ ## Adversarial Quality: Systematic Edge Case Discovery
2
+
3
+ Think like an attacker and a chaos engineer. Find the ways this code will break.
4
+
5
+ ### Failure Mode Analysis
6
+ For each component changed, ask:
7
+ 1. What happens when the input is empty? Null? Maximum size?
8
+ 2. What happens when an external dependency is down?
9
+ 3. What happens under concurrent access?
10
+ 4. What happens when disk is full? Memory is low? Network is flaky?
11
+ 5. What happens when the clock skews or timezone changes?
12
+
13
+ ### Edge Case Categories
14
+
15
+ **Data Edge Cases:**
16
+ - Empty collections, single-element collections, max-size collections
17
+ - Unicode, emoji, RTL text, null bytes in strings
18
+ - Numeric overflow, underflow, NaN, Infinity, negative zero
19
+ - Date boundaries: midnight, DST transitions, leap seconds, year 2038
20
+
21
+ **Timing Edge Cases:**
22
+ - Race conditions between concurrent operations
23
+ - Operations that span a retry/timeout boundary
24
+ - Stale cache reads during updates
25
+ - Clock skew between distributed components
26
+
27
+ **State Edge Cases:**
28
+ - Partially completed operations (crash mid-write)
29
+ - Re-entrant calls (function called while already executing)
30
+ - State corruption from previous failed operations
31
+ - Idempotency violations (same request processed twice)
32
+
33
+ ### Negative Testing Prompts
34
+ - What if a user deliberately sends malformed input?
35
+ - What if the network drops mid-request?
36
+ - What if the database returns stale data?
37
+ - What if two users modify the same resource simultaneously?
38
+ - What if the system runs for 30 days without restart?
39
+
40
+ ### Adversarial Thinking
41
+ - How could a malicious user exploit this change?
42
+ - What error messages leak internal implementation details?
43
+ - Are there timing side-channels in security-sensitive operations?
44
+ - Can rate limits be bypassed by parameter manipulation?
45
+
46
+ ### Definition of Done for Quality
47
+ - All happy paths tested
48
+ - All identified edge cases tested or documented as known limitations
49
+ - Error paths return meaningful messages (not stack traces)
50
+ - Resource cleanup happens even on failure (finally/defer patterns)
51
+
52
+ ### Required Output (Mandatory)
53
+
54
+ Your output MUST include these sections when this skill is active:
55
+
56
+ 1. **Failure Modes Found**: For each component, list what happens when it fails (5+ specific scenarios)
57
+ 2. **Negative Test Cases**: Specific test cases covering empty input, null, maximum size, concurrent access, resource exhaustion
58
+ 3. **Edge Cases Tested**: Data edge cases (Unicode, numeric overflow), timing edge cases (race conditions), state edge cases (partial failure recovery)
59
+ 4. **Definition of Done for Quality**: Confirmation that all happy paths are tested, edge cases are covered or documented as known limitations, error messages are clear
60
+
61
+ If any section is not applicable, explicitly state why it's skipped.
@@ -0,0 +1,44 @@
1
+ ## API Design Expertise
2
+
3
+ Apply these API design patterns:
4
+
5
+ ### RESTful Conventions
6
+ - Use nouns for resources, HTTP verbs for actions (GET /users, POST /users, DELETE /users/:id)
7
+ - Return appropriate status codes: 200 OK, 201 Created, 400 Bad Request, 404 Not Found, 422 Unprocessable
8
+ - Use consistent error response format: `{ "error": { "code": "...", "message": "..." } }`
9
+ - Version APIs when breaking changes are needed (/v1/users, /v2/users)
10
+
11
+ ### Request/Response Design
12
+ - Accept and return JSON (Content-Type: application/json)
13
+ - Use camelCase for JSON field names
14
+ - Include pagination for list endpoints (limit, offset or cursor)
15
+ - Support filtering and sorting via query parameters
16
+
17
+ ### Input Validation
18
+ - Validate ALL input at the API boundary — never trust client data
19
+ - Return specific validation errors with field names
20
+ - Sanitize strings against injection (SQL, XSS, command injection)
21
+ - Set reasonable size limits on request bodies
22
+
23
+ ### Error Handling
24
+ - Never expose stack traces or internal errors to clients
25
+ - Log full error details server-side
26
+ - Use consistent error codes that clients can programmatically handle
27
+ - Include request-id in responses for debugging
28
+
29
+ ### Authentication & Authorization
30
+ - Verify auth on EVERY endpoint (don't rely on frontend-only checks)
31
+ - Use principle of least privilege for authorization
32
+ - Validate tokens/sessions on each request
33
+ - Rate limit sensitive endpoints (login, password reset)
34
+
35
+ ### Required Output (Mandatory)
36
+
37
+ Your output MUST include these sections when this skill is active:
38
+
39
+ 1. **Endpoint Specification**: For each endpoint: HTTP method, path, request body schema, response schema, success/error status codes
40
+ 2. **Error Codes**: Complete list of all possible error responses with status code and error message format
41
+ 3. **Rate Limiting**: If applicable, specify rate limit strategy (requests per minute, burst limits, throttle behavior)
42
+ 4. **Versioning**: API version number and deprecation policy if breaking changes are possible
43
+
44
+ If any section is not applicable, explicitly state why it's skipped.