shipwright-cli 3.0.0 → 3.2.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/README.md +21 -7
- package/completions/_shipwright +247 -93
- package/completions/shipwright.bash +69 -15
- package/completions/shipwright.fish +309 -41
- package/config/decision-tiers.json +55 -0
- package/config/defaults.json +25 -2
- package/config/event-schema.json +142 -5
- package/config/policy.json +8 -0
- package/dashboard/public/index.html +6 -0
- package/dashboard/public/styles.css +76 -0
- package/dashboard/server.ts +51 -0
- package/dashboard/src/core/api.ts +5 -0
- package/dashboard/src/types/api.ts +10 -0
- package/dashboard/src/views/metrics.ts +69 -1
- package/package.json +3 -3
- package/scripts/lib/architecture.sh +2 -1
- package/scripts/lib/bootstrap.sh +0 -0
- package/scripts/lib/config.sh +0 -0
- package/scripts/lib/daemon-adaptive.sh +4 -2
- package/scripts/lib/daemon-dispatch.sh +24 -1
- package/scripts/lib/daemon-failure.sh +0 -0
- package/scripts/lib/daemon-health.sh +0 -0
- package/scripts/lib/daemon-patrol.sh +42 -7
- package/scripts/lib/daemon-poll.sh +17 -0
- package/scripts/lib/daemon-state.sh +17 -0
- package/scripts/lib/daemon-triage.sh +1 -1
- package/scripts/lib/decide-autonomy.sh +295 -0
- package/scripts/lib/decide-scoring.sh +228 -0
- package/scripts/lib/decide-signals.sh +462 -0
- package/scripts/lib/fleet-failover.sh +0 -0
- package/scripts/lib/helpers.sh +19 -18
- package/scripts/lib/pipeline-detection.sh +1 -1
- package/scripts/lib/pipeline-github.sh +0 -0
- package/scripts/lib/pipeline-intelligence.sh +23 -4
- package/scripts/lib/pipeline-quality-checks.sh +11 -6
- package/scripts/lib/pipeline-quality.sh +0 -0
- package/scripts/lib/pipeline-stages.sh +330 -33
- package/scripts/lib/pipeline-state.sh +14 -0
- package/scripts/lib/policy.sh +0 -0
- package/scripts/lib/test-helpers.sh +0 -0
- package/scripts/postinstall.mjs +75 -1
- package/scripts/signals/example-collector.sh +36 -0
- package/scripts/sw +8 -4
- package/scripts/sw-activity.sh +1 -7
- package/scripts/sw-adaptive.sh +7 -7
- package/scripts/sw-adversarial.sh +1 -1
- package/scripts/sw-architecture-enforcer.sh +1 -1
- package/scripts/sw-auth.sh +1 -1
- package/scripts/sw-autonomous.sh +1 -1
- package/scripts/sw-changelog.sh +1 -1
- package/scripts/sw-checkpoint.sh +1 -1
- package/scripts/sw-ci.sh +11 -6
- package/scripts/sw-cleanup.sh +1 -1
- package/scripts/sw-code-review.sh +36 -17
- package/scripts/sw-connect.sh +1 -1
- package/scripts/sw-context.sh +1 -1
- package/scripts/sw-cost.sh +71 -5
- package/scripts/sw-daemon.sh +6 -3
- package/scripts/sw-dashboard.sh +1 -1
- package/scripts/sw-db.sh +53 -38
- package/scripts/sw-decide.sh +685 -0
- package/scripts/sw-decompose.sh +1 -1
- package/scripts/sw-deps.sh +1 -1
- package/scripts/sw-developer-simulation.sh +1 -1
- package/scripts/sw-discovery.sh +80 -4
- package/scripts/sw-doc-fleet.sh +1 -1
- package/scripts/sw-docs-agent.sh +1 -1
- package/scripts/sw-docs.sh +1 -1
- package/scripts/sw-doctor.sh +1 -1
- package/scripts/sw-dora.sh +1 -1
- package/scripts/sw-durable.sh +9 -5
- package/scripts/sw-e2e-orchestrator.sh +1 -1
- package/scripts/sw-eventbus.sh +7 -4
- package/scripts/sw-evidence.sh +1 -1
- package/scripts/sw-feedback.sh +1 -1
- package/scripts/sw-fix.sh +1 -1
- package/scripts/sw-fleet-discover.sh +1 -1
- package/scripts/sw-fleet-viz.sh +6 -4
- package/scripts/sw-fleet.sh +1 -1
- package/scripts/sw-github-app.sh +3 -2
- package/scripts/sw-github-checks.sh +1 -1
- package/scripts/sw-github-deploy.sh +1 -1
- package/scripts/sw-github-graphql.sh +1 -1
- package/scripts/sw-guild.sh +1 -1
- package/scripts/sw-heartbeat.sh +1 -1
- package/scripts/sw-hygiene.sh +5 -3
- package/scripts/sw-incident.sh +9 -5
- package/scripts/sw-init.sh +1 -1
- package/scripts/sw-instrument.sh +1 -1
- package/scripts/sw-intelligence.sh +11 -6
- package/scripts/sw-jira.sh +1 -1
- package/scripts/sw-launchd.sh +1 -1
- package/scripts/sw-linear.sh +1 -1
- package/scripts/sw-logs.sh +1 -1
- package/scripts/sw-loop.sh +338 -32
- package/scripts/sw-memory.sh +23 -6
- package/scripts/sw-mission-control.sh +1 -1
- package/scripts/sw-model-router.sh +3 -2
- package/scripts/sw-otel.sh +8 -4
- package/scripts/sw-oversight.sh +1 -1
- package/scripts/sw-pipeline-composer.sh +3 -1
- package/scripts/sw-pipeline-vitals.sh +11 -6
- package/scripts/sw-pipeline.sh +92 -8
- package/scripts/sw-pm.sh +5 -4
- package/scripts/sw-pr-lifecycle.sh +7 -4
- package/scripts/sw-predictive.sh +11 -5
- package/scripts/sw-prep.sh +1 -1
- package/scripts/sw-ps.sh +1 -1
- package/scripts/sw-public-dashboard.sh +3 -2
- package/scripts/sw-quality.sh +21 -10
- package/scripts/sw-reaper.sh +1 -1
- package/scripts/sw-recruit.sh +1 -1
- package/scripts/sw-regression.sh +1 -1
- package/scripts/sw-release-manager.sh +1 -1
- package/scripts/sw-release.sh +1 -1
- package/scripts/sw-remote.sh +1 -1
- package/scripts/sw-replay.sh +1 -1
- package/scripts/sw-retro.sh +1 -1
- package/scripts/sw-review-rerun.sh +1 -1
- package/scripts/sw-scale.sh +69 -11
- package/scripts/sw-security-audit.sh +1 -1
- package/scripts/sw-self-optimize.sh +168 -4
- package/scripts/sw-session.sh +3 -3
- package/scripts/sw-setup.sh +1 -1
- package/scripts/sw-standup.sh +1 -1
- package/scripts/sw-status.sh +1 -1
- package/scripts/sw-strategic.sh +11 -6
- package/scripts/sw-stream.sh +7 -4
- package/scripts/sw-swarm.sh +3 -2
- package/scripts/sw-team-stages.sh +1 -1
- package/scripts/sw-templates.sh +3 -3
- package/scripts/sw-testgen.sh +11 -6
- package/scripts/sw-tmux-pipeline.sh +1 -1
- package/scripts/sw-tmux.sh +35 -1
- package/scripts/sw-trace.sh +1 -1
- package/scripts/sw-tracker.sh +1 -1
- package/scripts/sw-triage.sh +7 -7
- package/scripts/sw-upgrade.sh +1 -1
- package/scripts/sw-ux.sh +1 -1
- package/scripts/sw-webhook.sh +3 -2
- package/scripts/sw-widgets.sh +7 -4
- package/scripts/sw-worktree.sh +1 -1
- package/scripts/update-homebrew-sha.sh +21 -15
|
@@ -1,8 +1,125 @@
|
|
|
1
|
-
# pipeline-stages.sh — Stage implementations (intake, plan, build, test, review, pr, merge, deploy, validate, monitor) for sw-pipeline.sh
|
|
1
|
+
# pipeline-stages.sh — Stage implementations (intake, plan, build, test, review, compound_quality, pr, merge, deploy, validate, monitor) for sw-pipeline.sh
|
|
2
2
|
# Source from sw-pipeline.sh. Requires all pipeline globals and state/github/detection/quality modules.
|
|
3
3
|
[[ -n "${_PIPELINE_STAGES_LOADED:-}" ]] && return 0
|
|
4
4
|
_PIPELINE_STAGES_LOADED=1
|
|
5
5
|
|
|
6
|
+
# ─── Context pruning helpers ────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
# prune_context_section — Intelligently truncate a context section to fit a char budget.
|
|
9
|
+
# $1: section name (for logging/markers)
|
|
10
|
+
# $2: content string
|
|
11
|
+
# $3: max_chars (default 5000)
|
|
12
|
+
# For JSON content (starts with { or [): extracts summary fields via jq.
|
|
13
|
+
# For text content: sandwich approach — keeps first + last N lines.
|
|
14
|
+
# Outputs the (possibly truncated) content to stdout.
|
|
15
|
+
prune_context_section() {
|
|
16
|
+
local section_name="${1:-section}"
|
|
17
|
+
local content="${2:-}"
|
|
18
|
+
local max_chars="${3:-5000}"
|
|
19
|
+
|
|
20
|
+
[[ -z "$content" ]] && return 0
|
|
21
|
+
|
|
22
|
+
local content_len=${#content}
|
|
23
|
+
if [[ "$content_len" -le "$max_chars" ]]; then
|
|
24
|
+
printf '%s' "$content"
|
|
25
|
+
return 0
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
# JSON content — try jq summary extraction
|
|
29
|
+
local first_char="${content:0:1}"
|
|
30
|
+
if [[ "$first_char" == "{" || "$first_char" == "[" ]]; then
|
|
31
|
+
local summary=""
|
|
32
|
+
# Try extracting summary/results fields
|
|
33
|
+
summary=$(printf '%s' "$content" | jq -r '
|
|
34
|
+
if type == "object" then
|
|
35
|
+
to_entries | map(
|
|
36
|
+
if (.value | type) == "array" then
|
|
37
|
+
"\(.key): \(.value | length) items"
|
|
38
|
+
elif (.value | type) == "object" then
|
|
39
|
+
"\(.key): \(.value | keys | join(", "))"
|
|
40
|
+
else
|
|
41
|
+
"\(.key): \(.value)"
|
|
42
|
+
end
|
|
43
|
+
) | join("\n")
|
|
44
|
+
elif type == "array" then
|
|
45
|
+
.[:5] | map(tostring) | join("\n")
|
|
46
|
+
else . end
|
|
47
|
+
' 2>/dev/null) || true
|
|
48
|
+
|
|
49
|
+
if [[ -n "$summary" && ${#summary} -le "$max_chars" ]]; then
|
|
50
|
+
printf '%s' "$summary"
|
|
51
|
+
return 0
|
|
52
|
+
fi
|
|
53
|
+
# jq failed or still too large — fall through to text truncation
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Text content — sandwich approach (first N + last N lines)
|
|
57
|
+
local line_count=0
|
|
58
|
+
line_count=$(printf '%s\n' "$content" | wc -l | xargs)
|
|
59
|
+
|
|
60
|
+
# Calculate how many lines to keep from each end
|
|
61
|
+
# Approximate chars-per-line to figure out line budget
|
|
62
|
+
local avg_chars_per_line=80
|
|
63
|
+
if [[ "$line_count" -gt 0 ]]; then
|
|
64
|
+
avg_chars_per_line=$(( content_len / line_count ))
|
|
65
|
+
[[ "$avg_chars_per_line" -lt 20 ]] && avg_chars_per_line=20
|
|
66
|
+
fi
|
|
67
|
+
local total_lines_budget=$(( max_chars / avg_chars_per_line ))
|
|
68
|
+
[[ "$total_lines_budget" -lt 4 ]] && total_lines_budget=4
|
|
69
|
+
local half=$(( total_lines_budget / 2 ))
|
|
70
|
+
|
|
71
|
+
local head_part=""
|
|
72
|
+
local tail_part=""
|
|
73
|
+
head_part=$(printf '%s\n' "$content" | head -"$half")
|
|
74
|
+
tail_part=$(printf '%s\n' "$content" | tail -"$half")
|
|
75
|
+
|
|
76
|
+
printf '%s\n[... %s truncated: %d→%d chars ...]\n%s' \
|
|
77
|
+
"$head_part" "$section_name" "$content_len" "$max_chars" "$tail_part"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# guard_prompt_size — Warn and hard-truncate if prompt exceeds budget.
|
|
81
|
+
# $1: stage name (for logging)
|
|
82
|
+
# $2: prompt content
|
|
83
|
+
# $3: max_chars (default 100000)
|
|
84
|
+
# Outputs the (possibly truncated) prompt to stdout.
|
|
85
|
+
PIPELINE_PROMPT_BUDGET="${PIPELINE_PROMPT_BUDGET:-100000}"
|
|
86
|
+
|
|
87
|
+
guard_prompt_size() {
|
|
88
|
+
local stage_name="${1:-stage}"
|
|
89
|
+
local prompt="${2:-}"
|
|
90
|
+
local max_chars="${3:-$PIPELINE_PROMPT_BUDGET}"
|
|
91
|
+
|
|
92
|
+
local prompt_len=${#prompt}
|
|
93
|
+
if [[ "$prompt_len" -le "$max_chars" ]]; then
|
|
94
|
+
printf '%s' "$prompt"
|
|
95
|
+
return 0
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
warn "${stage_name} prompt too large (${prompt_len} chars, budget ${max_chars}) — truncating"
|
|
99
|
+
emit_event "pipeline.prompt_truncated" \
|
|
100
|
+
"stage=$stage_name" \
|
|
101
|
+
"original=$prompt_len" \
|
|
102
|
+
"budget=$max_chars" 2>/dev/null || true
|
|
103
|
+
|
|
104
|
+
printf '%s\n\n... [CONTEXT TRUNCATED: %s prompt exceeded %d char budget. Focus on the goal and requirements.]' \
|
|
105
|
+
"${prompt:0:$max_chars}" "$stage_name" "$max_chars"
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# ─── Safe git helpers ────────────────────────────────────────────────────────
|
|
109
|
+
# BASE_BRANCH may not exist locally (e.g. --local mode with no remote).
|
|
110
|
+
# These helpers return empty output instead of crashing under set -euo pipefail.
|
|
111
|
+
_safe_base_log() {
|
|
112
|
+
local branch="${BASE_BRANCH:-main}"
|
|
113
|
+
git rev-parse --verify "$branch" >/dev/null 2>&1 || { echo ""; return 0; }
|
|
114
|
+
git log "$@" "${branch}..HEAD" 2>/dev/null || true
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
_safe_base_diff() {
|
|
118
|
+
local branch="${BASE_BRANCH:-main}"
|
|
119
|
+
git rev-parse --verify "$branch" >/dev/null 2>&1 || { git diff HEAD~5 "$@" 2>/dev/null || true; return 0; }
|
|
120
|
+
git diff "${branch}...HEAD" "$@" 2>/dev/null || true
|
|
121
|
+
}
|
|
122
|
+
|
|
6
123
|
show_stage_preview() {
|
|
7
124
|
local stage_id="$1"
|
|
8
125
|
echo ""
|
|
@@ -15,6 +132,7 @@ show_stage_preview() {
|
|
|
15
132
|
test_first) echo -e " Generate tests from requirements (TDD mode) before implementation" ;;
|
|
16
133
|
test) echo -e " Run test suite and check coverage" ;;
|
|
17
134
|
review) echo -e " AI code review on the diff, post findings" ;;
|
|
135
|
+
compound_quality) echo -e " Adversarial review, negative tests, e2e, DoD audit" ;;
|
|
18
136
|
pr) echo -e " Create GitHub PR with labels, reviewers, milestone" ;;
|
|
19
137
|
merge) echo -e " Wait for CI checks, merge PR, optionally delete branch" ;;
|
|
20
138
|
deploy) echo -e " Deploy to staging/production with rollback" ;;
|
|
@@ -162,6 +280,7 @@ ${ISSUE_BODY}
|
|
|
162
280
|
|
|
163
281
|
# Inject architecture context (import graph, modules, test map)
|
|
164
282
|
if [[ -n "$arch_context" ]]; then
|
|
283
|
+
arch_context=$(prune_context_section "architecture" "$arch_context" 5000)
|
|
165
284
|
plan_prompt="${plan_prompt}
|
|
166
285
|
## Architecture Context
|
|
167
286
|
${arch_context}
|
|
@@ -173,6 +292,7 @@ ${arch_context}
|
|
|
173
292
|
if [[ -f "$_context_bundle" ]]; then
|
|
174
293
|
local _cb_content
|
|
175
294
|
_cb_content=$(cat "$_context_bundle" 2>/dev/null | head -100 || true)
|
|
295
|
+
_cb_content=$(prune_context_section "context-bundle" "$_cb_content" 8000)
|
|
176
296
|
if [[ -n "$_cb_content" ]]; then
|
|
177
297
|
plan_prompt="${plan_prompt}
|
|
178
298
|
## Pipeline Context
|
|
@@ -188,6 +308,7 @@ ${_cb_content}
|
|
|
188
308
|
if [[ -n "$plan_memory" && "$plan_memory" != *'"results":[]'* && "$plan_memory" != *'"error"'* ]]; then
|
|
189
309
|
local memory_summary
|
|
190
310
|
memory_summary=$(echo "$plan_memory" | jq -r '.results[]? | "- \(.)"' 2>/dev/null | head -10 || true)
|
|
311
|
+
memory_summary=$(prune_context_section "memory" "$memory_summary" 10000)
|
|
191
312
|
if [[ -n "$memory_summary" ]]; then
|
|
192
313
|
plan_prompt="${plan_prompt}
|
|
193
314
|
## Historical Context (from previous pipelines)
|
|
@@ -212,6 +333,7 @@ ${plan_hint}
|
|
|
212
333
|
if [[ -x "$SCRIPT_DIR/sw-discovery.sh" ]]; then
|
|
213
334
|
local plan_discoveries
|
|
214
335
|
plan_discoveries=$("$SCRIPT_DIR/sw-discovery.sh" inject "*.md,*.json" 2>/dev/null | head -20 || true)
|
|
336
|
+
plan_discoveries=$(prune_context_section "discoveries" "$plan_discoveries" 3000)
|
|
215
337
|
if [[ -n "$plan_discoveries" ]]; then
|
|
216
338
|
plan_prompt="${plan_prompt}
|
|
217
339
|
## Discoveries from Other Pipelines
|
|
@@ -232,6 +354,7 @@ ${plan_discoveries}
|
|
|
232
354
|
"Patterns: \((.patterns // []) | join(", "))",
|
|
233
355
|
"Rules: \((.rules // []) | join("; "))"
|
|
234
356
|
' "$arch_file_plan" 2>/dev/null || true)
|
|
357
|
+
arch_patterns=$(prune_context_section "intelligence" "$arch_patterns" 5000)
|
|
235
358
|
if [[ -n "$arch_patterns" ]]; then
|
|
236
359
|
plan_prompt="${plan_prompt}
|
|
237
360
|
## Architecture Patterns
|
|
@@ -268,6 +391,12 @@ Focus on: threat modeling, OWASP top 10, input validation, authentication/author
|
|
|
268
391
|
- Test command: ${TEST_CMD:-not configured}
|
|
269
392
|
- Task type: ${TASK_TYPE:-feature}
|
|
270
393
|
|
|
394
|
+
## Context Efficiency
|
|
395
|
+
- Batch independent tool calls in parallel when possible
|
|
396
|
+
- Read specific file sections (offset/limit) instead of entire large files
|
|
397
|
+
- Use targeted grep searches — avoid scanning entire codebases into context
|
|
398
|
+
- Delegate multi-file analysis to subagents when available
|
|
399
|
+
|
|
271
400
|
## Required Output
|
|
272
401
|
Create a Markdown plan with these sections:
|
|
273
402
|
|
|
@@ -290,6 +419,9 @@ How to verify the implementation works.
|
|
|
290
419
|
Checklist of completion criteria.
|
|
291
420
|
"
|
|
292
421
|
|
|
422
|
+
# Guard total prompt size
|
|
423
|
+
plan_prompt=$(guard_prompt_size "plan" "$plan_prompt")
|
|
424
|
+
|
|
293
425
|
local plan_model
|
|
294
426
|
plan_model=$(jq -r --arg id "plan" '(.stages[] | select(.id == $id) | .config.model) // .defaults.model // "opus"' "$PIPELINE_CONFIG" 2>/dev/null) || true
|
|
295
427
|
[[ -n "$MODEL" ]] && plan_model="$MODEL"
|
|
@@ -300,10 +432,22 @@ Checklist of completion criteria.
|
|
|
300
432
|
fi
|
|
301
433
|
|
|
302
434
|
local _token_log="${ARTIFACTS_DIR}/.claude-tokens-plan.log"
|
|
303
|
-
claude --print --model "$plan_model" --max-turns 25 \
|
|
435
|
+
claude --print --model "$plan_model" --max-turns 25 --dangerously-skip-permissions \
|
|
304
436
|
"$plan_prompt" < /dev/null > "$plan_file" 2>"$_token_log" || true
|
|
305
437
|
parse_claude_tokens "$_token_log"
|
|
306
438
|
|
|
439
|
+
# Claude may write to disk via tools instead of stdout — rescue those files
|
|
440
|
+
local _plan_rescue
|
|
441
|
+
for _plan_rescue in "${PROJECT_ROOT}/PLAN.md" "${PROJECT_ROOT}/plan.md" \
|
|
442
|
+
"${PROJECT_ROOT}/implementation-plan.md"; do
|
|
443
|
+
if [[ -s "$_plan_rescue" ]] && [[ $(wc -l < "$plan_file" 2>/dev/null | xargs) -lt 10 ]]; then
|
|
444
|
+
info "Plan written to ${_plan_rescue} via tools — adopting as plan artifact"
|
|
445
|
+
cat "$_plan_rescue" >> "$plan_file"
|
|
446
|
+
rm -f "$_plan_rescue"
|
|
447
|
+
break
|
|
448
|
+
fi
|
|
449
|
+
done
|
|
450
|
+
|
|
307
451
|
if [[ ! -s "$plan_file" ]]; then
|
|
308
452
|
error "Plan generation failed — empty output"
|
|
309
453
|
return 1
|
|
@@ -587,6 +731,7 @@ stage_design() {
|
|
|
587
731
|
if type gather_architecture_context &>/dev/null; then
|
|
588
732
|
arch_struct_context=$(gather_architecture_context "${PROJECT_ROOT:-.}" 2>/dev/null || true)
|
|
589
733
|
fi
|
|
734
|
+
arch_struct_context=$(prune_context_section "architecture" "$arch_struct_context" 5000)
|
|
590
735
|
|
|
591
736
|
# Memory integration — inject context if memory system available
|
|
592
737
|
local memory_context=""
|
|
@@ -597,12 +742,14 @@ stage_design() {
|
|
|
597
742
|
if [[ -z "$memory_context" ]] && [[ -x "$SCRIPT_DIR/sw-memory.sh" ]]; then
|
|
598
743
|
memory_context=$(bash "$SCRIPT_DIR/sw-memory.sh" inject "design" 2>/dev/null) || true
|
|
599
744
|
fi
|
|
745
|
+
memory_context=$(prune_context_section "memory" "$memory_context" 10000)
|
|
600
746
|
|
|
601
747
|
# Inject cross-pipeline discoveries for design stage
|
|
602
748
|
local design_discoveries=""
|
|
603
749
|
if [[ -x "$SCRIPT_DIR/sw-discovery.sh" ]]; then
|
|
604
750
|
design_discoveries=$("$SCRIPT_DIR/sw-discovery.sh" inject "*.md,*.ts,*.tsx,*.js" 2>/dev/null | head -20 || true)
|
|
605
751
|
fi
|
|
752
|
+
design_discoveries=$(prune_context_section "discoveries" "$design_discoveries" 3000)
|
|
606
753
|
|
|
607
754
|
# Inject architecture model patterns if available
|
|
608
755
|
local arch_context=""
|
|
@@ -626,6 +773,7 @@ ${arch_patterns}
|
|
|
626
773
|
${arch_layers}}"
|
|
627
774
|
fi
|
|
628
775
|
fi
|
|
776
|
+
arch_context=$(prune_context_section "intelligence" "$arch_context" 5000)
|
|
629
777
|
|
|
630
778
|
# Inject rejected design approaches and anti-patterns from memory
|
|
631
779
|
local design_antipatterns=""
|
|
@@ -633,6 +781,7 @@ ${arch_layers}}"
|
|
|
633
781
|
local rejected_designs
|
|
634
782
|
rejected_designs=$(intelligence_search_memory "rejected design approaches anti-patterns for: ${GOAL:-}" "${HOME}/.shipwright/memory" 3 2>/dev/null) || true
|
|
635
783
|
if [[ -n "$rejected_designs" ]]; then
|
|
784
|
+
rejected_designs=$(prune_context_section "antipatterns" "$rejected_designs" 5000)
|
|
636
785
|
design_antipatterns="
|
|
637
786
|
## Rejected Approaches (from past reviews)
|
|
638
787
|
These design approaches were rejected in past reviews. Avoid repeating them:
|
|
@@ -698,6 +847,9 @@ Produce this EXACT format:
|
|
|
698
847
|
|
|
699
848
|
Be concrete and specific. Reference actual file paths in the codebase. Consider edge cases and failure modes."
|
|
700
849
|
|
|
850
|
+
# Guard total prompt size
|
|
851
|
+
design_prompt=$(guard_prompt_size "design" "$design_prompt")
|
|
852
|
+
|
|
701
853
|
local design_model
|
|
702
854
|
design_model=$(jq -r --arg id "design" '(.stages[] | select(.id == $id) | .config.model) // .defaults.model // "opus"' "$PIPELINE_CONFIG" 2>/dev/null) || true
|
|
703
855
|
[[ -n "$MODEL" ]] && design_model="$MODEL"
|
|
@@ -708,10 +860,22 @@ Be concrete and specific. Reference actual file paths in the codebase. Consider
|
|
|
708
860
|
fi
|
|
709
861
|
|
|
710
862
|
local _token_log="${ARTIFACTS_DIR}/.claude-tokens-design.log"
|
|
711
|
-
claude --print --model "$design_model" --max-turns 25 \
|
|
863
|
+
claude --print --model "$design_model" --max-turns 25 --dangerously-skip-permissions \
|
|
712
864
|
"$design_prompt" < /dev/null > "$design_file" 2>"$_token_log" || true
|
|
713
865
|
parse_claude_tokens "$_token_log"
|
|
714
866
|
|
|
867
|
+
# Claude may write to disk via tools instead of stdout — rescue those files
|
|
868
|
+
local _design_rescue
|
|
869
|
+
for _design_rescue in "${PROJECT_ROOT}/design-adr.md" "${PROJECT_ROOT}/design.md" \
|
|
870
|
+
"${PROJECT_ROOT}/ADR.md" "${PROJECT_ROOT}/DESIGN.md"; do
|
|
871
|
+
if [[ -s "$_design_rescue" ]] && [[ $(wc -l < "$design_file" 2>/dev/null | xargs) -lt 10 ]]; then
|
|
872
|
+
info "Design written to ${_design_rescue} via tools — adopting as design artifact"
|
|
873
|
+
cat "$_design_rescue" >> "$design_file"
|
|
874
|
+
rm -f "$_design_rescue"
|
|
875
|
+
break
|
|
876
|
+
fi
|
|
877
|
+
done
|
|
878
|
+
|
|
715
879
|
if [[ ! -s "$design_file" ]]; then
|
|
716
880
|
error "Design generation failed — empty output"
|
|
717
881
|
return 1
|
|
@@ -739,7 +903,7 @@ Be concrete and specific. Reference actual file paths in the codebase. Consider
|
|
|
739
903
|
files_to_modify=$(sed -n '/Files to modify/,/^-\|^#\|^$/p' "$design_file" 2>/dev/null | grep -E '^\s*-' | head -20 || true)
|
|
740
904
|
|
|
741
905
|
if [[ -n "$files_to_create" || -n "$files_to_modify" ]]; then
|
|
742
|
-
info "Design scope: ${DIM}$(echo "$files_to_create $files_to_modify" | grep -c '^\s*-' ||
|
|
906
|
+
info "Design scope: ${DIM}$(echo "$files_to_create $files_to_modify" | grep -c '^\s*-' || true) file(s)${RESET}"
|
|
743
907
|
fi
|
|
744
908
|
|
|
745
909
|
# Post design to GitHub issue
|
|
@@ -1077,8 +1241,9 @@ ${prevention_text}"
|
|
|
1077
1241
|
loop_args+=(--resume)
|
|
1078
1242
|
fi
|
|
1079
1243
|
|
|
1080
|
-
# Skip permissions
|
|
1081
|
-
|
|
1244
|
+
# Skip permissions — pipeline runs headlessly (claude -p) and has no terminal
|
|
1245
|
+
# for interactive permission prompts. Without this flag, agents can't write files.
|
|
1246
|
+
loop_args+=(--skip-permissions)
|
|
1082
1247
|
|
|
1083
1248
|
info "Starting build loop: ${DIM}shipwright loop${RESET} (max ${max_iter} iterations, ${agents} agent(s))"
|
|
1084
1249
|
|
|
@@ -1131,13 +1296,13 @@ ${prevention_text}"
|
|
|
1131
1296
|
|
|
1132
1297
|
# Count commits made during build
|
|
1133
1298
|
local commit_count
|
|
1134
|
-
commit_count=$(
|
|
1299
|
+
commit_count=$(_safe_base_log --oneline | wc -l | xargs)
|
|
1135
1300
|
info "Build produced ${BOLD}$commit_count${RESET} commit(s)"
|
|
1136
1301
|
|
|
1137
1302
|
# Commit quality evaluation when intelligence is enabled
|
|
1138
1303
|
if type intelligence_search_memory >/dev/null 2>&1 && command -v claude >/dev/null 2>&1 && [[ "${commit_count:-0}" -gt 0 ]]; then
|
|
1139
1304
|
local commit_msgs
|
|
1140
|
-
commit_msgs=$(
|
|
1305
|
+
commit_msgs=$(_safe_base_log --format="%s" | head -20)
|
|
1141
1306
|
local quality_score
|
|
1142
1307
|
quality_score=$(claude --print --output-format text -p "Rate the quality of these git commit messages on a scale of 0-100. Consider: focus (one thing per commit), clarity (describes the why), atomicity (small logical units). Reply with ONLY a number 0-100.
|
|
1143
1308
|
|
|
@@ -1201,7 +1366,8 @@ stage_test() {
|
|
|
1201
1366
|
# Post failure to GitHub with more context
|
|
1202
1367
|
if [[ -n "$ISSUE_NUMBER" ]]; then
|
|
1203
1368
|
local log_lines
|
|
1204
|
-
log_lines=$(wc -l < "$test_log" 2>/dev/null ||
|
|
1369
|
+
log_lines=$(wc -l < "$test_log" 2>/dev/null || true)
|
|
1370
|
+
log_lines="${log_lines:-0}"
|
|
1205
1371
|
local log_excerpt
|
|
1206
1372
|
if [[ "$log_lines" -lt 60 ]]; then
|
|
1207
1373
|
log_excerpt="$(cat "$test_log" 2>/dev/null || true)"
|
|
@@ -1276,8 +1442,7 @@ stage_review() {
|
|
|
1276
1442
|
local diff_file="$ARTIFACTS_DIR/review-diff.patch"
|
|
1277
1443
|
local review_file="$ARTIFACTS_DIR/review.md"
|
|
1278
1444
|
|
|
1279
|
-
|
|
1280
|
-
git diff HEAD~5 > "$diff_file" 2>/dev/null || true
|
|
1445
|
+
_safe_base_diff > "$diff_file" 2>/dev/null || true
|
|
1281
1446
|
|
|
1282
1447
|
if [[ ! -s "$diff_file" ]]; then
|
|
1283
1448
|
warn "No diff found — skipping review"
|
|
@@ -1290,13 +1455,13 @@ stage_review() {
|
|
|
1290
1455
|
fi
|
|
1291
1456
|
|
|
1292
1457
|
local diff_stats
|
|
1293
|
-
diff_stats=$(
|
|
1458
|
+
diff_stats=$(_safe_base_diff --stat | tail -1 || echo "")
|
|
1294
1459
|
info "Running AI code review... ${DIM}($diff_stats)${RESET}"
|
|
1295
1460
|
|
|
1296
1461
|
# Semantic risk scoring when intelligence is enabled
|
|
1297
1462
|
if type intelligence_search_memory >/dev/null 2>&1 && command -v claude >/dev/null 2>&1; then
|
|
1298
1463
|
local diff_files
|
|
1299
|
-
diff_files=$(
|
|
1464
|
+
diff_files=$(_safe_base_diff --name-only || true)
|
|
1300
1465
|
local risk_score="low"
|
|
1301
1466
|
# Fast heuristic: flag high-risk file patterns
|
|
1302
1467
|
if echo "$diff_files" | grep -qiE 'migration|schema|auth|crypto|security|password|token|secret|\.env'; then
|
|
@@ -1343,6 +1508,7 @@ If no issues are found, write: \"Review clean — no issues found.\"
|
|
|
1343
1508
|
if type intelligence_search_memory >/dev/null 2>&1; then
|
|
1344
1509
|
local review_memory
|
|
1345
1510
|
review_memory=$(intelligence_search_memory "code review findings anti-patterns for: ${GOAL:-}" "${HOME}/.shipwright/memory" 5 2>/dev/null) || true
|
|
1511
|
+
review_memory=$(prune_context_section "memory" "$review_memory" 10000)
|
|
1346
1512
|
if [[ -n "$review_memory" ]]; then
|
|
1347
1513
|
review_prompt+="
|
|
1348
1514
|
## Known Issues from Previous Reviews
|
|
@@ -1390,11 +1556,12 @@ $(cat "$dod_file")
|
|
|
1390
1556
|
## Diff to Review
|
|
1391
1557
|
$(cat "$diff_file")"
|
|
1392
1558
|
|
|
1393
|
-
#
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1559
|
+
# Guard total prompt size
|
|
1560
|
+
review_prompt=$(guard_prompt_size "review" "$review_prompt")
|
|
1561
|
+
|
|
1562
|
+
# Skip permissions — pipeline runs headlessly (claude -p) and has no terminal
|
|
1563
|
+
# for interactive permission prompts. Same rationale as build stage (line ~1083).
|
|
1564
|
+
local review_args=(--print --model "$review_model" --max-turns 25 --dangerously-skip-permissions)
|
|
1398
1565
|
|
|
1399
1566
|
claude "${review_args[@]}" "$review_prompt" < /dev/null > "$review_file" 2>"${ARTIFACTS_DIR}/.claude-tokens-review.log" || true
|
|
1400
1567
|
parse_claude_tokens "${ARTIFACTS_DIR}/.claude-tokens-review.log"
|
|
@@ -1539,15 +1706,143 @@ ${review_summary}
|
|
|
1539
1706
|
log_stage "review" "AI review complete ($total_issues issues: $critical_count critical, $bug_count bugs, $warning_count suggestions)"
|
|
1540
1707
|
}
|
|
1541
1708
|
|
|
1709
|
+
# ─── Compound Quality (fallback) ────────────────────────────────────────────
|
|
1710
|
+
# Basic implementation: adversarial review, negative testing, e2e checks, DoD audit.
|
|
1711
|
+
# If pipeline-intelligence.sh was sourced first, its enhanced version takes priority.
|
|
1712
|
+
if ! type stage_compound_quality >/dev/null 2>&1; then
|
|
1713
|
+
stage_compound_quality() {
|
|
1714
|
+
CURRENT_STAGE_ID="compound_quality"
|
|
1715
|
+
|
|
1716
|
+
# Read stage config from pipeline template
|
|
1717
|
+
local cfg
|
|
1718
|
+
cfg=$(jq -r '.stages[] | select(.id == "compound_quality") | .config // {}' "$PIPELINE_CONFIG" 2>/dev/null) || cfg="{}"
|
|
1719
|
+
|
|
1720
|
+
local do_adversarial do_negative do_e2e do_dod max_cycles blocking
|
|
1721
|
+
do_adversarial=$(echo "$cfg" | jq -r '.adversarial // false')
|
|
1722
|
+
do_negative=$(echo "$cfg" | jq -r '.negative // false')
|
|
1723
|
+
do_e2e=$(echo "$cfg" | jq -r '.e2e // false')
|
|
1724
|
+
do_dod=$(echo "$cfg" | jq -r '.dod_audit // false')
|
|
1725
|
+
max_cycles=$(echo "$cfg" | jq -r '.max_cycles // 1')
|
|
1726
|
+
blocking=$(echo "$cfg" | jq -r '.compound_quality_blocking // false')
|
|
1727
|
+
|
|
1728
|
+
local pass_count=0 fail_count=0 total=0
|
|
1729
|
+
local compound_log="$ARTIFACTS_DIR/compound-quality.log"
|
|
1730
|
+
: > "$compound_log"
|
|
1731
|
+
|
|
1732
|
+
# ── Adversarial review ──
|
|
1733
|
+
if [[ "$do_adversarial" == "true" ]]; then
|
|
1734
|
+
total=$((total + 1))
|
|
1735
|
+
info "Running adversarial review..."
|
|
1736
|
+
if [[ -x "$SCRIPT_DIR/sw-adversarial.sh" ]]; then
|
|
1737
|
+
if bash "$SCRIPT_DIR/sw-adversarial.sh" --repo "${REPO_DIR:-.}" >> "$compound_log" 2>&1; then
|
|
1738
|
+
pass_count=$((pass_count + 1))
|
|
1739
|
+
success "Adversarial review passed"
|
|
1740
|
+
else
|
|
1741
|
+
fail_count=$((fail_count + 1))
|
|
1742
|
+
warn "Adversarial review found issues"
|
|
1743
|
+
fi
|
|
1744
|
+
else
|
|
1745
|
+
warn "sw-adversarial.sh not found, skipping"
|
|
1746
|
+
fi
|
|
1747
|
+
fi
|
|
1748
|
+
|
|
1749
|
+
# ── Negative / edge-case testing ──
|
|
1750
|
+
if [[ "$do_negative" == "true" ]]; then
|
|
1751
|
+
total=$((total + 1))
|
|
1752
|
+
info "Running negative test pass..."
|
|
1753
|
+
if [[ -n "${TEST_CMD:-}" ]]; then
|
|
1754
|
+
if eval "$TEST_CMD" >> "$compound_log" 2>&1; then
|
|
1755
|
+
pass_count=$((pass_count + 1))
|
|
1756
|
+
success "Negative test pass passed"
|
|
1757
|
+
else
|
|
1758
|
+
fail_count=$((fail_count + 1))
|
|
1759
|
+
warn "Negative test pass found failures"
|
|
1760
|
+
fi
|
|
1761
|
+
else
|
|
1762
|
+
pass_count=$((pass_count + 1))
|
|
1763
|
+
info "No test command configured, skipping negative tests"
|
|
1764
|
+
fi
|
|
1765
|
+
fi
|
|
1766
|
+
|
|
1767
|
+
# ── E2E checks ──
|
|
1768
|
+
if [[ "$do_e2e" == "true" ]]; then
|
|
1769
|
+
total=$((total + 1))
|
|
1770
|
+
info "Running e2e checks..."
|
|
1771
|
+
if [[ -x "$SCRIPT_DIR/sw-e2e-orchestrator.sh" ]]; then
|
|
1772
|
+
if bash "$SCRIPT_DIR/sw-e2e-orchestrator.sh" run >> "$compound_log" 2>&1; then
|
|
1773
|
+
pass_count=$((pass_count + 1))
|
|
1774
|
+
success "E2E checks passed"
|
|
1775
|
+
else
|
|
1776
|
+
fail_count=$((fail_count + 1))
|
|
1777
|
+
warn "E2E checks found issues"
|
|
1778
|
+
fi
|
|
1779
|
+
else
|
|
1780
|
+
pass_count=$((pass_count + 1))
|
|
1781
|
+
info "sw-e2e-orchestrator.sh not found, skipping e2e"
|
|
1782
|
+
fi
|
|
1783
|
+
fi
|
|
1784
|
+
|
|
1785
|
+
# ── Definition of Done audit ──
|
|
1786
|
+
if [[ "$do_dod" == "true" ]]; then
|
|
1787
|
+
total=$((total + 1))
|
|
1788
|
+
info "Running definition-of-done audit..."
|
|
1789
|
+
if [[ -x "$SCRIPT_DIR/sw-quality.sh" ]]; then
|
|
1790
|
+
if bash "$SCRIPT_DIR/sw-quality.sh" validate >> "$compound_log" 2>&1; then
|
|
1791
|
+
pass_count=$((pass_count + 1))
|
|
1792
|
+
success "DoD audit passed"
|
|
1793
|
+
else
|
|
1794
|
+
fail_count=$((fail_count + 1))
|
|
1795
|
+
warn "DoD audit found gaps"
|
|
1796
|
+
fi
|
|
1797
|
+
else
|
|
1798
|
+
pass_count=$((pass_count + 1))
|
|
1799
|
+
info "sw-quality.sh not found, skipping DoD audit"
|
|
1800
|
+
fi
|
|
1801
|
+
fi
|
|
1802
|
+
|
|
1803
|
+
# ── Summary ──
|
|
1804
|
+
log_stage "compound_quality" "Compound quality: $pass_count/$total checks passed, $fail_count failed"
|
|
1805
|
+
|
|
1806
|
+
if [[ "$fail_count" -gt 0 && "$blocking" == "true" ]]; then
|
|
1807
|
+
error "Compound quality gate failed: $fail_count of $total checks failed"
|
|
1808
|
+
return 1
|
|
1809
|
+
fi
|
|
1810
|
+
|
|
1811
|
+
return 0
|
|
1812
|
+
}
|
|
1813
|
+
fi # end fallback stage_compound_quality
|
|
1814
|
+
|
|
1542
1815
|
stage_pr() {
|
|
1543
1816
|
CURRENT_STAGE_ID="pr"
|
|
1544
1817
|
local plan_file="$ARTIFACTS_DIR/plan.md"
|
|
1545
1818
|
local test_log="$ARTIFACTS_DIR/test-results.log"
|
|
1546
1819
|
local review_file="$ARTIFACTS_DIR/review.md"
|
|
1547
1820
|
|
|
1821
|
+
# ── Skip PR in local/no-github mode ──
|
|
1822
|
+
if [[ "${NO_GITHUB:-false}" == "true" || "${SHIPWRIGHT_LOCAL:-}" == "1" || "${LOCAL_MODE:-false}" == "true" ]]; then
|
|
1823
|
+
info "Skipping PR stage — running in local/no-github mode"
|
|
1824
|
+
# Save a PR draft locally for reference
|
|
1825
|
+
local branch_name
|
|
1826
|
+
branch_name=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
|
|
1827
|
+
local commit_count
|
|
1828
|
+
commit_count=$(_safe_base_log --oneline | wc -l | xargs)
|
|
1829
|
+
{
|
|
1830
|
+
echo "# PR Draft (local mode)"
|
|
1831
|
+
echo ""
|
|
1832
|
+
echo "**Branch:** ${branch_name}"
|
|
1833
|
+
echo "**Commits:** ${commit_count:-0}"
|
|
1834
|
+
echo "**Goal:** ${GOAL:-N/A}"
|
|
1835
|
+
echo ""
|
|
1836
|
+
echo "## Changes"
|
|
1837
|
+
_safe_base_diff --stat || true
|
|
1838
|
+
} > ".claude/pr-draft.md" 2>/dev/null || true
|
|
1839
|
+
emit_event "pr.skipped" "issue=${ISSUE_NUMBER:-0}" "reason=local_mode"
|
|
1840
|
+
return 0
|
|
1841
|
+
fi
|
|
1842
|
+
|
|
1548
1843
|
# ── PR Hygiene Checks (informational) ──
|
|
1549
1844
|
local hygiene_commit_count
|
|
1550
|
-
hygiene_commit_count=$(
|
|
1845
|
+
hygiene_commit_count=$(_safe_base_log --oneline | wc -l | xargs)
|
|
1551
1846
|
hygiene_commit_count="${hygiene_commit_count:-0}"
|
|
1552
1847
|
|
|
1553
1848
|
if [[ "$hygiene_commit_count" -gt 20 ]]; then
|
|
@@ -1556,7 +1851,7 @@ stage_pr() {
|
|
|
1556
1851
|
|
|
1557
1852
|
# Check for WIP/fixup/squash commits (expanded patterns)
|
|
1558
1853
|
local wip_commits
|
|
1559
|
-
wip_commits=$(
|
|
1854
|
+
wip_commits=$(_safe_base_log --oneline | grep -ciE '^[0-9a-f]+ (WIP|fixup!|squash!|TODO|HACK|TEMP|BROKEN|wip[:-]|temp[:-]|broken[:-]|do not merge)' || true)
|
|
1560
1855
|
wip_commits="${wip_commits:-0}"
|
|
1561
1856
|
if [[ "$wip_commits" -gt 0 ]]; then
|
|
1562
1857
|
warn "Branch has ${wip_commits} WIP/fixup/squash/temp commit(s) — consider cleaning up"
|
|
@@ -1564,7 +1859,7 @@ stage_pr() {
|
|
|
1564
1859
|
|
|
1565
1860
|
# ── PR Quality Gate: reject PRs with no real code changes ──
|
|
1566
1861
|
local real_files
|
|
1567
|
-
real_files=$(
|
|
1862
|
+
real_files=$(_safe_base_diff --name-only | grep -v '^\.claude/' | grep -v '^\.github/' || true)
|
|
1568
1863
|
if [[ -z "$real_files" ]]; then
|
|
1569
1864
|
error "No real code changes detected — only pipeline artifacts (.claude/ logs)."
|
|
1570
1865
|
error "The build agent did not produce meaningful changes. Skipping PR creation."
|
|
@@ -1614,7 +1909,7 @@ stage_pr() {
|
|
|
1614
1909
|
if [[ "$sim_enabled" == "true" ]]; then
|
|
1615
1910
|
info "Running developer simulation review..."
|
|
1616
1911
|
local diff_for_sim
|
|
1617
|
-
diff_for_sim=$(
|
|
1912
|
+
diff_for_sim=$(_safe_base_diff || true)
|
|
1618
1913
|
if [[ -n "$diff_for_sim" ]]; then
|
|
1619
1914
|
local sim_result
|
|
1620
1915
|
sim_result=$(simulation_review "$diff_for_sim" "${GOAL:-}" 2>/dev/null || echo "")
|
|
@@ -1644,7 +1939,7 @@ stage_pr() {
|
|
|
1644
1939
|
if [[ "$arch_enabled" == "true" ]]; then
|
|
1645
1940
|
info "Validating architecture..."
|
|
1646
1941
|
local diff_for_arch
|
|
1647
|
-
diff_for_arch=$(
|
|
1942
|
+
diff_for_arch=$(_safe_base_diff || true)
|
|
1648
1943
|
if [[ -n "$diff_for_arch" ]]; then
|
|
1649
1944
|
local arch_result
|
|
1650
1945
|
arch_result=$(architecture_validate_changes "$diff_for_arch" "" 2>/dev/null || echo "")
|
|
@@ -1668,10 +1963,11 @@ stage_pr() {
|
|
|
1668
1963
|
|
|
1669
1964
|
# Pre-PR diff gate — verify meaningful code changes exist (not just bookkeeping)
|
|
1670
1965
|
local real_changes
|
|
1671
|
-
real_changes=$(
|
|
1966
|
+
real_changes=$(_safe_base_diff --name-only \
|
|
1672
1967
|
-- . ':!.claude/loop-state.md' ':!.claude/pipeline-state.md' \
|
|
1673
1968
|
':!.claude/pipeline-artifacts/*' ':!**/progress.md' \
|
|
1674
|
-
':!**/error-summary.json'
|
|
1969
|
+
':!**/error-summary.json' | wc -l | xargs || true)
|
|
1970
|
+
real_changes="${real_changes:-0}"
|
|
1675
1971
|
if [[ "${real_changes:-0}" -eq 0 ]]; then
|
|
1676
1972
|
error "No meaningful code changes detected — only bookkeeping files modified"
|
|
1677
1973
|
error "Refusing to create PR with zero real changes"
|
|
@@ -1726,10 +2022,10 @@ stage_pr() {
|
|
|
1726
2022
|
[[ -n "${GITHUB_ISSUE:-}" ]] && closes_line="Closes ${GITHUB_ISSUE}"
|
|
1727
2023
|
|
|
1728
2024
|
local diff_stats
|
|
1729
|
-
diff_stats=$(
|
|
2025
|
+
diff_stats=$(_safe_base_diff --stat | tail -1 || echo "")
|
|
1730
2026
|
|
|
1731
2027
|
local commit_count
|
|
1732
|
-
commit_count=$(
|
|
2028
|
+
commit_count=$(_safe_base_log --oneline | wc -l | xargs)
|
|
1733
2029
|
|
|
1734
2030
|
local total_dur=""
|
|
1735
2031
|
if [[ -n "$PIPELINE_START_EPOCH" ]]; then
|
|
@@ -1774,7 +2070,7 @@ EOF
|
|
|
1774
2070
|
risk_tier="low"
|
|
1775
2071
|
if [[ -f "$REPO_DIR/config/policy.json" ]]; then
|
|
1776
2072
|
local changed_files
|
|
1777
|
-
changed_files=$(
|
|
2073
|
+
changed_files=$(_safe_base_diff --name-only || true)
|
|
1778
2074
|
if [[ -n "$changed_files" ]]; then
|
|
1779
2075
|
local policy_file="$REPO_DIR/config/policy.json"
|
|
1780
2076
|
check_tier_match() {
|
|
@@ -1906,7 +2202,7 @@ EOF
|
|
|
1906
2202
|
codeowners_json=$(gh_codeowners "$REPO_OWNER" "$REPO_NAME" 2>/dev/null || echo "[]")
|
|
1907
2203
|
if [[ "$codeowners_json" != "[]" && -n "$codeowners_json" ]]; then
|
|
1908
2204
|
local changed_files
|
|
1909
|
-
changed_files=$(
|
|
2205
|
+
changed_files=$(_safe_base_diff --name-only || true)
|
|
1910
2206
|
if [[ -n "$changed_files" ]]; then
|
|
1911
2207
|
local co_reviewers
|
|
1912
2208
|
co_reviewers=$(echo "$codeowners_json" | jq -r '.[].owners[]' 2>/dev/null | sort -u | head -3 || true)
|
|
@@ -1980,13 +2276,14 @@ stage_merge() {
|
|
|
1980
2276
|
local merge_diff_file="${ARTIFACTS_DIR}/review-diff.patch"
|
|
1981
2277
|
local merge_review_file="${ARTIFACTS_DIR}/review.md"
|
|
1982
2278
|
if [[ ! -s "$merge_diff_file" ]]; then
|
|
1983
|
-
|
|
1984
|
-
git diff HEAD~5 > "$merge_diff_file" 2>/dev/null || true
|
|
2279
|
+
_safe_base_diff > "$merge_diff_file" 2>/dev/null || true
|
|
1985
2280
|
fi
|
|
1986
2281
|
if [[ -s "$merge_diff_file" ]]; then
|
|
1987
2282
|
local _merge_critical _merge_sec _merge_blocking _merge_reject
|
|
1988
|
-
_merge_critical=$(grep -ciE '\*\*\[?Critical\]?\*\*' "$merge_review_file" 2>/dev/null ||
|
|
1989
|
-
|
|
2283
|
+
_merge_critical=$(grep -ciE '\*\*\[?Critical\]?\*\*' "$merge_review_file" 2>/dev/null || true)
|
|
2284
|
+
_merge_critical="${_merge_critical:-0}"
|
|
2285
|
+
_merge_sec=$(grep -ciE '\*\*\[?Security\]?\*\*' "$merge_review_file" 2>/dev/null || true)
|
|
2286
|
+
_merge_sec="${_merge_sec:-0}"
|
|
1990
2287
|
_merge_blocking=$((${_merge_critical:-0} + ${_merge_sec:-0}))
|
|
1991
2288
|
[[ "$_merge_blocking" -gt 0 ]] && _merge_reject="Review found ${_merge_blocking} critical/security issue(s)"
|
|
1992
2289
|
if ! bash "$SCRIPT_DIR/sw-oversight.sh" gate --diff "$merge_diff_file" --description "${GOAL:-Pipeline merge}" --reject-if "${_merge_reject:-}" >/dev/null 2>&1; then
|
|
@@ -176,6 +176,13 @@ mark_stage_complete() {
|
|
|
176
176
|
write_state
|
|
177
177
|
|
|
178
178
|
record_stage_effectiveness "$stage_id" "complete"
|
|
179
|
+
|
|
180
|
+
# Record stage completion in SQLite pipeline_stages table
|
|
181
|
+
if type record_stage >/dev/null 2>&1; then
|
|
182
|
+
local _stage_secs
|
|
183
|
+
_stage_secs=$(get_stage_timing_seconds "$stage_id")
|
|
184
|
+
record_stage "${SHIPWRIGHT_PIPELINE_ID:-}" "$stage_id" "complete" "${_stage_secs:-0}" "" 2>/dev/null || true
|
|
185
|
+
fi
|
|
179
186
|
# Update memory baselines and predictive baselines for stage durations
|
|
180
187
|
if [[ "$stage_id" == "test" || "$stage_id" == "build" ]]; then
|
|
181
188
|
local secs
|
|
@@ -354,6 +361,13 @@ mark_stage_failed() {
|
|
|
354
361
|
log_stage "$stage_id" "failed (${timing})"
|
|
355
362
|
write_state
|
|
356
363
|
|
|
364
|
+
# Record stage failure in SQLite pipeline_stages table
|
|
365
|
+
if type record_stage >/dev/null 2>&1; then
|
|
366
|
+
local _stage_secs
|
|
367
|
+
_stage_secs=$(get_stage_timing_seconds "$stage_id")
|
|
368
|
+
record_stage "${SHIPWRIGHT_PIPELINE_ID:-}" "$stage_id" "failed" "${_stage_secs:-0}" "" 2>/dev/null || true
|
|
369
|
+
fi
|
|
370
|
+
|
|
357
371
|
# Update GitHub progress + comment failure
|
|
358
372
|
if [[ -n "$ISSUE_NUMBER" ]]; then
|
|
359
373
|
local body
|
package/scripts/lib/policy.sh
CHANGED
|
File without changes
|
|
File without changes
|