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.
- package/.claude/agents/code-reviewer.md +2 -0
- package/.claude/agents/devops-engineer.md +2 -0
- package/.claude/agents/doc-fleet-agent.md +2 -0
- package/.claude/agents/pipeline-agent.md +2 -0
- package/.claude/agents/shell-script-specialist.md +2 -0
- package/.claude/agents/test-specialist.md +2 -0
- package/.claude/hooks/agent-crash-capture.sh +32 -0
- package/.claude/hooks/post-tool-use.sh +3 -2
- package/.claude/hooks/pre-tool-use.sh +35 -3
- package/README.md +4 -4
- package/claude-code/hooks/config-change.sh +18 -0
- package/claude-code/hooks/instructions-reloaded.sh +7 -0
- package/claude-code/hooks/worktree-create.sh +25 -0
- package/claude-code/hooks/worktree-remove.sh +20 -0
- package/config/code-constitution.json +130 -0
- package/dashboard/middleware/auth.ts +134 -0
- package/dashboard/middleware/constants.ts +21 -0
- package/dashboard/public/index.html +2 -6
- package/dashboard/public/styles.css +100 -97
- package/dashboard/routes/auth.ts +38 -0
- package/dashboard/server.ts +66 -25
- package/dashboard/services/config.ts +26 -0
- package/dashboard/services/db.ts +118 -0
- package/dashboard/src/canvas/pixel-agent.ts +298 -0
- package/dashboard/src/canvas/pixel-sprites.ts +440 -0
- package/dashboard/src/canvas/shipyard-effects.ts +367 -0
- package/dashboard/src/canvas/shipyard-scene.ts +616 -0
- package/dashboard/src/canvas/submarine-layout.ts +267 -0
- package/dashboard/src/components/header.ts +8 -7
- package/dashboard/src/core/router.ts +1 -0
- package/dashboard/src/design/submarine-theme.ts +253 -0
- package/dashboard/src/main.ts +2 -0
- package/dashboard/src/types/api.ts +2 -1
- package/dashboard/src/views/activity.ts +2 -1
- package/dashboard/src/views/shipyard.ts +39 -0
- package/dashboard/types/index.ts +166 -0
- package/docs/plans/2026-02-28-compound-audit-and-shipyard-design.md +186 -0
- package/docs/plans/2026-02-28-skipper-shipwright-implementation-plan.md +1182 -0
- package/docs/plans/2026-02-28-skipper-shipwright-integration-design.md +531 -0
- package/docs/plans/2026-03-01-ai-powered-skill-injection-design.md +298 -0
- package/docs/plans/2026-03-01-ai-powered-skill-injection-plan.md +1109 -0
- package/docs/plans/2026-03-01-capabilities-cleanup-plan.md +658 -0
- package/docs/plans/2026-03-01-clean-architecture-plan.md +924 -0
- package/docs/plans/2026-03-01-compound-audit-cascade-design.md +191 -0
- package/docs/plans/2026-03-01-compound-audit-cascade-plan.md +921 -0
- package/docs/plans/2026-03-01-deep-integration-plan.md +851 -0
- package/docs/plans/2026-03-01-pipeline-audit-trail-design.md +145 -0
- package/docs/plans/2026-03-01-pipeline-audit-trail-plan.md +770 -0
- package/docs/plans/2026-03-01-refined-depths-brand-design.md +382 -0
- package/docs/plans/2026-03-01-refined-depths-implementation.md +599 -0
- package/docs/plans/2026-03-01-skipper-kernel-integration-design.md +203 -0
- package/docs/plans/2026-03-01-unified-platform-design.md +272 -0
- package/docs/plans/2026-03-07-claude-code-feature-integration-design.md +189 -0
- package/docs/plans/2026-03-07-claude-code-feature-integration-plan.md +1165 -0
- package/docs/research/BACKLOG_QUICK_REFERENCE.md +352 -0
- package/docs/research/CUTTING_EDGE_RESEARCH_2026.md +546 -0
- package/docs/research/RESEARCH_INDEX.md +439 -0
- package/docs/research/RESEARCH_SOURCES.md +440 -0
- package/docs/research/RESEARCH_SUMMARY.txt +275 -0
- package/docs/superpowers/specs/2026-03-10-pipeline-quality-revolution-design.md +341 -0
- package/package.json +2 -2
- package/scripts/lib/adaptive-model.sh +427 -0
- package/scripts/lib/adaptive-timeout.sh +316 -0
- package/scripts/lib/audit-trail.sh +309 -0
- package/scripts/lib/auto-recovery.sh +471 -0
- package/scripts/lib/bandit-selector.sh +431 -0
- package/scripts/lib/bootstrap.sh +104 -2
- package/scripts/lib/causal-graph.sh +455 -0
- package/scripts/lib/compat.sh +126 -0
- package/scripts/lib/compound-audit.sh +337 -0
- package/scripts/lib/constitutional.sh +454 -0
- package/scripts/lib/context-budget.sh +359 -0
- package/scripts/lib/convergence.sh +594 -0
- package/scripts/lib/cost-optimizer.sh +634 -0
- package/scripts/lib/daemon-adaptive.sh +10 -0
- package/scripts/lib/daemon-dispatch.sh +106 -17
- package/scripts/lib/daemon-failure.sh +34 -4
- package/scripts/lib/daemon-patrol.sh +23 -2
- package/scripts/lib/daemon-poll-github.sh +361 -0
- package/scripts/lib/daemon-poll-health.sh +299 -0
- package/scripts/lib/daemon-poll.sh +27 -611
- package/scripts/lib/daemon-state.sh +112 -66
- package/scripts/lib/daemon-triage.sh +10 -0
- package/scripts/lib/dod-scorecard.sh +442 -0
- package/scripts/lib/error-actionability.sh +300 -0
- package/scripts/lib/formal-spec.sh +461 -0
- package/scripts/lib/helpers.sh +177 -4
- package/scripts/lib/intent-analysis.sh +409 -0
- package/scripts/lib/loop-convergence.sh +350 -0
- package/scripts/lib/loop-iteration.sh +682 -0
- package/scripts/lib/loop-progress.sh +48 -0
- package/scripts/lib/loop-restart.sh +185 -0
- package/scripts/lib/memory-effectiveness.sh +506 -0
- package/scripts/lib/mutation-executor.sh +352 -0
- package/scripts/lib/outcome-feedback.sh +521 -0
- package/scripts/lib/pipeline-cli.sh +336 -0
- package/scripts/lib/pipeline-commands.sh +1216 -0
- package/scripts/lib/pipeline-detection.sh +100 -2
- package/scripts/lib/pipeline-execution.sh +897 -0
- package/scripts/lib/pipeline-github.sh +28 -3
- package/scripts/lib/pipeline-intelligence-compound.sh +431 -0
- package/scripts/lib/pipeline-intelligence-scoring.sh +407 -0
- package/scripts/lib/pipeline-intelligence-skip.sh +181 -0
- package/scripts/lib/pipeline-intelligence.sh +100 -1136
- package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
- package/scripts/lib/pipeline-quality-checks.sh +17 -715
- package/scripts/lib/pipeline-quality-gates.sh +563 -0
- package/scripts/lib/pipeline-stages-build.sh +730 -0
- package/scripts/lib/pipeline-stages-delivery.sh +965 -0
- package/scripts/lib/pipeline-stages-intake.sh +1133 -0
- package/scripts/lib/pipeline-stages-monitor.sh +407 -0
- package/scripts/lib/pipeline-stages-review.sh +1022 -0
- package/scripts/lib/pipeline-stages.sh +59 -2929
- package/scripts/lib/pipeline-state.sh +36 -5
- package/scripts/lib/pipeline-util.sh +487 -0
- package/scripts/lib/policy-learner.sh +438 -0
- package/scripts/lib/process-reward.sh +493 -0
- package/scripts/lib/project-detect.sh +649 -0
- package/scripts/lib/quality-profile.sh +334 -0
- package/scripts/lib/recruit-commands.sh +885 -0
- package/scripts/lib/recruit-learning.sh +739 -0
- package/scripts/lib/recruit-roles.sh +648 -0
- package/scripts/lib/reward-aggregator.sh +458 -0
- package/scripts/lib/rl-optimizer.sh +362 -0
- package/scripts/lib/root-cause.sh +427 -0
- package/scripts/lib/scope-enforcement.sh +445 -0
- package/scripts/lib/session-restart.sh +493 -0
- package/scripts/lib/skill-memory.sh +300 -0
- package/scripts/lib/skill-registry.sh +775 -0
- package/scripts/lib/spec-driven.sh +476 -0
- package/scripts/lib/test-helpers.sh +18 -7
- package/scripts/lib/test-holdout.sh +429 -0
- package/scripts/lib/test-optimizer.sh +511 -0
- package/scripts/shipwright-file-suggest.sh +45 -0
- package/scripts/skills/adversarial-quality.md +61 -0
- package/scripts/skills/api-design.md +44 -0
- package/scripts/skills/architecture-design.md +50 -0
- package/scripts/skills/brainstorming.md +43 -0
- package/scripts/skills/data-pipeline.md +44 -0
- package/scripts/skills/deploy-safety.md +64 -0
- package/scripts/skills/documentation.md +38 -0
- package/scripts/skills/frontend-design.md +45 -0
- package/scripts/skills/generated/.gitkeep +0 -0
- package/scripts/skills/generated/_refinements/.gitkeep +0 -0
- package/scripts/skills/generated/_refinements/adversarial-quality.patch.md +3 -0
- package/scripts/skills/generated/_refinements/architecture-design.patch.md +3 -0
- package/scripts/skills/generated/_refinements/brainstorming.patch.md +3 -0
- package/scripts/skills/generated/cli-version-management.md +29 -0
- package/scripts/skills/generated/collection-system-validation.md +99 -0
- package/scripts/skills/generated/large-scale-c-refactoring-coordination.md +97 -0
- package/scripts/skills/generated/pattern-matching-similarity-scoring.md +195 -0
- package/scripts/skills/generated/test-parallelization-detection.md +65 -0
- package/scripts/skills/observability.md +79 -0
- package/scripts/skills/performance.md +48 -0
- package/scripts/skills/pr-quality.md +49 -0
- package/scripts/skills/product-thinking.md +43 -0
- package/scripts/skills/security-audit.md +49 -0
- package/scripts/skills/systematic-debugging.md +40 -0
- package/scripts/skills/testing-strategy.md +47 -0
- package/scripts/skills/two-stage-review.md +52 -0
- package/scripts/skills/validation-thoroughness.md +55 -0
- package/scripts/sw +9 -3
- package/scripts/sw-activity.sh +9 -2
- package/scripts/sw-adaptive.sh +2 -1
- package/scripts/sw-adversarial.sh +2 -1
- package/scripts/sw-architecture-enforcer.sh +3 -1
- package/scripts/sw-auth.sh +12 -2
- package/scripts/sw-autonomous.sh +5 -1
- package/scripts/sw-changelog.sh +4 -1
- package/scripts/sw-checkpoint.sh +2 -1
- package/scripts/sw-ci.sh +5 -1
- package/scripts/sw-cleanup.sh +4 -26
- package/scripts/sw-code-review.sh +10 -4
- package/scripts/sw-connect.sh +2 -1
- package/scripts/sw-context.sh +2 -1
- package/scripts/sw-cost.sh +48 -3
- package/scripts/sw-daemon.sh +66 -9
- package/scripts/sw-dashboard.sh +3 -1
- package/scripts/sw-db.sh +59 -16
- package/scripts/sw-decide.sh +8 -2
- package/scripts/sw-decompose.sh +360 -17
- package/scripts/sw-deps.sh +4 -1
- package/scripts/sw-developer-simulation.sh +4 -1
- package/scripts/sw-discovery.sh +325 -2
- package/scripts/sw-doc-fleet.sh +4 -1
- package/scripts/sw-docs-agent.sh +3 -1
- package/scripts/sw-docs.sh +2 -1
- package/scripts/sw-doctor.sh +453 -2
- package/scripts/sw-dora.sh +4 -1
- package/scripts/sw-durable.sh +4 -3
- package/scripts/sw-e2e-orchestrator.sh +17 -16
- package/scripts/sw-eventbus.sh +7 -1
- package/scripts/sw-evidence.sh +364 -12
- package/scripts/sw-feedback.sh +550 -9
- package/scripts/sw-fix.sh +20 -1
- package/scripts/sw-fleet-discover.sh +6 -2
- package/scripts/sw-fleet-viz.sh +4 -1
- package/scripts/sw-fleet.sh +5 -1
- package/scripts/sw-github-app.sh +16 -3
- package/scripts/sw-github-checks.sh +3 -2
- package/scripts/sw-github-deploy.sh +3 -2
- package/scripts/sw-github-graphql.sh +18 -7
- package/scripts/sw-guild.sh +5 -1
- package/scripts/sw-heartbeat.sh +5 -30
- package/scripts/sw-hello.sh +67 -0
- package/scripts/sw-hygiene.sh +6 -1
- package/scripts/sw-incident.sh +265 -1
- package/scripts/sw-init.sh +18 -2
- package/scripts/sw-instrument.sh +10 -2
- package/scripts/sw-intelligence.sh +42 -6
- package/scripts/sw-jira.sh +5 -1
- package/scripts/sw-launchd.sh +2 -1
- package/scripts/sw-linear.sh +4 -1
- package/scripts/sw-logs.sh +4 -1
- package/scripts/sw-loop.sh +432 -1128
- package/scripts/sw-memory.sh +356 -2
- package/scripts/sw-mission-control.sh +6 -1
- package/scripts/sw-model-router.sh +481 -26
- package/scripts/sw-otel.sh +13 -4
- package/scripts/sw-oversight.sh +14 -5
- package/scripts/sw-patrol-meta.sh +334 -0
- package/scripts/sw-pipeline-composer.sh +5 -1
- package/scripts/sw-pipeline-vitals.sh +2 -1
- package/scripts/sw-pipeline.sh +53 -2664
- package/scripts/sw-pm.sh +12 -5
- package/scripts/sw-pr-lifecycle.sh +2 -1
- package/scripts/sw-predictive.sh +7 -1
- package/scripts/sw-prep.sh +185 -2
- package/scripts/sw-ps.sh +5 -25
- package/scripts/sw-public-dashboard.sh +15 -3
- package/scripts/sw-quality.sh +2 -1
- package/scripts/sw-reaper.sh +8 -25
- package/scripts/sw-recruit.sh +156 -2303
- package/scripts/sw-regression.sh +19 -12
- package/scripts/sw-release-manager.sh +3 -1
- package/scripts/sw-release.sh +4 -1
- package/scripts/sw-remote.sh +3 -1
- package/scripts/sw-replay.sh +7 -1
- package/scripts/sw-retro.sh +158 -1
- package/scripts/sw-review-rerun.sh +3 -1
- package/scripts/sw-scale.sh +10 -3
- package/scripts/sw-security-audit.sh +6 -1
- package/scripts/sw-self-optimize.sh +6 -3
- package/scripts/sw-session.sh +9 -3
- package/scripts/sw-setup.sh +3 -1
- package/scripts/sw-stall-detector.sh +406 -0
- package/scripts/sw-standup.sh +15 -7
- package/scripts/sw-status.sh +3 -1
- package/scripts/sw-strategic.sh +4 -1
- package/scripts/sw-stream.sh +7 -1
- package/scripts/sw-swarm.sh +18 -6
- package/scripts/sw-team-stages.sh +13 -6
- package/scripts/sw-templates.sh +5 -29
- package/scripts/sw-testgen.sh +7 -1
- package/scripts/sw-tmux-pipeline.sh +4 -1
- package/scripts/sw-tmux-role-color.sh +2 -0
- package/scripts/sw-tmux-status.sh +1 -1
- package/scripts/sw-tmux.sh +3 -1
- package/scripts/sw-trace.sh +3 -1
- package/scripts/sw-tracker-github.sh +3 -0
- package/scripts/sw-tracker-jira.sh +3 -0
- package/scripts/sw-tracker-linear.sh +3 -0
- package/scripts/sw-tracker.sh +3 -1
- package/scripts/sw-triage.sh +2 -1
- package/scripts/sw-upgrade.sh +3 -1
- package/scripts/sw-ux.sh +5 -2
- package/scripts/sw-webhook.sh +3 -1
- package/scripts/sw-widgets.sh +3 -1
- package/scripts/sw-worktree.sh +15 -3
- package/scripts/test-skill-injection.sh +1233 -0
- package/templates/pipelines/autonomous.json +27 -3
- package/templates/pipelines/cost-aware.json +34 -8
- package/templates/pipelines/deployed.json +12 -0
- package/templates/pipelines/enterprise.json +12 -0
- package/templates/pipelines/fast.json +6 -0
- package/templates/pipelines/full.json +27 -3
- package/templates/pipelines/hotfix.json +6 -0
- package/templates/pipelines/standard.json +12 -0
- 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
|
+
```
|