shipwright-cli 3.1.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/code-reviewer.md +2 -0
- package/.claude/agents/devops-engineer.md +2 -0
- package/.claude/agents/doc-fleet-agent.md +2 -0
- package/.claude/agents/pipeline-agent.md +2 -0
- package/.claude/agents/shell-script-specialist.md +2 -0
- package/.claude/agents/test-specialist.md +2 -0
- package/.claude/hooks/agent-crash-capture.sh +32 -0
- package/.claude/hooks/post-tool-use.sh +3 -2
- package/.claude/hooks/pre-tool-use.sh +35 -3
- package/README.md +22 -8
- package/claude-code/hooks/config-change.sh +18 -0
- package/claude-code/hooks/instructions-reloaded.sh +7 -0
- package/claude-code/hooks/worktree-create.sh +25 -0
- package/claude-code/hooks/worktree-remove.sh +20 -0
- package/config/code-constitution.json +130 -0
- package/config/defaults.json +25 -2
- package/config/policy.json +1 -1
- package/dashboard/middleware/auth.ts +134 -0
- package/dashboard/middleware/constants.ts +21 -0
- package/dashboard/public/index.html +8 -6
- package/dashboard/public/styles.css +176 -97
- package/dashboard/routes/auth.ts +38 -0
- package/dashboard/server.ts +117 -25
- package/dashboard/services/config.ts +26 -0
- package/dashboard/services/db.ts +118 -0
- package/dashboard/src/canvas/pixel-agent.ts +298 -0
- package/dashboard/src/canvas/pixel-sprites.ts +440 -0
- package/dashboard/src/canvas/shipyard-effects.ts +367 -0
- package/dashboard/src/canvas/shipyard-scene.ts +616 -0
- package/dashboard/src/canvas/submarine-layout.ts +267 -0
- package/dashboard/src/components/header.ts +8 -7
- package/dashboard/src/core/api.ts +5 -0
- package/dashboard/src/core/router.ts +1 -0
- package/dashboard/src/design/submarine-theme.ts +253 -0
- package/dashboard/src/main.ts +2 -0
- package/dashboard/src/types/api.ts +12 -1
- package/dashboard/src/views/activity.ts +2 -1
- package/dashboard/src/views/metrics.ts +69 -1
- package/dashboard/src/views/shipyard.ts +39 -0
- package/dashboard/types/index.ts +166 -0
- package/docs/plans/2026-02-28-compound-audit-and-shipyard-design.md +186 -0
- package/docs/plans/2026-02-28-skipper-shipwright-implementation-plan.md +1182 -0
- package/docs/plans/2026-02-28-skipper-shipwright-integration-design.md +531 -0
- package/docs/plans/2026-03-01-ai-powered-skill-injection-design.md +298 -0
- package/docs/plans/2026-03-01-ai-powered-skill-injection-plan.md +1109 -0
- package/docs/plans/2026-03-01-capabilities-cleanup-plan.md +658 -0
- package/docs/plans/2026-03-01-clean-architecture-plan.md +924 -0
- package/docs/plans/2026-03-01-compound-audit-cascade-design.md +191 -0
- package/docs/plans/2026-03-01-compound-audit-cascade-plan.md +921 -0
- package/docs/plans/2026-03-01-deep-integration-plan.md +851 -0
- package/docs/plans/2026-03-01-pipeline-audit-trail-design.md +145 -0
- package/docs/plans/2026-03-01-pipeline-audit-trail-plan.md +770 -0
- package/docs/plans/2026-03-01-refined-depths-brand-design.md +382 -0
- package/docs/plans/2026-03-01-refined-depths-implementation.md +599 -0
- package/docs/plans/2026-03-01-skipper-kernel-integration-design.md +203 -0
- package/docs/plans/2026-03-01-unified-platform-design.md +272 -0
- package/docs/plans/2026-03-07-claude-code-feature-integration-design.md +189 -0
- package/docs/plans/2026-03-07-claude-code-feature-integration-plan.md +1165 -0
- package/docs/research/BACKLOG_QUICK_REFERENCE.md +352 -0
- package/docs/research/CUTTING_EDGE_RESEARCH_2026.md +546 -0
- package/docs/research/RESEARCH_INDEX.md +439 -0
- package/docs/research/RESEARCH_SOURCES.md +440 -0
- package/docs/research/RESEARCH_SUMMARY.txt +275 -0
- package/docs/superpowers/specs/2026-03-10-pipeline-quality-revolution-design.md +341 -0
- package/package.json +2 -2
- package/scripts/lib/adaptive-model.sh +427 -0
- package/scripts/lib/adaptive-timeout.sh +316 -0
- package/scripts/lib/audit-trail.sh +309 -0
- package/scripts/lib/auto-recovery.sh +471 -0
- package/scripts/lib/bandit-selector.sh +431 -0
- package/scripts/lib/bootstrap.sh +104 -2
- package/scripts/lib/causal-graph.sh +455 -0
- package/scripts/lib/compat.sh +126 -0
- package/scripts/lib/compound-audit.sh +337 -0
- package/scripts/lib/constitutional.sh +454 -0
- package/scripts/lib/context-budget.sh +359 -0
- package/scripts/lib/convergence.sh +594 -0
- package/scripts/lib/cost-optimizer.sh +634 -0
- package/scripts/lib/daemon-adaptive.sh +14 -2
- package/scripts/lib/daemon-dispatch.sh +106 -17
- package/scripts/lib/daemon-failure.sh +34 -4
- package/scripts/lib/daemon-patrol.sh +25 -4
- package/scripts/lib/daemon-poll-github.sh +361 -0
- package/scripts/lib/daemon-poll-health.sh +299 -0
- package/scripts/lib/daemon-poll.sh +27 -611
- package/scripts/lib/daemon-state.sh +119 -66
- package/scripts/lib/daemon-triage.sh +10 -0
- package/scripts/lib/dod-scorecard.sh +442 -0
- package/scripts/lib/error-actionability.sh +300 -0
- package/scripts/lib/formal-spec.sh +461 -0
- package/scripts/lib/helpers.sh +180 -5
- package/scripts/lib/intent-analysis.sh +409 -0
- package/scripts/lib/loop-convergence.sh +350 -0
- package/scripts/lib/loop-iteration.sh +682 -0
- package/scripts/lib/loop-progress.sh +48 -0
- package/scripts/lib/loop-restart.sh +185 -0
- package/scripts/lib/memory-effectiveness.sh +506 -0
- package/scripts/lib/mutation-executor.sh +352 -0
- package/scripts/lib/outcome-feedback.sh +521 -0
- package/scripts/lib/pipeline-cli.sh +336 -0
- package/scripts/lib/pipeline-commands.sh +1216 -0
- package/scripts/lib/pipeline-detection.sh +101 -3
- package/scripts/lib/pipeline-execution.sh +897 -0
- package/scripts/lib/pipeline-github.sh +28 -3
- package/scripts/lib/pipeline-intelligence-compound.sh +431 -0
- package/scripts/lib/pipeline-intelligence-scoring.sh +407 -0
- package/scripts/lib/pipeline-intelligence-skip.sh +181 -0
- package/scripts/lib/pipeline-intelligence.sh +104 -1138
- package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
- package/scripts/lib/pipeline-quality-checks.sh +17 -711
- package/scripts/lib/pipeline-quality-gates.sh +563 -0
- package/scripts/lib/pipeline-stages-build.sh +730 -0
- package/scripts/lib/pipeline-stages-delivery.sh +965 -0
- package/scripts/lib/pipeline-stages-intake.sh +1133 -0
- package/scripts/lib/pipeline-stages-monitor.sh +407 -0
- package/scripts/lib/pipeline-stages-review.sh +1022 -0
- package/scripts/lib/pipeline-stages.sh +161 -2901
- package/scripts/lib/pipeline-state.sh +36 -5
- package/scripts/lib/pipeline-util.sh +487 -0
- package/scripts/lib/policy-learner.sh +438 -0
- package/scripts/lib/process-reward.sh +493 -0
- package/scripts/lib/project-detect.sh +649 -0
- package/scripts/lib/quality-profile.sh +334 -0
- package/scripts/lib/recruit-commands.sh +885 -0
- package/scripts/lib/recruit-learning.sh +739 -0
- package/scripts/lib/recruit-roles.sh +648 -0
- package/scripts/lib/reward-aggregator.sh +458 -0
- package/scripts/lib/rl-optimizer.sh +362 -0
- package/scripts/lib/root-cause.sh +427 -0
- package/scripts/lib/scope-enforcement.sh +445 -0
- package/scripts/lib/session-restart.sh +493 -0
- package/scripts/lib/skill-memory.sh +300 -0
- package/scripts/lib/skill-registry.sh +775 -0
- package/scripts/lib/spec-driven.sh +476 -0
- package/scripts/lib/test-helpers.sh +18 -7
- package/scripts/lib/test-holdout.sh +429 -0
- package/scripts/lib/test-optimizer.sh +511 -0
- package/scripts/shipwright-file-suggest.sh +45 -0
- package/scripts/skills/adversarial-quality.md +61 -0
- package/scripts/skills/api-design.md +44 -0
- package/scripts/skills/architecture-design.md +50 -0
- package/scripts/skills/brainstorming.md +43 -0
- package/scripts/skills/data-pipeline.md +44 -0
- package/scripts/skills/deploy-safety.md +64 -0
- package/scripts/skills/documentation.md +38 -0
- package/scripts/skills/frontend-design.md +45 -0
- package/scripts/skills/generated/.gitkeep +0 -0
- package/scripts/skills/generated/_refinements/.gitkeep +0 -0
- package/scripts/skills/generated/_refinements/adversarial-quality.patch.md +3 -0
- package/scripts/skills/generated/_refinements/architecture-design.patch.md +3 -0
- package/scripts/skills/generated/_refinements/brainstorming.patch.md +3 -0
- package/scripts/skills/generated/cli-version-management.md +29 -0
- package/scripts/skills/generated/collection-system-validation.md +99 -0
- package/scripts/skills/generated/large-scale-c-refactoring-coordination.md +97 -0
- package/scripts/skills/generated/pattern-matching-similarity-scoring.md +195 -0
- package/scripts/skills/generated/test-parallelization-detection.md +65 -0
- package/scripts/skills/observability.md +79 -0
- package/scripts/skills/performance.md +48 -0
- package/scripts/skills/pr-quality.md +49 -0
- package/scripts/skills/product-thinking.md +43 -0
- package/scripts/skills/security-audit.md +49 -0
- package/scripts/skills/systematic-debugging.md +40 -0
- package/scripts/skills/testing-strategy.md +47 -0
- package/scripts/skills/two-stage-review.md +52 -0
- package/scripts/skills/validation-thoroughness.md +55 -0
- package/scripts/sw +9 -3
- package/scripts/sw-activity.sh +9 -8
- package/scripts/sw-adaptive.sh +8 -7
- package/scripts/sw-adversarial.sh +2 -1
- package/scripts/sw-architecture-enforcer.sh +3 -1
- package/scripts/sw-auth.sh +12 -2
- package/scripts/sw-autonomous.sh +5 -1
- package/scripts/sw-changelog.sh +4 -1
- package/scripts/sw-checkpoint.sh +2 -1
- package/scripts/sw-ci.sh +15 -6
- package/scripts/sw-cleanup.sh +4 -26
- package/scripts/sw-code-review.sh +45 -20
- package/scripts/sw-connect.sh +2 -1
- package/scripts/sw-context.sh +2 -1
- package/scripts/sw-cost.sh +107 -5
- package/scripts/sw-daemon.sh +71 -11
- package/scripts/sw-dashboard.sh +3 -1
- package/scripts/sw-db.sh +71 -20
- package/scripts/sw-decide.sh +8 -2
- package/scripts/sw-decompose.sh +360 -17
- package/scripts/sw-deps.sh +4 -1
- package/scripts/sw-developer-simulation.sh +4 -1
- package/scripts/sw-discovery.sh +378 -5
- package/scripts/sw-doc-fleet.sh +4 -1
- package/scripts/sw-docs-agent.sh +3 -1
- package/scripts/sw-docs.sh +2 -1
- package/scripts/sw-doctor.sh +453 -2
- package/scripts/sw-dora.sh +4 -1
- package/scripts/sw-durable.sh +12 -7
- package/scripts/sw-e2e-orchestrator.sh +17 -16
- package/scripts/sw-eventbus.sh +13 -4
- package/scripts/sw-evidence.sh +364 -12
- package/scripts/sw-feedback.sh +550 -9
- package/scripts/sw-fix.sh +20 -1
- package/scripts/sw-fleet-discover.sh +6 -2
- package/scripts/sw-fleet-viz.sh +9 -4
- package/scripts/sw-fleet.sh +5 -1
- package/scripts/sw-github-app.sh +18 -4
- package/scripts/sw-github-checks.sh +3 -2
- package/scripts/sw-github-deploy.sh +3 -2
- package/scripts/sw-github-graphql.sh +18 -7
- package/scripts/sw-guild.sh +5 -1
- package/scripts/sw-heartbeat.sh +5 -30
- package/scripts/sw-hello.sh +67 -0
- package/scripts/sw-hygiene.sh +10 -3
- package/scripts/sw-incident.sh +273 -5
- package/scripts/sw-init.sh +18 -2
- package/scripts/sw-instrument.sh +10 -2
- package/scripts/sw-intelligence.sh +44 -7
- package/scripts/sw-jira.sh +5 -1
- package/scripts/sw-launchd.sh +2 -1
- package/scripts/sw-linear.sh +4 -1
- package/scripts/sw-logs.sh +4 -1
- package/scripts/sw-loop.sh +436 -1076
- package/scripts/sw-memory.sh +357 -3
- package/scripts/sw-mission-control.sh +6 -1
- package/scripts/sw-model-router.sh +483 -27
- package/scripts/sw-otel.sh +15 -4
- package/scripts/sw-oversight.sh +14 -5
- package/scripts/sw-patrol-meta.sh +334 -0
- package/scripts/sw-pipeline-composer.sh +7 -1
- package/scripts/sw-pipeline-vitals.sh +12 -6
- package/scripts/sw-pipeline.sh +54 -2653
- package/scripts/sw-pm.sh +16 -8
- package/scripts/sw-pr-lifecycle.sh +2 -1
- package/scripts/sw-predictive.sh +17 -5
- package/scripts/sw-prep.sh +185 -2
- package/scripts/sw-ps.sh +5 -25
- package/scripts/sw-public-dashboard.sh +17 -4
- package/scripts/sw-quality.sh +14 -6
- package/scripts/sw-reaper.sh +8 -25
- package/scripts/sw-recruit.sh +156 -2303
- package/scripts/sw-regression.sh +19 -12
- package/scripts/sw-release-manager.sh +3 -1
- package/scripts/sw-release.sh +4 -1
- package/scripts/sw-remote.sh +3 -1
- package/scripts/sw-replay.sh +7 -1
- package/scripts/sw-retro.sh +158 -1
- package/scripts/sw-review-rerun.sh +3 -1
- package/scripts/sw-scale.sh +14 -5
- package/scripts/sw-security-audit.sh +6 -1
- package/scripts/sw-self-optimize.sh +173 -6
- package/scripts/sw-session.sh +9 -3
- package/scripts/sw-setup.sh +3 -1
- package/scripts/sw-stall-detector.sh +406 -0
- package/scripts/sw-standup.sh +15 -7
- package/scripts/sw-status.sh +3 -1
- package/scripts/sw-strategic.sh +14 -6
- package/scripts/sw-stream.sh +13 -4
- package/scripts/sw-swarm.sh +20 -7
- package/scripts/sw-team-stages.sh +13 -6
- package/scripts/sw-templates.sh +7 -31
- package/scripts/sw-testgen.sh +17 -6
- package/scripts/sw-tmux-pipeline.sh +4 -1
- package/scripts/sw-tmux-role-color.sh +2 -0
- package/scripts/sw-tmux-status.sh +1 -1
- package/scripts/sw-tmux.sh +37 -1
- package/scripts/sw-trace.sh +3 -1
- package/scripts/sw-tracker-github.sh +3 -0
- package/scripts/sw-tracker-jira.sh +3 -0
- package/scripts/sw-tracker-linear.sh +3 -0
- package/scripts/sw-tracker.sh +3 -1
- package/scripts/sw-triage.sh +3 -2
- package/scripts/sw-upgrade.sh +3 -1
- package/scripts/sw-ux.sh +5 -2
- package/scripts/sw-webhook.sh +5 -2
- package/scripts/sw-widgets.sh +9 -4
- package/scripts/sw-worktree.sh +15 -3
- package/scripts/test-skill-injection.sh +1233 -0
- package/templates/pipelines/autonomous.json +27 -3
- package/templates/pipelines/cost-aware.json +34 -8
- package/templates/pipelines/deployed.json +12 -0
- package/templates/pipelines/enterprise.json +12 -0
- package/templates/pipelines/fast.json +6 -0
- package/templates/pipelines/full.json +27 -3
- package/templates/pipelines/hotfix.json +6 -0
- package/templates/pipelines/standard.json +12 -0
- package/templates/pipelines/tdd.json +12 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Module guard - prevent double-sourcing
|
|
3
|
+
[[ -n "${_LOOP_CONVERGENCE_LOADED:-}" ]] && return 0
|
|
4
|
+
_LOOP_CONVERGENCE_LOADED=1
|
|
5
|
+
|
|
6
|
+
# ─── Auto-Recovery Integration ───────────────────────────────────────────────
|
|
7
|
+
# Source the autonomous recovery system for pre-circuit-breaker recovery attempts.
|
|
8
|
+
_CONVERGENCE_SCRIPT_DIR="${_CONVERGENCE_SCRIPT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"
|
|
9
|
+
[[ -f "${_CONVERGENCE_SCRIPT_DIR}/auto-recovery.sh" ]] && source "${_CONVERGENCE_SCRIPT_DIR}/auto-recovery.sh"
|
|
10
|
+
|
|
11
|
+
# ─── Convergence Detection ────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
track_iteration_velocity() {
|
|
14
|
+
local changes
|
|
15
|
+
changes="$(git -C "$PROJECT_ROOT" diff --stat HEAD~1 2>/dev/null | tail -1 || echo "")"
|
|
16
|
+
local insertions
|
|
17
|
+
insertions="$(echo "$changes" | grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+' || echo 0)"
|
|
18
|
+
ITERATION_LINES_CHANGED="${insertions:-0}"
|
|
19
|
+
if [[ -n "$VELOCITY_HISTORY" ]]; then
|
|
20
|
+
VELOCITY_HISTORY="${VELOCITY_HISTORY},${ITERATION_LINES_CHANGED}"
|
|
21
|
+
else
|
|
22
|
+
VELOCITY_HISTORY="${ITERATION_LINES_CHANGED}"
|
|
23
|
+
fi
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# Compute average lines/iteration from recent history
|
|
27
|
+
compute_velocity_avg() {
|
|
28
|
+
if [[ -z "$VELOCITY_HISTORY" ]]; then
|
|
29
|
+
echo "0"
|
|
30
|
+
return 0
|
|
31
|
+
fi
|
|
32
|
+
local total=0 count=0
|
|
33
|
+
local IFS=','
|
|
34
|
+
local val
|
|
35
|
+
for val in $VELOCITY_HISTORY; do
|
|
36
|
+
total=$((total + val))
|
|
37
|
+
count=$((count + 1))
|
|
38
|
+
done
|
|
39
|
+
if [[ "$count" -gt 0 ]]; then
|
|
40
|
+
echo $((total / count))
|
|
41
|
+
else
|
|
42
|
+
echo "0"
|
|
43
|
+
fi
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
check_progress() {
|
|
47
|
+
local changes
|
|
48
|
+
# Exclude loop bookkeeping files — only count real code changes as progress
|
|
49
|
+
changes="$(git -C "$PROJECT_ROOT" diff --stat HEAD~1 \
|
|
50
|
+
-- . ':!.claude/loop-state.md' ':!.claude/pipeline-state.md' \
|
|
51
|
+
':!**/progress.md' ':!**/error-summary.json' \
|
|
52
|
+
2>/dev/null | tail -1 || echo "")"
|
|
53
|
+
local insertions
|
|
54
|
+
insertions="$(echo "$changes" | grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+' || echo 0)"
|
|
55
|
+
if [[ "${insertions:-0}" -lt "$MIN_PROGRESS_LINES" ]]; then
|
|
56
|
+
return 1 # No meaningful progress
|
|
57
|
+
fi
|
|
58
|
+
return 0
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
check_completion() {
|
|
62
|
+
local log_file="$1"
|
|
63
|
+
grep -q "LOOP_COMPLETE" "$log_file" 2>/dev/null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
check_circuit_breaker() {
|
|
67
|
+
# Vitals-driven circuit breaker (preferred over static threshold)
|
|
68
|
+
if type pipeline_compute_vitals >/dev/null 2>&1 && type pipeline_health_verdict >/dev/null 2>&1; then
|
|
69
|
+
local _vitals_json _verdict
|
|
70
|
+
local _loop_state="${STATE_FILE:-}"
|
|
71
|
+
local _loop_artifacts="${ARTIFACTS_DIR:-}"
|
|
72
|
+
local _loop_issue="${ISSUE_NUMBER:-}"
|
|
73
|
+
_vitals_json=$(pipeline_compute_vitals "$_loop_state" "$_loop_artifacts" "$_loop_issue" 2>/dev/null) || true
|
|
74
|
+
if [[ -n "$_vitals_json" && "$_vitals_json" != "{}" ]]; then
|
|
75
|
+
_verdict=$(echo "$_vitals_json" | jq -r '.verdict // "continue"' 2>/dev/null || echo "continue")
|
|
76
|
+
if [[ "$_verdict" == "abort" ]]; then
|
|
77
|
+
local _health_score
|
|
78
|
+
_health_score=$(echo "$_vitals_json" | jq -r '.health_score // 0' 2>/dev/null || echo "0")
|
|
79
|
+
error "Vitals circuit breaker: health score ${_health_score}/100 — aborting (${CONSECUTIVE_FAILURES} stagnant iterations)"
|
|
80
|
+
STATUS="circuit_breaker"
|
|
81
|
+
return 1
|
|
82
|
+
fi
|
|
83
|
+
# Vitals say continue/warn/intervene — don't trip circuit breaker yet
|
|
84
|
+
if [[ "$_verdict" == "continue" || "$_verdict" == "warn" ]]; then
|
|
85
|
+
return 0
|
|
86
|
+
fi
|
|
87
|
+
fi
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# ─── Auto-Recovery: attempt fix before aborting ────────────────────────
|
|
91
|
+
if [[ "$CONSECUTIVE_FAILURES" -ge "$CIRCUIT_BREAKER_THRESHOLD" ]]; then
|
|
92
|
+
if type recovery_before_circuit_breaker >/dev/null 2>&1; then
|
|
93
|
+
local error_log="${ARTIFACTS_DIR:-${PROJECT_ROOT:-.}/.claude/pipeline-artifacts}/error-log.jsonl"
|
|
94
|
+
if recovery_before_circuit_breaker "$error_log" "${PROJECT_ROOT:-.}" "${TEST_CMD:-}"; then
|
|
95
|
+
info "Auto-recovery succeeded — resetting circuit breaker"
|
|
96
|
+
CONSECUTIVE_FAILURES=0
|
|
97
|
+
return 0
|
|
98
|
+
fi
|
|
99
|
+
fi
|
|
100
|
+
error "Circuit breaker tripped: ${CIRCUIT_BREAKER_THRESHOLD} consecutive iterations with no meaningful progress."
|
|
101
|
+
STATUS="circuit_breaker"
|
|
102
|
+
return 1
|
|
103
|
+
fi
|
|
104
|
+
return 0
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
check_max_iterations() {
|
|
108
|
+
if [[ "$ITERATION" -le "$MAX_ITERATIONS" ]]; then
|
|
109
|
+
return 0
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
# Hit the cap — check if we should auto-extend
|
|
113
|
+
if ! $AUTO_EXTEND || [[ "$EXTENSION_COUNT" -ge "$MAX_EXTENSIONS" ]]; then
|
|
114
|
+
if [[ "$EXTENSION_COUNT" -ge "$MAX_EXTENSIONS" ]]; then
|
|
115
|
+
warn "Hard cap reached: ${EXTENSION_COUNT} extensions applied (max ${MAX_EXTENSIONS})."
|
|
116
|
+
fi
|
|
117
|
+
warn "Max iterations ($MAX_ITERATIONS) reached."
|
|
118
|
+
STATUS="max_iterations"
|
|
119
|
+
return 1
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
# Checkpoint audit: is there meaningful progress worth extending for?
|
|
123
|
+
echo -e "\n ${CYAN}${BOLD}▸ Checkpoint${RESET} — max iterations ($MAX_ITERATIONS) reached, evaluating progress..."
|
|
124
|
+
|
|
125
|
+
local should_extend=false
|
|
126
|
+
local extension_reason=""
|
|
127
|
+
|
|
128
|
+
# Check 1: recent meaningful progress (not stuck)
|
|
129
|
+
if [[ "${CONSECUTIVE_FAILURES:-0}" -lt 2 ]]; then
|
|
130
|
+
# Check 2: agent hasn't signaled completion (if it did, guard_completion handles it)
|
|
131
|
+
local last_log="$LOG_DIR/iteration-$(( ITERATION - 1 )).log"
|
|
132
|
+
if [[ -f "$last_log" ]] && ! grep -q "LOOP_COMPLETE" "$last_log" 2>/dev/null; then
|
|
133
|
+
should_extend=true
|
|
134
|
+
extension_reason="work in progress with recent progress"
|
|
135
|
+
fi
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
# Check 3: if quality gates or tests are failing, extend to let agent fix them
|
|
139
|
+
if [[ "$TEST_PASSED" == "false" ]] || ! $QUALITY_GATE_PASSED; then
|
|
140
|
+
should_extend=true
|
|
141
|
+
extension_reason="quality gates or tests not yet passing"
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
if $should_extend; then
|
|
145
|
+
# Scale extension size by velocity — good progress earns more iterations
|
|
146
|
+
local velocity_avg
|
|
147
|
+
velocity_avg="$(compute_velocity_avg)"
|
|
148
|
+
local effective_extension="$EXTENSION_SIZE"
|
|
149
|
+
if [[ "$velocity_avg" -gt 20 ]]; then
|
|
150
|
+
# High velocity: grant more iterations
|
|
151
|
+
effective_extension=$(( EXTENSION_SIZE + 3 ))
|
|
152
|
+
elif [[ "$velocity_avg" -lt 5 ]]; then
|
|
153
|
+
# Low velocity: grant fewer iterations
|
|
154
|
+
effective_extension=$(( EXTENSION_SIZE > 2 ? EXTENSION_SIZE - 2 : 1 ))
|
|
155
|
+
fi
|
|
156
|
+
EXTENSION_COUNT=$(( EXTENSION_COUNT + 1 ))
|
|
157
|
+
MAX_ITERATIONS=$(( MAX_ITERATIONS + effective_extension ))
|
|
158
|
+
echo -e " ${GREEN}✓${RESET} Auto-extending: +${effective_extension} iterations (now ${MAX_ITERATIONS} max, extension ${EXTENSION_COUNT}/${MAX_EXTENSIONS})"
|
|
159
|
+
echo -e " ${DIM}Reason: ${extension_reason} | velocity: ~${velocity_avg} lines/iter${RESET}"
|
|
160
|
+
return 0
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
warn "Max iterations reached — no recent progress detected."
|
|
164
|
+
STATUS="max_iterations"
|
|
165
|
+
return 1
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
record_iteration_stuckness_data() {
|
|
169
|
+
local exit_code="${1:-0}"
|
|
170
|
+
[[ -z "$LOG_DIR" ]] && return 0
|
|
171
|
+
local tracking_file="${STUCKNESS_TRACKING_FILE:-$LOG_DIR/stuckness-tracking.txt}"
|
|
172
|
+
local diff_hash error_hash
|
|
173
|
+
diff_hash=$(git -C "${PROJECT_ROOT:-.}" diff HEAD 2>/dev/null | (md5 -q 2>/dev/null || md5sum 2>/dev/null | cut -d' ' -f1) || echo "none")
|
|
174
|
+
local error_log="${ARTIFACTS_DIR:-${STATE_DIR:-${PROJECT_ROOT:-.}/.claude}/pipeline-artifacts}/error-log.jsonl"
|
|
175
|
+
if [[ -f "$error_log" ]]; then
|
|
176
|
+
error_hash=$(tail -5 "$error_log" 2>/dev/null | sort -u | (md5 -q 2>/dev/null || md5sum 2>/dev/null | cut -d' ' -f1) || echo "none")
|
|
177
|
+
else
|
|
178
|
+
error_hash="none"
|
|
179
|
+
fi
|
|
180
|
+
echo "${diff_hash}|${error_hash}|${exit_code}" >> "$tracking_file"
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
detect_stuckness() {
|
|
184
|
+
STUCKNESS_HINT=""
|
|
185
|
+
local iteration="${ITERATION:-0}"
|
|
186
|
+
local stuckness_signals=0
|
|
187
|
+
local stuckness_reasons=()
|
|
188
|
+
local tracking_file="${STUCKNESS_TRACKING_FILE:-$LOG_DIR/stuckness-tracking.txt}"
|
|
189
|
+
local tracking_lines
|
|
190
|
+
tracking_lines=$(wc -l < "$tracking_file" 2>/dev/null || true)
|
|
191
|
+
tracking_lines="${tracking_lines:-0}"
|
|
192
|
+
|
|
193
|
+
# Signal 1: Text overlap (existing logic) — compare last 2 iteration logs
|
|
194
|
+
if [[ "$iteration" -ge 3 ]]; then
|
|
195
|
+
local log1="$LOG_DIR/iteration-$(( iteration - 1 )).log"
|
|
196
|
+
local log2="$LOG_DIR/iteration-$(( iteration - 2 )).log"
|
|
197
|
+
local log3="$LOG_DIR/iteration-$(( iteration - 3 )).log"
|
|
198
|
+
|
|
199
|
+
if [[ -f "$log1" && -f "$log2" ]]; then
|
|
200
|
+
local lines1 lines2 common total overlap_pct
|
|
201
|
+
lines1=$(tail -50 "$log1" 2>/dev/null | grep -v '^$' | sort || true)
|
|
202
|
+
lines2=$(tail -50 "$log2" 2>/dev/null | grep -v '^$' | sort || true)
|
|
203
|
+
|
|
204
|
+
if [[ -n "$lines1" && -n "$lines2" ]]; then
|
|
205
|
+
total=$(echo "$lines1" | wc -l | tr -d ' ')
|
|
206
|
+
common=$(comm -12 <(echo "$lines1") <(echo "$lines2") 2>/dev/null | wc -l | tr -d ' ' || true)
|
|
207
|
+
common="${common:-0}"
|
|
208
|
+
if [[ "$total" -gt 0 ]]; then
|
|
209
|
+
overlap_pct=$(( common * 100 / total ))
|
|
210
|
+
else
|
|
211
|
+
overlap_pct=0
|
|
212
|
+
fi
|
|
213
|
+
if [[ "${overlap_pct:-0}" -ge 90 ]]; then
|
|
214
|
+
stuckness_signals=$((stuckness_signals + 1))
|
|
215
|
+
stuckness_reasons+=("high text overlap (${overlap_pct}%) between iterations")
|
|
216
|
+
fi
|
|
217
|
+
fi
|
|
218
|
+
fi
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
# Signal 2: Git diff hash — last 3 iterations produced zero or identical diffs
|
|
222
|
+
if [[ -f "$tracking_file" ]] && [[ "$tracking_lines" -ge 3 ]]; then
|
|
223
|
+
local last_three
|
|
224
|
+
last_three=$(tail -3 "$tracking_file" 2>/dev/null | cut -d'|' -f1 || true)
|
|
225
|
+
local unique_hashes
|
|
226
|
+
unique_hashes=$(echo "$last_three" | sort -u | grep -v '^$' | wc -l | tr -d ' ')
|
|
227
|
+
if [[ "$unique_hashes" -le 1 ]] && [[ -n "$last_three" ]]; then
|
|
228
|
+
stuckness_signals=$((stuckness_signals + 1))
|
|
229
|
+
stuckness_reasons+=("identical or zero git diffs in last 3 iterations")
|
|
230
|
+
fi
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
# Signal 3: Error repetition — same error hash in last 3 iterations
|
|
234
|
+
if [[ -f "$tracking_file" ]] && [[ "$tracking_lines" -ge 3 ]]; then
|
|
235
|
+
local last_three_errors
|
|
236
|
+
last_three_errors=$(tail -3 "$tracking_file" 2>/dev/null | cut -d'|' -f2 || true)
|
|
237
|
+
local unique_error_hashes
|
|
238
|
+
unique_error_hashes=$(echo "$last_three_errors" | sort -u | grep -v '^none$' | grep -v '^$' | wc -l | tr -d ' ')
|
|
239
|
+
if [[ "$unique_error_hashes" -eq 1 ]] && [[ -n "$(echo "$last_three_errors" | grep -v '^none$')" ]]; then
|
|
240
|
+
stuckness_signals=$((stuckness_signals + 1))
|
|
241
|
+
stuckness_reasons+=("same error in last 3 iterations")
|
|
242
|
+
fi
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
# Signal 4: Same error repeating 3+ times (legacy check on error-log content)
|
|
246
|
+
local error_log
|
|
247
|
+
error_log="${ARTIFACTS_DIR:-$PROJECT_ROOT/.claude/pipeline-artifacts}/error-log.jsonl"
|
|
248
|
+
if [[ -f "$error_log" ]]; then
|
|
249
|
+
local last_errors
|
|
250
|
+
last_errors=$(tail -5 "$error_log" 2>/dev/null | jq -r '.error // .message // .error_hash // empty' 2>/dev/null | sort | uniq -c | sort -rn | head -1 || true)
|
|
251
|
+
local repeat_count
|
|
252
|
+
repeat_count=$(echo "$last_errors" | awk '{print $1}' 2>/dev/null || echo "0")
|
|
253
|
+
if [[ "${repeat_count:-0}" -ge 3 ]]; then
|
|
254
|
+
stuckness_signals=$((stuckness_signals + 1))
|
|
255
|
+
stuckness_reasons+=("same error repeated ${repeat_count} times")
|
|
256
|
+
fi
|
|
257
|
+
fi
|
|
258
|
+
|
|
259
|
+
# Signal 5: Exit code pattern — last 3 iterations had same non-zero exit code
|
|
260
|
+
if [[ -f "$tracking_file" ]] && [[ "$tracking_lines" -ge 3 ]]; then
|
|
261
|
+
local last_three_exits
|
|
262
|
+
last_three_exits=$(tail -3 "$tracking_file" 2>/dev/null | cut -d'|' -f3 || true)
|
|
263
|
+
local first_exit
|
|
264
|
+
first_exit=$(echo "$last_three_exits" | head -1)
|
|
265
|
+
if [[ "$first_exit" =~ ^[0-9]+$ ]] && [[ "$first_exit" -ne 0 ]]; then
|
|
266
|
+
local all_same=true
|
|
267
|
+
while IFS= read -r ex; do
|
|
268
|
+
[[ "$ex" != "$first_exit" ]] && all_same=false
|
|
269
|
+
done <<< "$last_three_exits"
|
|
270
|
+
if [[ "$all_same" == true ]]; then
|
|
271
|
+
stuckness_signals=$((stuckness_signals + 1))
|
|
272
|
+
stuckness_reasons+=("same non-zero exit code (${first_exit}) in last 3 iterations")
|
|
273
|
+
fi
|
|
274
|
+
fi
|
|
275
|
+
fi
|
|
276
|
+
|
|
277
|
+
# Signal 6: Git diff size — no or minimal code changes (existing)
|
|
278
|
+
local diff_lines
|
|
279
|
+
diff_lines=$(git -C "${PROJECT_ROOT:-.}" diff HEAD 2>/dev/null | wc -l | tr -d ' ' || true)
|
|
280
|
+
diff_lines="${diff_lines:-0}"
|
|
281
|
+
if [[ "${diff_lines:-0}" -lt 5 ]] && [[ "$iteration" -gt 2 ]]; then
|
|
282
|
+
stuckness_signals=$((stuckness_signals + 1))
|
|
283
|
+
stuckness_reasons+=("no code changes in last iteration")
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
# Signal 7: Iteration budget — used >70% without passing tests
|
|
287
|
+
local max_iter="${MAX_ITERATIONS:-20}"
|
|
288
|
+
local progress_pct=0
|
|
289
|
+
if [[ "$max_iter" -gt 0 ]]; then
|
|
290
|
+
progress_pct=$(( iteration * 100 / max_iter ))
|
|
291
|
+
fi
|
|
292
|
+
if [[ "$progress_pct" -gt 70 ]] && [[ "${TEST_PASSED:-false}" != "true" ]]; then
|
|
293
|
+
stuckness_signals=$((stuckness_signals + 1))
|
|
294
|
+
stuckness_reasons+=("used ${progress_pct}% of iteration budget without passing tests")
|
|
295
|
+
fi
|
|
296
|
+
|
|
297
|
+
# Gate-aware dampening: if tests pass and the agent has made progress overall,
|
|
298
|
+
# reduce stuckness signal count. The "no code changes" and "identical diffs" signals
|
|
299
|
+
# fire when code is already complete and the agent is fighting evaluator quirks —
|
|
300
|
+
# that's not genuine stuckness, it's "done but gates disagree."
|
|
301
|
+
if [[ "${TEST_PASSED:-}" == "true" ]] && [[ "$stuckness_signals" -ge 2 ]]; then
|
|
302
|
+
# If at least one quality signal is positive, dampen by 1
|
|
303
|
+
if [[ "${AUDIT_RESULT:-}" == "pass" ]] || $QUALITY_GATE_PASSED 2>/dev/null; then
|
|
304
|
+
stuckness_signals=$((stuckness_signals - 1))
|
|
305
|
+
fi
|
|
306
|
+
fi
|
|
307
|
+
|
|
308
|
+
# Decision: 2+ signals = stuck
|
|
309
|
+
if [[ "$stuckness_signals" -ge 2 ]]; then
|
|
310
|
+
STUCKNESS_COUNT=$(( STUCKNESS_COUNT + 1 ))
|
|
311
|
+
STUCKNESS_DIAGNOSIS="${stuckness_reasons[*]}"
|
|
312
|
+
if type emit_event >/dev/null 2>&1; then
|
|
313
|
+
emit_event "loop.stuckness_detected" "signals=$stuckness_signals" "count=$STUCKNESS_COUNT" "iteration=$iteration" "reasons=${stuckness_reasons[*]}"
|
|
314
|
+
fi
|
|
315
|
+
STUCKNESS_HINT="IMPORTANT: The loop appears stuck. Previous approaches have not worked. You MUST try a fundamentally different strategy. Reasons: ${stuckness_reasons[*]}"
|
|
316
|
+
warn "Stuckness detected (${stuckness_signals} signals, count ${STUCKNESS_COUNT}): ${stuckness_reasons[*]}"
|
|
317
|
+
|
|
318
|
+
local diff_summary=""
|
|
319
|
+
local log1="$LOG_DIR/iteration-$(( iteration - 1 )).log"
|
|
320
|
+
local log3="$LOG_DIR/iteration-$(( iteration - 3 )).log"
|
|
321
|
+
if [[ -f "$log3" && -f "$log1" ]]; then
|
|
322
|
+
diff_summary=$(diff <(tail -30 "$log3" 2>/dev/null) <(tail -30 "$log1" 2>/dev/null) 2>/dev/null | head -10 || true)
|
|
323
|
+
fi
|
|
324
|
+
|
|
325
|
+
local alternatives=""
|
|
326
|
+
if type memory_inject_context >/dev/null 2>&1; then
|
|
327
|
+
alternatives=$(memory_inject_context "build" 2>/dev/null | grep -i "fix:" | head -3 || true)
|
|
328
|
+
fi
|
|
329
|
+
|
|
330
|
+
cat <<STUCK_SECTION
|
|
331
|
+
## Stuckness Detected
|
|
332
|
+
${STUCKNESS_HINT}
|
|
333
|
+
|
|
334
|
+
${diff_summary:+Changes between recent iterations:
|
|
335
|
+
$diff_summary
|
|
336
|
+
}
|
|
337
|
+
${alternatives:+Consider these alternative approaches from past fixes:
|
|
338
|
+
$alternatives
|
|
339
|
+
}
|
|
340
|
+
Try a fundamentally different approach:
|
|
341
|
+
- Break the problem into smaller steps
|
|
342
|
+
- Look for an entirely different implementation strategy
|
|
343
|
+
- Check if there's a dependency or configuration issue blocking progress
|
|
344
|
+
- Read error messages more carefully — the root cause may differ from your assumption
|
|
345
|
+
STUCK_SECTION
|
|
346
|
+
return 0
|
|
347
|
+
fi
|
|
348
|
+
|
|
349
|
+
return 1
|
|
350
|
+
}
|