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
|
@@ -3,1142 +3,27 @@
|
|
|
3
3
|
[[ -n "${_PIPELINE_INTELLIGENCE_LOADED:-}" ]] && return 0
|
|
4
4
|
_PIPELINE_INTELLIGENCE_LOADED=1
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
case "$stage_id" in
|
|
28
|
-
test|review|compound_quality)
|
|
29
|
-
reason="label:documentation"
|
|
30
|
-
;;
|
|
31
|
-
esac
|
|
32
|
-
fi
|
|
33
|
-
|
|
34
|
-
# Hotfix issues: skip plan, design, compound_quality
|
|
35
|
-
if echo ",$labels," | grep -qiE ',hotfix,|,urgent,|,p0,'; then
|
|
36
|
-
case "$stage_id" in
|
|
37
|
-
plan|design|compound_quality)
|
|
38
|
-
reason="label:hotfix"
|
|
39
|
-
;;
|
|
40
|
-
esac
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
# ── Signal 3: Intelligence complexity ──
|
|
44
|
-
if [[ -z "$reason" && "$complexity" -gt 0 ]]; then
|
|
45
|
-
# Complexity 1-2: very simple → skip design, compound_quality, review
|
|
46
|
-
if [[ "$complexity" -le 2 ]]; then
|
|
47
|
-
case "$stage_id" in
|
|
48
|
-
design|compound_quality|review)
|
|
49
|
-
reason="complexity:${complexity}/10"
|
|
50
|
-
;;
|
|
51
|
-
esac
|
|
52
|
-
# Complexity 1-3: simple → skip design
|
|
53
|
-
elif [[ "$complexity" -le 3 ]]; then
|
|
54
|
-
case "$stage_id" in
|
|
55
|
-
design)
|
|
56
|
-
reason="complexity:${complexity}/10"
|
|
57
|
-
;;
|
|
58
|
-
esac
|
|
59
|
-
fi
|
|
60
|
-
fi
|
|
61
|
-
|
|
62
|
-
# ── Signal 4: Diff size (after build) ──
|
|
63
|
-
if [[ -z "$reason" && "$stage_id" == "compound_quality" ]]; then
|
|
64
|
-
local diff_lines=0
|
|
65
|
-
local _skip_stat
|
|
66
|
-
_skip_stat=$(git diff "${BASE_BRANCH:-main}...HEAD" --stat 2>/dev/null | tail -1) || true
|
|
67
|
-
if [[ -n "${_skip_stat:-}" ]]; then
|
|
68
|
-
local _s_ins _s_del
|
|
69
|
-
_s_ins=$(echo "$_skip_stat" | grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+') || true
|
|
70
|
-
_s_del=$(echo "$_skip_stat" | grep -oE '[0-9]+ deletion' | grep -oE '[0-9]+') || true
|
|
71
|
-
diff_lines=$(( ${_s_ins:-0} + ${_s_del:-0} ))
|
|
72
|
-
fi
|
|
73
|
-
diff_lines="${diff_lines:-0}"
|
|
74
|
-
if [[ "$diff_lines" -gt 0 && "$diff_lines" -lt 20 ]]; then
|
|
75
|
-
reason="diff_size:${diff_lines}_lines"
|
|
76
|
-
fi
|
|
77
|
-
fi
|
|
78
|
-
|
|
79
|
-
# ── Signal 5: Mid-pipeline reassessment override ──
|
|
80
|
-
if [[ -z "$reason" && -f "$ARTIFACTS_DIR/reassessment.json" ]]; then
|
|
81
|
-
local skip_stages
|
|
82
|
-
skip_stages=$(jq -r '.skip_stages // [] | .[]' "$ARTIFACTS_DIR/reassessment.json" 2>/dev/null || true)
|
|
83
|
-
if echo "$skip_stages" | grep -qx "$stage_id" 2>/dev/null; then
|
|
84
|
-
reason="reassessment:simpler_than_expected"
|
|
85
|
-
fi
|
|
86
|
-
fi
|
|
87
|
-
|
|
88
|
-
if [[ -n "$reason" ]]; then
|
|
89
|
-
emit_event "intelligence.stage_skipped" \
|
|
90
|
-
"issue=${ISSUE_NUMBER:-0}" \
|
|
91
|
-
"stage=$stage_id" \
|
|
92
|
-
"reason=$reason" \
|
|
93
|
-
"complexity=${complexity}" \
|
|
94
|
-
"labels=${labels}"
|
|
95
|
-
echo "$reason"
|
|
96
|
-
return 0
|
|
97
|
-
fi
|
|
98
|
-
|
|
99
|
-
return 1
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
103
|
-
# 2. Smart Finding Classification & Routing
|
|
104
|
-
# Parses compound quality findings and classifies each as:
|
|
105
|
-
# architecture, security, correctness, style
|
|
106
|
-
# Returns JSON with classified findings and routing recommendations.
|
|
107
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
108
|
-
classify_quality_findings() {
|
|
109
|
-
local findings_dir="${1:-$ARTIFACTS_DIR}"
|
|
110
|
-
local result_file="$findings_dir/classified-findings.json"
|
|
111
|
-
|
|
112
|
-
# Build combined content for semantic classification
|
|
113
|
-
local content=""
|
|
114
|
-
if [[ -f "$findings_dir/adversarial-review.md" ]]; then
|
|
115
|
-
content="${content}
|
|
116
|
-
--- adversarial-review.md ---
|
|
117
|
-
$(head -500 "$findings_dir/adversarial-review.md" 2>/dev/null)"
|
|
118
|
-
fi
|
|
119
|
-
if [[ -f "$findings_dir/negative-review.md" ]]; then
|
|
120
|
-
content="${content}
|
|
121
|
-
--- negative-review.md ---
|
|
122
|
-
$(head -300 "$findings_dir/negative-review.md" 2>/dev/null)"
|
|
123
|
-
fi
|
|
124
|
-
if [[ -f "$findings_dir/security-audit.log" ]]; then
|
|
125
|
-
content="${content}
|
|
126
|
-
--- security-audit.log ---
|
|
127
|
-
$(cat "$findings_dir/security-audit.log" 2>/dev/null)"
|
|
128
|
-
fi
|
|
129
|
-
if [[ -f "$findings_dir/compound-architecture-validation.json" ]]; then
|
|
130
|
-
content="${content}
|
|
131
|
-
--- compound-architecture-validation.json ---
|
|
132
|
-
$(jq -r '.[] | "\(.severity): \(.message // .description // .)"' "$findings_dir/compound-architecture-validation.json" 2>/dev/null | head -50)"
|
|
133
|
-
fi
|
|
134
|
-
|
|
135
|
-
# Try semantic classification first when Claude is available
|
|
136
|
-
local route=""
|
|
137
|
-
if command -v claude &>/dev/null && [[ "${INTELLIGENCE_ENABLED:-false}" != "false" ]] && [[ -n "$content" ]]; then
|
|
138
|
-
local prompt="Classify these code review findings into exactly ONE primary category. Return ONLY a single word: security, architecture, correctness, performance, testing, documentation, style.
|
|
139
|
-
|
|
140
|
-
Findings:
|
|
141
|
-
$content"
|
|
142
|
-
local category
|
|
143
|
-
category=$(echo "$prompt" | timeout 30 claude -p --model sonnet 2>/dev/null | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')
|
|
144
|
-
if [[ "$category" =~ ^(security|architecture|correctness|performance|testing|documentation|style)$ ]]; then
|
|
145
|
-
route="$category"
|
|
146
|
-
fi
|
|
147
|
-
fi
|
|
148
|
-
|
|
149
|
-
# Initialize counters
|
|
150
|
-
local arch_count=0 security_count=0 correctness_count=0 performance_count=0 testing_count=0 style_count=0
|
|
151
|
-
|
|
152
|
-
# Start building JSON array
|
|
153
|
-
local findings_json="[]"
|
|
154
|
-
|
|
155
|
-
# ── Parse adversarial review ──
|
|
156
|
-
if [[ -f "$findings_dir/adversarial-review.md" ]]; then
|
|
157
|
-
local adv_content
|
|
158
|
-
adv_content=$(cat "$findings_dir/adversarial-review.md" 2>/dev/null || true)
|
|
159
|
-
|
|
160
|
-
# Architecture findings: dependency violations, layer breaches, circular refs
|
|
161
|
-
local arch_findings
|
|
162
|
-
arch_findings=$(echo "$adv_content" | grep -ciE 'architect|layer.*violation|circular.*depend|coupling|abstraction|design.*flaw|separation.*concern' 2>/dev/null || true)
|
|
163
|
-
arch_count=$((arch_count + ${arch_findings:-0}))
|
|
164
|
-
|
|
165
|
-
# Security findings
|
|
166
|
-
local sec_findings
|
|
167
|
-
sec_findings=$(echo "$adv_content" | grep -ciE 'security|vulnerab|injection|XSS|CSRF|auth.*bypass|privilege|sanitiz|escap' 2>/dev/null || true)
|
|
168
|
-
security_count=$((security_count + ${sec_findings:-0}))
|
|
169
|
-
|
|
170
|
-
# Correctness findings: bugs, logic errors, edge cases
|
|
171
|
-
local corr_findings
|
|
172
|
-
corr_findings=$(echo "$adv_content" | grep -ciE '\*\*\[?(Critical|Bug|Error|critical|high)\]?\*\*|race.*condition|null.*pointer|off.*by.*one|edge.*case|undefined.*behav' 2>/dev/null || true)
|
|
173
|
-
correctness_count=$((correctness_count + ${corr_findings:-0}))
|
|
174
|
-
|
|
175
|
-
# Performance findings
|
|
176
|
-
local perf_findings
|
|
177
|
-
perf_findings=$(echo "$adv_content" | grep -ciE 'latency|slow|memory leak|O\(n|N\+1|cache miss|performance|bottleneck|throughput' 2>/dev/null || true)
|
|
178
|
-
performance_count=$((performance_count + ${perf_findings:-0}))
|
|
179
|
-
|
|
180
|
-
# Testing findings
|
|
181
|
-
local test_findings
|
|
182
|
-
test_findings=$(echo "$adv_content" | grep -ciE 'untested|missing test|no coverage|flaky|test gap|test missing|coverage gap' 2>/dev/null || true)
|
|
183
|
-
testing_count=$((testing_count + ${test_findings:-0}))
|
|
184
|
-
|
|
185
|
-
# Style findings
|
|
186
|
-
local style_findings
|
|
187
|
-
style_findings=$(echo "$adv_content" | grep -ciE 'naming|convention|format|style|readabil|inconsisten|whitespace|comment' 2>/dev/null || true)
|
|
188
|
-
style_count=$((style_count + ${style_findings:-0}))
|
|
189
|
-
fi
|
|
190
|
-
|
|
191
|
-
# ── Parse architecture validation ──
|
|
192
|
-
if [[ -f "$findings_dir/compound-architecture-validation.json" ]]; then
|
|
193
|
-
local arch_json_count
|
|
194
|
-
arch_json_count=$(jq '[.[] | select(.severity == "critical" or .severity == "high")] | length' "$findings_dir/compound-architecture-validation.json" 2>/dev/null || echo "0")
|
|
195
|
-
arch_count=$((arch_count + ${arch_json_count:-0}))
|
|
196
|
-
fi
|
|
197
|
-
|
|
198
|
-
# ── Parse security audit ──
|
|
199
|
-
if [[ -f "$findings_dir/security-audit.log" ]]; then
|
|
200
|
-
local sec_audit
|
|
201
|
-
sec_audit=$(grep -ciE 'critical|high' "$findings_dir/security-audit.log" 2>/dev/null || true)
|
|
202
|
-
security_count=$((security_count + ${sec_audit:-0}))
|
|
203
|
-
fi
|
|
204
|
-
|
|
205
|
-
# ── Parse negative review ──
|
|
206
|
-
if [[ -f "$findings_dir/negative-review.md" ]]; then
|
|
207
|
-
local neg_corr
|
|
208
|
-
neg_corr=$(grep -ciE '\[Critical\]|\[High\]' "$findings_dir/negative-review.md" 2>/dev/null || true)
|
|
209
|
-
correctness_count=$((correctness_count + ${neg_corr:-0}))
|
|
210
|
-
fi
|
|
211
|
-
|
|
212
|
-
# ── Determine routing ──
|
|
213
|
-
# Use semantic classification when available; else fall back to grep-derived priority
|
|
214
|
-
local needs_backtrack=false
|
|
215
|
-
local priority_findings=""
|
|
216
|
-
|
|
217
|
-
if [[ -z "$route" ]]; then
|
|
218
|
-
# Fallback: grep-based priority order: security > architecture > correctness > performance > testing > style
|
|
219
|
-
route="correctness"
|
|
220
|
-
|
|
221
|
-
if [[ "$security_count" -gt 0 ]]; then
|
|
222
|
-
route="security"
|
|
223
|
-
priority_findings="security:${security_count}"
|
|
224
|
-
fi
|
|
225
|
-
|
|
226
|
-
if [[ "$arch_count" -gt 0 ]]; then
|
|
227
|
-
if [[ "$route" == "correctness" ]]; then
|
|
228
|
-
route="architecture"
|
|
229
|
-
needs_backtrack=true
|
|
230
|
-
fi
|
|
231
|
-
priority_findings="${priority_findings:+${priority_findings},}architecture:${arch_count}"
|
|
232
|
-
fi
|
|
233
|
-
|
|
234
|
-
if [[ "$correctness_count" -gt 0 ]]; then
|
|
235
|
-
priority_findings="${priority_findings:+${priority_findings},}correctness:${correctness_count}"
|
|
236
|
-
fi
|
|
237
|
-
|
|
238
|
-
if [[ "$performance_count" -gt 0 ]]; then
|
|
239
|
-
if [[ "$route" == "correctness" && "$correctness_count" -eq 0 ]]; then
|
|
240
|
-
route="performance"
|
|
241
|
-
fi
|
|
242
|
-
priority_findings="${priority_findings:+${priority_findings},}performance:${performance_count}"
|
|
243
|
-
fi
|
|
244
|
-
|
|
245
|
-
if [[ "$testing_count" -gt 0 ]]; then
|
|
246
|
-
if [[ "$route" == "correctness" && "$correctness_count" -eq 0 && "$performance_count" -eq 0 ]]; then
|
|
247
|
-
route="testing"
|
|
248
|
-
fi
|
|
249
|
-
priority_findings="${priority_findings:+${priority_findings},}testing:${testing_count}"
|
|
250
|
-
fi
|
|
251
|
-
else
|
|
252
|
-
# Semantic route: build priority_findings from counts, set needs_backtrack for architecture
|
|
253
|
-
[[ "$route" == "architecture" ]] && needs_backtrack=true
|
|
254
|
-
[[ "$arch_count" -gt 0 ]] && priority_findings="architecture:${arch_count}"
|
|
255
|
-
[[ "$security_count" -gt 0 ]] && priority_findings="${priority_findings:+${priority_findings},}security:${security_count}"
|
|
256
|
-
[[ "$correctness_count" -gt 0 ]] && priority_findings="${priority_findings:+${priority_findings},}correctness:${correctness_count}"
|
|
257
|
-
[[ "$performance_count" -gt 0 ]] && priority_findings="${priority_findings:+${priority_findings},}performance:${performance_count}"
|
|
258
|
-
[[ "$testing_count" -gt 0 ]] && priority_findings="${priority_findings:+${priority_findings},}testing:${testing_count}"
|
|
259
|
-
[[ -z "$priority_findings" ]] && priority_findings="${route}:1"
|
|
260
|
-
fi
|
|
261
|
-
|
|
262
|
-
# Style findings don't affect routing or count toward failure threshold
|
|
263
|
-
local total_blocking=$((arch_count + security_count + correctness_count + performance_count + testing_count))
|
|
264
|
-
|
|
265
|
-
# Write classified findings
|
|
266
|
-
local tmp_findings
|
|
267
|
-
tmp_findings="$(mktemp)"
|
|
268
|
-
jq -n \
|
|
269
|
-
--argjson arch "$arch_count" \
|
|
270
|
-
--argjson security "$security_count" \
|
|
271
|
-
--argjson correctness "$correctness_count" \
|
|
272
|
-
--argjson performance "$performance_count" \
|
|
273
|
-
--argjson testing "$testing_count" \
|
|
274
|
-
--argjson style "$style_count" \
|
|
275
|
-
--argjson total_blocking "$total_blocking" \
|
|
276
|
-
--arg route "$route" \
|
|
277
|
-
--argjson needs_backtrack "$needs_backtrack" \
|
|
278
|
-
--arg priority "$priority_findings" \
|
|
279
|
-
'{
|
|
280
|
-
architecture: $arch,
|
|
281
|
-
security: $security,
|
|
282
|
-
correctness: $correctness,
|
|
283
|
-
performance: $performance,
|
|
284
|
-
testing: $testing,
|
|
285
|
-
style: $style,
|
|
286
|
-
total_blocking: $total_blocking,
|
|
287
|
-
route: $route,
|
|
288
|
-
needs_backtrack: $needs_backtrack,
|
|
289
|
-
priority_findings: $priority
|
|
290
|
-
}' > "$tmp_findings" 2>/dev/null && mv "$tmp_findings" "$result_file" || rm -f "$tmp_findings"
|
|
291
|
-
|
|
292
|
-
emit_event "intelligence.findings_classified" \
|
|
293
|
-
"issue=${ISSUE_NUMBER:-0}" \
|
|
294
|
-
"architecture=$arch_count" \
|
|
295
|
-
"security=$security_count" \
|
|
296
|
-
"correctness=$correctness_count" \
|
|
297
|
-
"performance=$performance_count" \
|
|
298
|
-
"testing=$testing_count" \
|
|
299
|
-
"style=$style_count" \
|
|
300
|
-
"route=$route" \
|
|
301
|
-
"needs_backtrack=$needs_backtrack"
|
|
302
|
-
|
|
303
|
-
echo "$route"
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
307
|
-
# 3. Adaptive Cycle Limits
|
|
308
|
-
# Replaces hardcoded max_cycles with convergence-driven limits.
|
|
309
|
-
# Takes the base limit, returns an adjusted limit based on:
|
|
310
|
-
# - Learned iteration model
|
|
311
|
-
# - Convergence/divergence signals
|
|
312
|
-
# - Budget constraints
|
|
313
|
-
# - Hard ceiling (2x template max)
|
|
314
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
315
|
-
pipeline_adaptive_cycles() {
|
|
316
|
-
local base_limit="$1"
|
|
317
|
-
local context="${2:-compound_quality}" # compound_quality or build_test
|
|
318
|
-
local current_issue_count="${3:-0}"
|
|
319
|
-
local prev_issue_count="${4:--1}"
|
|
320
|
-
|
|
321
|
-
local adjusted="$base_limit"
|
|
322
|
-
local hard_ceiling=$((base_limit * 2))
|
|
323
|
-
|
|
324
|
-
# ── Learned iteration model ──
|
|
325
|
-
local model_file="${HOME}/.shipwright/optimization/iteration-model.json"
|
|
326
|
-
if [[ -f "$model_file" ]]; then
|
|
327
|
-
local learned
|
|
328
|
-
learned=$(jq -r --arg ctx "$context" '.[$ctx].recommended_cycles // 0' "$model_file" 2>/dev/null || echo "0")
|
|
329
|
-
if [[ "$learned" -gt 0 && "$learned" -le "$hard_ceiling" ]]; then
|
|
330
|
-
adjusted="$learned"
|
|
331
|
-
fi
|
|
332
|
-
fi
|
|
333
|
-
|
|
334
|
-
# ── Convergence acceleration ──
|
|
335
|
-
# If issue count drops >50% per cycle, extend limit by 1 (we're making progress)
|
|
336
|
-
if [[ "$prev_issue_count" -gt 0 && "$current_issue_count" -ge 0 ]]; then
|
|
337
|
-
local half_prev=$((prev_issue_count / 2))
|
|
338
|
-
if [[ "$current_issue_count" -le "$half_prev" && "$current_issue_count" -gt 0 ]]; then
|
|
339
|
-
# Rapid convergence — extend by 1
|
|
340
|
-
local new_limit=$((adjusted + 1))
|
|
341
|
-
if [[ "$new_limit" -le "$hard_ceiling" ]]; then
|
|
342
|
-
adjusted="$new_limit"
|
|
343
|
-
emit_event "intelligence.convergence_acceleration" \
|
|
344
|
-
"issue=${ISSUE_NUMBER:-0}" \
|
|
345
|
-
"context=$context" \
|
|
346
|
-
"prev_issues=$prev_issue_count" \
|
|
347
|
-
"current_issues=$current_issue_count" \
|
|
348
|
-
"new_limit=$adjusted"
|
|
349
|
-
fi
|
|
350
|
-
fi
|
|
351
|
-
|
|
352
|
-
# ── Divergence detection ──
|
|
353
|
-
# If issue count increases, reduce remaining cycles
|
|
354
|
-
if [[ "$current_issue_count" -gt "$prev_issue_count" ]]; then
|
|
355
|
-
local reduced=$((adjusted - 1))
|
|
356
|
-
if [[ "$reduced" -ge 1 ]]; then
|
|
357
|
-
adjusted="$reduced"
|
|
358
|
-
emit_event "intelligence.divergence_detected" \
|
|
359
|
-
"issue=${ISSUE_NUMBER:-0}" \
|
|
360
|
-
"context=$context" \
|
|
361
|
-
"prev_issues=$prev_issue_count" \
|
|
362
|
-
"current_issues=$current_issue_count" \
|
|
363
|
-
"new_limit=$adjusted"
|
|
364
|
-
fi
|
|
365
|
-
fi
|
|
366
|
-
fi
|
|
367
|
-
|
|
368
|
-
# ── Budget gate ──
|
|
369
|
-
if [[ "$IGNORE_BUDGET" != "true" ]] && [[ -x "$SCRIPT_DIR/sw-cost.sh" ]]; then
|
|
370
|
-
local budget_rc=0
|
|
371
|
-
bash "$SCRIPT_DIR/sw-cost.sh" check-budget 2>/dev/null || budget_rc=$?
|
|
372
|
-
if [[ "$budget_rc" -eq 2 ]]; then
|
|
373
|
-
# Budget exhausted — cap at current cycle
|
|
374
|
-
adjusted=0
|
|
375
|
-
emit_event "intelligence.budget_cap" \
|
|
376
|
-
"issue=${ISSUE_NUMBER:-0}" \
|
|
377
|
-
"context=$context"
|
|
378
|
-
fi
|
|
379
|
-
fi
|
|
380
|
-
|
|
381
|
-
# ── Enforce hard ceiling ──
|
|
382
|
-
if [[ "$adjusted" -gt "$hard_ceiling" ]]; then
|
|
383
|
-
adjusted="$hard_ceiling"
|
|
384
|
-
fi
|
|
385
|
-
|
|
386
|
-
echo "$adjusted"
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
390
|
-
# 5. Intelligent Audit Selection
|
|
391
|
-
# AI-driven audit selection — all audits enabled, intensity varies.
|
|
392
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
393
|
-
pipeline_select_audits() {
|
|
394
|
-
local audit_intensity
|
|
395
|
-
audit_intensity=$(jq -r --arg id "compound_quality" \
|
|
396
|
-
'(.stages[] | select(.id == $id) | .config.audit_intensity) // "auto"' \
|
|
397
|
-
"$PIPELINE_CONFIG" 2>/dev/null) || true
|
|
398
|
-
[[ -z "$audit_intensity" || "$audit_intensity" == "null" ]] && audit_intensity="auto"
|
|
399
|
-
|
|
400
|
-
# Short-circuit for explicit overrides
|
|
401
|
-
case "$audit_intensity" in
|
|
402
|
-
off)
|
|
403
|
-
echo '{"adversarial":"off","architecture":"off","simulation":"off","security":"off","dod":"off"}'
|
|
404
|
-
return 0
|
|
405
|
-
;;
|
|
406
|
-
full|lightweight)
|
|
407
|
-
jq -n --arg i "$audit_intensity" \
|
|
408
|
-
'{adversarial:$i,architecture:$i,simulation:$i,security:$i,dod:$i}'
|
|
409
|
-
return 0
|
|
410
|
-
;;
|
|
411
|
-
esac
|
|
412
|
-
|
|
413
|
-
# ── Auto mode: data-driven intensity ──
|
|
414
|
-
local default_intensity="targeted"
|
|
415
|
-
local security_intensity="targeted"
|
|
416
|
-
|
|
417
|
-
# Read last 5 quality scores for this repo
|
|
418
|
-
local quality_scores_file="${HOME}/.shipwright/optimization/quality-scores.jsonl"
|
|
419
|
-
local repo_name
|
|
420
|
-
repo_name=$(basename "${PROJECT_ROOT:-.}") || true
|
|
421
|
-
if [[ -f "$quality_scores_file" ]]; then
|
|
422
|
-
local recent_scores
|
|
423
|
-
recent_scores=$(grep "\"repo\":\"${repo_name}\"" "$quality_scores_file" 2>/dev/null | tail -5) || true
|
|
424
|
-
if [[ -n "$recent_scores" ]]; then
|
|
425
|
-
# Check for critical findings in recent history
|
|
426
|
-
local has_critical
|
|
427
|
-
has_critical=$(echo "$recent_scores" | jq -s '[.[].findings.critical // 0] | add' 2>/dev/null || echo "0")
|
|
428
|
-
has_critical="${has_critical:-0}"
|
|
429
|
-
if [[ "$has_critical" -gt 0 ]]; then
|
|
430
|
-
security_intensity="full"
|
|
431
|
-
fi
|
|
432
|
-
|
|
433
|
-
# Compute average quality score
|
|
434
|
-
local avg_score
|
|
435
|
-
avg_score=$(echo "$recent_scores" | jq -s 'if length > 0 then ([.[].quality_score] | add / length | floor) else 70 end' 2>/dev/null || echo "70")
|
|
436
|
-
avg_score="${avg_score:-70}"
|
|
437
|
-
|
|
438
|
-
if [[ "$avg_score" -lt 60 ]]; then
|
|
439
|
-
default_intensity="full"
|
|
440
|
-
security_intensity="full"
|
|
441
|
-
elif [[ "$avg_score" -gt 80 ]]; then
|
|
442
|
-
default_intensity="lightweight"
|
|
443
|
-
[[ "$security_intensity" != "full" ]] && security_intensity="lightweight"
|
|
444
|
-
fi
|
|
445
|
-
fi
|
|
446
|
-
fi
|
|
447
|
-
|
|
448
|
-
# Intelligence cache: upgrade targeted→full for complex changes
|
|
449
|
-
local intel_cache="${PROJECT_ROOT}/.claude/intelligence-cache.json"
|
|
450
|
-
if [[ -f "$intel_cache" && "$default_intensity" == "targeted" ]]; then
|
|
451
|
-
local complexity
|
|
452
|
-
complexity=$(jq -r '.complexity // "medium"' "$intel_cache" 2>/dev/null || echo "medium")
|
|
453
|
-
if [[ "$complexity" == "high" || "$complexity" == "very_high" ]]; then
|
|
454
|
-
default_intensity="full"
|
|
455
|
-
security_intensity="full"
|
|
456
|
-
fi
|
|
457
|
-
fi
|
|
458
|
-
|
|
459
|
-
emit_event "pipeline.audit_selection" \
|
|
460
|
-
"issue=${ISSUE_NUMBER:-0}" \
|
|
461
|
-
"default_intensity=$default_intensity" \
|
|
462
|
-
"security_intensity=$security_intensity" \
|
|
463
|
-
"repo=$repo_name"
|
|
464
|
-
|
|
465
|
-
jq -n \
|
|
466
|
-
--arg adv "$default_intensity" \
|
|
467
|
-
--arg arch "$default_intensity" \
|
|
468
|
-
--arg sim "$default_intensity" \
|
|
469
|
-
--arg sec "$security_intensity" \
|
|
470
|
-
--arg dod "$default_intensity" \
|
|
471
|
-
'{adversarial:$adv,architecture:$arch,simulation:$sim,security:$sec,dod:$dod}'
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
475
|
-
# 6. Definition of Done Verification
|
|
476
|
-
# Strict DoD enforcement after compound quality completes.
|
|
477
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
478
|
-
pipeline_verify_dod() {
|
|
479
|
-
local artifacts_dir="${1:-$ARTIFACTS_DIR}"
|
|
480
|
-
local checks_total=0 checks_passed=0
|
|
481
|
-
local results=""
|
|
482
|
-
|
|
483
|
-
# 1. Test coverage: verify changed source files have test counterparts
|
|
484
|
-
local changed_files
|
|
485
|
-
changed_files=$(git diff --name-only "${BASE_BRANCH:-main}...HEAD" 2>/dev/null || true)
|
|
486
|
-
local missing_tests=""
|
|
487
|
-
local files_checked=0
|
|
488
|
-
|
|
489
|
-
if [[ -n "$changed_files" ]]; then
|
|
490
|
-
while IFS= read -r src_file; do
|
|
491
|
-
[[ -z "$src_file" ]] && continue
|
|
492
|
-
# Only check source code files
|
|
493
|
-
case "$src_file" in
|
|
494
|
-
*.ts|*.js|*.tsx|*.jsx|*.py|*.go|*.rs|*.sh)
|
|
495
|
-
# Skip test files themselves and config files
|
|
496
|
-
case "$src_file" in
|
|
497
|
-
*test*|*spec*|*__tests__*|*.config.*|*.d.ts) continue ;;
|
|
498
|
-
esac
|
|
499
|
-
files_checked=$((files_checked + 1))
|
|
500
|
-
checks_total=$((checks_total + 1))
|
|
501
|
-
# Check for corresponding test file
|
|
502
|
-
local base_name dir_name ext
|
|
503
|
-
base_name=$(basename "$src_file")
|
|
504
|
-
dir_name=$(dirname "$src_file")
|
|
505
|
-
ext="${base_name##*.}"
|
|
506
|
-
local stem="${base_name%.*}"
|
|
507
|
-
local test_found=false
|
|
508
|
-
# Common test file patterns
|
|
509
|
-
for pattern in \
|
|
510
|
-
"${dir_name}/${stem}.test.${ext}" \
|
|
511
|
-
"${dir_name}/${stem}.spec.${ext}" \
|
|
512
|
-
"${dir_name}/__tests__/${stem}.test.${ext}" \
|
|
513
|
-
"${dir_name}/${stem}-test.${ext}" \
|
|
514
|
-
"${dir_name}/test_${stem}.${ext}" \
|
|
515
|
-
"${dir_name}/${stem}_test.${ext}"; do
|
|
516
|
-
if [[ -f "$pattern" ]]; then
|
|
517
|
-
test_found=true
|
|
518
|
-
break
|
|
519
|
-
fi
|
|
520
|
-
done
|
|
521
|
-
if $test_found; then
|
|
522
|
-
checks_passed=$((checks_passed + 1))
|
|
523
|
-
else
|
|
524
|
-
missing_tests="${missing_tests}${src_file}\n"
|
|
525
|
-
fi
|
|
526
|
-
;;
|
|
527
|
-
esac
|
|
528
|
-
done <<EOF
|
|
529
|
-
$changed_files
|
|
530
|
-
EOF
|
|
531
|
-
fi
|
|
532
|
-
|
|
533
|
-
# 2. Test-added verification: if significant logic added, ensure tests were also added
|
|
534
|
-
local logic_lines=0 test_lines=0
|
|
535
|
-
if [[ -n "$changed_files" ]]; then
|
|
536
|
-
local full_diff
|
|
537
|
-
full_diff=$(git diff "${BASE_BRANCH:-main}...HEAD" 2>/dev/null || true)
|
|
538
|
-
if [[ -n "$full_diff" ]]; then
|
|
539
|
-
# Count added lines matching source patterns (rough heuristic)
|
|
540
|
-
logic_lines=$(echo "$full_diff" | grep -cE '^\+.*(function |class |if |for |while |return |export )' 2>/dev/null || true)
|
|
541
|
-
logic_lines="${logic_lines:-0}"
|
|
542
|
-
# Count added lines in test files
|
|
543
|
-
test_lines=$(echo "$full_diff" | grep -cE '^\+.*(it\(|test\(|describe\(|expect\(|assert|def test_|func Test)' 2>/dev/null || true)
|
|
544
|
-
test_lines="${test_lines:-0}"
|
|
545
|
-
fi
|
|
546
|
-
fi
|
|
547
|
-
checks_total=$((checks_total + 1))
|
|
548
|
-
local test_ratio_passed=true
|
|
549
|
-
if [[ "$logic_lines" -gt 20 && "$test_lines" -eq 0 ]]; then
|
|
550
|
-
test_ratio_passed=false
|
|
551
|
-
warn "DoD verification: ${logic_lines} logic lines added but no test lines detected"
|
|
552
|
-
else
|
|
553
|
-
checks_passed=$((checks_passed + 1))
|
|
554
|
-
fi
|
|
555
|
-
|
|
556
|
-
# 3. Behavioral verification: check DoD audit artifacts for evidence
|
|
557
|
-
local dod_audit_file="$artifacts_dir/dod-audit.md"
|
|
558
|
-
local dod_verified=0 dod_total_items=0
|
|
559
|
-
if [[ -f "$dod_audit_file" ]]; then
|
|
560
|
-
# Count items marked as passing
|
|
561
|
-
dod_total_items=$(grep -cE '^\s*-\s*\[x\]' "$dod_audit_file" 2>/dev/null || true)
|
|
562
|
-
dod_total_items="${dod_total_items:-0}"
|
|
563
|
-
local dod_failing
|
|
564
|
-
dod_failing=$(grep -cE '^\s*-\s*\[\s\]' "$dod_audit_file" 2>/dev/null || true)
|
|
565
|
-
dod_failing="${dod_failing:-0}"
|
|
566
|
-
dod_verified=$dod_total_items
|
|
567
|
-
checks_total=$((checks_total + dod_total_items + ${dod_failing:-0}))
|
|
568
|
-
checks_passed=$((checks_passed + dod_total_items))
|
|
569
|
-
fi
|
|
570
|
-
|
|
571
|
-
# Compute pass rate
|
|
572
|
-
local pass_rate=100
|
|
573
|
-
if [[ "$checks_total" -gt 0 ]]; then
|
|
574
|
-
pass_rate=$(( (checks_passed * 100) / checks_total ))
|
|
575
|
-
fi
|
|
576
|
-
|
|
577
|
-
# Write results
|
|
578
|
-
local tmp_result
|
|
579
|
-
tmp_result=$(mktemp)
|
|
580
|
-
jq -n \
|
|
581
|
-
--argjson checks_total "$checks_total" \
|
|
582
|
-
--argjson checks_passed "$checks_passed" \
|
|
583
|
-
--argjson pass_rate "$pass_rate" \
|
|
584
|
-
--argjson files_checked "$files_checked" \
|
|
585
|
-
--arg missing_tests "$(echo -e "$missing_tests" | head -20)" \
|
|
586
|
-
--argjson logic_lines "$logic_lines" \
|
|
587
|
-
--argjson test_lines "$test_lines" \
|
|
588
|
-
--argjson test_ratio_passed "$test_ratio_passed" \
|
|
589
|
-
--argjson dod_verified "$dod_verified" \
|
|
590
|
-
'{
|
|
591
|
-
checks_total: $checks_total,
|
|
592
|
-
checks_passed: $checks_passed,
|
|
593
|
-
pass_rate: $pass_rate,
|
|
594
|
-
files_checked: $files_checked,
|
|
595
|
-
missing_tests: ($missing_tests | split("\n") | map(select(. != ""))),
|
|
596
|
-
logic_lines: $logic_lines,
|
|
597
|
-
test_lines: $test_lines,
|
|
598
|
-
test_ratio_passed: $test_ratio_passed,
|
|
599
|
-
dod_verified: $dod_verified
|
|
600
|
-
}' > "$tmp_result" 2>/dev/null
|
|
601
|
-
mv "$tmp_result" "$artifacts_dir/dod-verification.json"
|
|
602
|
-
|
|
603
|
-
emit_event "pipeline.dod_verification" \
|
|
604
|
-
"issue=${ISSUE_NUMBER:-0}" \
|
|
605
|
-
"checks_total=$checks_total" \
|
|
606
|
-
"checks_passed=$checks_passed" \
|
|
607
|
-
"pass_rate=$pass_rate"
|
|
608
|
-
|
|
609
|
-
# Fail if pass rate < 70%
|
|
610
|
-
if [[ "$pass_rate" -lt 70 ]]; then
|
|
611
|
-
warn "DoD verification: ${pass_rate}% pass rate (${checks_passed}/${checks_total} checks)"
|
|
612
|
-
return 1
|
|
613
|
-
fi
|
|
614
|
-
|
|
615
|
-
success "DoD verification: ${pass_rate}% pass rate (${checks_passed}/${checks_total} checks)"
|
|
616
|
-
return 0
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
620
|
-
# 7. Source Code Security Scan
|
|
621
|
-
# Grep-based vulnerability pattern matching on changed files.
|
|
622
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
623
|
-
pipeline_security_source_scan() {
|
|
624
|
-
local base_branch="${1:-${BASE_BRANCH:-main}}"
|
|
625
|
-
local findings="[]"
|
|
626
|
-
local finding_count=0
|
|
627
|
-
|
|
628
|
-
local changed_files
|
|
629
|
-
changed_files=$(git diff --name-only "${base_branch}...HEAD" 2>/dev/null || true)
|
|
630
|
-
[[ -z "$changed_files" ]] && { echo "[]"; return 0; }
|
|
631
|
-
|
|
632
|
-
local tmp_findings
|
|
633
|
-
tmp_findings=$(mktemp)
|
|
634
|
-
echo "[]" > "$tmp_findings"
|
|
635
|
-
|
|
636
|
-
while IFS= read -r file; do
|
|
637
|
-
[[ -z "$file" || ! -f "$file" ]] && continue
|
|
638
|
-
# Only scan code files
|
|
639
|
-
case "$file" in
|
|
640
|
-
*.ts|*.js|*.tsx|*.jsx|*.py|*.go|*.rs|*.java|*.rb|*.php|*.sh) ;;
|
|
641
|
-
*) continue ;;
|
|
642
|
-
esac
|
|
643
|
-
|
|
644
|
-
# SQL injection patterns
|
|
645
|
-
local sql_matches
|
|
646
|
-
sql_matches=$(grep -nE '(query|execute|sql)\s*\(?\s*[`"'"'"']\s*.*\$\{|\.query\s*\(\s*[`"'"'"'].*\+' "$file" 2>/dev/null || true)
|
|
647
|
-
if [[ -n "$sql_matches" ]]; then
|
|
648
|
-
while IFS= read -r match; do
|
|
649
|
-
[[ -z "$match" ]] && continue
|
|
650
|
-
local line_num="${match%%:*}"
|
|
651
|
-
finding_count=$((finding_count + 1))
|
|
652
|
-
local current
|
|
653
|
-
current=$(cat "$tmp_findings")
|
|
654
|
-
echo "$current" | jq --arg f "$file" --arg l "$line_num" --arg p "sql_injection" \
|
|
655
|
-
'. + [{"file":$f,"line":($l|tonumber),"pattern":$p,"severity":"critical","description":"Potential SQL injection via string concatenation"}]' \
|
|
656
|
-
> "$tmp_findings" 2>/dev/null || true
|
|
657
|
-
done <<SQLEOF
|
|
658
|
-
$sql_matches
|
|
659
|
-
SQLEOF
|
|
660
|
-
fi
|
|
661
|
-
|
|
662
|
-
# XSS patterns
|
|
663
|
-
local xss_matches
|
|
664
|
-
xss_matches=$(grep -nE 'innerHTML\s*=|document\.write\s*\(|dangerouslySetInnerHTML' "$file" 2>/dev/null || true)
|
|
665
|
-
if [[ -n "$xss_matches" ]]; then
|
|
666
|
-
while IFS= read -r match; do
|
|
667
|
-
[[ -z "$match" ]] && continue
|
|
668
|
-
local line_num="${match%%:*}"
|
|
669
|
-
finding_count=$((finding_count + 1))
|
|
670
|
-
local current
|
|
671
|
-
current=$(cat "$tmp_findings")
|
|
672
|
-
echo "$current" | jq --arg f "$file" --arg l "$line_num" --arg p "xss" \
|
|
673
|
-
'. + [{"file":$f,"line":($l|tonumber),"pattern":$p,"severity":"critical","description":"Potential XSS via unsafe DOM manipulation"}]' \
|
|
674
|
-
> "$tmp_findings" 2>/dev/null || true
|
|
675
|
-
done <<XSSEOF
|
|
676
|
-
$xss_matches
|
|
677
|
-
XSSEOF
|
|
678
|
-
fi
|
|
679
|
-
|
|
680
|
-
# Command injection patterns
|
|
681
|
-
local cmd_matches
|
|
682
|
-
cmd_matches=$(grep -nE 'eval\s*\(|child_process|os\.system\s*\(|subprocess\.(call|run|Popen)\s*\(' "$file" 2>/dev/null || true)
|
|
683
|
-
if [[ -n "$cmd_matches" ]]; then
|
|
684
|
-
while IFS= read -r match; do
|
|
685
|
-
[[ -z "$match" ]] && continue
|
|
686
|
-
local line_num="${match%%:*}"
|
|
687
|
-
finding_count=$((finding_count + 1))
|
|
688
|
-
local current
|
|
689
|
-
current=$(cat "$tmp_findings")
|
|
690
|
-
echo "$current" | jq --arg f "$file" --arg l "$line_num" --arg p "command_injection" \
|
|
691
|
-
'. + [{"file":$f,"line":($l|tonumber),"pattern":$p,"severity":"critical","description":"Potential command injection via unsafe execution"}]' \
|
|
692
|
-
> "$tmp_findings" 2>/dev/null || true
|
|
693
|
-
done <<CMDEOF
|
|
694
|
-
$cmd_matches
|
|
695
|
-
CMDEOF
|
|
696
|
-
fi
|
|
697
|
-
|
|
698
|
-
# Hardcoded secrets patterns
|
|
699
|
-
local secret_matches
|
|
700
|
-
secret_matches=$(grep -nEi '(password|api_key|secret|token)\s*=\s*['"'"'"][A-Za-z0-9+/=]{8,}['"'"'"]' "$file" 2>/dev/null || true)
|
|
701
|
-
if [[ -n "$secret_matches" ]]; then
|
|
702
|
-
while IFS= read -r match; do
|
|
703
|
-
[[ -z "$match" ]] && continue
|
|
704
|
-
local line_num="${match%%:*}"
|
|
705
|
-
finding_count=$((finding_count + 1))
|
|
706
|
-
local current
|
|
707
|
-
current=$(cat "$tmp_findings")
|
|
708
|
-
echo "$current" | jq --arg f "$file" --arg l "$line_num" --arg p "hardcoded_secret" \
|
|
709
|
-
'. + [{"file":$f,"line":($l|tonumber),"pattern":$p,"severity":"critical","description":"Potential hardcoded secret or credential"}]' \
|
|
710
|
-
> "$tmp_findings" 2>/dev/null || true
|
|
711
|
-
done <<SECEOF
|
|
712
|
-
$secret_matches
|
|
713
|
-
SECEOF
|
|
714
|
-
fi
|
|
715
|
-
|
|
716
|
-
# Insecure crypto patterns
|
|
717
|
-
local crypto_matches
|
|
718
|
-
crypto_matches=$(grep -nE '(md5|MD5|sha1|SHA1)\s*\(' "$file" 2>/dev/null || true)
|
|
719
|
-
if [[ -n "$crypto_matches" ]]; then
|
|
720
|
-
while IFS= read -r match; do
|
|
721
|
-
[[ -z "$match" ]] && continue
|
|
722
|
-
local line_num="${match%%:*}"
|
|
723
|
-
finding_count=$((finding_count + 1))
|
|
724
|
-
local current
|
|
725
|
-
current=$(cat "$tmp_findings")
|
|
726
|
-
echo "$current" | jq --arg f "$file" --arg l "$line_num" --arg p "insecure_crypto" \
|
|
727
|
-
'. + [{"file":$f,"line":($l|tonumber),"pattern":$p,"severity":"major","description":"Weak cryptographic function (consider SHA-256+)"}]' \
|
|
728
|
-
> "$tmp_findings" 2>/dev/null || true
|
|
729
|
-
done <<CRYEOF
|
|
730
|
-
$crypto_matches
|
|
731
|
-
CRYEOF
|
|
732
|
-
fi
|
|
733
|
-
done <<FILESEOF
|
|
734
|
-
$changed_files
|
|
735
|
-
FILESEOF
|
|
736
|
-
|
|
737
|
-
# Write to artifacts and output
|
|
738
|
-
findings=$(cat "$tmp_findings")
|
|
739
|
-
rm -f "$tmp_findings"
|
|
740
|
-
|
|
741
|
-
if [[ -n "${ARTIFACTS_DIR:-}" ]]; then
|
|
742
|
-
local tmp_scan
|
|
743
|
-
tmp_scan=$(mktemp)
|
|
744
|
-
echo "$findings" > "$tmp_scan"
|
|
745
|
-
mv "$tmp_scan" "$ARTIFACTS_DIR/security-source-scan.json"
|
|
746
|
-
fi
|
|
747
|
-
|
|
748
|
-
emit_event "pipeline.security_source_scan" \
|
|
749
|
-
"issue=${ISSUE_NUMBER:-0}" \
|
|
750
|
-
"findings=$finding_count"
|
|
751
|
-
|
|
752
|
-
echo "$finding_count"
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
756
|
-
# 8. Quality Score Recording
|
|
757
|
-
# Writes quality scores to JSONL for learning.
|
|
758
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
759
|
-
pipeline_record_quality_score() {
|
|
760
|
-
local quality_score="${1:-0}"
|
|
761
|
-
local critical="${2:-0}"
|
|
762
|
-
local major="${3:-0}"
|
|
763
|
-
local minor="${4:-0}"
|
|
764
|
-
local dod_pass_rate="${5:-0}"
|
|
765
|
-
local audits_run="${6:-}"
|
|
766
|
-
|
|
767
|
-
local scores_dir="${HOME}/.shipwright/optimization"
|
|
768
|
-
local scores_file="${scores_dir}/quality-scores.jsonl"
|
|
769
|
-
mkdir -p "$scores_dir"
|
|
770
|
-
|
|
771
|
-
local repo_name
|
|
772
|
-
repo_name=$(basename "${PROJECT_ROOT:-.}") || true
|
|
773
|
-
|
|
774
|
-
local tmp_score
|
|
775
|
-
tmp_score=$(mktemp)
|
|
776
|
-
jq -n \
|
|
777
|
-
--arg repo "$repo_name" \
|
|
778
|
-
--arg issue "${ISSUE_NUMBER:-0}" \
|
|
779
|
-
--arg ts "$(now_iso)" \
|
|
780
|
-
--argjson score "$quality_score" \
|
|
781
|
-
--argjson critical "$critical" \
|
|
782
|
-
--argjson major "$major" \
|
|
783
|
-
--argjson minor "$minor" \
|
|
784
|
-
--argjson dod "$dod_pass_rate" \
|
|
785
|
-
--arg template "${PIPELINE_NAME:-standard}" \
|
|
786
|
-
--arg audits "$audits_run" \
|
|
787
|
-
'{
|
|
788
|
-
repo: $repo,
|
|
789
|
-
issue: ($issue | tonumber),
|
|
790
|
-
timestamp: $ts,
|
|
791
|
-
quality_score: $score,
|
|
792
|
-
findings: {critical: $critical, major: $major, minor: $minor},
|
|
793
|
-
dod_pass_rate: $dod,
|
|
794
|
-
template: $template,
|
|
795
|
-
audits_run: ($audits | split(",") | map(select(. != "")))
|
|
796
|
-
}' > "$tmp_score" 2>/dev/null
|
|
797
|
-
|
|
798
|
-
cat "$tmp_score" >> "$scores_file"
|
|
799
|
-
rm -f "$tmp_score"
|
|
800
|
-
|
|
801
|
-
# Rotate quality scores file to prevent unbounded growth
|
|
802
|
-
type rotate_jsonl >/dev/null 2>&1 && rotate_jsonl "$scores_file" 5000
|
|
803
|
-
|
|
804
|
-
emit_event "pipeline.quality_score_recorded" \
|
|
805
|
-
"issue=${ISSUE_NUMBER:-0}" \
|
|
806
|
-
"quality_score=$quality_score" \
|
|
807
|
-
"critical=$critical" \
|
|
808
|
-
"major=$major" \
|
|
809
|
-
"minor=$minor"
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
813
|
-
# 4. Mid-Pipeline Complexity Re-evaluation
|
|
814
|
-
# After build+test completes, compares actual effort to initial estimate.
|
|
815
|
-
# Updates skip recommendations and model routing for remaining stages.
|
|
816
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
817
|
-
pipeline_reassess_complexity() {
|
|
818
|
-
local initial_complexity="${INTELLIGENCE_COMPLEXITY:-5}"
|
|
819
|
-
local reassessment_file="$ARTIFACTS_DIR/reassessment.json"
|
|
820
|
-
|
|
821
|
-
# ── Gather actual metrics ──
|
|
822
|
-
local files_changed=0 lines_changed=0 first_try_pass=false self_heal_cycles=0
|
|
823
|
-
|
|
824
|
-
files_changed=$(git diff "${BASE_BRANCH:-main}...HEAD" --name-only 2>/dev/null | wc -l | tr -d ' ') || files_changed=0
|
|
825
|
-
files_changed="${files_changed:-0}"
|
|
826
|
-
|
|
827
|
-
# Count lines changed (insertions + deletions) without pipefail issues
|
|
828
|
-
lines_changed=0
|
|
829
|
-
local _diff_stat
|
|
830
|
-
_diff_stat=$(git diff "${BASE_BRANCH:-main}...HEAD" --stat 2>/dev/null | tail -1) || true
|
|
831
|
-
if [[ -n "${_diff_stat:-}" ]]; then
|
|
832
|
-
local _ins _del
|
|
833
|
-
_ins=$(echo "$_diff_stat" | grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+') || true
|
|
834
|
-
_del=$(echo "$_diff_stat" | grep -oE '[0-9]+ deletion' | grep -oE '[0-9]+') || true
|
|
835
|
-
lines_changed=$(( ${_ins:-0} + ${_del:-0} ))
|
|
836
|
-
fi
|
|
837
|
-
|
|
838
|
-
self_heal_cycles="${SELF_HEAL_COUNT:-0}"
|
|
839
|
-
if [[ "$self_heal_cycles" -eq 0 ]]; then
|
|
840
|
-
first_try_pass=true
|
|
841
|
-
fi
|
|
842
|
-
|
|
843
|
-
# ── Compare to expectations ──
|
|
844
|
-
local actual_complexity="$initial_complexity"
|
|
845
|
-
local assessment="as_expected"
|
|
846
|
-
local skip_stages="[]"
|
|
847
|
-
|
|
848
|
-
# Simpler than expected: small diff, tests passed first try
|
|
849
|
-
if [[ "$lines_changed" -lt 50 && "$first_try_pass" == "true" && "$files_changed" -lt 5 ]]; then
|
|
850
|
-
actual_complexity=$((initial_complexity > 2 ? initial_complexity - 2 : 1))
|
|
851
|
-
assessment="simpler_than_expected"
|
|
852
|
-
# Mark compound_quality as skippable, simplify review
|
|
853
|
-
skip_stages='["compound_quality"]'
|
|
854
|
-
# Much simpler
|
|
855
|
-
elif [[ "$lines_changed" -lt 20 && "$first_try_pass" == "true" && "$files_changed" -lt 3 ]]; then
|
|
856
|
-
actual_complexity=1
|
|
857
|
-
assessment="much_simpler"
|
|
858
|
-
skip_stages='["compound_quality","review"]'
|
|
859
|
-
# Harder than expected: large diff, multiple self-heal cycles
|
|
860
|
-
elif [[ "$lines_changed" -gt 500 || "$self_heal_cycles" -gt 2 ]]; then
|
|
861
|
-
actual_complexity=$((initial_complexity < 9 ? initial_complexity + 2 : 10))
|
|
862
|
-
assessment="harder_than_expected"
|
|
863
|
-
# Ensure compound_quality runs, possibly upgrade model
|
|
864
|
-
skip_stages='[]'
|
|
865
|
-
# Much harder
|
|
866
|
-
elif [[ "$lines_changed" -gt 1000 || "$self_heal_cycles" -gt 4 ]]; then
|
|
867
|
-
actual_complexity=10
|
|
868
|
-
assessment="much_harder"
|
|
869
|
-
skip_stages='[]'
|
|
870
|
-
fi
|
|
871
|
-
|
|
872
|
-
# ── Write reassessment ──
|
|
873
|
-
local tmp_reassess
|
|
874
|
-
tmp_reassess="$(mktemp)"
|
|
875
|
-
jq -n \
|
|
876
|
-
--argjson initial "$initial_complexity" \
|
|
877
|
-
--argjson actual "$actual_complexity" \
|
|
878
|
-
--arg assessment "$assessment" \
|
|
879
|
-
--argjson files_changed "$files_changed" \
|
|
880
|
-
--argjson lines_changed "$lines_changed" \
|
|
881
|
-
--argjson self_heal_cycles "$self_heal_cycles" \
|
|
882
|
-
--argjson first_try "$first_try_pass" \
|
|
883
|
-
--argjson skip_stages "$skip_stages" \
|
|
884
|
-
'{
|
|
885
|
-
initial_complexity: $initial,
|
|
886
|
-
actual_complexity: $actual,
|
|
887
|
-
assessment: $assessment,
|
|
888
|
-
files_changed: $files_changed,
|
|
889
|
-
lines_changed: $lines_changed,
|
|
890
|
-
self_heal_cycles: $self_heal_cycles,
|
|
891
|
-
first_try_pass: $first_try,
|
|
892
|
-
skip_stages: $skip_stages
|
|
893
|
-
}' > "$tmp_reassess" 2>/dev/null && mv "$tmp_reassess" "$reassessment_file" || rm -f "$tmp_reassess"
|
|
894
|
-
|
|
895
|
-
# Update global complexity for downstream stages
|
|
896
|
-
PIPELINE_ADAPTIVE_COMPLEXITY="$actual_complexity"
|
|
897
|
-
|
|
898
|
-
emit_event "intelligence.reassessment" \
|
|
899
|
-
"issue=${ISSUE_NUMBER:-0}" \
|
|
900
|
-
"initial=$initial_complexity" \
|
|
901
|
-
"actual=$actual_complexity" \
|
|
902
|
-
"assessment=$assessment" \
|
|
903
|
-
"files=$files_changed" \
|
|
904
|
-
"lines=$lines_changed" \
|
|
905
|
-
"self_heals=$self_heal_cycles"
|
|
906
|
-
|
|
907
|
-
# ── Store for learning ──
|
|
908
|
-
local learning_file="${HOME}/.shipwright/optimization/complexity-actuals.jsonl"
|
|
909
|
-
mkdir -p "${HOME}/.shipwright/optimization" 2>/dev/null || true
|
|
910
|
-
echo "{\"issue\":\"${ISSUE_NUMBER:-0}\",\"initial\":$initial_complexity,\"actual\":$actual_complexity,\"files\":$files_changed,\"lines\":$lines_changed,\"ts\":\"$(now_iso)\"}" \
|
|
911
|
-
>> "$learning_file" 2>/dev/null || true
|
|
912
|
-
|
|
913
|
-
echo "$assessment"
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
917
|
-
# 5. Backtracking Support
|
|
918
|
-
# When compound_quality detects architecture-level problems, backtracks to
|
|
919
|
-
# the design stage instead of just feeding findings to the build loop.
|
|
920
|
-
# Limited to 1 backtrack per pipeline run to prevent infinite loops.
|
|
921
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
922
|
-
pipeline_backtrack_to_stage() {
|
|
923
|
-
local target_stage="$1"
|
|
924
|
-
local reason="${2:-architecture_violation}"
|
|
925
|
-
|
|
926
|
-
# Prevent infinite backtracking
|
|
927
|
-
if [[ "$PIPELINE_BACKTRACK_COUNT" -ge "$PIPELINE_MAX_BACKTRACKS" ]]; then
|
|
928
|
-
warn "Max backtracks ($PIPELINE_MAX_BACKTRACKS) reached — cannot backtrack to $target_stage"
|
|
929
|
-
emit_event "intelligence.backtrack_blocked" \
|
|
930
|
-
"issue=${ISSUE_NUMBER:-0}" \
|
|
931
|
-
"target=$target_stage" \
|
|
932
|
-
"reason=max_backtracks_reached" \
|
|
933
|
-
"count=$PIPELINE_BACKTRACK_COUNT"
|
|
934
|
-
return 1
|
|
935
|
-
fi
|
|
936
|
-
|
|
937
|
-
PIPELINE_BACKTRACK_COUNT=$((PIPELINE_BACKTRACK_COUNT + 1))
|
|
938
|
-
|
|
939
|
-
info "Backtracking to ${BOLD}${target_stage}${RESET} stage (reason: ${reason})"
|
|
940
|
-
|
|
941
|
-
emit_event "intelligence.backtrack" \
|
|
942
|
-
"issue=${ISSUE_NUMBER:-0}" \
|
|
943
|
-
"target=$target_stage" \
|
|
944
|
-
"reason=$reason"
|
|
945
|
-
|
|
946
|
-
# Gather architecture context from findings
|
|
947
|
-
local arch_context=""
|
|
948
|
-
if [[ -f "$ARTIFACTS_DIR/compound-architecture-validation.json" ]]; then
|
|
949
|
-
arch_context=$(jq -r '[.[] | select(.severity == "critical" or .severity == "high") | .message // .description // ""] | join("\n")' \
|
|
950
|
-
"$ARTIFACTS_DIR/compound-architecture-validation.json" 2>/dev/null || true)
|
|
951
|
-
fi
|
|
952
|
-
if [[ -f "$ARTIFACTS_DIR/adversarial-review.md" ]]; then
|
|
953
|
-
local arch_lines
|
|
954
|
-
arch_lines=$(grep -iE 'architect|layer.*violation|circular.*depend|coupling|design.*flaw' \
|
|
955
|
-
"$ARTIFACTS_DIR/adversarial-review.md" 2>/dev/null || true)
|
|
956
|
-
if [[ -n "$arch_lines" ]]; then
|
|
957
|
-
arch_context="${arch_context}
|
|
958
|
-
${arch_lines}"
|
|
959
|
-
fi
|
|
960
|
-
fi
|
|
961
|
-
|
|
962
|
-
# Reset stages from target onward
|
|
963
|
-
set_stage_status "$target_stage" "pending"
|
|
964
|
-
set_stage_status "build" "pending"
|
|
965
|
-
set_stage_status "test" "pending"
|
|
966
|
-
|
|
967
|
-
# Augment goal with architecture context for re-run
|
|
968
|
-
local original_goal="$GOAL"
|
|
969
|
-
if [[ -n "$arch_context" ]]; then
|
|
970
|
-
GOAL="$GOAL
|
|
971
|
-
|
|
972
|
-
IMPORTANT — Architecture violations were detected during quality review. Redesign to fix:
|
|
973
|
-
$arch_context
|
|
974
|
-
|
|
975
|
-
Update the design to address these violations, then rebuild."
|
|
976
|
-
fi
|
|
977
|
-
|
|
978
|
-
# Re-run design stage
|
|
979
|
-
info "Re-running ${BOLD}${target_stage}${RESET} with architecture context..."
|
|
980
|
-
if "stage_${target_stage}" 2>/dev/null; then
|
|
981
|
-
mark_stage_complete "$target_stage"
|
|
982
|
-
success "Backtrack: ${target_stage} re-run complete"
|
|
983
|
-
else
|
|
984
|
-
GOAL="$original_goal"
|
|
985
|
-
error "Backtrack: ${target_stage} re-run failed"
|
|
986
|
-
return 1
|
|
987
|
-
fi
|
|
988
|
-
|
|
989
|
-
# Re-run build+test
|
|
990
|
-
info "Re-running build→test after backtracked ${target_stage}..."
|
|
991
|
-
if self_healing_build_test; then
|
|
992
|
-
success "Backtrack: build→test passed after ${target_stage} redesign"
|
|
993
|
-
GOAL="$original_goal"
|
|
994
|
-
return 0
|
|
995
|
-
else
|
|
996
|
-
GOAL="$original_goal"
|
|
997
|
-
error "Backtrack: build→test failed after ${target_stage} redesign"
|
|
998
|
-
return 1
|
|
999
|
-
fi
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
compound_rebuild_with_feedback() {
|
|
1003
|
-
local feedback_file="$ARTIFACTS_DIR/quality-feedback.md"
|
|
1004
|
-
|
|
1005
|
-
# ── Intelligence: classify findings and determine routing ──
|
|
1006
|
-
local route="correctness"
|
|
1007
|
-
route=$(classify_quality_findings 2>/dev/null) || route="correctness"
|
|
1008
|
-
|
|
1009
|
-
# ── Build structured findings JSON alongside markdown ──
|
|
1010
|
-
local structured_findings="[]"
|
|
1011
|
-
local s_total_critical=0 s_total_major=0 s_total_minor=0
|
|
1012
|
-
|
|
1013
|
-
if [[ -f "$ARTIFACTS_DIR/classified-findings.json" ]]; then
|
|
1014
|
-
s_total_critical=$(jq -r '.security // 0' "$ARTIFACTS_DIR/classified-findings.json" 2>/dev/null || echo "0")
|
|
1015
|
-
s_total_major=$(jq -r '.correctness // 0' "$ARTIFACTS_DIR/classified-findings.json" 2>/dev/null || echo "0")
|
|
1016
|
-
s_total_minor=$(jq -r '.style // 0' "$ARTIFACTS_DIR/classified-findings.json" 2>/dev/null || echo "0")
|
|
1017
|
-
fi
|
|
1018
|
-
|
|
1019
|
-
local tmp_qf
|
|
1020
|
-
tmp_qf="$(mktemp)"
|
|
1021
|
-
jq -n \
|
|
1022
|
-
--arg route "$route" \
|
|
1023
|
-
--argjson total_critical "$s_total_critical" \
|
|
1024
|
-
--argjson total_major "$s_total_major" \
|
|
1025
|
-
--argjson total_minor "$s_total_minor" \
|
|
1026
|
-
'{route: $route, total_critical: $total_critical, total_major: $total_major, total_minor: $total_minor}' \
|
|
1027
|
-
> "$tmp_qf" 2>/dev/null && mv "$tmp_qf" "$ARTIFACTS_DIR/quality-findings.json" || rm -f "$tmp_qf"
|
|
1028
|
-
|
|
1029
|
-
# ── Architecture route: backtrack to design instead of rebuild ──
|
|
1030
|
-
if [[ "$route" == "architecture" ]]; then
|
|
1031
|
-
info "Architecture-level findings detected — attempting backtrack to design"
|
|
1032
|
-
if pipeline_backtrack_to_stage "design" "architecture_violation" 2>/dev/null; then
|
|
1033
|
-
return 0
|
|
1034
|
-
fi
|
|
1035
|
-
# Backtrack failed or already used — fall through to standard rebuild
|
|
1036
|
-
warn "Backtrack unavailable — falling through to standard rebuild"
|
|
1037
|
-
fi
|
|
1038
|
-
|
|
1039
|
-
# Collect all findings (prioritized by classification)
|
|
1040
|
-
{
|
|
1041
|
-
echo "# Quality Feedback — Issues to Fix"
|
|
1042
|
-
echo ""
|
|
1043
|
-
|
|
1044
|
-
# Security findings first (highest priority)
|
|
1045
|
-
if [[ "$route" == "security" || -f "$ARTIFACTS_DIR/security-audit.log" ]] && grep -qiE 'critical|high' "$ARTIFACTS_DIR/security-audit.log" 2>/dev/null; then
|
|
1046
|
-
echo "## 🔴 PRIORITY: Security Findings (fix these first)"
|
|
1047
|
-
cat "$ARTIFACTS_DIR/security-audit.log"
|
|
1048
|
-
echo ""
|
|
1049
|
-
echo "Security issues MUST be resolved before any other changes."
|
|
1050
|
-
echo ""
|
|
1051
|
-
fi
|
|
1052
|
-
|
|
1053
|
-
# Correctness findings
|
|
1054
|
-
if [[ -f "$ARTIFACTS_DIR/adversarial-review.md" ]]; then
|
|
1055
|
-
echo "## Adversarial Review Findings"
|
|
1056
|
-
cat "$ARTIFACTS_DIR/adversarial-review.md"
|
|
1057
|
-
echo ""
|
|
1058
|
-
fi
|
|
1059
|
-
if [[ -f "$ARTIFACTS_DIR/negative-review.md" ]]; then
|
|
1060
|
-
echo "## Negative Prompting Concerns"
|
|
1061
|
-
cat "$ARTIFACTS_DIR/negative-review.md"
|
|
1062
|
-
echo ""
|
|
1063
|
-
fi
|
|
1064
|
-
if [[ -f "$ARTIFACTS_DIR/dod-audit.md" ]]; then
|
|
1065
|
-
echo "## DoD Audit Failures"
|
|
1066
|
-
grep "❌" "$ARTIFACTS_DIR/dod-audit.md" 2>/dev/null || true
|
|
1067
|
-
echo ""
|
|
1068
|
-
fi
|
|
1069
|
-
if [[ -f "$ARTIFACTS_DIR/api-compat.log" ]] && grep -qi 'BREAKING' "$ARTIFACTS_DIR/api-compat.log" 2>/dev/null; then
|
|
1070
|
-
echo "## API Breaking Changes"
|
|
1071
|
-
cat "$ARTIFACTS_DIR/api-compat.log"
|
|
1072
|
-
echo ""
|
|
1073
|
-
fi
|
|
1074
|
-
|
|
1075
|
-
# Style findings last (deprioritized, informational)
|
|
1076
|
-
if [[ -f "$ARTIFACTS_DIR/classified-findings.json" ]]; then
|
|
1077
|
-
local style_count
|
|
1078
|
-
style_count=$(jq -r '.style // 0' "$ARTIFACTS_DIR/classified-findings.json" 2>/dev/null || echo "0")
|
|
1079
|
-
if [[ "$style_count" -gt 0 ]]; then
|
|
1080
|
-
echo "## Style Notes (non-blocking, address if time permits)"
|
|
1081
|
-
echo "${style_count} style suggestions found. These do not block the build."
|
|
1082
|
-
echo ""
|
|
1083
|
-
fi
|
|
1084
|
-
fi
|
|
1085
|
-
} > "$feedback_file"
|
|
1086
|
-
|
|
1087
|
-
# Validate feedback file has actual content
|
|
1088
|
-
if [[ ! -s "$feedback_file" ]]; then
|
|
1089
|
-
warn "No quality feedback collected — skipping rebuild"
|
|
1090
|
-
return 1
|
|
1091
|
-
fi
|
|
1092
|
-
|
|
1093
|
-
# Reset build/test stages
|
|
1094
|
-
set_stage_status "build" "pending"
|
|
1095
|
-
set_stage_status "test" "pending"
|
|
1096
|
-
set_stage_status "review" "pending"
|
|
1097
|
-
|
|
1098
|
-
# Augment GOAL with quality feedback (route-specific instructions)
|
|
1099
|
-
local original_goal="$GOAL"
|
|
1100
|
-
local feedback_content
|
|
1101
|
-
feedback_content=$(cat "$feedback_file")
|
|
1102
|
-
|
|
1103
|
-
local route_instruction=""
|
|
1104
|
-
case "$route" in
|
|
1105
|
-
security)
|
|
1106
|
-
route_instruction="SECURITY PRIORITY: Fix all security vulnerabilities FIRST, then address other issues. Security issues are BLOCKING."
|
|
1107
|
-
;;
|
|
1108
|
-
performance)
|
|
1109
|
-
route_instruction="PERFORMANCE PRIORITY: Address performance regressions and optimizations. Check for N+1 queries, memory leaks, and algorithmic complexity."
|
|
1110
|
-
;;
|
|
1111
|
-
testing)
|
|
1112
|
-
route_instruction="TESTING PRIORITY: Add missing test coverage and fix flaky tests before addressing other issues."
|
|
1113
|
-
;;
|
|
1114
|
-
correctness)
|
|
1115
|
-
route_instruction="Fix every issue listed above while keeping all existing functionality working."
|
|
1116
|
-
;;
|
|
1117
|
-
architecture)
|
|
1118
|
-
route_instruction="ARCHITECTURE: Fix structural issues. Check dependency direction, layer boundaries, and separation of concerns."
|
|
1119
|
-
;;
|
|
1120
|
-
*)
|
|
1121
|
-
route_instruction="Fix every issue listed above while keeping all existing functionality working."
|
|
1122
|
-
;;
|
|
1123
|
-
esac
|
|
1124
|
-
|
|
1125
|
-
GOAL="$GOAL
|
|
1126
|
-
|
|
1127
|
-
IMPORTANT — Compound quality review found issues (route: ${route}). Fix ALL of these:
|
|
1128
|
-
$feedback_content
|
|
1129
|
-
|
|
1130
|
-
${route_instruction}"
|
|
1131
|
-
|
|
1132
|
-
# Re-run self-healing build→test
|
|
1133
|
-
info "Rebuilding with quality feedback (route: ${route})..."
|
|
1134
|
-
if self_healing_build_test; then
|
|
1135
|
-
GOAL="$original_goal"
|
|
1136
|
-
return 0
|
|
1137
|
-
else
|
|
1138
|
-
GOAL="$original_goal"
|
|
1139
|
-
return 1
|
|
1140
|
-
fi
|
|
1141
|
-
}
|
|
6
|
+
# Defaults for variables normally set by sw-pipeline.sh (safe under set -u).
|
|
7
|
+
ARTIFACTS_DIR="${ARTIFACTS_DIR:-.claude/pipeline-artifacts}"
|
|
8
|
+
SCRIPT_DIR="${SCRIPT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
|
9
|
+
NO_GITHUB="${NO_GITHUB:-false}"
|
|
10
|
+
|
|
11
|
+
# Source compound audit cascade library (fail-open)
|
|
12
|
+
if [[ -f "${SCRIPT_DIR}/lib/compound-audit.sh" ]]; then
|
|
13
|
+
_COMPOUND_AUDIT_LOADED=""
|
|
14
|
+
source "${SCRIPT_DIR}/lib/compound-audit.sh"
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Source sub-modules
|
|
18
|
+
if [[ -f "${SCRIPT_DIR}/lib/pipeline-intelligence-skip.sh" ]]; then
|
|
19
|
+
source "${SCRIPT_DIR}/lib/pipeline-intelligence-skip.sh"
|
|
20
|
+
fi
|
|
21
|
+
if [[ -f "${SCRIPT_DIR}/lib/pipeline-intelligence-scoring.sh" ]]; then
|
|
22
|
+
source "${SCRIPT_DIR}/lib/pipeline-intelligence-scoring.sh"
|
|
23
|
+
fi
|
|
24
|
+
if [[ -f "${SCRIPT_DIR}/lib/pipeline-intelligence-compound.sh" ]]; then
|
|
25
|
+
source "${SCRIPT_DIR}/lib/pipeline-intelligence-compound.sh"
|
|
26
|
+
fi
|
|
1142
27
|
|
|
1143
28
|
stage_compound_quality() {
|
|
1144
29
|
CURRENT_STAGE_ID="compound_quality"
|
|
@@ -1148,13 +33,15 @@ stage_compound_quality() {
|
|
|
1148
33
|
_cq_real_changes=$(git diff --name-only "origin/${BASE_BRANCH:-main}...HEAD" \
|
|
1149
34
|
-- . ':!.claude/loop-state.md' ':!.claude/pipeline-state.md' \
|
|
1150
35
|
':!.claude/pipeline-artifacts/*' ':!**/progress.md' \
|
|
1151
|
-
':!**/error-summary.json' 2>/dev/null | wc -l ||
|
|
36
|
+
':!**/error-summary.json' 2>/dev/null | wc -l || true)
|
|
37
|
+
_cq_real_changes="${_cq_real_changes:-0}"
|
|
1152
38
|
_cq_real_changes=$(echo "$_cq_real_changes" | tr -d '[:space:]')
|
|
1153
39
|
[[ -z "$_cq_real_changes" ]] && _cq_real_changes=0
|
|
1154
40
|
# Fallback: if no remote, compare against first commit
|
|
1155
41
|
if [[ "$_cq_real_changes" -eq 0 ]] 2>/dev/null; then
|
|
1156
42
|
_cq_real_changes=$(git diff --name-only "$(git rev-list --max-parents=0 HEAD 2>/dev/null)...HEAD" \
|
|
1157
|
-
-- . ':!.claude/*' ':!**/progress.md' ':!**/error-summary.json' 2>/dev/null | wc -l ||
|
|
43
|
+
-- . ':!.claude/*' ':!**/progress.md' ':!**/error-summary.json' 2>/dev/null | wc -l || true)
|
|
44
|
+
_cq_real_changes="${_cq_real_changes:-0}"
|
|
1158
45
|
_cq_real_changes=$(echo "$_cq_real_changes" | tr -d '[:space:]')
|
|
1159
46
|
[[ -z "$_cq_real_changes" ]] && _cq_real_changes=0
|
|
1160
47
|
fi
|
|
@@ -1303,6 +190,16 @@ stage_compound_quality() {
|
|
|
1303
190
|
# Convergence tracking
|
|
1304
191
|
local prev_issue_count=-1
|
|
1305
192
|
|
|
193
|
+
# Compound audit cascade state (persists across cycles)
|
|
194
|
+
local _cascade_all_findings="[]"
|
|
195
|
+
local _cascade_active_agents="logic integration completeness"
|
|
196
|
+
local _cascade_diff=""
|
|
197
|
+
_cascade_diff=$(git diff "${BASE_BRANCH:-main}...HEAD" 2>/dev/null | head -5000) || _cascade_diff=""
|
|
198
|
+
local _cascade_plan=""
|
|
199
|
+
if [[ -f "$ARTIFACTS_DIR/plan.md" ]]; then
|
|
200
|
+
_cascade_plan=$(head -200 "$ARTIFACTS_DIR/plan.md" 2>/dev/null) || true
|
|
201
|
+
fi
|
|
202
|
+
|
|
1306
203
|
local cycle=0
|
|
1307
204
|
while [[ "$cycle" -lt "$max_cycles" ]]; do
|
|
1308
205
|
cycle=$((cycle + 1))
|
|
@@ -1488,6 +385,67 @@ stage_compound_quality() {
|
|
|
1488
385
|
success "Multi-dimensional quality: all checks passed"
|
|
1489
386
|
fi
|
|
1490
387
|
|
|
388
|
+
# 8. Compound Audit Cascade (adaptive multi-agent probing)
|
|
389
|
+
if type compound_audit_run_cycle >/dev/null 2>&1; then
|
|
390
|
+
echo ""
|
|
391
|
+
info "Running compound audit cascade (agents: $_cascade_active_agents)..."
|
|
392
|
+
|
|
393
|
+
local cascade_findings
|
|
394
|
+
cascade_findings=$(compound_audit_run_cycle "$_cascade_active_agents" "$_cascade_diff" "$_cascade_plan" "$_cascade_all_findings" "$cycle") || cascade_findings="[]"
|
|
395
|
+
|
|
396
|
+
# Dedup within this cycle
|
|
397
|
+
if type compound_audit_dedup_structural >/dev/null 2>&1; then
|
|
398
|
+
cascade_findings=$(compound_audit_dedup_structural "$cascade_findings") || cascade_findings="[]"
|
|
399
|
+
fi
|
|
400
|
+
|
|
401
|
+
local cascade_count
|
|
402
|
+
cascade_count=$(echo "$cascade_findings" | jq 'length' 2>/dev/null || echo "0")
|
|
403
|
+
local cascade_crit
|
|
404
|
+
cascade_crit=$(echo "$cascade_findings" | jq '[.[] | select(.severity == "critical" or .severity == "high")] | length' 2>/dev/null || echo "0")
|
|
405
|
+
|
|
406
|
+
if [[ "$cascade_count" -gt 0 ]]; then
|
|
407
|
+
warn "Compound audit: ${cascade_count} findings (${cascade_crit} critical/high)"
|
|
408
|
+
total_critical=$((total_critical + $(echo "$cascade_findings" | jq '[.[] | select(.severity == "critical")] | length' 2>/dev/null || echo "0")))
|
|
409
|
+
total_major=$((total_major + $(echo "$cascade_findings" | jq '[.[] | select(.severity == "high")] | length' 2>/dev/null || echo "0")))
|
|
410
|
+
total_minor=$((total_minor + $(echo "$cascade_findings" | jq '[.[] | select(.severity == "medium" or .severity == "low")] | length' 2>/dev/null || echo "0")))
|
|
411
|
+
[[ "$cascade_crit" -gt 0 ]] && all_passed=false
|
|
412
|
+
else
|
|
413
|
+
success "Compound audit: no findings"
|
|
414
|
+
fi
|
|
415
|
+
|
|
416
|
+
# Check cascade convergence
|
|
417
|
+
local cascade_converge=""
|
|
418
|
+
if type compound_audit_converged >/dev/null 2>&1; then
|
|
419
|
+
cascade_converge=$(compound_audit_converged "$cascade_findings" "$_cascade_all_findings" "$cycle" "$max_cycles") || cascade_converge=""
|
|
420
|
+
fi
|
|
421
|
+
|
|
422
|
+
type audit_emit >/dev/null 2>&1 && \
|
|
423
|
+
audit_emit "compound.cycle_complete" "cycle=$cycle" "findings=$cascade_count" \
|
|
424
|
+
"critical_high=$cascade_crit" "converged=$cascade_converge" || true
|
|
425
|
+
|
|
426
|
+
if [[ -n "$cascade_converge" ]]; then
|
|
427
|
+
success "Compound audit converged: $cascade_converge"
|
|
428
|
+
type audit_emit >/dev/null 2>&1 && \
|
|
429
|
+
audit_emit "compound.converged" "reason=$cascade_converge" "total_cycles=$cycle" || true
|
|
430
|
+
fi
|
|
431
|
+
|
|
432
|
+
# Merge findings for next cycle's context
|
|
433
|
+
_cascade_all_findings=$(echo "$_cascade_all_findings" "$cascade_findings" | jq -s '.[0] + .[1]' 2>/dev/null || echo "$_cascade_all_findings")
|
|
434
|
+
|
|
435
|
+
# Escalate: trigger specialists for next cycle
|
|
436
|
+
if type compound_audit_escalate >/dev/null 2>&1; then
|
|
437
|
+
local cascade_specialists
|
|
438
|
+
cascade_specialists=$(compound_audit_escalate "$cascade_findings") || cascade_specialists=""
|
|
439
|
+
if [[ -n "$cascade_specialists" ]]; then
|
|
440
|
+
info "Compound audit escalation: adding $cascade_specialists"
|
|
441
|
+
_cascade_active_agents="logic integration completeness $cascade_specialists"
|
|
442
|
+
fi
|
|
443
|
+
fi
|
|
444
|
+
|
|
445
|
+
# Save all findings to artifact
|
|
446
|
+
echo "$_cascade_all_findings" > "$ARTIFACTS_DIR/compound-audit-findings.json" 2>/dev/null || true
|
|
447
|
+
fi
|
|
448
|
+
|
|
1491
449
|
# ── Convergence Detection ──
|
|
1492
450
|
# Count critical/high issues from all review artifacts
|
|
1493
451
|
local current_issue_count=0
|
|
@@ -1508,6 +466,14 @@ stage_compound_quality() {
|
|
|
1508
466
|
fi
|
|
1509
467
|
current_issue_count=$((current_issue_count + quality_failures))
|
|
1510
468
|
|
|
469
|
+
# Add compound audit cascade findings to convergence count
|
|
470
|
+
if [[ -f "$ARTIFACTS_DIR/compound-audit-findings.json" ]]; then
|
|
471
|
+
local cascade_crit_count
|
|
472
|
+
cascade_crit_count=$(jq '[.[] | select(.severity == "critical" or .severity == "high")] | length' \
|
|
473
|
+
"$ARTIFACTS_DIR/compound-audit-findings.json" 2>/dev/null || echo "0")
|
|
474
|
+
current_issue_count=$((current_issue_count + ${cascade_crit_count:-0}))
|
|
475
|
+
fi
|
|
476
|
+
|
|
1511
477
|
emit_event "compound.cycle" \
|
|
1512
478
|
"issue=${ISSUE_NUMBER:-0}" \
|
|
1513
479
|
"cycle=$cycle" \
|