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,921 @@
|
|
|
1
|
+
# Compound Audit Cascade — Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** Replace the one-shot compound_quality stage with an adaptive multi-agent cascade that iteratively probes for bugs until confidence is high.
|
|
6
|
+
|
|
7
|
+
**Architecture:** New library `compound-audit.sh` with four functions (run_cycle, dedup, escalate, converged). Integrates into existing `stage_compound_quality()` in `pipeline-intelligence.sh`. Agents run as parallel `claude -p --model haiku` calls. Dedup uses structural matching + haiku LLM judge. Convergence stops on no new critical/high OR >98% dup rate OR max_cycles.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Bash 3.2, `claude -p`, `jq`, audit-trail.sh JSONL events
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
### Task 1: Compound Audit Library — Core Scaffolding + Tests
|
|
14
|
+
|
|
15
|
+
**Files:**
|
|
16
|
+
- Create: `scripts/lib/compound-audit.sh`
|
|
17
|
+
- Create: `scripts/sw-lib-compound-audit-test.sh`
|
|
18
|
+
|
|
19
|
+
**Step 1: Write the test file scaffold**
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
#!/usr/bin/env bash
|
|
23
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
24
|
+
# ║ compound-audit test suite ║
|
|
25
|
+
# ║ Tests adaptive cascade audit: agents, dedup, escalation, convergence ║
|
|
26
|
+
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
27
|
+
set -euo pipefail
|
|
28
|
+
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
29
|
+
|
|
30
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
31
|
+
source "$SCRIPT_DIR/lib/test-helpers.sh"
|
|
32
|
+
|
|
33
|
+
print_test_header "Lib: compound-audit Tests"
|
|
34
|
+
|
|
35
|
+
setup_test_env "sw-lib-compound-audit-test"
|
|
36
|
+
trap cleanup_test_env EXIT
|
|
37
|
+
|
|
38
|
+
mock_claude
|
|
39
|
+
|
|
40
|
+
# Source dependencies
|
|
41
|
+
export ARTIFACTS_DIR="$TEST_TEMP_DIR/artifacts"
|
|
42
|
+
mkdir -p "$ARTIFACTS_DIR"
|
|
43
|
+
export LOG_DIR="$TEST_TEMP_DIR/logs"
|
|
44
|
+
mkdir -p "$LOG_DIR"
|
|
45
|
+
|
|
46
|
+
_AUDIT_TRAIL_LOADED=""
|
|
47
|
+
source "$SCRIPT_DIR/lib/audit-trail.sh"
|
|
48
|
+
audit_init --issue 99 --goal "Test compound audit"
|
|
49
|
+
|
|
50
|
+
_COMPOUND_AUDIT_LOADED=""
|
|
51
|
+
source "$SCRIPT_DIR/lib/compound-audit.sh"
|
|
52
|
+
|
|
53
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
54
|
+
# compound_audit_build_prompt
|
|
55
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
56
|
+
print_test_section "compound_audit_build_prompt"
|
|
57
|
+
|
|
58
|
+
# Test: logic auditor prompt contains specialization
|
|
59
|
+
result=$(compound_audit_build_prompt "logic" "diff --git a/foo.sh" "plan summary" "[]")
|
|
60
|
+
assert_contains "Logic auditor prompt mentions bugs" "$result" "logic error"
|
|
61
|
+
|
|
62
|
+
# Test: integration auditor prompt contains specialization
|
|
63
|
+
result=$(compound_audit_build_prompt "integration" "diff --git a/foo.sh" "plan summary" "[]")
|
|
64
|
+
assert_contains "Integration auditor prompt mentions wiring" "$result" "wiring"
|
|
65
|
+
|
|
66
|
+
# Test: completeness auditor prompt mentions spec
|
|
67
|
+
result=$(compound_audit_build_prompt "completeness" "diff --git a/foo.sh" "plan summary" "[]")
|
|
68
|
+
assert_contains "Completeness auditor prompt mentions spec" "$result" "spec"
|
|
69
|
+
|
|
70
|
+
# Test: prompt includes previous findings when provided
|
|
71
|
+
result=$(compound_audit_build_prompt "logic" "diff" "plan" '[{"description":"known bug"}]')
|
|
72
|
+
assert_contains "Prompt includes previous findings" "$result" "known bug"
|
|
73
|
+
|
|
74
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
75
|
+
# compound_audit_parse_findings
|
|
76
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
77
|
+
print_test_section "compound_audit_parse_findings"
|
|
78
|
+
|
|
79
|
+
# Test: parses valid JSON findings
|
|
80
|
+
agent_output='{"findings":[{"severity":"high","category":"logic","file":"foo.sh","line":10,"description":"Off-by-one","evidence":"i < n","suggestion":"Use i <= n"}]}'
|
|
81
|
+
result=$(compound_audit_parse_findings "$agent_output")
|
|
82
|
+
count=$(echo "$result" | jq 'length')
|
|
83
|
+
assert_eq "Parses one finding" "1" "$count"
|
|
84
|
+
|
|
85
|
+
# Test: extracts severity correctly
|
|
86
|
+
sev=$(echo "$result" | jq -r '.[0].severity')
|
|
87
|
+
assert_eq "Severity is high" "high" "$sev"
|
|
88
|
+
|
|
89
|
+
# Test: handles empty findings array
|
|
90
|
+
result=$(compound_audit_parse_findings '{"findings":[]}')
|
|
91
|
+
count=$(echo "$result" | jq 'length')
|
|
92
|
+
assert_eq "Empty findings returns empty array" "0" "$count"
|
|
93
|
+
|
|
94
|
+
# Test: handles malformed output gracefully
|
|
95
|
+
result=$(compound_audit_parse_findings "This is not JSON at all")
|
|
96
|
+
count=$(echo "$result" | jq 'length' 2>/dev/null || echo "0")
|
|
97
|
+
assert_eq "Malformed output returns empty" "0" "$count"
|
|
98
|
+
|
|
99
|
+
# Test: handles output with markdown wrapping
|
|
100
|
+
result=$(compound_audit_parse_findings '```json
|
|
101
|
+
{"findings":[{"severity":"low","category":"completeness","file":"bar.sh","line":5,"description":"Missing test","evidence":"no test file","suggestion":"Add test"}]}
|
|
102
|
+
```')
|
|
103
|
+
count=$(echo "$result" | jq 'length')
|
|
104
|
+
assert_eq "Markdown-wrapped JSON parsed" "1" "$count"
|
|
105
|
+
|
|
106
|
+
print_summary
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Step 2: Write the library scaffold with prompt builder and parser**
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
#!/usr/bin/env bash
|
|
113
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
114
|
+
# ║ compound-audit — Adaptive multi-agent audit cascade ║
|
|
115
|
+
# ║ ║
|
|
116
|
+
# ║ Runs specialized audit agents in parallel, deduplicates findings, ║
|
|
117
|
+
# ║ escalates to specialists when needed, and converges when confidence ║
|
|
118
|
+
# ║ is high. All functions fail-open with || return 0. ║
|
|
119
|
+
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
120
|
+
|
|
121
|
+
[[ -n "${_COMPOUND_AUDIT_LOADED:-}" ]] && return 0
|
|
122
|
+
_COMPOUND_AUDIT_LOADED=1
|
|
123
|
+
|
|
124
|
+
# ─── Agent prompt templates ────────────────────────────────────────────────
|
|
125
|
+
# Each agent gets the same context but a specialized lens.
|
|
126
|
+
|
|
127
|
+
_COMPOUND_AGENT_PROMPTS_logic="You are a Logic Auditor. Focus ONLY on:
|
|
128
|
+
- Control flow bugs, off-by-one errors, wrong conditions
|
|
129
|
+
- Algorithm errors, incorrect logic, null/undefined paths
|
|
130
|
+
- Race conditions, state management bugs
|
|
131
|
+
- Edge cases in arithmetic or string operations
|
|
132
|
+
Do NOT report style issues, missing features, or integration problems."
|
|
133
|
+
|
|
134
|
+
_COMPOUND_AGENT_PROMPTS_integration="You are an Integration Auditor. Focus ONLY on:
|
|
135
|
+
- Missing imports, broken call chains, unconnected components
|
|
136
|
+
- Mismatched interfaces between modules
|
|
137
|
+
- Functions called with wrong arguments or missing arguments
|
|
138
|
+
- Wiring gaps where new code isn't connected to existing code
|
|
139
|
+
Do NOT report logic bugs, style issues, or missing features."
|
|
140
|
+
|
|
141
|
+
_COMPOUND_AGENT_PROMPTS_completeness="You are a Completeness Auditor. Focus ONLY on:
|
|
142
|
+
- Spec vs. implementation gaps (does the code do what the plan says?)
|
|
143
|
+
- Missing test coverage for new functionality
|
|
144
|
+
- TODO/FIXME/placeholder code left behind
|
|
145
|
+
- Partial implementations (feature started but not finished)
|
|
146
|
+
Do NOT report logic bugs, style issues, or integration problems."
|
|
147
|
+
|
|
148
|
+
_COMPOUND_AGENT_PROMPTS_security="You are a Security Auditor. Focus ONLY on:
|
|
149
|
+
- Command injection, path traversal, input validation gaps
|
|
150
|
+
- Credential/secret exposure in code or logs
|
|
151
|
+
- Authentication/authorization bypass paths
|
|
152
|
+
- OWASP top 10 vulnerability patterns
|
|
153
|
+
Do NOT report non-security issues."
|
|
154
|
+
|
|
155
|
+
_COMPOUND_AGENT_PROMPTS_error_handling="You are an Error Handling Auditor. Focus ONLY on:
|
|
156
|
+
- Silent error swallowing (empty catch blocks, ignored return codes)
|
|
157
|
+
- Missing error paths (what happens when X fails?)
|
|
158
|
+
- Inconsistent error handling patterns
|
|
159
|
+
- Unchecked return values from external commands
|
|
160
|
+
Do NOT report non-error-handling issues."
|
|
161
|
+
|
|
162
|
+
_COMPOUND_AGENT_PROMPTS_performance="You are a Performance Auditor. Focus ONLY on:
|
|
163
|
+
- O(n^2) or worse patterns in loops
|
|
164
|
+
- Unbounded memory allocation or file reads
|
|
165
|
+
- Missing pagination or streaming for large data
|
|
166
|
+
- Repeated expensive operations that could be cached
|
|
167
|
+
Do NOT report non-performance issues."
|
|
168
|
+
|
|
169
|
+
_COMPOUND_AGENT_PROMPTS_edge_case="You are an Edge Case Auditor. Focus ONLY on:
|
|
170
|
+
- Zero-length inputs, empty strings, empty arrays
|
|
171
|
+
- Maximum/minimum boundary values
|
|
172
|
+
- Unicode, special characters, newlines in data
|
|
173
|
+
- Concurrent access, timing-dependent behavior
|
|
174
|
+
Do NOT report non-edge-case issues."
|
|
175
|
+
|
|
176
|
+
# ─── compound_audit_build_prompt ───────────────────────────────────────────
|
|
177
|
+
# Builds the full prompt for a specific agent type.
|
|
178
|
+
#
|
|
179
|
+
# Usage: compound_audit_build_prompt "logic" "$diff" "$plan" "$prev_findings_json"
|
|
180
|
+
compound_audit_build_prompt() {
|
|
181
|
+
local agent_type="$1"
|
|
182
|
+
local diff="$2"
|
|
183
|
+
local plan_summary="$3"
|
|
184
|
+
local prev_findings="$4"
|
|
185
|
+
|
|
186
|
+
# Get agent-specific instructions
|
|
187
|
+
local varname="_COMPOUND_AGENT_PROMPTS_${agent_type}"
|
|
188
|
+
local specialization="${!varname:-"You are a code auditor. Review the changes for issues."}"
|
|
189
|
+
|
|
190
|
+
cat <<EOF
|
|
191
|
+
${specialization}
|
|
192
|
+
|
|
193
|
+
## Code Changes (cumulative diff)
|
|
194
|
+
\`\`\`
|
|
195
|
+
${diff}
|
|
196
|
+
\`\`\`
|
|
197
|
+
|
|
198
|
+
## Implementation Plan/Spec
|
|
199
|
+
${plan_summary}
|
|
200
|
+
|
|
201
|
+
## Previously Found Issues (do NOT repeat these)
|
|
202
|
+
${prev_findings}
|
|
203
|
+
|
|
204
|
+
## Output Format
|
|
205
|
+
Return ONLY valid JSON (no markdown, no explanation):
|
|
206
|
+
{"findings":[{"severity":"critical|high|medium|low","category":"${agent_type}","file":"path/to/file","line":0,"description":"One sentence","evidence":"The specific code","suggestion":"How to fix"}]}
|
|
207
|
+
|
|
208
|
+
If no issues found, return: {"findings":[]}
|
|
209
|
+
EOF
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
# ─── compound_audit_parse_findings ─────────────────────────────────────────
|
|
213
|
+
# Parses agent output into a findings array. Handles malformed output.
|
|
214
|
+
#
|
|
215
|
+
# Usage: compound_audit_parse_findings "$agent_stdout"
|
|
216
|
+
# Output: JSON array of findings (or empty array on failure)
|
|
217
|
+
compound_audit_parse_findings() {
|
|
218
|
+
local raw_output="$1"
|
|
219
|
+
|
|
220
|
+
# Strip markdown code fences if present
|
|
221
|
+
local cleaned
|
|
222
|
+
cleaned=$(echo "$raw_output" | sed 's/^```json//;s/^```//;s/```$//' | tr -d '\r')
|
|
223
|
+
|
|
224
|
+
# Try to extract findings array
|
|
225
|
+
local findings
|
|
226
|
+
findings=$(echo "$cleaned" | jq -r '.findings // []' 2>/dev/null) || findings="[]"
|
|
227
|
+
|
|
228
|
+
# Validate it's actually an array
|
|
229
|
+
if echo "$findings" | jq -e 'type == "array"' >/dev/null 2>&1; then
|
|
230
|
+
echo "$findings"
|
|
231
|
+
else
|
|
232
|
+
echo "[]"
|
|
233
|
+
fi
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Step 3: Run tests to verify they pass**
|
|
238
|
+
|
|
239
|
+
Run: `bash scripts/sw-lib-compound-audit-test.sh`
|
|
240
|
+
Expected: All tests pass (prompt builder returns correct content, parser handles valid/invalid/wrapped JSON)
|
|
241
|
+
|
|
242
|
+
**Step 4: Commit**
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
git add scripts/lib/compound-audit.sh scripts/sw-lib-compound-audit-test.sh
|
|
246
|
+
git commit -m "feat: compound audit library scaffold with prompt builder and parser"
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
### Task 2: Deduplication — Structural + LLM Judge
|
|
252
|
+
|
|
253
|
+
**Files:**
|
|
254
|
+
- Modify: `scripts/lib/compound-audit.sh`
|
|
255
|
+
- Modify: `scripts/sw-lib-compound-audit-test.sh`
|
|
256
|
+
|
|
257
|
+
**Step 1: Add dedup tests to test file**
|
|
258
|
+
|
|
259
|
+
Append to `scripts/sw-lib-compound-audit-test.sh` (before `print_summary`):
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
263
|
+
# compound_audit_dedup_structural
|
|
264
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
265
|
+
print_test_section "compound_audit_dedup_structural"
|
|
266
|
+
|
|
267
|
+
# Test: same file + category + nearby lines = duplicate
|
|
268
|
+
findings='[
|
|
269
|
+
{"severity":"high","category":"logic","file":"foo.sh","line":10,"description":"Bug A","evidence":"code","suggestion":"fix"},
|
|
270
|
+
{"severity":"high","category":"logic","file":"foo.sh","line":12,"description":"Bug B","evidence":"code","suggestion":"fix"},
|
|
271
|
+
{"severity":"medium","category":"integration","file":"bar.sh","line":50,"description":"Wiring gap","evidence":"code","suggestion":"fix"}
|
|
272
|
+
]'
|
|
273
|
+
result=$(compound_audit_dedup_structural "$findings")
|
|
274
|
+
count=$(echo "$result" | jq 'length')
|
|
275
|
+
assert_eq "Structural dedup merges nearby same-file same-category" "2" "$count"
|
|
276
|
+
|
|
277
|
+
# Test: different files are NOT deduped
|
|
278
|
+
findings='[
|
|
279
|
+
{"severity":"high","category":"logic","file":"foo.sh","line":10,"description":"Bug A","evidence":"code","suggestion":"fix"},
|
|
280
|
+
{"severity":"high","category":"logic","file":"bar.sh","line":10,"description":"Bug B","evidence":"code","suggestion":"fix"}
|
|
281
|
+
]'
|
|
282
|
+
result=$(compound_audit_dedup_structural "$findings")
|
|
283
|
+
count=$(echo "$result" | jq 'length')
|
|
284
|
+
assert_eq "Different files not deduped" "2" "$count"
|
|
285
|
+
|
|
286
|
+
# Test: different categories are NOT deduped
|
|
287
|
+
findings='[
|
|
288
|
+
{"severity":"high","category":"logic","file":"foo.sh","line":10,"description":"Bug A","evidence":"code","suggestion":"fix"},
|
|
289
|
+
{"severity":"high","category":"security","file":"foo.sh","line":10,"description":"Bug B","evidence":"code","suggestion":"fix"}
|
|
290
|
+
]'
|
|
291
|
+
result=$(compound_audit_dedup_structural "$findings")
|
|
292
|
+
count=$(echo "$result" | jq 'length')
|
|
293
|
+
assert_eq "Different categories not deduped" "2" "$count"
|
|
294
|
+
|
|
295
|
+
# Test: empty input returns empty
|
|
296
|
+
result=$(compound_audit_dedup_structural "[]")
|
|
297
|
+
count=$(echo "$result" | jq 'length')
|
|
298
|
+
assert_eq "Empty input returns empty" "0" "$count"
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**Step 2: Run tests to verify they fail**
|
|
302
|
+
|
|
303
|
+
Run: `bash scripts/sw-lib-compound-audit-test.sh`
|
|
304
|
+
Expected: FAIL — `compound_audit_dedup_structural: command not found`
|
|
305
|
+
|
|
306
|
+
**Step 3: Implement structural dedup**
|
|
307
|
+
|
|
308
|
+
Add to `scripts/lib/compound-audit.sh`:
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
# ─── compound_audit_dedup_structural ───────────────────────────────────────
|
|
312
|
+
# Tier 1 dedup: same file + same category + lines within 5 = duplicate.
|
|
313
|
+
# Keeps the first (highest severity) finding in each group.
|
|
314
|
+
#
|
|
315
|
+
# Usage: compound_audit_dedup_structural "$findings_json_array"
|
|
316
|
+
# Output: Deduplicated JSON array
|
|
317
|
+
compound_audit_dedup_structural() {
|
|
318
|
+
local findings="$1"
|
|
319
|
+
|
|
320
|
+
[[ -z "$findings" || "$findings" == "[]" ]] && { echo "[]"; return 0; }
|
|
321
|
+
|
|
322
|
+
# Use jq to group by file+category, then within each group merge findings
|
|
323
|
+
# whose lines are within 5 of each other
|
|
324
|
+
echo "$findings" | jq '
|
|
325
|
+
# Sort by severity priority (critical first) then by line
|
|
326
|
+
def sev_order: if . == "critical" then 0 elif . == "high" then 1
|
|
327
|
+
elif . == "medium" then 2 else 3 end;
|
|
328
|
+
|
|
329
|
+
sort_by([(.severity | sev_order), .line]) |
|
|
330
|
+
|
|
331
|
+
# Group by file + category
|
|
332
|
+
group_by([.file, .category]) |
|
|
333
|
+
|
|
334
|
+
# Within each group, merge findings with lines within 5
|
|
335
|
+
map(
|
|
336
|
+
reduce .[] as $item ([];
|
|
337
|
+
if length == 0 then [$item]
|
|
338
|
+
elif (. | last | .line) and $item.line and
|
|
339
|
+
(($item.line - (. | last | .line)) | fabs) <= 5
|
|
340
|
+
then . # Skip duplicate (nearby line, same file+category)
|
|
341
|
+
else . + [$item]
|
|
342
|
+
end
|
|
343
|
+
)
|
|
344
|
+
) | flatten
|
|
345
|
+
' 2>/dev/null || echo "$findings"
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Step 4: Run tests to verify they pass**
|
|
350
|
+
|
|
351
|
+
Run: `bash scripts/sw-lib-compound-audit-test.sh`
|
|
352
|
+
Expected: All structural dedup tests pass
|
|
353
|
+
|
|
354
|
+
**Step 5: Commit**
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
git add scripts/lib/compound-audit.sh scripts/sw-lib-compound-audit-test.sh
|
|
358
|
+
git commit -m "feat: structural dedup for compound audit findings"
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
### Task 3: Escalation Logic
|
|
364
|
+
|
|
365
|
+
**Files:**
|
|
366
|
+
- Modify: `scripts/lib/compound-audit.sh`
|
|
367
|
+
- Modify: `scripts/sw-lib-compound-audit-test.sh`
|
|
368
|
+
|
|
369
|
+
**Step 1: Add escalation tests**
|
|
370
|
+
|
|
371
|
+
Append to test file (before `print_summary`):
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
375
|
+
# compound_audit_escalate
|
|
376
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
377
|
+
print_test_section "compound_audit_escalate"
|
|
378
|
+
|
|
379
|
+
# Test: security keyword triggers security specialist
|
|
380
|
+
findings='[{"severity":"high","category":"logic","file":"auth.sh","line":10,"description":"Missing input validation allows injection","evidence":"eval $input","suggestion":"Sanitize"}]'
|
|
381
|
+
result=$(compound_audit_escalate "$findings")
|
|
382
|
+
assert_contains "Injection triggers security" "$result" "security"
|
|
383
|
+
|
|
384
|
+
# Test: error handling keyword triggers error_handling specialist
|
|
385
|
+
findings='[{"severity":"high","category":"integration","file":"foo.sh","line":10,"description":"Empty catch block silently swallows errors","evidence":".catch(() => {})","suggestion":"Log error"}]'
|
|
386
|
+
result=$(compound_audit_escalate "$findings")
|
|
387
|
+
assert_contains "Silent catch triggers error_handling" "$result" "error_handling"
|
|
388
|
+
|
|
389
|
+
# Test: no trigger keywords returns empty
|
|
390
|
+
findings='[{"severity":"low","category":"completeness","file":"readme.md","line":1,"description":"Missing section in docs","evidence":"no API section","suggestion":"Add it"}]'
|
|
391
|
+
result=$(compound_audit_escalate "$findings")
|
|
392
|
+
assert_eq "No triggers returns empty" "" "$result"
|
|
393
|
+
|
|
394
|
+
# Test: multiple triggers return unique list
|
|
395
|
+
findings='[
|
|
396
|
+
{"severity":"high","category":"logic","file":"auth.sh","line":10,"description":"SQL injection in query","evidence":"code","suggestion":"fix"},
|
|
397
|
+
{"severity":"high","category":"logic","file":"api.sh","line":20,"description":"Missing auth check allows bypass","evidence":"code","suggestion":"fix"}
|
|
398
|
+
]'
|
|
399
|
+
result=$(compound_audit_escalate "$findings")
|
|
400
|
+
# Should contain security but only once
|
|
401
|
+
count=$(echo "$result" | tr ' ' '\n' | grep -c "security" || true)
|
|
402
|
+
assert_eq "Security appears once even with multiple triggers" "1" "$count"
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**Step 2: Run tests to verify they fail**
|
|
406
|
+
|
|
407
|
+
Run: `bash scripts/sw-lib-compound-audit-test.sh`
|
|
408
|
+
Expected: FAIL — `compound_audit_escalate: command not found`
|
|
409
|
+
|
|
410
|
+
**Step 3: Implement escalation**
|
|
411
|
+
|
|
412
|
+
Add to `scripts/lib/compound-audit.sh`:
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
# ─── Escalation trigger keywords ──────────────────────────────────────────
|
|
416
|
+
_COMPOUND_TRIGGERS_security="injection|auth|secret|credential|permission|bypass|xss|csrf|traversal|sanitiz"
|
|
417
|
+
_COMPOUND_TRIGGERS_error_handling="catch|swallow|silent|ignore.*error|missing.*error|unchecked|unhandled"
|
|
418
|
+
_COMPOUND_TRIGGERS_performance="O(n|loop.*loop|unbounded|pagination|cache|memory.*leak|quadratic"
|
|
419
|
+
_COMPOUND_TRIGGERS_edge_case="boundary|empty.*input|null.*check|zero.*length|unicode|concurrent|race"
|
|
420
|
+
|
|
421
|
+
# ─── compound_audit_escalate ──────────────────────────────────────────────
|
|
422
|
+
# Scans findings for trigger keywords, returns space-separated specialist list.
|
|
423
|
+
#
|
|
424
|
+
# Usage: compound_audit_escalate "$findings_json_array"
|
|
425
|
+
# Output: Space-separated specialist names (e.g., "security error_handling")
|
|
426
|
+
compound_audit_escalate() {
|
|
427
|
+
local findings="$1"
|
|
428
|
+
|
|
429
|
+
[[ -z "$findings" || "$findings" == "[]" ]] && return 0
|
|
430
|
+
|
|
431
|
+
# Flatten all finding text for keyword scanning
|
|
432
|
+
local all_text
|
|
433
|
+
all_text=$(echo "$findings" | jq -r '.[].description + " " + .[].evidence + " " + .[].file' 2>/dev/null | tr '[:upper:]' '[:lower:]') || return 0
|
|
434
|
+
|
|
435
|
+
local specialists=""
|
|
436
|
+
local spec
|
|
437
|
+
for spec in security error_handling performance edge_case; do
|
|
438
|
+
local varname="_COMPOUND_TRIGGERS_${spec}"
|
|
439
|
+
local pattern="${!varname:-}"
|
|
440
|
+
if [[ -n "$pattern" ]] && echo "$all_text" | grep -qEi "$pattern" 2>/dev/null; then
|
|
441
|
+
specialists="${specialists:+${specialists} }${spec}"
|
|
442
|
+
fi
|
|
443
|
+
done
|
|
444
|
+
|
|
445
|
+
echo "$specialists"
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
**Step 4: Run tests to verify they pass**
|
|
450
|
+
|
|
451
|
+
Run: `bash scripts/sw-lib-compound-audit-test.sh`
|
|
452
|
+
Expected: All escalation tests pass
|
|
453
|
+
|
|
454
|
+
**Step 5: Commit**
|
|
455
|
+
|
|
456
|
+
```bash
|
|
457
|
+
git add scripts/lib/compound-audit.sh scripts/sw-lib-compound-audit-test.sh
|
|
458
|
+
git commit -m "feat: trigger-based escalation for compound audit"
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
### Task 4: Convergence Detection
|
|
464
|
+
|
|
465
|
+
**Files:**
|
|
466
|
+
- Modify: `scripts/lib/compound-audit.sh`
|
|
467
|
+
- Modify: `scripts/sw-lib-compound-audit-test.sh`
|
|
468
|
+
|
|
469
|
+
**Step 1: Add convergence tests**
|
|
470
|
+
|
|
471
|
+
Append to test file (before `print_summary`):
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
475
|
+
# compound_audit_converged
|
|
476
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
477
|
+
print_test_section "compound_audit_converged"
|
|
478
|
+
|
|
479
|
+
# Test: no critical/high findings = converged
|
|
480
|
+
result=$(compound_audit_converged "[]" "[]" 1 3)
|
|
481
|
+
assert_eq "No findings = converged" "no_criticals" "$result"
|
|
482
|
+
|
|
483
|
+
# Test: only low/medium findings = converged
|
|
484
|
+
new='[{"severity":"low","category":"logic","file":"foo.sh","line":1,"description":"Minor","evidence":"x","suggestion":"y"}]'
|
|
485
|
+
result=$(compound_audit_converged "$new" "[]" 1 3)
|
|
486
|
+
assert_eq "Only low findings = converged" "no_criticals" "$result"
|
|
487
|
+
|
|
488
|
+
# Test: critical finding = NOT converged
|
|
489
|
+
new='[{"severity":"critical","category":"logic","file":"foo.sh","line":1,"description":"Major bug","evidence":"x","suggestion":"y"}]'
|
|
490
|
+
result=$(compound_audit_converged "$new" "[]" 1 3)
|
|
491
|
+
assert_eq "Critical finding = not converged" "" "$result"
|
|
492
|
+
|
|
493
|
+
# Test: max cycles reached = converged regardless
|
|
494
|
+
new='[{"severity":"critical","category":"logic","file":"foo.sh","line":1,"description":"Major bug","evidence":"x","suggestion":"y"}]'
|
|
495
|
+
result=$(compound_audit_converged "$new" "[]" 3 3)
|
|
496
|
+
assert_eq "Max cycles = converged" "max_cycles" "$result"
|
|
497
|
+
|
|
498
|
+
# Test: all findings are duplicates of previous = converged (dup rate)
|
|
499
|
+
prev='[{"severity":"high","category":"logic","file":"foo.sh","line":10,"description":"Bug","evidence":"x","suggestion":"y"}]'
|
|
500
|
+
new='[{"severity":"high","category":"logic","file":"foo.sh","line":11,"description":"Same bug","evidence":"x","suggestion":"y"}]'
|
|
501
|
+
result=$(compound_audit_converged "$new" "$prev" 1 3)
|
|
502
|
+
assert_eq "All dupes = converged" "dup_rate" "$result"
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
**Step 2: Run tests to verify they fail**
|
|
506
|
+
|
|
507
|
+
Run: `bash scripts/sw-lib-compound-audit-test.sh`
|
|
508
|
+
Expected: FAIL — `compound_audit_converged: command not found`
|
|
509
|
+
|
|
510
|
+
**Step 3: Implement convergence**
|
|
511
|
+
|
|
512
|
+
Add to `scripts/lib/compound-audit.sh`:
|
|
513
|
+
|
|
514
|
+
```bash
|
|
515
|
+
# ─── compound_audit_converged ─────────────────────────────────────────────
|
|
516
|
+
# Checks stop conditions for the cascade loop.
|
|
517
|
+
#
|
|
518
|
+
# Usage: compound_audit_converged "$new_findings" "$all_prev_findings" $cycle $max_cycles
|
|
519
|
+
# Output: Reason string if converged ("no_criticals", "dup_rate", "max_cycles"), empty if not
|
|
520
|
+
compound_audit_converged() {
|
|
521
|
+
local new_findings="$1"
|
|
522
|
+
local prev_findings="$2"
|
|
523
|
+
local cycle="$3"
|
|
524
|
+
local max_cycles="$4"
|
|
525
|
+
|
|
526
|
+
# Hard cap: max cycles reached
|
|
527
|
+
if [[ "$cycle" -ge "$max_cycles" ]]; then
|
|
528
|
+
echo "max_cycles"
|
|
529
|
+
return 0
|
|
530
|
+
fi
|
|
531
|
+
|
|
532
|
+
# No findings at all = converged
|
|
533
|
+
local new_count
|
|
534
|
+
new_count=$(echo "$new_findings" | jq 'length' 2>/dev/null || echo "0")
|
|
535
|
+
if [[ "$new_count" -eq 0 ]]; then
|
|
536
|
+
echo "no_criticals"
|
|
537
|
+
return 0
|
|
538
|
+
fi
|
|
539
|
+
|
|
540
|
+
# Check for critical/high in new findings
|
|
541
|
+
local crit_high_count
|
|
542
|
+
crit_high_count=$(echo "$new_findings" | jq '[.[] | select(.severity == "critical" or .severity == "high")] | length' 2>/dev/null || echo "0")
|
|
543
|
+
|
|
544
|
+
# If previous findings exist, check duplicate rate via structural match
|
|
545
|
+
local prev_count
|
|
546
|
+
prev_count=$(echo "$prev_findings" | jq 'length' 2>/dev/null || echo "0")
|
|
547
|
+
if [[ "$prev_count" -gt 0 && "$new_count" -gt 0 ]]; then
|
|
548
|
+
# Count how many new findings structurally match previous ones
|
|
549
|
+
local dup_count=0
|
|
550
|
+
local i=0
|
|
551
|
+
while [[ "$i" -lt "$new_count" ]]; do
|
|
552
|
+
local nf nc nl
|
|
553
|
+
nf=$(echo "$new_findings" | jq -r ".[$i].file // \"\"" 2>/dev/null)
|
|
554
|
+
nc=$(echo "$new_findings" | jq -r ".[$i].category // \"\"" 2>/dev/null)
|
|
555
|
+
nl=$(echo "$new_findings" | jq -r ".[$i].line // 0" 2>/dev/null)
|
|
556
|
+
|
|
557
|
+
# Check if any previous finding matches file+category+nearby line
|
|
558
|
+
local match
|
|
559
|
+
match=$(echo "$prev_findings" | jq --arg f "$nf" --arg c "$nc" --argjson l "$nl" \
|
|
560
|
+
'[.[] | select(.file == $f and .category == $c and ((.line // 0) - $l | fabs) <= 5)] | length' 2>/dev/null || echo "0")
|
|
561
|
+
[[ "$match" -gt 0 ]] && dup_count=$((dup_count + 1))
|
|
562
|
+
i=$((i + 1))
|
|
563
|
+
done
|
|
564
|
+
|
|
565
|
+
# If all findings are duplicates, converged
|
|
566
|
+
if [[ "$dup_count" -eq "$new_count" ]]; then
|
|
567
|
+
echo "dup_rate"
|
|
568
|
+
return 0
|
|
569
|
+
fi
|
|
570
|
+
fi
|
|
571
|
+
|
|
572
|
+
# No critical/high = converged
|
|
573
|
+
if [[ "$crit_high_count" -eq 0 ]]; then
|
|
574
|
+
echo "no_criticals"
|
|
575
|
+
return 0
|
|
576
|
+
fi
|
|
577
|
+
|
|
578
|
+
# Not converged
|
|
579
|
+
echo ""
|
|
580
|
+
return 0
|
|
581
|
+
}
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
**Step 4: Run tests to verify they pass**
|
|
585
|
+
|
|
586
|
+
Run: `bash scripts/sw-lib-compound-audit-test.sh`
|
|
587
|
+
Expected: All convergence tests pass
|
|
588
|
+
|
|
589
|
+
**Step 5: Commit**
|
|
590
|
+
|
|
591
|
+
```bash
|
|
592
|
+
git add scripts/lib/compound-audit.sh scripts/sw-lib-compound-audit-test.sh
|
|
593
|
+
git commit -m "feat: convergence detection for compound audit cascade"
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
---
|
|
597
|
+
|
|
598
|
+
### Task 5: Run Cycle — Parallel Agent Execution
|
|
599
|
+
|
|
600
|
+
**Files:**
|
|
601
|
+
- Modify: `scripts/lib/compound-audit.sh`
|
|
602
|
+
- Modify: `scripts/sw-lib-compound-audit-test.sh`
|
|
603
|
+
|
|
604
|
+
**Step 1: Add run_cycle tests**
|
|
605
|
+
|
|
606
|
+
Append to test file (before `print_summary`):
|
|
607
|
+
|
|
608
|
+
```bash
|
|
609
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
610
|
+
# compound_audit_run_cycle
|
|
611
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
612
|
+
print_test_section "compound_audit_run_cycle"
|
|
613
|
+
|
|
614
|
+
# Mock claude to return predictable findings
|
|
615
|
+
cat > "$TEST_TEMP_DIR/bin/claude" <<'MOCK'
|
|
616
|
+
#!/usr/bin/env bash
|
|
617
|
+
# Return findings based on prompt content
|
|
618
|
+
if echo "$*" | grep -q "Logic Auditor"; then
|
|
619
|
+
echo '{"findings":[{"severity":"high","category":"logic","file":"foo.sh","line":10,"description":"Off by one","evidence":"i < n","suggestion":"Use <="}]}'
|
|
620
|
+
elif echo "$*" | grep -q "Integration Auditor"; then
|
|
621
|
+
echo '{"findings":[]}'
|
|
622
|
+
elif echo "$*" | grep -q "Completeness Auditor"; then
|
|
623
|
+
echo '{"findings":[{"severity":"low","category":"completeness","file":"bar.sh","line":5,"description":"Missing test","evidence":"no test","suggestion":"Add test"}]}'
|
|
624
|
+
else
|
|
625
|
+
echo '{"findings":[]}'
|
|
626
|
+
fi
|
|
627
|
+
MOCK
|
|
628
|
+
chmod +x "$TEST_TEMP_DIR/bin/claude"
|
|
629
|
+
|
|
630
|
+
# Test: run_cycle with core agents returns merged findings
|
|
631
|
+
export COMPOUND_AUDIT_MODEL="haiku"
|
|
632
|
+
result=$(compound_audit_run_cycle "logic integration completeness" "diff content" "plan summary" "[]" 1)
|
|
633
|
+
count=$(echo "$result" | jq 'length')
|
|
634
|
+
assert_eq "Core cycle returns 2 findings" "2" "$count"
|
|
635
|
+
|
|
636
|
+
# Test: findings include correct categories
|
|
637
|
+
cats=$(echo "$result" | jq -r '.[].category' | sort | tr '\n' ' ' | sed 's/ $//')
|
|
638
|
+
assert_eq "Categories are logic and completeness" "completeness logic" "$cats"
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
**Step 2: Run tests to verify they fail**
|
|
642
|
+
|
|
643
|
+
Run: `bash scripts/sw-lib-compound-audit-test.sh`
|
|
644
|
+
Expected: FAIL — `compound_audit_run_cycle: command not found`
|
|
645
|
+
|
|
646
|
+
**Step 3: Implement run_cycle**
|
|
647
|
+
|
|
648
|
+
Add to `scripts/lib/compound-audit.sh`:
|
|
649
|
+
|
|
650
|
+
```bash
|
|
651
|
+
# ─── compound_audit_run_cycle ─────────────────────────────────────────────
|
|
652
|
+
# Runs multiple agents in parallel and collects their findings.
|
|
653
|
+
#
|
|
654
|
+
# Usage: compound_audit_run_cycle "logic integration completeness" "$diff" "$plan" "$prev_findings" $cycle
|
|
655
|
+
# Output: Merged JSON array of all findings
|
|
656
|
+
compound_audit_run_cycle() {
|
|
657
|
+
local agents="$1"
|
|
658
|
+
local diff="$2"
|
|
659
|
+
local plan_summary="$3"
|
|
660
|
+
local prev_findings="$4"
|
|
661
|
+
local cycle="$5"
|
|
662
|
+
|
|
663
|
+
local model="${COMPOUND_AUDIT_MODEL:-haiku}"
|
|
664
|
+
local temp_dir
|
|
665
|
+
temp_dir=$(mktemp -d) || return 0
|
|
666
|
+
|
|
667
|
+
# Emit cycle start event
|
|
668
|
+
type audit_emit >/dev/null 2>&1 && \
|
|
669
|
+
audit_emit "compound.cycle_start" "cycle=$cycle" "agents=$agents" || true
|
|
670
|
+
|
|
671
|
+
# Launch agents in parallel
|
|
672
|
+
local pids=()
|
|
673
|
+
local agent
|
|
674
|
+
for agent in $agents; do
|
|
675
|
+
local prompt
|
|
676
|
+
prompt=$(compound_audit_build_prompt "$agent" "$diff" "$plan_summary" "$prev_findings")
|
|
677
|
+
|
|
678
|
+
(
|
|
679
|
+
local output
|
|
680
|
+
output=$(echo "$prompt" | claude -p --model "$model" 2>/dev/null) || output='{"findings":[]}'
|
|
681
|
+
echo "$output" > "$temp_dir/${agent}.json"
|
|
682
|
+
) &
|
|
683
|
+
pids+=($!)
|
|
684
|
+
done
|
|
685
|
+
|
|
686
|
+
# Wait for all agents
|
|
687
|
+
local pid
|
|
688
|
+
for pid in "${pids[@]}"; do
|
|
689
|
+
wait "$pid" 2>/dev/null || true
|
|
690
|
+
done
|
|
691
|
+
|
|
692
|
+
# Merge findings from all agents
|
|
693
|
+
local all_findings="[]"
|
|
694
|
+
for agent in $agents; do
|
|
695
|
+
local agent_file="$temp_dir/${agent}.json"
|
|
696
|
+
if [[ -f "$agent_file" ]]; then
|
|
697
|
+
local agent_findings
|
|
698
|
+
agent_findings=$(compound_audit_parse_findings "$(cat "$agent_file")")
|
|
699
|
+
|
|
700
|
+
# Emit individual findings as audit events
|
|
701
|
+
local i=0
|
|
702
|
+
local fc
|
|
703
|
+
fc=$(echo "$agent_findings" | jq 'length' 2>/dev/null || echo "0")
|
|
704
|
+
while [[ "$i" -lt "$fc" ]]; do
|
|
705
|
+
local sev desc file line
|
|
706
|
+
sev=$(echo "$agent_findings" | jq -r ".[$i].severity" 2>/dev/null)
|
|
707
|
+
desc=$(echo "$agent_findings" | jq -r ".[$i].description" 2>/dev/null)
|
|
708
|
+
file=$(echo "$agent_findings" | jq -r ".[$i].file" 2>/dev/null)
|
|
709
|
+
line=$(echo "$agent_findings" | jq -r ".[$i].line" 2>/dev/null)
|
|
710
|
+
type audit_emit >/dev/null 2>&1 && \
|
|
711
|
+
audit_emit "compound.finding" "cycle=$cycle" "agent=$agent" \
|
|
712
|
+
"severity=$sev" "file=$file" "line=$line" "description=$desc" || true
|
|
713
|
+
i=$((i + 1))
|
|
714
|
+
done
|
|
715
|
+
|
|
716
|
+
# Merge into all_findings
|
|
717
|
+
all_findings=$(echo "$all_findings" "$agent_findings" | jq -s '.[0] + .[1]' 2>/dev/null || echo "$all_findings")
|
|
718
|
+
fi
|
|
719
|
+
done
|
|
720
|
+
|
|
721
|
+
# Cleanup
|
|
722
|
+
rm -rf "$temp_dir" 2>/dev/null || true
|
|
723
|
+
|
|
724
|
+
echo "$all_findings"
|
|
725
|
+
}
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
**Step 4: Run tests to verify they pass**
|
|
729
|
+
|
|
730
|
+
Run: `bash scripts/sw-lib-compound-audit-test.sh`
|
|
731
|
+
Expected: All run_cycle tests pass
|
|
732
|
+
|
|
733
|
+
**Step 5: Commit**
|
|
734
|
+
|
|
735
|
+
```bash
|
|
736
|
+
git add scripts/lib/compound-audit.sh scripts/sw-lib-compound-audit-test.sh
|
|
737
|
+
git commit -m "feat: parallel agent execution for compound audit cycles"
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
---
|
|
741
|
+
|
|
742
|
+
### Task 6: Pipeline Integration — Replace compound_quality Body
|
|
743
|
+
|
|
744
|
+
**Files:**
|
|
745
|
+
- Modify: `scripts/lib/pipeline-intelligence.sh` (~line 1310, inside the cycle loop)
|
|
746
|
+
- Modify: `scripts/lib/pipeline-intelligence.sh` (~line 1148, source compound-audit.sh)
|
|
747
|
+
|
|
748
|
+
**Step 1: Source compound-audit.sh in pipeline-intelligence.sh**
|
|
749
|
+
|
|
750
|
+
Find the imports section at the top of `pipeline-intelligence.sh` and add:
|
|
751
|
+
|
|
752
|
+
```bash
|
|
753
|
+
# Source compound audit cascade library
|
|
754
|
+
if [[ -f "$SCRIPT_DIR/lib/compound-audit.sh" ]]; then
|
|
755
|
+
source "$SCRIPT_DIR/lib/compound-audit.sh"
|
|
756
|
+
fi
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
**Step 2: Replace the cycle body (lines ~1310-1365)**
|
|
760
|
+
|
|
761
|
+
Inside `stage_compound_quality()`, after the hardened quality gates and before the summary, replace the existing cycle loop with the cascade:
|
|
762
|
+
|
|
763
|
+
```bash
|
|
764
|
+
# ── ADAPTIVE CASCADE AUDIT ──
|
|
765
|
+
local all_findings="[]"
|
|
766
|
+
local active_agents="logic integration completeness" # Core 3
|
|
767
|
+
|
|
768
|
+
# Gather context for agents
|
|
769
|
+
local _cascade_diff
|
|
770
|
+
_cascade_diff=$(git diff "${BASE_BRANCH:-main}...HEAD" 2>/dev/null | head -5000) || _cascade_diff=""
|
|
771
|
+
local _cascade_plan=""
|
|
772
|
+
if [[ -f "$ARTIFACTS_DIR/plan.md" ]]; then
|
|
773
|
+
_cascade_plan=$(head -200 "$ARTIFACTS_DIR/plan.md" 2>/dev/null) || true
|
|
774
|
+
fi
|
|
775
|
+
|
|
776
|
+
local cycle=0
|
|
777
|
+
while [[ "$cycle" -lt "$max_cycles" ]]; do
|
|
778
|
+
cycle=$((cycle + 1))
|
|
779
|
+
|
|
780
|
+
echo ""
|
|
781
|
+
echo -e "${PURPLE}${BOLD}━━━ Compound Audit — Cycle ${cycle}/${max_cycles} ━━━${RESET}"
|
|
782
|
+
info "Agents: $active_agents"
|
|
783
|
+
|
|
784
|
+
# Run agents in parallel
|
|
785
|
+
local cycle_findings
|
|
786
|
+
cycle_findings=$(compound_audit_run_cycle "$active_agents" "$_cascade_diff" "$_cascade_plan" "$all_findings" "$cycle") || cycle_findings="[]"
|
|
787
|
+
|
|
788
|
+
# Dedup within this cycle
|
|
789
|
+
cycle_findings=$(compound_audit_dedup_structural "$cycle_findings") || cycle_findings="[]"
|
|
790
|
+
|
|
791
|
+
local cycle_count
|
|
792
|
+
cycle_count=$(echo "$cycle_findings" | jq 'length' 2>/dev/null || echo "0")
|
|
793
|
+
local cycle_crit
|
|
794
|
+
cycle_crit=$(echo "$cycle_findings" | jq '[.[] | select(.severity == "critical" or .severity == "high")] | length' 2>/dev/null || echo "0")
|
|
795
|
+
|
|
796
|
+
# Report findings
|
|
797
|
+
if [[ "$cycle_count" -gt 0 ]]; then
|
|
798
|
+
warn "Cycle ${cycle}: ${cycle_count} findings (${cycle_crit} critical/high)"
|
|
799
|
+
# Count for pipeline scoring
|
|
800
|
+
total_critical=$((total_critical + $(echo "$cycle_findings" | jq '[.[] | select(.severity == "critical")] | length' 2>/dev/null || echo "0")))
|
|
801
|
+
total_major=$((total_major + $(echo "$cycle_findings" | jq '[.[] | select(.severity == "high")] | length' 2>/dev/null || echo "0")))
|
|
802
|
+
total_minor=$((total_minor + $(echo "$cycle_findings" | jq '[.[] | select(.severity == "medium" or .severity == "low")] | length' 2>/dev/null || echo "0")))
|
|
803
|
+
else
|
|
804
|
+
success "Cycle ${cycle}: no findings"
|
|
805
|
+
fi
|
|
806
|
+
|
|
807
|
+
# Check convergence
|
|
808
|
+
local converge_reason
|
|
809
|
+
converge_reason=$(compound_audit_converged "$cycle_findings" "$all_findings" "$cycle" "$max_cycles") || converge_reason=""
|
|
810
|
+
|
|
811
|
+
# Emit cycle complete event
|
|
812
|
+
type audit_emit >/dev/null 2>&1 && \
|
|
813
|
+
audit_emit "compound.cycle_complete" "cycle=$cycle" "findings=$cycle_count" \
|
|
814
|
+
"critical_high=$cycle_crit" "converged=$converge_reason" || true
|
|
815
|
+
|
|
816
|
+
if [[ -n "$converge_reason" ]]; then
|
|
817
|
+
success "Converged: $converge_reason"
|
|
818
|
+
type audit_emit >/dev/null 2>&1 && \
|
|
819
|
+
audit_emit "compound.converged" "reason=$converge_reason" "total_cycles=$cycle" || true
|
|
820
|
+
break
|
|
821
|
+
fi
|
|
822
|
+
|
|
823
|
+
# Merge findings for next cycle's context
|
|
824
|
+
all_findings=$(echo "$all_findings" "$cycle_findings" | jq -s '.[0] + .[1]' 2>/dev/null || echo "$all_findings")
|
|
825
|
+
|
|
826
|
+
# Escalation: trigger specialists for next cycle
|
|
827
|
+
if type compound_audit_escalate >/dev/null 2>&1; then
|
|
828
|
+
local specialists
|
|
829
|
+
specialists=$(compound_audit_escalate "$cycle_findings") || specialists=""
|
|
830
|
+
if [[ -n "$specialists" ]]; then
|
|
831
|
+
info "Escalating: adding $specialists"
|
|
832
|
+
active_agents="logic integration completeness $specialists"
|
|
833
|
+
fi
|
|
834
|
+
fi
|
|
835
|
+
done
|
|
836
|
+
|
|
837
|
+
# Save all findings to artifact
|
|
838
|
+
echo "$all_findings" > "$ARTIFACTS_DIR/compound-audit-findings.json" 2>/dev/null || true
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
**Step 3: Validate syntax**
|
|
842
|
+
|
|
843
|
+
Run: `bash -n scripts/lib/pipeline-intelligence.sh`
|
|
844
|
+
Expected: No errors
|
|
845
|
+
|
|
846
|
+
**Step 4: Run existing tests to verify no regression**
|
|
847
|
+
|
|
848
|
+
Run: `bash scripts/sw-lib-compound-audit-test.sh && bash scripts/sw-lib-audit-trail-test.sh`
|
|
849
|
+
Expected: Both pass (28/28 audit trail, all compound audit tests)
|
|
850
|
+
|
|
851
|
+
**Step 5: Commit**
|
|
852
|
+
|
|
853
|
+
```bash
|
|
854
|
+
git add scripts/lib/pipeline-intelligence.sh scripts/lib/compound-audit.sh
|
|
855
|
+
git commit -m "feat: integrate compound audit cascade into compound_quality stage"
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
---
|
|
859
|
+
|
|
860
|
+
### Task 7: Audit Trail Finalize — Include Compound Findings in Reports
|
|
861
|
+
|
|
862
|
+
**Files:**
|
|
863
|
+
- Modify: `scripts/lib/audit-trail.sh` (~line 188, `_audit_build_markdown`)
|
|
864
|
+
|
|
865
|
+
**Step 1: Add compound findings section to markdown report**
|
|
866
|
+
|
|
867
|
+
In `_audit_build_markdown()`, after the "Build Loop" section (line ~243), add:
|
|
868
|
+
|
|
869
|
+
```bash
|
|
870
|
+
# Compound audit findings section
|
|
871
|
+
local compound_events
|
|
872
|
+
compound_events=$(grep '"type":"compound.finding"' "$jsonl_file" 2>/dev/null || true)
|
|
873
|
+
if [[ -n "$compound_events" ]]; then
|
|
874
|
+
cat <<'EOF'
|
|
875
|
+
|
|
876
|
+
## Compound Audit Findings
|
|
877
|
+
|
|
878
|
+
EOF
|
|
879
|
+
echo "$compound_events" | while IFS= read -r line; do
|
|
880
|
+
local sev file desc
|
|
881
|
+
sev=$(echo "$line" | grep -o '"severity":"[^"]*' | cut -d'"' -f4)
|
|
882
|
+
file=$(echo "$line" | grep -o '"file":"[^"]*' | cut -d'"' -f4)
|
|
883
|
+
desc=$(echo "$line" | grep -o '"description":"[^"]*' | cut -d'"' -f4)
|
|
884
|
+
echo "- **[$sev]** \`$file\`: $desc"
|
|
885
|
+
done
|
|
886
|
+
|
|
887
|
+
# Convergence summary
|
|
888
|
+
local converge_line
|
|
889
|
+
converge_line=$(grep '"type":"compound.converged"' "$jsonl_file" 2>/dev/null | tail -1 || true)
|
|
890
|
+
if [[ -n "$converge_line" ]]; then
|
|
891
|
+
local reason cycles
|
|
892
|
+
reason=$(echo "$converge_line" | grep -o '"reason":"[^"]*' | cut -d'"' -f4)
|
|
893
|
+
cycles=$(echo "$converge_line" | grep -o '"total_cycles":"[^"]*' | cut -d'"' -f4)
|
|
894
|
+
echo ""
|
|
895
|
+
echo "**Converged** after ${cycles} cycle(s): ${reason}"
|
|
896
|
+
fi
|
|
897
|
+
fi
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
**Step 2: Run audit trail tests**
|
|
901
|
+
|
|
902
|
+
Run: `bash scripts/sw-lib-audit-trail-test.sh`
|
|
903
|
+
Expected: 28/28 pass (existing tests unaffected)
|
|
904
|
+
|
|
905
|
+
**Step 3: Commit**
|
|
906
|
+
|
|
907
|
+
```bash
|
|
908
|
+
git add scripts/lib/audit-trail.sh
|
|
909
|
+
git commit -m "feat: include compound audit findings in markdown report"
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
---
|
|
913
|
+
|
|
914
|
+
## Verification Checklist
|
|
915
|
+
|
|
916
|
+
1. **Unit tests**: `bash scripts/sw-lib-compound-audit-test.sh` — all tests pass
|
|
917
|
+
2. **Audit trail tests**: `bash scripts/sw-lib-audit-trail-test.sh` — 28/28 pass
|
|
918
|
+
3. **Loop tests**: `bash scripts/sw-loop-test.sh` — 56/61 (5 pre-existing)
|
|
919
|
+
4. **Detection tests**: `bash scripts/sw-lib-pipeline-detection-test.sh` — 70/70
|
|
920
|
+
5. **Syntax check**: `bash -n scripts/lib/compound-audit.sh && bash -n scripts/lib/pipeline-intelligence.sh`
|
|
921
|
+
6. **E2E**: Run pipeline with compound_quality enabled, verify `compound.cycle_start`, `compound.finding`, and `compound.converged` events in JSONL
|