shipwright-cli 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/.claude/agents/code-reviewer.md +2 -0
  2. package/.claude/agents/devops-engineer.md +2 -0
  3. package/.claude/agents/doc-fleet-agent.md +2 -0
  4. package/.claude/agents/pipeline-agent.md +2 -0
  5. package/.claude/agents/shell-script-specialist.md +2 -0
  6. package/.claude/agents/test-specialist.md +2 -0
  7. package/.claude/hooks/agent-crash-capture.sh +32 -0
  8. package/.claude/hooks/post-tool-use.sh +3 -2
  9. package/.claude/hooks/pre-tool-use.sh +35 -3
  10. package/README.md +4 -4
  11. package/claude-code/hooks/config-change.sh +18 -0
  12. package/claude-code/hooks/instructions-reloaded.sh +7 -0
  13. package/claude-code/hooks/worktree-create.sh +25 -0
  14. package/claude-code/hooks/worktree-remove.sh +20 -0
  15. package/config/code-constitution.json +130 -0
  16. package/dashboard/middleware/auth.ts +134 -0
  17. package/dashboard/middleware/constants.ts +21 -0
  18. package/dashboard/public/index.html +2 -6
  19. package/dashboard/public/styles.css +100 -97
  20. package/dashboard/routes/auth.ts +38 -0
  21. package/dashboard/server.ts +66 -25
  22. package/dashboard/services/config.ts +26 -0
  23. package/dashboard/services/db.ts +118 -0
  24. package/dashboard/src/canvas/pixel-agent.ts +298 -0
  25. package/dashboard/src/canvas/pixel-sprites.ts +440 -0
  26. package/dashboard/src/canvas/shipyard-effects.ts +367 -0
  27. package/dashboard/src/canvas/shipyard-scene.ts +616 -0
  28. package/dashboard/src/canvas/submarine-layout.ts +267 -0
  29. package/dashboard/src/components/header.ts +8 -7
  30. package/dashboard/src/core/router.ts +1 -0
  31. package/dashboard/src/design/submarine-theme.ts +253 -0
  32. package/dashboard/src/main.ts +2 -0
  33. package/dashboard/src/types/api.ts +2 -1
  34. package/dashboard/src/views/activity.ts +2 -1
  35. package/dashboard/src/views/shipyard.ts +39 -0
  36. package/dashboard/types/index.ts +166 -0
  37. package/docs/plans/2026-02-28-compound-audit-and-shipyard-design.md +186 -0
  38. package/docs/plans/2026-02-28-skipper-shipwright-implementation-plan.md +1182 -0
  39. package/docs/plans/2026-02-28-skipper-shipwright-integration-design.md +531 -0
  40. package/docs/plans/2026-03-01-ai-powered-skill-injection-design.md +298 -0
  41. package/docs/plans/2026-03-01-ai-powered-skill-injection-plan.md +1109 -0
  42. package/docs/plans/2026-03-01-capabilities-cleanup-plan.md +658 -0
  43. package/docs/plans/2026-03-01-clean-architecture-plan.md +924 -0
  44. package/docs/plans/2026-03-01-compound-audit-cascade-design.md +191 -0
  45. package/docs/plans/2026-03-01-compound-audit-cascade-plan.md +921 -0
  46. package/docs/plans/2026-03-01-deep-integration-plan.md +851 -0
  47. package/docs/plans/2026-03-01-pipeline-audit-trail-design.md +145 -0
  48. package/docs/plans/2026-03-01-pipeline-audit-trail-plan.md +770 -0
  49. package/docs/plans/2026-03-01-refined-depths-brand-design.md +382 -0
  50. package/docs/plans/2026-03-01-refined-depths-implementation.md +599 -0
  51. package/docs/plans/2026-03-01-skipper-kernel-integration-design.md +203 -0
  52. package/docs/plans/2026-03-01-unified-platform-design.md +272 -0
  53. package/docs/plans/2026-03-07-claude-code-feature-integration-design.md +189 -0
  54. package/docs/plans/2026-03-07-claude-code-feature-integration-plan.md +1165 -0
  55. package/docs/research/BACKLOG_QUICK_REFERENCE.md +352 -0
  56. package/docs/research/CUTTING_EDGE_RESEARCH_2026.md +546 -0
  57. package/docs/research/RESEARCH_INDEX.md +439 -0
  58. package/docs/research/RESEARCH_SOURCES.md +440 -0
  59. package/docs/research/RESEARCH_SUMMARY.txt +275 -0
  60. package/docs/superpowers/specs/2026-03-10-pipeline-quality-revolution-design.md +341 -0
  61. package/package.json +2 -2
  62. package/scripts/lib/adaptive-model.sh +427 -0
  63. package/scripts/lib/adaptive-timeout.sh +316 -0
  64. package/scripts/lib/audit-trail.sh +309 -0
  65. package/scripts/lib/auto-recovery.sh +471 -0
  66. package/scripts/lib/bandit-selector.sh +431 -0
  67. package/scripts/lib/bootstrap.sh +104 -2
  68. package/scripts/lib/causal-graph.sh +455 -0
  69. package/scripts/lib/compat.sh +126 -0
  70. package/scripts/lib/compound-audit.sh +337 -0
  71. package/scripts/lib/constitutional.sh +454 -0
  72. package/scripts/lib/context-budget.sh +359 -0
  73. package/scripts/lib/convergence.sh +594 -0
  74. package/scripts/lib/cost-optimizer.sh +634 -0
  75. package/scripts/lib/daemon-adaptive.sh +10 -0
  76. package/scripts/lib/daemon-dispatch.sh +106 -17
  77. package/scripts/lib/daemon-failure.sh +34 -4
  78. package/scripts/lib/daemon-patrol.sh +23 -2
  79. package/scripts/lib/daemon-poll-github.sh +361 -0
  80. package/scripts/lib/daemon-poll-health.sh +299 -0
  81. package/scripts/lib/daemon-poll.sh +27 -611
  82. package/scripts/lib/daemon-state.sh +112 -66
  83. package/scripts/lib/daemon-triage.sh +10 -0
  84. package/scripts/lib/dod-scorecard.sh +442 -0
  85. package/scripts/lib/error-actionability.sh +300 -0
  86. package/scripts/lib/formal-spec.sh +461 -0
  87. package/scripts/lib/helpers.sh +177 -4
  88. package/scripts/lib/intent-analysis.sh +409 -0
  89. package/scripts/lib/loop-convergence.sh +350 -0
  90. package/scripts/lib/loop-iteration.sh +682 -0
  91. package/scripts/lib/loop-progress.sh +48 -0
  92. package/scripts/lib/loop-restart.sh +185 -0
  93. package/scripts/lib/memory-effectiveness.sh +506 -0
  94. package/scripts/lib/mutation-executor.sh +352 -0
  95. package/scripts/lib/outcome-feedback.sh +521 -0
  96. package/scripts/lib/pipeline-cli.sh +336 -0
  97. package/scripts/lib/pipeline-commands.sh +1216 -0
  98. package/scripts/lib/pipeline-detection.sh +100 -2
  99. package/scripts/lib/pipeline-execution.sh +897 -0
  100. package/scripts/lib/pipeline-github.sh +28 -3
  101. package/scripts/lib/pipeline-intelligence-compound.sh +431 -0
  102. package/scripts/lib/pipeline-intelligence-scoring.sh +407 -0
  103. package/scripts/lib/pipeline-intelligence-skip.sh +181 -0
  104. package/scripts/lib/pipeline-intelligence.sh +100 -1136
  105. package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
  106. package/scripts/lib/pipeline-quality-checks.sh +17 -715
  107. package/scripts/lib/pipeline-quality-gates.sh +563 -0
  108. package/scripts/lib/pipeline-stages-build.sh +730 -0
  109. package/scripts/lib/pipeline-stages-delivery.sh +965 -0
  110. package/scripts/lib/pipeline-stages-intake.sh +1133 -0
  111. package/scripts/lib/pipeline-stages-monitor.sh +407 -0
  112. package/scripts/lib/pipeline-stages-review.sh +1022 -0
  113. package/scripts/lib/pipeline-stages.sh +59 -2929
  114. package/scripts/lib/pipeline-state.sh +36 -5
  115. package/scripts/lib/pipeline-util.sh +487 -0
  116. package/scripts/lib/policy-learner.sh +438 -0
  117. package/scripts/lib/process-reward.sh +493 -0
  118. package/scripts/lib/project-detect.sh +649 -0
  119. package/scripts/lib/quality-profile.sh +334 -0
  120. package/scripts/lib/recruit-commands.sh +885 -0
  121. package/scripts/lib/recruit-learning.sh +739 -0
  122. package/scripts/lib/recruit-roles.sh +648 -0
  123. package/scripts/lib/reward-aggregator.sh +458 -0
  124. package/scripts/lib/rl-optimizer.sh +362 -0
  125. package/scripts/lib/root-cause.sh +427 -0
  126. package/scripts/lib/scope-enforcement.sh +445 -0
  127. package/scripts/lib/session-restart.sh +493 -0
  128. package/scripts/lib/skill-memory.sh +300 -0
  129. package/scripts/lib/skill-registry.sh +775 -0
  130. package/scripts/lib/spec-driven.sh +476 -0
  131. package/scripts/lib/test-helpers.sh +18 -7
  132. package/scripts/lib/test-holdout.sh +429 -0
  133. package/scripts/lib/test-optimizer.sh +511 -0
  134. package/scripts/shipwright-file-suggest.sh +45 -0
  135. package/scripts/skills/adversarial-quality.md +61 -0
  136. package/scripts/skills/api-design.md +44 -0
  137. package/scripts/skills/architecture-design.md +50 -0
  138. package/scripts/skills/brainstorming.md +43 -0
  139. package/scripts/skills/data-pipeline.md +44 -0
  140. package/scripts/skills/deploy-safety.md +64 -0
  141. package/scripts/skills/documentation.md +38 -0
  142. package/scripts/skills/frontend-design.md +45 -0
  143. package/scripts/skills/generated/.gitkeep +0 -0
  144. package/scripts/skills/generated/_refinements/.gitkeep +0 -0
  145. package/scripts/skills/generated/_refinements/adversarial-quality.patch.md +3 -0
  146. package/scripts/skills/generated/_refinements/architecture-design.patch.md +3 -0
  147. package/scripts/skills/generated/_refinements/brainstorming.patch.md +3 -0
  148. package/scripts/skills/generated/cli-version-management.md +29 -0
  149. package/scripts/skills/generated/collection-system-validation.md +99 -0
  150. package/scripts/skills/generated/large-scale-c-refactoring-coordination.md +97 -0
  151. package/scripts/skills/generated/pattern-matching-similarity-scoring.md +195 -0
  152. package/scripts/skills/generated/test-parallelization-detection.md +65 -0
  153. package/scripts/skills/observability.md +79 -0
  154. package/scripts/skills/performance.md +48 -0
  155. package/scripts/skills/pr-quality.md +49 -0
  156. package/scripts/skills/product-thinking.md +43 -0
  157. package/scripts/skills/security-audit.md +49 -0
  158. package/scripts/skills/systematic-debugging.md +40 -0
  159. package/scripts/skills/testing-strategy.md +47 -0
  160. package/scripts/skills/two-stage-review.md +52 -0
  161. package/scripts/skills/validation-thoroughness.md +55 -0
  162. package/scripts/sw +9 -3
  163. package/scripts/sw-activity.sh +9 -2
  164. package/scripts/sw-adaptive.sh +2 -1
  165. package/scripts/sw-adversarial.sh +2 -1
  166. package/scripts/sw-architecture-enforcer.sh +3 -1
  167. package/scripts/sw-auth.sh +12 -2
  168. package/scripts/sw-autonomous.sh +5 -1
  169. package/scripts/sw-changelog.sh +4 -1
  170. package/scripts/sw-checkpoint.sh +2 -1
  171. package/scripts/sw-ci.sh +5 -1
  172. package/scripts/sw-cleanup.sh +4 -26
  173. package/scripts/sw-code-review.sh +10 -4
  174. package/scripts/sw-connect.sh +2 -1
  175. package/scripts/sw-context.sh +2 -1
  176. package/scripts/sw-cost.sh +48 -3
  177. package/scripts/sw-daemon.sh +66 -9
  178. package/scripts/sw-dashboard.sh +3 -1
  179. package/scripts/sw-db.sh +59 -16
  180. package/scripts/sw-decide.sh +8 -2
  181. package/scripts/sw-decompose.sh +360 -17
  182. package/scripts/sw-deps.sh +4 -1
  183. package/scripts/sw-developer-simulation.sh +4 -1
  184. package/scripts/sw-discovery.sh +325 -2
  185. package/scripts/sw-doc-fleet.sh +4 -1
  186. package/scripts/sw-docs-agent.sh +3 -1
  187. package/scripts/sw-docs.sh +2 -1
  188. package/scripts/sw-doctor.sh +453 -2
  189. package/scripts/sw-dora.sh +4 -1
  190. package/scripts/sw-durable.sh +4 -3
  191. package/scripts/sw-e2e-orchestrator.sh +17 -16
  192. package/scripts/sw-eventbus.sh +7 -1
  193. package/scripts/sw-evidence.sh +364 -12
  194. package/scripts/sw-feedback.sh +550 -9
  195. package/scripts/sw-fix.sh +20 -1
  196. package/scripts/sw-fleet-discover.sh +6 -2
  197. package/scripts/sw-fleet-viz.sh +4 -1
  198. package/scripts/sw-fleet.sh +5 -1
  199. package/scripts/sw-github-app.sh +16 -3
  200. package/scripts/sw-github-checks.sh +3 -2
  201. package/scripts/sw-github-deploy.sh +3 -2
  202. package/scripts/sw-github-graphql.sh +18 -7
  203. package/scripts/sw-guild.sh +5 -1
  204. package/scripts/sw-heartbeat.sh +5 -30
  205. package/scripts/sw-hello.sh +67 -0
  206. package/scripts/sw-hygiene.sh +6 -1
  207. package/scripts/sw-incident.sh +265 -1
  208. package/scripts/sw-init.sh +18 -2
  209. package/scripts/sw-instrument.sh +10 -2
  210. package/scripts/sw-intelligence.sh +42 -6
  211. package/scripts/sw-jira.sh +5 -1
  212. package/scripts/sw-launchd.sh +2 -1
  213. package/scripts/sw-linear.sh +4 -1
  214. package/scripts/sw-logs.sh +4 -1
  215. package/scripts/sw-loop.sh +432 -1128
  216. package/scripts/sw-memory.sh +356 -2
  217. package/scripts/sw-mission-control.sh +6 -1
  218. package/scripts/sw-model-router.sh +481 -26
  219. package/scripts/sw-otel.sh +13 -4
  220. package/scripts/sw-oversight.sh +14 -5
  221. package/scripts/sw-patrol-meta.sh +334 -0
  222. package/scripts/sw-pipeline-composer.sh +5 -1
  223. package/scripts/sw-pipeline-vitals.sh +2 -1
  224. package/scripts/sw-pipeline.sh +53 -2664
  225. package/scripts/sw-pm.sh +12 -5
  226. package/scripts/sw-pr-lifecycle.sh +2 -1
  227. package/scripts/sw-predictive.sh +7 -1
  228. package/scripts/sw-prep.sh +185 -2
  229. package/scripts/sw-ps.sh +5 -25
  230. package/scripts/sw-public-dashboard.sh +15 -3
  231. package/scripts/sw-quality.sh +2 -1
  232. package/scripts/sw-reaper.sh +8 -25
  233. package/scripts/sw-recruit.sh +156 -2303
  234. package/scripts/sw-regression.sh +19 -12
  235. package/scripts/sw-release-manager.sh +3 -1
  236. package/scripts/sw-release.sh +4 -1
  237. package/scripts/sw-remote.sh +3 -1
  238. package/scripts/sw-replay.sh +7 -1
  239. package/scripts/sw-retro.sh +158 -1
  240. package/scripts/sw-review-rerun.sh +3 -1
  241. package/scripts/sw-scale.sh +10 -3
  242. package/scripts/sw-security-audit.sh +6 -1
  243. package/scripts/sw-self-optimize.sh +6 -3
  244. package/scripts/sw-session.sh +9 -3
  245. package/scripts/sw-setup.sh +3 -1
  246. package/scripts/sw-stall-detector.sh +406 -0
  247. package/scripts/sw-standup.sh +15 -7
  248. package/scripts/sw-status.sh +3 -1
  249. package/scripts/sw-strategic.sh +4 -1
  250. package/scripts/sw-stream.sh +7 -1
  251. package/scripts/sw-swarm.sh +18 -6
  252. package/scripts/sw-team-stages.sh +13 -6
  253. package/scripts/sw-templates.sh +5 -29
  254. package/scripts/sw-testgen.sh +7 -1
  255. package/scripts/sw-tmux-pipeline.sh +4 -1
  256. package/scripts/sw-tmux-role-color.sh +2 -0
  257. package/scripts/sw-tmux-status.sh +1 -1
  258. package/scripts/sw-tmux.sh +3 -1
  259. package/scripts/sw-trace.sh +3 -1
  260. package/scripts/sw-tracker-github.sh +3 -0
  261. package/scripts/sw-tracker-jira.sh +3 -0
  262. package/scripts/sw-tracker-linear.sh +3 -0
  263. package/scripts/sw-tracker.sh +3 -1
  264. package/scripts/sw-triage.sh +2 -1
  265. package/scripts/sw-upgrade.sh +3 -1
  266. package/scripts/sw-ux.sh +5 -2
  267. package/scripts/sw-webhook.sh +3 -1
  268. package/scripts/sw-widgets.sh +3 -1
  269. package/scripts/sw-worktree.sh +15 -3
  270. package/scripts/test-skill-injection.sh +1233 -0
  271. package/templates/pipelines/autonomous.json +27 -3
  272. package/templates/pipelines/cost-aware.json +34 -8
  273. package/templates/pipelines/deployed.json +12 -0
  274. package/templates/pipelines/enterprise.json +12 -0
  275. package/templates/pipelines/fast.json +6 -0
  276. package/templates/pipelines/full.json +27 -3
  277. package/templates/pipelines/hotfix.json +6 -0
  278. package/templates/pipelines/standard.json +12 -0
  279. package/templates/pipelines/tdd.json +12 -0
@@ -0,0 +1,851 @@
1
+ # Stream 2: Deep Integration Implementation Plan
2
+
3
+ > **For Claude:** REQUIRED SUB-SKILL: Use `executing-plans` to implement this plan task-by-task.
4
+
5
+ **Goal:** Wire 8 skipper-shipwright tools to call real Shipwright bash scripts instead of in-memory stubs. Enable Skipper agents to execute full delivery pipelines, access persistent memory, read fleet/daemon state, and leverage intelligence analysis.
6
+
7
+ **Architecture:** Tools spawn subprocess calls to Shipwright bash scripts (`sw-pipeline.sh`, `sw-decide.sh`, `sw-intelligence.sh`, etc.) using `tokio::process::Command`. Results are parsed as JSON and cached. Graceful fallback to in-memory stubs if bash is unavailable.
8
+
9
+ **Tech Stack:** Rust (tokio, serde_json), bash scripts (existing), feature gates (`shipwright-integration`), subprocess monitoring, file I/O for memory/fleet state.
10
+
11
+ ---
12
+
13
+ ## Phase 1: Foundation — Subprocess & File Bridges (Tasks 1–7)
14
+
15
+ ### Task 1: Create subprocess.rs — Bash script spawning harness
16
+
17
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/subprocess.rs`
18
+
19
+ - [ ] Create empty file with module doc comment
20
+ - [ ] Import: `tokio::process::Command`, `std::time::Duration`, `Result<String, String>`, `tracing`
21
+ - [ ] Define `pub struct BashRunner`:
22
+ - `shipwright_scripts_dir: PathBuf` (from env var or config, default `~/.shipwright/scripts`)
23
+ - `timeout_seconds: u64` (default 300)
24
+ - `enable_caching: bool`
25
+ - `cache_dir: PathBuf` (default `~/.shipwright/subprocess-cache`)
26
+ - [ ] Define `pub enum BashError`:
27
+ - `ScriptNotFound(String)` — script file missing
28
+ - `ExecutionFailed(i32, String)` — non-zero exit with stderr
29
+ - `Timeout` — exceeded timeout_seconds
30
+ - `JsonParse(String)` — invalid JSON in stdout
31
+ - `IoError(String)`
32
+ - [ ] Implement `impl BashRunner`:
33
+ - `pub fn new() -> Self` — use defaults
34
+ - `pub fn with_dir(dir: PathBuf) -> Self` — explicit script dir
35
+ - `pub fn with_timeout(mut self, seconds: u64) -> Self` — fluent builder
36
+ - `pub async fn run(&self, script_name: &str, args: Vec<&str>) -> Result<String, BashError>` — spawn subprocess
37
+ - Check script exists at `scripts_dir/{script_name}`
38
+ - Build Command with script path and args
39
+ - Set timeout via `tokio::time::timeout`
40
+ - Capture stdout + stderr
41
+ - Parse stdout as JSON (validate with `serde_json::from_str`)
42
+ - Return `Ok(stdout_json_string)` or `Err(BashError)`
43
+ - `async fn run_with_cache(...)` — check cache dir for hash(script+args), use cached result if fresh (<1hr)
44
+ - `pub async fn run_json<T: DeserializeOwned>(&self, script: &str, args: Vec<&str>) -> Result<T, BashError>` — deserialize result directly
45
+
46
+ **Tests:**
47
+
48
+ ```bash
49
+ # Unit test: BashRunner::new() constructs defaults
50
+ # Unit test: run() fails if script not found
51
+ # Unit test: run() fails with ExecutionFailed on non-zero exit
52
+ # Unit test: run() parses JSON from stdout
53
+ ```
54
+
55
+ **Commit:** `git add src/subprocess.rs && git commit -m "feat(subprocess): bash script spawning harness"`
56
+
57
+ ---
58
+
59
+ ### Task 2: Create memory_bridge.rs — Real memory file I/O
60
+
61
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/memory_bridge.rs`
62
+
63
+ - [ ] Create file with module doc
64
+ - [ ] Import: `std::fs`, `std::path::{Path, PathBuf}`, `serde_json::{json, Value}`, `tokio::fs::{File, read_to_string, write}`, `Result<T, String>`
65
+ - [ ] Define `pub struct MemoryBridge`:
66
+ - `memory_root: PathBuf` (default `~/.shipwright/memory`)
67
+ - `repo_hash: String` (repo name or hash for scoped storage)
68
+ - [ ] Implement `impl MemoryBridge`:
69
+ - `pub fn new(repo: &str) -> Self` — compute repo_hash and set paths
70
+ - `pub async fn store_failure(&self, pattern: FailurePattern) -> Result<(), String>`
71
+ - Create `memory_root/{repo_hash}/failures/` if not exists
72
+ - Write pattern as JSON to `failures/{error_class}_{timestamp}.json`
73
+ - Return `Ok(())`
74
+ - `pub async fn search_failures(&self, query: &str, limit: usize) -> Result<Vec<FailurePattern>, String>`
75
+ - Scan `memory_root/{repo_hash}/failures/` for all JSON files
76
+ - Deserialize each to FailurePattern
77
+ - Filter by query substring (error_signature or root_cause contains query)
78
+ - Return top `limit` by recency
79
+ - `pub async fn load_learning_metadata(&self) -> Result<Value, String>`
80
+ - Read `memory_root/{repo_hash}/learning.json` if exists
81
+ - Return empty object `{}` if not found
82
+ - `pub async fn store_learning_metadata(&self, metadata: &Value) -> Result<(), String>`
83
+ - Write to `memory_root/{repo_hash}/learning.json`
84
+ - Atomic write: temp file + rename
85
+
86
+ **Tests:**
87
+
88
+ ```bash
89
+ # Unit test: store_failure() creates directories
90
+ # Unit test: search_failures() returns sorted by recency
91
+ # Unit test: search_failures() filters by query
92
+ # Unit test: concurrent reads don't block writes
93
+ ```
94
+
95
+ **Commit:** `git add src/memory_bridge.rs && git commit -m "feat(memory): real filesystem memory bridge"`
96
+
97
+ ---
98
+
99
+ ### Task 3: Create fleet_bridge.rs — Read daemon/fleet state
100
+
101
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/fleet_bridge.rs`
102
+
103
+ - [ ] Create file with module doc
104
+ - [ ] Import: `std::fs`, `serde_json::{json, Value}`, `tokio::fs::read_to_string`, `Result<T, String>`
105
+ - [ ] Define `pub struct FleetBridge`:
106
+ - `config_path: PathBuf` (default `~/.shipwright/fleet-config.json`)
107
+ - `daemon_state_path: PathBuf` (default `.claude/daemon-state.json` in repo)
108
+ - `costs_path: PathBuf` (default `~/.shipwright/costs.json`)
109
+ - [ ] Implement `impl FleetBridge`:
110
+ - `pub fn new() -> Self` — set defaults
111
+ - `pub async fn load_fleet_config(&self) -> Result<Value, String>`
112
+ - Read `config_path` if exists
113
+ - Return `{}` if missing (daemon not configured)
114
+ - `pub async fn load_daemon_state(&self) -> Result<Value, String>`
115
+ - Read `daemon_state_path` if exists in current repo
116
+ - Return `{"status": "not_running"}` if missing
117
+ - `pub async fn load_costs(&self) -> Result<Value, String>`
118
+ - Read `costs_path` if exists (dict of {repo: cost_usd})
119
+ - Return `{}` if missing
120
+ - `pub async fn get_fleet_status(&self) -> Result<Value, String>` — aggregate:
121
+ - Load fleet config, daemon state, costs
122
+ - Return object: `{active_pipelines, queued_issues, workers, repos: [{name, status, cost}]}`
123
+
124
+ **Tests:**
125
+
126
+ ```bash
127
+ # Unit test: load_fleet_config() returns empty object if missing
128
+ # Unit test: get_fleet_status() aggregates config + state + costs
129
+ ```
130
+
131
+ **Commit:** `git add src/fleet_bridge.rs && git commit -m "feat(fleet): read real daemon and fleet state"`
132
+
133
+ ---
134
+
135
+ ### Task 4: Wire subprocess.rs into tools.rs — dispatch_subprocess()
136
+
137
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/tools.rs`
138
+
139
+ - [ ] Add `mod subprocess;` and `mod memory_bridge;` and `mod fleet_bridge;` to top
140
+ - [ ] Add imports: `use crate::subprocess::{BashRunner, BashError};`
141
+ - [ ] Update `ShipwrightState` struct to include:
142
+ - `bash_runner: Arc<BashRunner>` — spawned once at kernel boot
143
+ - `memory_bridge: Arc<MemoryBridge>` — one per repo
144
+ - [ ] Implement `ShipwrightState::new_with_bridges(repo: &str) -> Self` constructor
145
+ - Initialize `bash_runner = Arc::new(BashRunner::new())`
146
+ - Initialize `memory_bridge = Arc::new(MemoryBridge::new(repo))`
147
+ - [ ] Add helper: `pub async fn dispatch_subprocess(&self, script: &str, args: Vec<&str>) -> Result<String, BashError>`
148
+ - Call `self.bash_runner.run_json(script, args).await`
149
+ - Return result as JSON string or error
150
+
151
+ **Tests:**
152
+
153
+ ```bash
154
+ # Unit test: ShipwrightState::new_with_bridges() initializes bridges
155
+ # Integration test: dispatch_subprocess() calls bash runner
156
+ ```
157
+
158
+ **Commit:** `git add src/tools.rs && git commit -m "feat(tools): wire subprocess bridges into ShipwrightState"`
159
+
160
+ ---
161
+
162
+ ### Task 5: Implement pipeline_start tool — spawn real sw-pipeline.sh
163
+
164
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/tools.rs`
165
+
166
+ Replace stub `fn pipeline_start(input, state) -> Result<String, String>`:
167
+
168
+ - [ ] Extract params: `goal`, `issue_number`, `template`, `repo_path`
169
+ - [ ] Build bash args: `["--goal", goal, "--template", template]` (or `--issue` variant)
170
+ - [ ] Call: `state.dispatch_subprocess("sw-pipeline.sh", args).await`
171
+ - [ ] On success:
172
+ - Parse JSON result
173
+ - Extract pipeline_id from stdout
174
+ - Store pipeline record in `state.pipelines` (in-memory for quick status checks)
175
+ - Return JSON with `pipeline_id`, `status: "started"`, `created_at`
176
+ - [ ] On error:
177
+ - If `BashError::ScriptNotFound`, fall back to in-memory stub
178
+ - If `BashError::Timeout`, return `{"error": "pipeline_startup_timeout"}`
179
+ - Otherwise return bash error message
180
+
181
+ **Note:** Make this async by changing dispatch signature.
182
+
183
+ **Tests:**
184
+
185
+ ```bash
186
+ # Mock test: succeeds when sw-pipeline.sh returns valid JSON
187
+ # Mock test: falls back to in-memory stub when script not found
188
+ # Mock test: returns timeout error on subprocess timeout
189
+ ```
190
+
191
+ **Commit:** `git add src/tools.rs && git commit -m "feat(tools): pipeline_start calls real sw-pipeline.sh"`
192
+
193
+ ---
194
+
195
+ ### Task 6: Implement pipeline_status tool — read .claude/pipeline-state.md + subprocess
196
+
197
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/tools.rs`
198
+
199
+ Replace stub `fn pipeline_status(input, state)`:
200
+
201
+ - [ ] Extract: `pipeline_id` (or use most recent)
202
+ - [ ] Check `state.pipelines` in-memory cache first (fast path)
203
+ - [ ] If not cached, call: `state.dispatch_subprocess("sw-pipeline.sh", ["status", "--id", pipeline_id]).await`
204
+ - [ ] Parse result and return:
205
+ - `pipeline_id`, `goal`, `template`, `current_stage`, `stage_progress`, `iteration_count`, `test_status`
206
+ - If available: `created_at`, `started_at`, `estimated_completion`
207
+ - [ ] On error: return best-effort result from in-memory cache
208
+
209
+ **Tests:**
210
+
211
+ ```bash
212
+ # Unit test: returns cached pipeline status if in memory
213
+ # Mock test: calls subprocess for live status
214
+ ```
215
+
216
+ **Commit:** `git add src/tools.rs && git commit -m "feat(tools): pipeline_status reads real pipeline state"`
217
+
218
+ ---
219
+
220
+ ### Task 7: Implement decision_run tool — call real sw-decide.sh
221
+
222
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/tools.rs`
223
+
224
+ Replace stub `fn decision_run(input)`:
225
+
226
+ - [ ] Extract: `dry_run`, `signal_filter` (optional)
227
+ - [ ] Build bash args: `["--dry-run"]` (if dry_run=true) or `[]`
228
+ - [ ] If signal_filter provided, add: `["--signal", signal_filter]`
229
+ - [ ] Call: `state.dispatch_subprocess("sw-decide.sh", args).await`
230
+ - [ ] Parse and return:
231
+ - `dry_run`, `candidates: [{issue, score, signals}]`, `recommended_template`, `confidence`
232
+ - [ ] On error: return `{"dry_run": true, "candidates": [], "message": "decision engine unavailable"}`
233
+
234
+ **Tests:**
235
+
236
+ ```bash
237
+ # Mock test: calls sw-decide.sh and returns candidates
238
+ # Mock test: falls back to empty candidates if script unavailable
239
+ ```
240
+
241
+ **Commit:** `git add src/tools.rs && git commit -m "feat(tools): decision_run calls real sw-decide.sh"`
242
+
243
+ ---
244
+
245
+ ## Phase 2: Memory & Intelligence (Tasks 8–11)
246
+
247
+ ### Task 8: Implement memory_search tool — use MemoryBridge
248
+
249
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/tools.rs`
250
+
251
+ Replace stub `fn memory_search(input, state)`:
252
+
253
+ - [ ] Extract: `query`, `repo`, `limit` (default 10)
254
+ - [ ] Call: `state.memory_bridge.search_failures(&query, limit).await`
255
+ - [ ] Format and return:
256
+ - `query`, `repo`, `results_count`, `results: [{error_class, error_signature, root_cause, fix_applied, stage}]`
257
+ - [ ] On error: return `{"results": [], "error": "memory store unavailable"}`
258
+
259
+ **Tests:**
260
+
261
+ ```bash
262
+ # Unit test: searches memory bridge and returns results
263
+ # Unit test: returns empty results if no matches
264
+ ```
265
+
266
+ **Commit:** `git add src/tools.rs && git commit -m "feat(tools): memory_search uses real memory bridge"`
267
+
268
+ ---
269
+
270
+ ### Task 9: Implement memory_store tool — use MemoryBridge
271
+
272
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/tools.rs`
273
+
274
+ Replace stub `fn memory_store_pattern(input, state)`:
275
+
276
+ - [ ] Extract: `repo`, `error_class`, `error_signature`, `root_cause`, `fix_applied`, `stage` (optional)
277
+ - [ ] Convert to `FailurePattern` struct
278
+ - [ ] Call: `state.memory_bridge.store_failure(pattern).await`
279
+ - [ ] Return: `{"stored": true, "repo": repo, "error_class": error_class}`
280
+ - [ ] On error: return `{"stored": false, "error": "..."}`
281
+
282
+ **Tests:**
283
+
284
+ ```bash
285
+ # Unit test: stores pattern in memory bridge
286
+ # Unit test: returns error if pattern invalid
287
+ ```
288
+
289
+ **Commit:** `git add src/tools.rs && git commit -m "feat(tools): memory_store uses real memory bridge"`
290
+
291
+ ---
292
+
293
+ ### Task 10: Implement intelligence tool — call real sw-intelligence.sh
294
+
295
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/tools.rs`
296
+
297
+ Replace stub `fn intelligence(input)`:
298
+
299
+ - [ ] Extract: `analysis_type` (dora, risk, optimize), `repo_path` (optional)
300
+ - [ ] Build bash args: `["analyze", "--type", analysis_type]`
301
+ - [ ] Call: `state.dispatch_subprocess("sw-intelligence.sh", args).await`
302
+ - [ ] Parse and return:
303
+ - For `dora`: `{lead_time_hours, deploy_frequency, change_failure_rate, mttr_hours, classification}`
304
+ - For `risk`: `{hotspots: [{file, churn_count, risk_score}]}`
305
+ - For `optimize`: `{suggestions: [{type, rationale}]}`
306
+ - [ ] On error: return stub metrics
307
+
308
+ **Tests:**
309
+
310
+ ```bash
311
+ # Mock test: calls sw-intelligence.sh for dora analysis
312
+ # Mock test: returns hotspots for risk analysis
313
+ # Mock test: falls back to defaults if script unavailable
314
+ ```
315
+
316
+ **Commit:** `git add src/tools.rs && git commit -m "feat(tools): intelligence calls real sw-intelligence.sh"`
317
+
318
+ ---
319
+
320
+ ### Task 11: Implement fleet_status tool — use FleetBridge
321
+
322
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/tools.rs`
323
+
324
+ Replace stub `fn fleet_status(state)`:
325
+
326
+ - [ ] Create: `fleet_bridge = FleetBridge::new()`
327
+ - [ ] Call: `fleet_bridge.get_fleet_status().await`
328
+ - [ ] Return aggregated JSON:
329
+ - `active_pipelines`, `queued_issues`, `total_workers`, `available_workers`, `total_cost_usd`
330
+ - `repos: [{name, active_pipelines, queued_issues, workers_allocated, cost_usd}]`
331
+ - [ ] On error: return minimal valid status with zeros
332
+
333
+ **Tests:**
334
+
335
+ ```bash
336
+ # Unit test: aggregates fleet config + state + costs
337
+ # Unit test: returns empty repos list if no fleet config
338
+ ```
339
+
340
+ **Commit:** `git add src/tools.rs && git commit -m "feat(tools): fleet_status reads real fleet state"`
341
+
342
+ ---
343
+
344
+ ## Phase 3: Async & Testing (Tasks 12–16)
345
+
346
+ ### Task 12: Make dispatch() async — convert tool handlers
347
+
348
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/tools.rs`
349
+
350
+ - [ ] Change dispatch signature: `pub async fn dispatch(tool_name: &str, input: &Value, state: &ShipwrightState) -> Result<String, String>`
351
+ - [ ] Convert all tool handler functions to async:
352
+ - Add `pub async fn pipeline_start(...)`
353
+ - Add `pub async fn pipeline_status(...)`
354
+ - Add `pub async fn decision_run(...)`
355
+ - Add `pub async fn memory_search(...)`
356
+ - Add `pub async fn memory_store_pattern(...)`
357
+ - Add `pub async fn fleet_status(...)`
358
+ - Add `pub async fn intelligence(...)`
359
+ - [ ] Add `.await` calls to all subprocess/bridge calls
360
+ - [ ] Update dispatch() match statement to `.await` each handler
361
+
362
+ **Tests:**
363
+
364
+ ```bash
365
+ # Integration test: dispatch() correctly awaits async handlers
366
+ ```
367
+
368
+ **Commit:** `git add src/tools.rs && git commit -m "feat(tools): make all handlers async for subprocess calls"`
369
+
370
+ ---
371
+
372
+ ### Task 13: Update tool_runner integration — async dispatch in skipper-runtime
373
+
374
+ **File:** `crates/skipper-runtime/src/tools/shipwright.rs` (or appropriate location)
375
+
376
+ - [ ] Check if feature gate `shipwright-integration` exists (add to Cargo.toml if not)
377
+ - [ ] Find where `dispatch()` is called from tool_runner
378
+ - [ ] Wrap dispatch in `tokio::spawn_blocking()` or update handler to be async
379
+ - [ ] If runtime is async (likely), change tool handler to:
380
+ ```rust
381
+ pub async fn handle_tool(name: &str, input: &Value, state: &Arc<ShipwrightState>) -> ToolResult {
382
+ match dispatch(name, input, state).await {
383
+ Ok(result) => ToolResult::success(result),
384
+ Err(e) => ToolResult::error(e),
385
+ }
386
+ }
387
+ ```
388
+
389
+ **Tests:**
390
+
391
+ ```bash
392
+ # Integration test: tool_runner calls async dispatch and gets result
393
+ ```
394
+
395
+ **Commit:** `git add crates/skipper-runtime && git commit -m "feat(runtime): wire async dispatch to tool_runner"`
396
+
397
+ ---
398
+
399
+ ### Task 14: Create unit tests for subprocess.rs
400
+
401
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/subprocess.rs` (bottom)
402
+
403
+ - [ ] Test module: `#[cfg(test)] mod tests`
404
+ - [ ] Test: `new()` initializes with defaults
405
+ - [ ] Test: `with_timeout()` builder sets timeout
406
+ - [ ] Test: `run()` fails if script not found → `BashError::ScriptNotFound`
407
+ - [ ] Test: `run()` captures stdout and parses JSON
408
+ - [ ] Test: `run()` on non-zero exit → `BashError::ExecutionFailed`
409
+ - [ ] Test: timeout behavior with slow script
410
+
411
+ Run:
412
+
413
+ ```bash
414
+ cargo test --lib skipper-shipwright::subprocess -- --nocapture
415
+ ```
416
+
417
+ **Commit:** `git add src/subprocess.rs && git commit -m "test(subprocess): add unit tests"`
418
+
419
+ ---
420
+
421
+ ### Task 15: Create unit tests for memory_bridge.rs
422
+
423
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/memory_bridge.rs` (bottom)
424
+
425
+ - [ ] Test module: `#[cfg(test)] mod tests`
426
+ - [ ] Test: `store_failure()` creates directories
427
+ - [ ] Test: `search_failures()` returns sorted by recency
428
+ - [ ] Test: `search_failures()` filters by query
429
+ - [ ] Test: `load_learning_metadata()` returns empty object if missing
430
+ - [ ] Test: `store_learning_metadata()` persists to disk
431
+
432
+ Run:
433
+
434
+ ```bash
435
+ cargo test --lib skipper-shipwright::memory_bridge -- --nocapture
436
+ ```
437
+
438
+ **Commit:** `git add src/memory_bridge.rs && git commit -m "test(memory): add unit tests"`
439
+
440
+ ---
441
+
442
+ ### Task 16: Create unit tests for fleet_bridge.rs
443
+
444
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/fleet_bridge.rs` (bottom)
445
+
446
+ - [ ] Test module: `#[cfg(test)] mod tests`
447
+ - [ ] Test: `load_fleet_config()` returns empty object if missing
448
+ - [ ] Test: `load_daemon_state()` returns empty object if missing
449
+ - [ ] Test: `load_costs()` reads costs.json
450
+ - [ ] Test: `get_fleet_status()` aggregates all three
451
+
452
+ Run:
453
+
454
+ ```bash
455
+ cargo test --lib skipper-shipwright::fleet_bridge -- --nocapture
456
+ ```
457
+
458
+ **Commit:** `git add src/fleet_bridge.rs && git commit -m "test(fleet): add unit tests"`
459
+
460
+ ---
461
+
462
+ ## Phase 4: Integration & API Routes (Tasks 17–20)
463
+
464
+ ### Task 17: Create crates/skipper-api/src/routes/pipelines.rs
465
+
466
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-api/src/routes/pipelines.rs`
467
+
468
+ - [ ] Create file with route handlers
469
+ - [ ] Import: `axum::{extract::*, response::IntoResponse, Router}`, `AppState`, `ShipwrightState`
470
+ - [ ] Define routes:
471
+ - `POST /api/pipelines/start` → `start_pipeline_handler()`
472
+ - Extract: `goal`, `issue_number`, `template`
473
+ - Call: `dispatch("shipwright_pipeline_start", input, state).await`
474
+ - Return: `Json(response)`
475
+ - `GET /api/pipelines/:id/status` → `get_pipeline_status_handler()`
476
+ - Extract: `id` path param
477
+ - Call: `dispatch("shipwright_pipeline_status", input, state).await`
478
+ - Return: `Json(response)`
479
+ - `POST /api/pipelines/:id/advance` → `advance_pipeline_handler()`
480
+ - Call: `dispatch("shipwright_stage_advance", input, state).await`
481
+ - [ ] Export: `pub fn pipeline_routes() -> Router`
482
+
483
+ **Tests:**
484
+
485
+ ```bash
486
+ # Integration test: POST /api/pipelines/start returns pipeline_id
487
+ # Integration test: GET /api/pipelines/{id}/status returns status
488
+ ```
489
+
490
+ **Commit:** `git add crates/skipper-api/src/routes/pipelines.rs && git commit -m "feat(api): add pipeline status routes"`
491
+
492
+ ---
493
+
494
+ ### Task 18: Register pipelines routes in skipper-api/src/routes/mod.rs
495
+
496
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-api/src/routes/mod.rs`
497
+
498
+ - [ ] Add: `mod pipelines;`
499
+ - [ ] Add: `pub use pipelines::pipeline_routes;`
500
+ - [ ] In the router builder function, add: `.nest("/api", pipeline_routes())`
501
+ (or merge with existing route tree)
502
+
503
+ **Commit:** `git add crates/skipper-api && git commit -m "feat(api): register pipeline routes"`
504
+
505
+ ---
506
+
507
+ ### Task 19: Add feature gate for shipwright-integration
508
+
509
+ **File:** `/Users/sethford/Documents/shipwright/skipper/Cargo.toml`
510
+
511
+ - [ ] In workspace config (if exists) or crate config, add:
512
+ ```toml
513
+ [features]
514
+ shipwright-integration = ["skipper-shipwright"]
515
+ ```
516
+ - [ ] In build script or documentation, note that:
517
+ - Feature `shipwright-integration` enables Shipwright tools
518
+ - Default: disabled (uses in-memory stubs)
519
+ - Enable with: `cargo build --features shipwright-integration`
520
+ - For daemon: `CARGO_FEATURES=shipwright-integration cargo build --release`
521
+
522
+ **Commit:** `git add Cargo.toml && git commit -m "feat: add shipwright-integration feature gate"`
523
+
524
+ ---
525
+
526
+ ### Task 20: Create integration test — test all tools end-to-end
527
+
528
+ **File:** `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/tests/integration_test.rs`
529
+
530
+ - [ ] Create file
531
+ - [ ] Test: `pipeline_start_and_status()`
532
+ - Call dispatch("shipwright_pipeline_start", {"goal": "test"}, state)
533
+ - Verify response has pipeline_id
534
+ - Call dispatch("shipwright_pipeline_status", {"pipeline_id": id}, state)
535
+ - Verify status includes current_stage
536
+ - [ ] Test: `decision_run()`
537
+ - Call dispatch("shipwright_decision_run", {}, state)
538
+ - Verify response has candidates or recommendation
539
+ - [ ] Test: `memory_store_and_search()`
540
+ - Call dispatch("shipwright_memory_store", {repo, error_class, ...}, state)
541
+ - Verify stored: true
542
+ - Call dispatch("shipwright_memory_search", {repo, query}, state)
543
+ - Verify results_count > 0
544
+ - [ ] Test: `fleet_status()`
545
+ - Call dispatch("shipwright_fleet_status", {}, state)
546
+ - Verify has active_pipelines, repos list
547
+ - [ ] Test: `intelligence()`
548
+ - Call dispatch("shipwright_intelligence", {analysis_type: "dora"}, state)
549
+ - Verify has metrics
550
+
551
+ Run:
552
+
553
+ ```bash
554
+ cargo test --test integration_test -- --nocapture
555
+ ```
556
+
557
+ **Commit:** `git add tests/integration_test.rs && git commit -m "test(integration): end-to-end tool tests"`
558
+
559
+ ---
560
+
561
+ ## Phase 5: Verification & Documentation (Tasks 21–24)
562
+
563
+ ### Task 21: Build and test all changes
564
+
565
+ **No file changes** — just run verification commands
566
+
567
+ - [ ] Run: `cargo build --workspace --lib`
568
+ - Should compile with no errors
569
+ - Note: may need to update Cargo.toml dependencies if needed
570
+ - [ ] Run: `cargo test --workspace`
571
+ - All existing tests should pass (2190+)
572
+ - New tests added in Tasks 14–20 should pass
573
+ - [ ] Run: `cargo clippy --workspace --all-targets -- -D warnings`
574
+ - Zero clippy warnings
575
+
576
+ If any test fails:
577
+
578
+ 1. Read the error message carefully
579
+ 2. Check if it's due to missing async `.await`
580
+ 3. Check if subprocess/bridge logic has a bug
581
+ 4. Fix and re-test
582
+
583
+ **Commit:** `git add . && git commit -m "build: verify all tests pass, no clippy warnings"`
584
+
585
+ ---
586
+
587
+ ### Task 22: Live integration test against real Shipwright bash scripts
588
+
589
+ **Manual testing** — run daemon and test tools against actual scripts
590
+
591
+ - [ ] Start Skipper daemon:
592
+ ```bash
593
+ cargo build --release -p skipper-cli --features shipwright-integration
594
+ GROQ_API_KEY=<key> target/release/skipper.exe start &
595
+ sleep 6
596
+ ```
597
+ - [ ] Check health:
598
+ ```bash
599
+ curl http://127.0.0.1:4200/api/health
600
+ ```
601
+ - [ ] Test pipeline_start:
602
+ ```bash
603
+ curl -X POST http://127.0.0.1:4200/api/pipelines/start \
604
+ -H "Content-Type: application/json" \
605
+ -d '{"goal": "Add login validation"}'
606
+ ```
607
+
608
+ - Should return `{"pipeline_id": "...", "status": "started"}`
609
+ - [ ] Test pipeline_status:
610
+ ```bash
611
+ curl http://127.0.0.1:4200/api/pipelines/{id}/status
612
+ ```
613
+
614
+ - Should return current stage, progress, etc.
615
+ - [ ] Test decision_run:
616
+ ```bash
617
+ curl -X POST http://127.0.0.1:4200/api/decisions/run \
618
+ -H "Content-Type: application/json" \
619
+ -d '{"dry_run": true}'
620
+ ```
621
+
622
+ - Should return candidates with scores
623
+ - [ ] Test memory_search:
624
+ ```bash
625
+ curl -X POST http://127.0.0.1:4200/api/memory/search \
626
+ -H "Content-Type: application/json" \
627
+ -d '{"repo": "myrepo", "query": "timeout"}'
628
+ ```
629
+
630
+ - Should return matching failure patterns
631
+ - [ ] Test fleet_status:
632
+ ```bash
633
+ curl http://127.0.0.1:4200/api/fleet/status
634
+ ```
635
+
636
+ - Should return worker allocation, repos, costs
637
+ - [ ] Verify subprocess.rs properly invokes bash scripts by watching Shipwright logs:
638
+ ```bash
639
+ tail -f ~/.shipwright/events.jsonl
640
+ ```
641
+
642
+ If any test fails:
643
+
644
+ - [ ] Check that sw-\*.sh scripts exist in `~/.shipwright/scripts/`
645
+ - [ ] Check script permissions: `chmod +x ~/.shipwright/scripts/sw-*.sh`
646
+ - [ ] Manually run the script to verify it works: `~/.shipwright/scripts/sw-pipeline.sh --goal "test"`
647
+ - [ ] Check subprocess.rs error handling and logging
648
+
649
+ **Document:** Findings in a test report (informal, for reference)
650
+
651
+ **Commit:** `git add . && git commit -m "test(integration): verified tools against real Shipwright scripts"`
652
+
653
+ ---
654
+
655
+ ### Task 23: Update module docs
656
+
657
+ **Files:**
658
+
659
+ - `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/lib.rs`
660
+ - `/Users/sethford/Documents/shipwright/skipper/crates/skipper-shipwright/src/tools.rs`
661
+
662
+ - [ ] In `lib.rs`, update module-level doc comment to reflect:
663
+ - Tools now call real bash scripts (subprocess)
664
+ - Memory system reads real filesystem (memory_bridge)
665
+ - Fleet status reads real daemon config (fleet_bridge)
666
+ - Example: "Tools spawn `sw-pipeline.sh` subprocess for autonomous delivery"
667
+ - [ ] In `tools.rs`, add doc comments to each tool handler explaining:
668
+ - Which bash script it calls
669
+ - Parameters and return format
670
+ - Fallback behavior if script unavailable
671
+ - [ ] Example snippet for `pipeline_start`:
672
+ ```rust
673
+ /// Starts a Shipwright delivery pipeline by spawning `sw-pipeline.sh`.
674
+ ///
675
+ /// Calls `$SHIPWRIGHT_SCRIPTS_DIR/sw-pipeline.sh --goal "..." --template "standard"`.
676
+ /// Returns pipeline ID and initial status. Falls back to in-memory stub if script unavailable.
677
+ async fn pipeline_start(input: &Value, state: &ShipwrightState) -> Result<String, String>
678
+ ```
679
+
680
+ **Commit:** `git add src/lib.rs src/tools.rs && git commit -m "docs: update module docs for subprocess integration"`
681
+
682
+ ---
683
+
684
+ ### Task 24: Create IMPLEMENTATION_NOTES.md
685
+
686
+ **File:** `/Users/sethford/Documents/shipwright/docs/plans/STREAM-2-IMPLEMENTATION-NOTES.md`
687
+
688
+ Document for future reference:
689
+
690
+ ````markdown
691
+ # Stream 2 Implementation Notes
692
+
693
+ ## What Was Implemented
694
+
695
+ - **subprocess.rs**: Bash script spawning harness with timeout and caching
696
+ - **memory_bridge.rs**: Real filesystem memory I/O (read/write failure patterns)
697
+ - **fleet_bridge.rs**: Fleet config and daemon state aggregator
698
+ - **Async tools**: All 8 tools converted to async and wired to subprocess calls
699
+ - **API routes**: POST /pipelines/start, GET /pipelines/{id}/status, etc.
700
+ - **Feature gate**: `shipwright-integration` to toggle real vs in-memory
701
+
702
+ ## How Tools Work
703
+
704
+ ### pipeline_start
705
+
706
+ - Spawns: `sw-pipeline.sh --goal "..." --template "..."`
707
+ - Returns: `{pipeline_id, status: "started", created_at}`
708
+
709
+ ### pipeline_status
710
+
711
+ - Spawns: `sw-pipeline.sh status --id {pipeline_id}`
712
+ - Returns: `{current_stage, progress, iteration, test_status}`
713
+
714
+ ### decision_run
715
+
716
+ - Spawns: `sw-decide.sh --dry-run`
717
+ - Returns: `{candidates: [{issue, score, signals}], recommended_template}`
718
+
719
+ ### memory_search
720
+
721
+ - Reads: `~/.shipwright/memory/{repo}/failures/`
722
+ - Returns: `{results: [{error_class, fix_applied, ...}]}`
723
+
724
+ ### memory_store
725
+
726
+ - Writes: `~/.shipwright/memory/{repo}/failures/{error_class}_{timestamp}.json`
727
+ - Returns: `{stored: true}`
728
+
729
+ ### fleet_status
730
+
731
+ - Reads: `~/.shipwright/fleet-config.json`, `.claude/daemon-state.json`, `~/.shipwright/costs.json`
732
+ - Returns: `{active_pipelines, repos: [{name, workers, cost}]}`
733
+
734
+ ### intelligence
735
+
736
+ - Spawns: `sw-intelligence.sh analyze --type dora|risk|optimize`
737
+ - Returns: `{lead_time_hours, deploy_frequency, ...}` (varies by type)
738
+
739
+ ## Graceful Fallbacks
740
+
741
+ All tools check if subprocess is available. If not (feature disabled or script missing):
742
+
743
+ - Return best-effort cached/in-memory result
744
+ - Log warning to tracing
745
+ - Do NOT fail hard — allow pipeline to continue with degraded capabilities
746
+
747
+ ## Testing Strategy
748
+
749
+ - Unit tests: subprocess.rs, memory_bridge.rs, fleet_bridge.rs
750
+ - Integration tests: all tools together
751
+ - Live tests: against real Shipwright bash scripts and daemon
752
+
753
+ ## Future: Dashboard Integration
754
+
755
+ Once this is complete, update skipper-api/src/dashboard.rs to:
756
+
757
+ - Show active pipelines with stage progress bars
758
+ - Show fleet status with worker allocation
759
+ - Show unified memory (Shipwright + Skipper) in search UI
760
+ - Wire Skipper memory to Shipwright bash scripts via memory_bridge
761
+
762
+ ## Known Limitations
763
+
764
+ 1. Subprocess timeout is fixed (300s) — may need tuning for long pipelines
765
+ 2. Memory search is linear scan — consider adding SQLite index for large repos
766
+ 3. Fleet state is eventually consistent — reads are point-in-time snapshots
767
+ 4. Subprocess caching (1hr TTL) may hide real-time changes — disable for interactive use
768
+
769
+ ## Debugging
770
+
771
+ Enable detailed logging:
772
+
773
+ ```bash
774
+ RUST_LOG=debug cargo build --release -p skipper-cli
775
+ RUST_LOG=skipper_shipwright=debug target/release/skipper.exe start
776
+ ```
777
+ ````
778
+
779
+ Watch subprocess calls:
780
+
781
+ ```bash
782
+ tail -f ~/.shipwright/events.jsonl
783
+ ```
784
+
785
+ Check subprocess cache:
786
+
787
+ ```bash
788
+ ls -la ~/.shipwright/subprocess-cache/
789
+ ```
790
+
791
+ ```
792
+
793
+ **Commit:** `git add docs/plans && git commit -m "docs: add Stream 2 implementation notes"`
794
+
795
+ ---
796
+
797
+ ## Success Criteria Checklist
798
+
799
+ - [ ] All 5 new files created and compile without errors
800
+ - [ ] All 7 tool handlers converted to async and call real bash scripts
801
+ - [ ] All subprocess calls have graceful fallback to in-memory stubs
802
+ - [ ] All 3 file bridges (subprocess, memory, fleet) have unit tests with >90% coverage
803
+ - [ ] Integration test covers all 8 tools end-to-end
804
+ - [ ] Live integration test passes against real Shipwright scripts
805
+ - [ ] `cargo test --workspace` passes with 0 failures
806
+ - [ ] `cargo clippy --workspace --all-targets -- -D warnings` returns 0 warnings
807
+ - [ ] API routes registered and tested via HTTP
808
+ - [ ] Feature gate `shipwright-integration` toggles between real and in-memory
809
+ - [ ] Documentation updated with subprocess details
810
+ - [ ] All commits follow conventional commit format
811
+
812
+ ---
813
+
814
+ ## Estimated Effort
815
+
816
+ | Phase | Tasks | Estimated Hours |
817
+ |----------|-------|-----------------|
818
+ | Phase 1 | 1–7 | 12–16 hours |
819
+ | Phase 2 | 8–11 | 6–8 hours |
820
+ | Phase 3 | 12–16 | 8–10 hours |
821
+ | Phase 4 | 17–20 | 6–8 hours |
822
+ | Phase 5 | 21–24 | 4–6 hours |
823
+ | **Total**| | **36–48 hours** |
824
+
825
+ ---
826
+
827
+ ## Merge and Next Steps
828
+
829
+ Once all tasks pass:
830
+
831
+ 1. **Merge this branch** into `main` (after Stream 1: Clean Architecture is merged)
832
+ 2. **Open PR** with:
833
+ - Title: `feat: Stream 2 — Deep Integration (Shipwright tools → real bash scripts)`
834
+ - Description: This plan doc + link to unified-platform-design.md
835
+ - 20+ commits (one per task)
836
+ 3. **Request review** from team lead (verify subprocess safety, error handling)
837
+ 4. **After merge**, Stream 3 (Claude Code + bash cleanup) can proceed in parallel
838
+
839
+ ---
840
+
841
+ ## Notes for Executors
842
+
843
+ - Use `/fast` mode in Claude Code to speed up code generation
844
+ - For each subprocess.rs feature, write a unit test first (TDD), then implementation
845
+ - When async becomes complex, use `tokio::task::spawn_blocking()` for fallbacks
846
+ - Memory_bridge should be atomic (temp file + rename) to avoid corruption
847
+ - Fleet_bridge reads are best-effort (no locking) — OK for occasional stale reads
848
+ - If subprocess timeout is too tight, increase from 300s in config
849
+ - Test with `RUST_LOG=debug` to see all subprocess invocations
850
+ - Keep shell invocations simple: avoid pipes, use script exit codes for errors
851
+ ```