shipwright-cli 3.1.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +22 -8
- 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/config/defaults.json +25 -2
- package/config/policy.json +1 -1
- package/dashboard/middleware/auth.ts +134 -0
- package/dashboard/middleware/constants.ts +21 -0
- package/dashboard/public/index.html +8 -6
- package/dashboard/public/styles.css +176 -97
- package/dashboard/routes/auth.ts +38 -0
- package/dashboard/server.ts +117 -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/api.ts +5 -0
- 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 +12 -1
- package/dashboard/src/views/activity.ts +2 -1
- package/dashboard/src/views/metrics.ts +69 -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 +14 -2
- package/scripts/lib/daemon-dispatch.sh +106 -17
- package/scripts/lib/daemon-failure.sh +34 -4
- package/scripts/lib/daemon-patrol.sh +25 -4
- 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 +119 -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 +180 -5
- 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 +101 -3
- 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 +104 -1138
- package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
- package/scripts/lib/pipeline-quality-checks.sh +17 -711
- 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 +161 -2901
- 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 -8
- package/scripts/sw-adaptive.sh +8 -7
- 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 +15 -6
- package/scripts/sw-cleanup.sh +4 -26
- package/scripts/sw-code-review.sh +45 -20
- package/scripts/sw-connect.sh +2 -1
- package/scripts/sw-context.sh +2 -1
- package/scripts/sw-cost.sh +107 -5
- package/scripts/sw-daemon.sh +71 -11
- package/scripts/sw-dashboard.sh +3 -1
- package/scripts/sw-db.sh +71 -20
- 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 +378 -5
- 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 +12 -7
- package/scripts/sw-e2e-orchestrator.sh +17 -16
- package/scripts/sw-eventbus.sh +13 -4
- 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 +9 -4
- package/scripts/sw-fleet.sh +5 -1
- package/scripts/sw-github-app.sh +18 -4
- 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 +10 -3
- package/scripts/sw-incident.sh +273 -5
- package/scripts/sw-init.sh +18 -2
- package/scripts/sw-instrument.sh +10 -2
- package/scripts/sw-intelligence.sh +44 -7
- 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 +436 -1076
- package/scripts/sw-memory.sh +357 -3
- package/scripts/sw-mission-control.sh +6 -1
- package/scripts/sw-model-router.sh +483 -27
- package/scripts/sw-otel.sh +15 -4
- package/scripts/sw-oversight.sh +14 -5
- package/scripts/sw-patrol-meta.sh +334 -0
- package/scripts/sw-pipeline-composer.sh +7 -1
- package/scripts/sw-pipeline-vitals.sh +12 -6
- package/scripts/sw-pipeline.sh +54 -2653
- package/scripts/sw-pm.sh +16 -8
- package/scripts/sw-pr-lifecycle.sh +2 -1
- package/scripts/sw-predictive.sh +17 -5
- package/scripts/sw-prep.sh +185 -2
- package/scripts/sw-ps.sh +5 -25
- package/scripts/sw-public-dashboard.sh +17 -4
- package/scripts/sw-quality.sh +14 -6
- 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 +14 -5
- package/scripts/sw-security-audit.sh +6 -1
- package/scripts/sw-self-optimize.sh +173 -6
- 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 +14 -6
- package/scripts/sw-stream.sh +13 -4
- package/scripts/sw-swarm.sh +20 -7
- package/scripts/sw-team-stages.sh +13 -6
- package/scripts/sw-templates.sh +7 -31
- package/scripts/sw-testgen.sh +17 -6
- 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 +37 -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 +3 -2
- package/scripts/sw-upgrade.sh +3 -1
- package/scripts/sw-ux.sh +5 -2
- package/scripts/sw-webhook.sh +5 -2
- package/scripts/sw-widgets.sh +9 -4
- 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,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
|