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,770 @@
1
+ # Pipeline Audit Trail Implementation Plan
2
+
3
+ > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4
+
5
+ **Goal:** Add structured, compliance-grade audit logging so every pipeline run produces full prompt-in → plan-out → test-evidence → audit-verdict → outcome traceability.
6
+
7
+ **Architecture:** A new `scripts/lib/audit-trail.sh` library provides four functions (`audit_init`, `audit_emit`, `audit_save_prompt`, `audit_finalize`). These are called inline from existing code at key decision points. Events append to a crash-safe JSONL file. At pipeline end, `audit_finalize` reads the JSONL and generates both `pipeline-audit.json` (machine-readable) and `pipeline-audit.md` (human-readable compliance report). All audit calls are wrapped in `|| true` (fail-open — never blocks the pipeline).
8
+
9
+ **Tech Stack:** Bash, jq, JSONL
10
+
11
+ ---
12
+
13
+ ### Task 1: Create `audit-trail.sh` Library — Tests First
14
+
15
+ **Files:**
16
+ - Create: `scripts/lib/audit-trail.sh`
17
+ - Create: `scripts/sw-lib-audit-trail-test.sh`
18
+
19
+ **Step 1: Write the test file skeleton**
20
+
21
+ Create `scripts/sw-lib-audit-trail-test.sh`:
22
+
23
+ ```bash
24
+ #!/usr/bin/env bash
25
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
26
+ # ║ audit-trail test suite ║
27
+ # ║ Tests audit init, emit, prompt save, and finalize ║
28
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
29
+ set -euo pipefail
30
+ trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
31
+
32
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
33
+ source "$SCRIPT_DIR/lib/test-helpers.sh"
34
+
35
+ print_test_header "Lib: audit-trail Tests"
36
+
37
+ setup_test_env "sw-lib-audit-trail-test"
38
+ trap cleanup_test_env EXIT
39
+
40
+ # Provide required globals that audit-trail.sh expects
41
+ ARTIFACTS_DIR="$TEST_TEMP_DIR/pipeline-artifacts"
42
+ LOG_DIR="$TEST_TEMP_DIR/loop-logs"
43
+ mkdir -p "$ARTIFACTS_DIR" "$LOG_DIR"
44
+
45
+ # ═══════════════════════════════════════════════════════════════════════════════
46
+ # Test: audit_init creates JSONL and writes pipeline.start event
47
+ # ═══════════════════════════════════════════════════════════════════════════════
48
+ test_audit_init() {
49
+ # Set up pipeline globals
50
+ ISSUE_NUMBER=42
51
+ GOAL="Add login page"
52
+ PIPELINE_NAME="standard"
53
+ MODEL="sonnet"
54
+
55
+ source "$SCRIPT_DIR/lib/audit-trail.sh"
56
+ audit_init
57
+
58
+ local jsonl="$ARTIFACTS_DIR/pipeline-audit.jsonl"
59
+ assert_pass "audit_init creates JSONL file" test -f "$jsonl"
60
+
61
+ local event_type
62
+ event_type=$(head -1 "$jsonl" | jq -r '.type')
63
+ assert_eq "first event is pipeline.start" "pipeline.start" "$event_type"
64
+
65
+ local issue
66
+ issue=$(head -1 "$jsonl" | jq -r '.issue')
67
+ assert_eq "pipeline.start has issue number" "42" "$issue"
68
+
69
+ local goal
70
+ goal=$(head -1 "$jsonl" | jq -r '.goal')
71
+ assert_eq "pipeline.start has goal" "Add login page" "$goal"
72
+ }
73
+ test_audit_init
74
+
75
+ # ═══════════════════════════════════════════════════════════════════════════════
76
+ # Test: audit_emit appends structured JSON lines
77
+ # ═══════════════════════════════════════════════════════════════════════════════
78
+ test_audit_emit() {
79
+ # Reset
80
+ ARTIFACTS_DIR="$TEST_TEMP_DIR/emit-test"
81
+ mkdir -p "$ARTIFACTS_DIR"
82
+
83
+ source "$SCRIPT_DIR/lib/audit-trail.sh"
84
+ _AUDIT_JSONL="$ARTIFACTS_DIR/pipeline-audit.jsonl"
85
+ : > "$_AUDIT_JSONL"
86
+
87
+ audit_emit "stage.start" "stage=build" "config=standard"
88
+ audit_emit "stage.complete" "stage=build" "verdict=pass" "duration_s=120"
89
+
90
+ local line_count
91
+ line_count=$(wc -l < "$_AUDIT_JSONL" | tr -d ' ')
92
+ assert_eq "two events emitted" "2" "$line_count"
93
+
94
+ local type1 type2
95
+ type1=$(sed -n '1p' "$_AUDIT_JSONL" | jq -r '.type')
96
+ type2=$(sed -n '2p' "$_AUDIT_JSONL" | jq -r '.type')
97
+ assert_eq "first event type" "stage.start" "$type1"
98
+ assert_eq "second event type" "stage.complete" "$type2"
99
+
100
+ local verdict
101
+ verdict=$(sed -n '2p' "$_AUDIT_JSONL" | jq -r '.verdict')
102
+ assert_eq "stage.complete has verdict" "pass" "$verdict"
103
+
104
+ # Verify each line is valid JSON
105
+ local valid=true
106
+ while IFS= read -r line; do
107
+ echo "$line" | jq . >/dev/null 2>&1 || valid=false
108
+ done < "$_AUDIT_JSONL"
109
+ assert_pass "all lines are valid JSON" $valid
110
+ }
111
+ test_audit_emit
112
+
113
+ # ═══════════════════════════════════════════════════════════════════════════════
114
+ # Test: audit_save_prompt writes prompt file and returns path
115
+ # ═══════════════════════════════════════════════════════════════════════════════
116
+ test_audit_save_prompt() {
117
+ LOG_DIR="$TEST_TEMP_DIR/prompt-test"
118
+ mkdir -p "$LOG_DIR"
119
+
120
+ source "$SCRIPT_DIR/lib/audit-trail.sh"
121
+
122
+ local prompt_text="You are a coding agent. Build the login page."
123
+ audit_save_prompt "$prompt_text" 3
124
+
125
+ local prompt_file="$LOG_DIR/iteration-3.prompt.txt"
126
+ assert_pass "prompt file created" test -f "$prompt_file"
127
+
128
+ local content
129
+ content=$(cat "$prompt_file")
130
+ assert_eq "prompt content matches" "$prompt_text" "$content"
131
+ }
132
+ test_audit_save_prompt
133
+
134
+ # ═══════════════════════════════════════════════════════════════════════════════
135
+ # Test: audit_emit with timestamp has ISO-8601 format
136
+ # ═══════════════════════════════════════════════════════════════════════════════
137
+ test_audit_timestamp_format() {
138
+ ARTIFACTS_DIR="$TEST_TEMP_DIR/ts-test"
139
+ mkdir -p "$ARTIFACTS_DIR"
140
+
141
+ source "$SCRIPT_DIR/lib/audit-trail.sh"
142
+ _AUDIT_JSONL="$ARTIFACTS_DIR/pipeline-audit.jsonl"
143
+ : > "$_AUDIT_JSONL"
144
+
145
+ audit_emit "test.event" "key=value"
146
+
147
+ local ts
148
+ ts=$(head -1 "$_AUDIT_JSONL" | jq -r '.ts')
149
+ # ISO-8601 format: YYYY-MM-DDTHH:MM:SSZ
150
+ assert_pass "timestamp is ISO-8601" echo "$ts" | grep -qE '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$'
151
+ }
152
+ test_audit_timestamp_format
153
+
154
+ # ═══════════════════════════════════════════════════════════════════════════════
155
+ # Test: audit_finalize generates JSON and Markdown reports
156
+ # ═══════════════════════════════════════════════════════════════════════════════
157
+ test_audit_finalize() {
158
+ ARTIFACTS_DIR="$TEST_TEMP_DIR/finalize-test"
159
+ LOG_DIR="$TEST_TEMP_DIR/finalize-logs"
160
+ mkdir -p "$ARTIFACTS_DIR" "$LOG_DIR"
161
+
162
+ ISSUE_NUMBER=99
163
+ GOAL="Fix bug"
164
+ PIPELINE_NAME="fast"
165
+ MODEL="haiku"
166
+ PIPELINE_START_EPOCH=$(date +%s)
167
+
168
+ source "$SCRIPT_DIR/lib/audit-trail.sh"
169
+ _AUDIT_JSONL="$ARTIFACTS_DIR/pipeline-audit.jsonl"
170
+
171
+ # Simulate a pipeline run
172
+ audit_emit "pipeline.start" "issue=99" "goal=Fix bug" "template=fast" "model=haiku"
173
+ audit_emit "stage.start" "stage=intake"
174
+ audit_emit "stage.complete" "stage=intake" "verdict=pass" "duration_s=30"
175
+ audit_emit "stage.start" "stage=build"
176
+ audit_emit "loop.prompt" "iteration=1" "chars=15000" "path=iteration-1.prompt.txt"
177
+ audit_emit "loop.response" "iteration=1" "chars=5000" "exit_code=0" "tokens_in=3750" "tokens_out=1250"
178
+ audit_emit "loop.test_gate" "iteration=1" "commands=1" "all_passed=true"
179
+ audit_emit "stage.complete" "stage=build" "verdict=pass" "duration_s=180"
180
+ audit_emit "pipeline.complete" "outcome=success" "duration_s=210"
181
+
182
+ audit_finalize "success"
183
+
184
+ # Check JSON report
185
+ local json_report="$ARTIFACTS_DIR/pipeline-audit.json"
186
+ assert_pass "JSON report created" test -f "$json_report"
187
+
188
+ local outcome
189
+ outcome=$(jq -r '.outcome' "$json_report")
190
+ assert_eq "JSON has outcome" "success" "$outcome"
191
+
192
+ local stage_count
193
+ stage_count=$(jq '.stages | length' "$json_report")
194
+ assert_pass "JSON has stages array" test "$stage_count" -gt 0
195
+
196
+ # Check Markdown report
197
+ local md_report="$ARTIFACTS_DIR/pipeline-audit.md"
198
+ assert_pass "Markdown report created" test -f "$md_report"
199
+ assert_pass "Markdown has header" grep -q "Pipeline Audit Report" "$md_report"
200
+ assert_pass "Markdown has stage table" grep -q "intake" "$md_report"
201
+ assert_pass "Markdown has outcome" grep -q "success" "$md_report"
202
+ }
203
+ test_audit_finalize
204
+
205
+ # ═══════════════════════════════════════════════════════════════════════════════
206
+ # Test: audit functions are fail-open (don't crash on bad input)
207
+ # ═══════════════════════════════════════════════════════════════════════════════
208
+ test_audit_fail_open() {
209
+ ARTIFACTS_DIR="/nonexistent/path"
210
+ LOG_DIR="/nonexistent/path"
211
+
212
+ source "$SCRIPT_DIR/lib/audit-trail.sh"
213
+
214
+ # These should not crash (fail-open)
215
+ audit_emit "test.event" "key=value" 2>/dev/null || true
216
+ assert_pass "audit_emit survives bad ARTIFACTS_DIR" true
217
+
218
+ audit_save_prompt "test prompt" 1 2>/dev/null || true
219
+ assert_pass "audit_save_prompt survives bad LOG_DIR" true
220
+ }
221
+ test_audit_fail_open
222
+
223
+ # ═══════════════════════════════════════════════════════════════════════════════
224
+ print_test_summary
225
+ ```
226
+
227
+ **Step 2: Run the tests to verify they fail**
228
+
229
+ Run: `bash scripts/sw-lib-audit-trail-test.sh`
230
+ Expected: FAIL with "audit-trail.sh: No such file" or "audit_init: command not found"
231
+
232
+ **Step 3: Write `scripts/lib/audit-trail.sh`**
233
+
234
+ ```bash
235
+ #!/usr/bin/env bash
236
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
237
+ # ║ audit-trail.sh — Structured pipeline audit logging ║
238
+ # ║ ║
239
+ # ║ Provides compliance-grade traceability for pipeline runs: ║
240
+ # ║ prompt in → plan out → test evidence → audit verdict → outcome. ║
241
+ # ║ ║
242
+ # ║ All functions are fail-open (|| true). Audit never blocks pipeline. ║
243
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
244
+ [[ -n "${_AUDIT_TRAIL_LOADED:-}" ]] && return 0
245
+ _AUDIT_TRAIL_LOADED=1
246
+
247
+ # ─── Internal State ──────────────────────────────────────────────────────────
248
+ _AUDIT_JSONL="${ARTIFACTS_DIR:-/tmp}/pipeline-audit.jsonl"
249
+
250
+ # ─── audit_init ──────────────────────────────────────────────────────────────
251
+ # Creates the JSONL file and writes the pipeline.start event.
252
+ # Call once at pipeline start (from sw-pipeline.sh).
253
+ audit_init() {
254
+ _AUDIT_JSONL="${ARTIFACTS_DIR:-/tmp}/pipeline-audit.jsonl"
255
+ mkdir -p "$(dirname "$_AUDIT_JSONL")" 2>/dev/null || return 0
256
+
257
+ local git_sha
258
+ git_sha=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
259
+
260
+ audit_emit "pipeline.start" \
261
+ "issue=${ISSUE_NUMBER:-0}" \
262
+ "goal=${GOAL:-}" \
263
+ "template=${PIPELINE_NAME:-unknown}" \
264
+ "model=${MODEL:-unknown}" \
265
+ "git_sha=$git_sha"
266
+ }
267
+
268
+ # ─── audit_emit ──────────────────────────────────────────────────────────────
269
+ # Appends one JSON line to the audit JSONL.
270
+ # Usage: audit_emit "event.type" "key1=val1" "key2=val2" ...
271
+ audit_emit() {
272
+ local event_type="${1:-unknown}"; shift
273
+
274
+ local ts
275
+ ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || echo "unknown")
276
+
277
+ # Build JSON object
278
+ local json="{\"ts\":\"$ts\",\"type\":\"$event_type\""
279
+ while [[ $# -gt 0 ]]; do
280
+ local key="${1%%=*}"
281
+ local val="${1#*=}"
282
+ # Escape double quotes in values
283
+ val="${val//\"/\\\"}"
284
+ json="${json},\"${key}\":\"${val}\""
285
+ shift
286
+ done
287
+ json="${json}}"
288
+
289
+ echo "$json" >> "$_AUDIT_JSONL" 2>/dev/null || true
290
+ }
291
+
292
+ # ─── audit_save_prompt ───────────────────────────────────────────────────────
293
+ # Saves the full prompt text to iteration-N.prompt.txt before sending to Claude.
294
+ # Usage: audit_save_prompt "$prompt_text" "$iteration_number"
295
+ audit_save_prompt() {
296
+ local prompt_text="${1:-}"
297
+ local iteration="${2:-0}"
298
+ local prompt_file="${LOG_DIR:-/tmp}/iteration-${iteration}.prompt.txt"
299
+
300
+ mkdir -p "$(dirname "$prompt_file")" 2>/dev/null || return 0
301
+ echo "$prompt_text" > "$prompt_file" 2>/dev/null || true
302
+ }
303
+
304
+ # ─── audit_finalize ──────────────────────────────────────────────────────────
305
+ # Reads the JSONL and generates pipeline-audit.json + pipeline-audit.md.
306
+ # Call once at pipeline end (success or failure).
307
+ # Usage: audit_finalize "success|failure"
308
+ audit_finalize() {
309
+ local outcome="${1:-unknown}"
310
+ local json_report="${ARTIFACTS_DIR:-/tmp}/pipeline-audit.json"
311
+ local md_report="${ARTIFACTS_DIR:-/tmp}/pipeline-audit.md"
312
+
313
+ [[ ! -f "$_AUDIT_JSONL" ]] && return 0
314
+
315
+ # ── Build JSON report ──
316
+ _audit_build_json "$outcome" > "$json_report" 2>/dev/null || true
317
+
318
+ # ── Build Markdown report ──
319
+ _audit_build_markdown "$outcome" > "$md_report" 2>/dev/null || true
320
+ }
321
+
322
+ # ─── Internal: JSON report builder ──────────────────────────────────────────
323
+ _audit_build_json() {
324
+ local outcome="$1"
325
+ local duration_s=0
326
+ [[ -n "${PIPELINE_START_EPOCH:-}" ]] && duration_s=$(( $(date +%s) - PIPELINE_START_EPOCH ))
327
+
328
+ # Extract stages from JSONL
329
+ local stages="[]"
330
+ if command -v jq >/dev/null 2>&1; then
331
+ # Group stage.start + stage.complete pairs
332
+ stages=$(jq -s '
333
+ [.[] | select(.type == "stage.complete")] |
334
+ map({
335
+ name: .stage,
336
+ verdict: .verdict,
337
+ duration_s: (.duration_s // "0" | tonumber)
338
+ })
339
+ ' "$_AUDIT_JSONL" 2>/dev/null || echo "[]")
340
+
341
+ # Extract iterations from loop events
342
+ local iterations="[]"
343
+ iterations=$(jq -s '
344
+ [.[] | select(.type == "loop.prompt" or .type == "loop.response" or .type == "loop.test_gate")] |
345
+ group_by(.iteration) |
346
+ map({
347
+ number: (.[0].iteration // "0" | tonumber),
348
+ prompt_chars: ([.[] | select(.type == "loop.prompt") | .chars // "0" | tonumber] | first // 0),
349
+ prompt_path: ([.[] | select(.type == "loop.prompt") | .path // ""] | first // ""),
350
+ response_chars: ([.[] | select(.type == "loop.response") | .chars // "0" | tonumber] | first // 0),
351
+ exit_code: ([.[] | select(.type == "loop.response") | .exit_code // "0" | tonumber] | first // 0),
352
+ tests_passed: ([.[] | select(.type == "loop.test_gate") | .all_passed // ""] | first // "")
353
+ }) | sort_by(.number)
354
+ ' "$_AUDIT_JSONL" 2>/dev/null || echo "[]")
355
+
356
+ # Assemble final JSON
357
+ jq -n \
358
+ --arg version "1.0" \
359
+ --arg pipeline_id "pipeline-${ISSUE_NUMBER:-0}" \
360
+ --argjson issue "${ISSUE_NUMBER:-0}" \
361
+ --arg outcome "$outcome" \
362
+ --arg goal "${GOAL:-}" \
363
+ --arg template "${PIPELINE_NAME:-unknown}" \
364
+ --arg model "${MODEL:-unknown}" \
365
+ --argjson duration_s "$duration_s" \
366
+ --argjson stages "$stages" \
367
+ --argjson iterations "$iterations" \
368
+ '{
369
+ version: $version,
370
+ pipeline_id: $pipeline_id,
371
+ issue: $issue,
372
+ outcome: $outcome,
373
+ goal: $goal,
374
+ template: $template,
375
+ model: $model,
376
+ duration_s: $duration_s,
377
+ stages: $stages,
378
+ iterations: $iterations
379
+ }'
380
+ else
381
+ # Fallback without jq
382
+ echo "{\"version\":\"1.0\",\"outcome\":\"$outcome\",\"duration_s\":$duration_s}"
383
+ fi
384
+ }
385
+
386
+ # ─── Internal: Markdown report builder ──────────────────────────────────────
387
+ _audit_build_markdown() {
388
+ local outcome="$1"
389
+ local duration_s=0
390
+ [[ -n "${PIPELINE_START_EPOCH:-}" ]] && duration_s=$(( $(date +%s) - PIPELINE_START_EPOCH ))
391
+
392
+ # Format duration
393
+ local duration_fmt="${duration_s}s"
394
+ if [[ "$duration_s" -ge 60 ]]; then
395
+ duration_fmt="$(( duration_s / 60 ))m $(( duration_s % 60 ))s"
396
+ fi
397
+
398
+ cat <<HEADER
399
+ # Pipeline Audit Report — Issue #${ISSUE_NUMBER:-0}
400
+
401
+ | Field | Value |
402
+ |---|---|
403
+ | **Pipeline** | ${PIPELINE_NAME:-unknown} |
404
+ | **Issue** | #${ISSUE_NUMBER:-0} |
405
+ | **Goal** | ${GOAL:-N/A} |
406
+ | **Model** | ${MODEL:-unknown} |
407
+ | **Duration** | ${duration_fmt} |
408
+ | **Outcome** | ${outcome} |
409
+
410
+ ## Stage Summary
411
+
412
+ | Stage | Duration | Verdict |
413
+ |---|---|---|
414
+ HEADER
415
+
416
+ # Extract stage rows from JSONL
417
+ if command -v jq >/dev/null 2>&1; then
418
+ jq -r 'select(.type == "stage.complete") |
419
+ "| \(.stage) | \(.duration_s // "?")s | \(.verdict // "?") |"' \
420
+ "$_AUDIT_JSONL" 2>/dev/null || true
421
+ fi
422
+
423
+ echo ""
424
+ echo "## Build Loop Detail"
425
+ echo ""
426
+
427
+ # Extract iteration details
428
+ if command -v jq >/dev/null 2>&1; then
429
+ jq -r 'select(.type == "loop.prompt") |
430
+ "### Iteration \(.iteration)\n- **Prompt**: \(.chars // "?") chars → \(.path // "N/A")\n"' \
431
+ "$_AUDIT_JSONL" 2>/dev/null || true
432
+
433
+ jq -r 'select(.type == "loop.response") |
434
+ "- **Response**: \(.chars // "?") chars, exit \(.exit_code // "?")\n"' \
435
+ "$_AUDIT_JSONL" 2>/dev/null || true
436
+
437
+ jq -r 'select(.type == "loop.test_gate") |
438
+ "- **Tests**: \(.commands // "?") commands, all passed: \(.all_passed // "?")\n"' \
439
+ "$_AUDIT_JSONL" 2>/dev/null || true
440
+ fi
441
+
442
+ echo ""
443
+ echo "---"
444
+ echo "*Generated by Shipwright audit-trail v1.0*"
445
+ }
446
+ ```
447
+
448
+ **Step 4: Run the tests**
449
+
450
+ Run: `bash scripts/sw-lib-audit-trail-test.sh`
451
+ Expected: All tests PASS
452
+
453
+ **Step 5: Commit**
454
+
455
+ ```bash
456
+ git add scripts/lib/audit-trail.sh scripts/sw-lib-audit-trail-test.sh
457
+ git commit -m "feat(audit): add audit-trail.sh library with tests
458
+
459
+ TDD: emit, save prompt, finalize to JSON + Markdown.
460
+ All functions fail-open (|| true)."
461
+ ```
462
+
463
+ ---
464
+
465
+ ### Task 2: Save Prompts in `loop-iteration.sh`
466
+
467
+ **Files:**
468
+ - Modify: `scripts/lib/loop-iteration.sh:459-470`
469
+
470
+ **Step 1: Write the test**
471
+
472
+ Add to `scripts/sw-lib-audit-trail-test.sh` (or verify manually):
473
+
474
+ The integration test is: after a loop iteration runs, `iteration-N.prompt.txt` exists in LOG_DIR.
475
+
476
+ For now we verify by reading the code change — the unit tests in Task 1 cover `audit_save_prompt`.
477
+
478
+ **Step 2: Modify `run_claude_iteration()` in `scripts/lib/loop-iteration.sh`**
479
+
480
+ Find this block (lines 465-470):
481
+
482
+ ```bash
483
+ final_prompt=$(manage_context_window "$prompt")
484
+
485
+ local raw_prompt_chars=${#prompt}
486
+ local prompt_chars=${#final_prompt}
487
+ local approx_tokens=$((prompt_chars / 4))
488
+ info "Prompt: ~${approx_tokens} tokens (${prompt_chars} chars)"
489
+ ```
490
+
491
+ Insert after `info "Prompt:..."` (after line 470):
492
+
493
+ ```bash
494
+ # Audit: save full prompt to disk for traceability
495
+ if type audit_save_prompt >/dev/null 2>&1; then
496
+ audit_save_prompt "$final_prompt" "$ITERATION" || true
497
+ fi
498
+ if type audit_emit >/dev/null 2>&1; then
499
+ audit_emit "loop.prompt" "iteration=$ITERATION" "chars=$prompt_chars" \
500
+ "raw_chars=$raw_prompt_chars" "path=iteration-${ITERATION}.prompt.txt" || true
501
+ fi
502
+ ```
503
+
504
+ After the Claude response is processed (after line 530, the `accumulate_loop_tokens` call), add:
505
+
506
+ ```bash
507
+ # Audit: record response metadata
508
+ if type audit_emit >/dev/null 2>&1; then
509
+ local response_chars=0
510
+ [[ -f "$log_file" ]] && response_chars=$(wc -c < "$log_file" | tr -d ' ')
511
+ audit_emit "loop.response" "iteration=$ITERATION" "chars=$response_chars" \
512
+ "exit_code=$exit_code" "duration_s=$iter_duration" \
513
+ "path=iteration-${ITERATION}.json" || true
514
+ fi
515
+ ```
516
+
517
+ **Step 3: Run the loop tests**
518
+
519
+ Run: `bash scripts/sw-loop-test.sh`
520
+ Expected: 56/61 pass (same as before — 5 pre-existing failures)
521
+
522
+ **Step 4: Commit**
523
+
524
+ ```bash
525
+ git add scripts/lib/loop-iteration.sh
526
+ git commit -m "feat(audit): save prompts + emit loop events
527
+
528
+ Captures iteration-N.prompt.txt before each Claude call.
529
+ Emits loop.prompt and loop.response audit events."
530
+ ```
531
+
532
+ ---
533
+
534
+ ### Task 3: Emit Test Gate and Verification Gap Events in `sw-loop.sh`
535
+
536
+ **Files:**
537
+ - Modify: `scripts/sw-loop.sh:987-992` (after test evidence JSON write)
538
+ - Modify: `scripts/sw-loop.sh:2231-2240` (verification gap handler)
539
+
540
+ **Step 1: Add test gate audit event**
541
+
542
+ After the existing test evidence write (line 989-990):
543
+
544
+ ```bash
545
+ # Write structured test evidence
546
+ if command -v jq >/dev/null 2>&1; then
547
+ echo "$test_results" > "${LOG_DIR}/test-evidence-iter-${ITERATION}.json"
548
+ fi
549
+ ```
550
+
551
+ Add:
552
+
553
+ ```bash
554
+ # Audit: emit test gate event
555
+ if type audit_emit >/dev/null 2>&1; then
556
+ local cmd_count=0
557
+ command -v jq >/dev/null 2>&1 && cmd_count=$(echo "$test_results" | jq 'length' 2>/dev/null || echo 0)
558
+ audit_emit "loop.test_gate" "iteration=$ITERATION" "commands=$cmd_count" \
559
+ "all_passed=$all_passed" "evidence_path=test-evidence-iter-${ITERATION}.json" || true
560
+ fi
561
+ ```
562
+
563
+ **Step 2: Add verification gap audit events**
564
+
565
+ In the verification gap handler (after line 2234, the "override_audit" emit_event), add:
566
+
567
+ ```bash
568
+ audit_emit "loop.verification_gap" "iteration=$ITERATION" \
569
+ "resolution=override" "tests_recheck=pass" || true
570
+ ```
571
+
572
+ After line 2238 (the "retry" emit_event), add:
573
+
574
+ ```bash
575
+ audit_emit "loop.verification_gap" "iteration=$ITERATION" \
576
+ "resolution=retry" "tests_recheck=fail" || true
577
+ ```
578
+
579
+ **Step 3: Source `audit-trail.sh` in sw-loop.sh imports**
580
+
581
+ At line 43 (after the error-actionability source), add:
582
+
583
+ ```bash
584
+ # Audit trail for compliance-grade pipeline traceability
585
+ [[ -f "$SCRIPT_DIR/lib/audit-trail.sh" ]] && source "$SCRIPT_DIR/lib/audit-trail.sh" 2>/dev/null || true
586
+ ```
587
+
588
+ **Step 4: Run the tests**
589
+
590
+ Run: `bash scripts/sw-loop-test.sh`
591
+ Expected: 56/61 pass (unchanged)
592
+
593
+ Run: `bash scripts/sw-lib-audit-trail-test.sh`
594
+ Expected: All pass
595
+
596
+ **Step 5: Commit**
597
+
598
+ ```bash
599
+ git add scripts/sw-loop.sh
600
+ git commit -m "feat(audit): emit test gate + verification gap events
601
+
602
+ Adds audit events in run_test_gate() and verification gap handler.
603
+ Sources audit-trail.sh library."
604
+ ```
605
+
606
+ ---
607
+
608
+ ### Task 4: Add Stage Events and Finalization in `sw-pipeline.sh`
609
+
610
+ **Files:**
611
+ - Modify: `scripts/sw-pipeline.sh:1374-1395` (run_pipeline start)
612
+ - Modify: `scripts/sw-pipeline.sh:2500-2565` (pipeline completion)
613
+
614
+ **Step 1: Source audit-trail.sh and call audit_init**
615
+
616
+ Near the top of `sw-pipeline.sh` (after existing source statements), add:
617
+
618
+ ```bash
619
+ # Audit trail for compliance-grade pipeline traceability
620
+ [[ -f "$SCRIPT_DIR/lib/audit-trail.sh" ]] && source "$SCRIPT_DIR/lib/audit-trail.sh" 2>/dev/null || true
621
+ ```
622
+
623
+ At the start of `run_pipeline()` (line 1376, after `rotate_event_log_if_needed`), add:
624
+
625
+ ```bash
626
+ # Initialize audit trail for this pipeline run
627
+ if type audit_init >/dev/null 2>&1; then
628
+ audit_init || true
629
+ fi
630
+ ```
631
+
632
+ **Step 2: Add stage start/complete events**
633
+
634
+ In the `run_pipeline()` stage loop, find where each stage runs. There's a section where `stage_*` functions are called (inside the `while IFS= read` loop). Before each stage function call (after the enabled check around line 1433), add:
635
+
636
+ ```bash
637
+ # Audit: stage start
638
+ if type audit_emit >/dev/null 2>&1; then
639
+ audit_emit "stage.start" "stage=$id" || true
640
+ fi
641
+ ```
642
+
643
+ After each stage completes (near the "Stage X complete" success message), add:
644
+
645
+ ```bash
646
+ # Audit: stage complete
647
+ if type audit_emit >/dev/null 2>&1; then
648
+ audit_emit "stage.complete" "stage=$id" "verdict=pass" \
649
+ "duration_s=${stage_duration:-0}" || true
650
+ fi
651
+ ```
652
+
653
+ For stage failures (near the "Pipeline failed at stage" error message around line 1678), add:
654
+
655
+ ```bash
656
+ if type audit_emit >/dev/null 2>&1; then
657
+ audit_emit "stage.complete" "stage=$id" "verdict=fail" \
658
+ "duration_s=${stage_duration:-0}" || true
659
+ fi
660
+ ```
661
+
662
+ **Step 3: Call audit_finalize at pipeline completion**
663
+
664
+ At line 2520 (after the success `emit_event "pipeline.completed"`), add:
665
+
666
+ ```bash
667
+ # Finalize audit trail
668
+ if type audit_finalize >/dev/null 2>&1; then
669
+ audit_finalize "success" || true
670
+ fi
671
+ ```
672
+
673
+ At line 2565 (after the failure `emit_event "pipeline.completed"`), add:
674
+
675
+ ```bash
676
+ # Finalize audit trail
677
+ if type audit_finalize >/dev/null 2>&1; then
678
+ audit_finalize "failure" || true
679
+ fi
680
+ ```
681
+
682
+ **Step 4: Run existing pipeline tests**
683
+
684
+ Run: `bash scripts/sw-pipeline-test.sh`
685
+ Expected: All existing tests pass (audit is fail-open, won't break anything)
686
+
687
+ **Step 5: Commit**
688
+
689
+ ```bash
690
+ git add scripts/sw-pipeline.sh
691
+ git commit -m "feat(audit): pipeline-level audit init, stage events, finalize
692
+
693
+ Calls audit_init at pipeline start, emits stage.start/complete
694
+ for each stage, and audit_finalize at pipeline end to generate
695
+ pipeline-audit.json + pipeline-audit.md reports."
696
+ ```
697
+
698
+ ---
699
+
700
+ ### Task 5: Integration Verification
701
+
702
+ **Files:**
703
+ - No new files — verification only
704
+
705
+ **Step 1: Run all audit tests**
706
+
707
+ Run: `bash scripts/sw-lib-audit-trail-test.sh`
708
+ Expected: All pass
709
+
710
+ **Step 2: Run loop tests**
711
+
712
+ Run: `bash scripts/sw-loop-test.sh`
713
+ Expected: 56/61 pass (5 pre-existing)
714
+
715
+ **Step 3: Run detection tests**
716
+
717
+ Run: `bash scripts/sw-lib-pipeline-detection-test.sh`
718
+ Expected: 70/70 pass
719
+
720
+ **Step 4: Run full npm test suite**
721
+
722
+ Run: `npm test`
723
+ Expected: 191+ tests pass
724
+
725
+ **Step 5: Run patrol meta-check (bash compat)**
726
+
727
+ Run: `bash scripts/sw-patrol-meta.sh scripts/lib/audit-trail.sh`
728
+ Expected: No `readarray`/`mapfile` violations
729
+
730
+ **Step 6: Commit version bump**
731
+
732
+ ```bash
733
+ # Bump sw-loop.sh VERSION to 3.4.0
734
+ sed -i '' 's/VERSION="3.3.0"/VERSION="3.4.0"/' scripts/sw-loop.sh
735
+ git add scripts/sw-loop.sh
736
+ git commit -m "chore: bump sw-loop version to 3.4.0
737
+
738
+ Audit trail feature complete:
739
+ - pipeline-audit.jsonl (crash-safe event log)
740
+ - pipeline-audit.json (machine-readable summary)
741
+ - pipeline-audit.md (compliance report)
742
+ - iteration-N.prompt.txt (full prompt preservation)"
743
+ ```
744
+
745
+ **Step 7: Push**
746
+
747
+ ```bash
748
+ git push origin main
749
+ ```
750
+
751
+ ---
752
+
753
+ ## File Summary
754
+
755
+ | File | Action | Purpose |
756
+ |---|---|---|
757
+ | `scripts/lib/audit-trail.sh` | CREATE | Library: audit_init, audit_emit, audit_save_prompt, audit_finalize |
758
+ | `scripts/sw-lib-audit-trail-test.sh` | CREATE | Tests for all audit functions |
759
+ | `scripts/lib/loop-iteration.sh` | MODIFY | Save prompts, emit loop.prompt/loop.response events |
760
+ | `scripts/sw-loop.sh` | MODIFY | Source library, emit test_gate/verification_gap events |
761
+ | `scripts/sw-pipeline.sh` | MODIFY | Source library, audit_init, stage events, audit_finalize |
762
+
763
+ ## Verification Checklist
764
+
765
+ - [ ] `bash scripts/sw-lib-audit-trail-test.sh` — all pass
766
+ - [ ] `bash scripts/sw-loop-test.sh` — 56/61 (5 pre-existing)
767
+ - [ ] `bash scripts/sw-lib-pipeline-detection-test.sh` — 70/70
768
+ - [ ] `npm test` — 191+ pass
769
+ - [ ] `bash scripts/sw-patrol-meta.sh scripts/lib/audit-trail.sh` — no bash compat violations
770
+ - [ ] Run real pipeline — verify `pipeline-audit.jsonl`, `.json`, `.md` generated