shipwright-cli 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/code-reviewer.md +2 -0
- package/.claude/agents/devops-engineer.md +2 -0
- package/.claude/agents/doc-fleet-agent.md +2 -0
- package/.claude/agents/pipeline-agent.md +2 -0
- package/.claude/agents/shell-script-specialist.md +2 -0
- package/.claude/agents/test-specialist.md +2 -0
- package/.claude/hooks/agent-crash-capture.sh +32 -0
- package/.claude/hooks/post-tool-use.sh +3 -2
- package/.claude/hooks/pre-tool-use.sh +35 -3
- package/README.md +4 -4
- package/claude-code/hooks/config-change.sh +18 -0
- package/claude-code/hooks/instructions-reloaded.sh +7 -0
- package/claude-code/hooks/worktree-create.sh +25 -0
- package/claude-code/hooks/worktree-remove.sh +20 -0
- package/config/code-constitution.json +130 -0
- package/dashboard/middleware/auth.ts +134 -0
- package/dashboard/middleware/constants.ts +21 -0
- package/dashboard/public/index.html +2 -6
- package/dashboard/public/styles.css +100 -97
- package/dashboard/routes/auth.ts +38 -0
- package/dashboard/server.ts +66 -25
- package/dashboard/services/config.ts +26 -0
- package/dashboard/services/db.ts +118 -0
- package/dashboard/src/canvas/pixel-agent.ts +298 -0
- package/dashboard/src/canvas/pixel-sprites.ts +440 -0
- package/dashboard/src/canvas/shipyard-effects.ts +367 -0
- package/dashboard/src/canvas/shipyard-scene.ts +616 -0
- package/dashboard/src/canvas/submarine-layout.ts +267 -0
- package/dashboard/src/components/header.ts +8 -7
- package/dashboard/src/core/router.ts +1 -0
- package/dashboard/src/design/submarine-theme.ts +253 -0
- package/dashboard/src/main.ts +2 -0
- package/dashboard/src/types/api.ts +2 -1
- package/dashboard/src/views/activity.ts +2 -1
- package/dashboard/src/views/shipyard.ts +39 -0
- package/dashboard/types/index.ts +166 -0
- package/docs/plans/2026-02-28-compound-audit-and-shipyard-design.md +186 -0
- package/docs/plans/2026-02-28-skipper-shipwright-implementation-plan.md +1182 -0
- package/docs/plans/2026-02-28-skipper-shipwright-integration-design.md +531 -0
- package/docs/plans/2026-03-01-ai-powered-skill-injection-design.md +298 -0
- package/docs/plans/2026-03-01-ai-powered-skill-injection-plan.md +1109 -0
- package/docs/plans/2026-03-01-capabilities-cleanup-plan.md +658 -0
- package/docs/plans/2026-03-01-clean-architecture-plan.md +924 -0
- package/docs/plans/2026-03-01-compound-audit-cascade-design.md +191 -0
- package/docs/plans/2026-03-01-compound-audit-cascade-plan.md +921 -0
- package/docs/plans/2026-03-01-deep-integration-plan.md +851 -0
- package/docs/plans/2026-03-01-pipeline-audit-trail-design.md +145 -0
- package/docs/plans/2026-03-01-pipeline-audit-trail-plan.md +770 -0
- package/docs/plans/2026-03-01-refined-depths-brand-design.md +382 -0
- package/docs/plans/2026-03-01-refined-depths-implementation.md +599 -0
- package/docs/plans/2026-03-01-skipper-kernel-integration-design.md +203 -0
- package/docs/plans/2026-03-01-unified-platform-design.md +272 -0
- package/docs/plans/2026-03-07-claude-code-feature-integration-design.md +189 -0
- package/docs/plans/2026-03-07-claude-code-feature-integration-plan.md +1165 -0
- package/docs/research/BACKLOG_QUICK_REFERENCE.md +352 -0
- package/docs/research/CUTTING_EDGE_RESEARCH_2026.md +546 -0
- package/docs/research/RESEARCH_INDEX.md +439 -0
- package/docs/research/RESEARCH_SOURCES.md +440 -0
- package/docs/research/RESEARCH_SUMMARY.txt +275 -0
- package/docs/superpowers/specs/2026-03-10-pipeline-quality-revolution-design.md +341 -0
- package/package.json +2 -2
- package/scripts/lib/adaptive-model.sh +427 -0
- package/scripts/lib/adaptive-timeout.sh +316 -0
- package/scripts/lib/audit-trail.sh +309 -0
- package/scripts/lib/auto-recovery.sh +471 -0
- package/scripts/lib/bandit-selector.sh +431 -0
- package/scripts/lib/bootstrap.sh +104 -2
- package/scripts/lib/causal-graph.sh +455 -0
- package/scripts/lib/compat.sh +126 -0
- package/scripts/lib/compound-audit.sh +337 -0
- package/scripts/lib/constitutional.sh +454 -0
- package/scripts/lib/context-budget.sh +359 -0
- package/scripts/lib/convergence.sh +594 -0
- package/scripts/lib/cost-optimizer.sh +634 -0
- package/scripts/lib/daemon-adaptive.sh +10 -0
- package/scripts/lib/daemon-dispatch.sh +106 -17
- package/scripts/lib/daemon-failure.sh +34 -4
- package/scripts/lib/daemon-patrol.sh +23 -2
- package/scripts/lib/daemon-poll-github.sh +361 -0
- package/scripts/lib/daemon-poll-health.sh +299 -0
- package/scripts/lib/daemon-poll.sh +27 -611
- package/scripts/lib/daemon-state.sh +112 -66
- package/scripts/lib/daemon-triage.sh +10 -0
- package/scripts/lib/dod-scorecard.sh +442 -0
- package/scripts/lib/error-actionability.sh +300 -0
- package/scripts/lib/formal-spec.sh +461 -0
- package/scripts/lib/helpers.sh +177 -4
- package/scripts/lib/intent-analysis.sh +409 -0
- package/scripts/lib/loop-convergence.sh +350 -0
- package/scripts/lib/loop-iteration.sh +682 -0
- package/scripts/lib/loop-progress.sh +48 -0
- package/scripts/lib/loop-restart.sh +185 -0
- package/scripts/lib/memory-effectiveness.sh +506 -0
- package/scripts/lib/mutation-executor.sh +352 -0
- package/scripts/lib/outcome-feedback.sh +521 -0
- package/scripts/lib/pipeline-cli.sh +336 -0
- package/scripts/lib/pipeline-commands.sh +1216 -0
- package/scripts/lib/pipeline-detection.sh +100 -2
- package/scripts/lib/pipeline-execution.sh +897 -0
- package/scripts/lib/pipeline-github.sh +28 -3
- package/scripts/lib/pipeline-intelligence-compound.sh +431 -0
- package/scripts/lib/pipeline-intelligence-scoring.sh +407 -0
- package/scripts/lib/pipeline-intelligence-skip.sh +181 -0
- package/scripts/lib/pipeline-intelligence.sh +100 -1136
- package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
- package/scripts/lib/pipeline-quality-checks.sh +17 -715
- package/scripts/lib/pipeline-quality-gates.sh +563 -0
- package/scripts/lib/pipeline-stages-build.sh +730 -0
- package/scripts/lib/pipeline-stages-delivery.sh +965 -0
- package/scripts/lib/pipeline-stages-intake.sh +1133 -0
- package/scripts/lib/pipeline-stages-monitor.sh +407 -0
- package/scripts/lib/pipeline-stages-review.sh +1022 -0
- package/scripts/lib/pipeline-stages.sh +59 -2929
- package/scripts/lib/pipeline-state.sh +36 -5
- package/scripts/lib/pipeline-util.sh +487 -0
- package/scripts/lib/policy-learner.sh +438 -0
- package/scripts/lib/process-reward.sh +493 -0
- package/scripts/lib/project-detect.sh +649 -0
- package/scripts/lib/quality-profile.sh +334 -0
- package/scripts/lib/recruit-commands.sh +885 -0
- package/scripts/lib/recruit-learning.sh +739 -0
- package/scripts/lib/recruit-roles.sh +648 -0
- package/scripts/lib/reward-aggregator.sh +458 -0
- package/scripts/lib/rl-optimizer.sh +362 -0
- package/scripts/lib/root-cause.sh +427 -0
- package/scripts/lib/scope-enforcement.sh +445 -0
- package/scripts/lib/session-restart.sh +493 -0
- package/scripts/lib/skill-memory.sh +300 -0
- package/scripts/lib/skill-registry.sh +775 -0
- package/scripts/lib/spec-driven.sh +476 -0
- package/scripts/lib/test-helpers.sh +18 -7
- package/scripts/lib/test-holdout.sh +429 -0
- package/scripts/lib/test-optimizer.sh +511 -0
- package/scripts/shipwright-file-suggest.sh +45 -0
- package/scripts/skills/adversarial-quality.md +61 -0
- package/scripts/skills/api-design.md +44 -0
- package/scripts/skills/architecture-design.md +50 -0
- package/scripts/skills/brainstorming.md +43 -0
- package/scripts/skills/data-pipeline.md +44 -0
- package/scripts/skills/deploy-safety.md +64 -0
- package/scripts/skills/documentation.md +38 -0
- package/scripts/skills/frontend-design.md +45 -0
- package/scripts/skills/generated/.gitkeep +0 -0
- package/scripts/skills/generated/_refinements/.gitkeep +0 -0
- package/scripts/skills/generated/_refinements/adversarial-quality.patch.md +3 -0
- package/scripts/skills/generated/_refinements/architecture-design.patch.md +3 -0
- package/scripts/skills/generated/_refinements/brainstorming.patch.md +3 -0
- package/scripts/skills/generated/cli-version-management.md +29 -0
- package/scripts/skills/generated/collection-system-validation.md +99 -0
- package/scripts/skills/generated/large-scale-c-refactoring-coordination.md +97 -0
- package/scripts/skills/generated/pattern-matching-similarity-scoring.md +195 -0
- package/scripts/skills/generated/test-parallelization-detection.md +65 -0
- package/scripts/skills/observability.md +79 -0
- package/scripts/skills/performance.md +48 -0
- package/scripts/skills/pr-quality.md +49 -0
- package/scripts/skills/product-thinking.md +43 -0
- package/scripts/skills/security-audit.md +49 -0
- package/scripts/skills/systematic-debugging.md +40 -0
- package/scripts/skills/testing-strategy.md +47 -0
- package/scripts/skills/two-stage-review.md +52 -0
- package/scripts/skills/validation-thoroughness.md +55 -0
- package/scripts/sw +9 -3
- package/scripts/sw-activity.sh +9 -2
- package/scripts/sw-adaptive.sh +2 -1
- package/scripts/sw-adversarial.sh +2 -1
- package/scripts/sw-architecture-enforcer.sh +3 -1
- package/scripts/sw-auth.sh +12 -2
- package/scripts/sw-autonomous.sh +5 -1
- package/scripts/sw-changelog.sh +4 -1
- package/scripts/sw-checkpoint.sh +2 -1
- package/scripts/sw-ci.sh +5 -1
- package/scripts/sw-cleanup.sh +4 -26
- package/scripts/sw-code-review.sh +10 -4
- package/scripts/sw-connect.sh +2 -1
- package/scripts/sw-context.sh +2 -1
- package/scripts/sw-cost.sh +48 -3
- package/scripts/sw-daemon.sh +66 -9
- package/scripts/sw-dashboard.sh +3 -1
- package/scripts/sw-db.sh +59 -16
- package/scripts/sw-decide.sh +8 -2
- package/scripts/sw-decompose.sh +360 -17
- package/scripts/sw-deps.sh +4 -1
- package/scripts/sw-developer-simulation.sh +4 -1
- package/scripts/sw-discovery.sh +325 -2
- package/scripts/sw-doc-fleet.sh +4 -1
- package/scripts/sw-docs-agent.sh +3 -1
- package/scripts/sw-docs.sh +2 -1
- package/scripts/sw-doctor.sh +453 -2
- package/scripts/sw-dora.sh +4 -1
- package/scripts/sw-durable.sh +4 -3
- package/scripts/sw-e2e-orchestrator.sh +17 -16
- package/scripts/sw-eventbus.sh +7 -1
- package/scripts/sw-evidence.sh +364 -12
- package/scripts/sw-feedback.sh +550 -9
- package/scripts/sw-fix.sh +20 -1
- package/scripts/sw-fleet-discover.sh +6 -2
- package/scripts/sw-fleet-viz.sh +4 -1
- package/scripts/sw-fleet.sh +5 -1
- package/scripts/sw-github-app.sh +16 -3
- package/scripts/sw-github-checks.sh +3 -2
- package/scripts/sw-github-deploy.sh +3 -2
- package/scripts/sw-github-graphql.sh +18 -7
- package/scripts/sw-guild.sh +5 -1
- package/scripts/sw-heartbeat.sh +5 -30
- package/scripts/sw-hello.sh +67 -0
- package/scripts/sw-hygiene.sh +6 -1
- package/scripts/sw-incident.sh +265 -1
- package/scripts/sw-init.sh +18 -2
- package/scripts/sw-instrument.sh +10 -2
- package/scripts/sw-intelligence.sh +42 -6
- package/scripts/sw-jira.sh +5 -1
- package/scripts/sw-launchd.sh +2 -1
- package/scripts/sw-linear.sh +4 -1
- package/scripts/sw-logs.sh +4 -1
- package/scripts/sw-loop.sh +432 -1128
- package/scripts/sw-memory.sh +356 -2
- package/scripts/sw-mission-control.sh +6 -1
- package/scripts/sw-model-router.sh +481 -26
- package/scripts/sw-otel.sh +13 -4
- package/scripts/sw-oversight.sh +14 -5
- package/scripts/sw-patrol-meta.sh +334 -0
- package/scripts/sw-pipeline-composer.sh +5 -1
- package/scripts/sw-pipeline-vitals.sh +2 -1
- package/scripts/sw-pipeline.sh +53 -2664
- package/scripts/sw-pm.sh +12 -5
- package/scripts/sw-pr-lifecycle.sh +2 -1
- package/scripts/sw-predictive.sh +7 -1
- package/scripts/sw-prep.sh +185 -2
- package/scripts/sw-ps.sh +5 -25
- package/scripts/sw-public-dashboard.sh +15 -3
- package/scripts/sw-quality.sh +2 -1
- package/scripts/sw-reaper.sh +8 -25
- package/scripts/sw-recruit.sh +156 -2303
- package/scripts/sw-regression.sh +19 -12
- package/scripts/sw-release-manager.sh +3 -1
- package/scripts/sw-release.sh +4 -1
- package/scripts/sw-remote.sh +3 -1
- package/scripts/sw-replay.sh +7 -1
- package/scripts/sw-retro.sh +158 -1
- package/scripts/sw-review-rerun.sh +3 -1
- package/scripts/sw-scale.sh +10 -3
- package/scripts/sw-security-audit.sh +6 -1
- package/scripts/sw-self-optimize.sh +6 -3
- package/scripts/sw-session.sh +9 -3
- package/scripts/sw-setup.sh +3 -1
- package/scripts/sw-stall-detector.sh +406 -0
- package/scripts/sw-standup.sh +15 -7
- package/scripts/sw-status.sh +3 -1
- package/scripts/sw-strategic.sh +4 -1
- package/scripts/sw-stream.sh +7 -1
- package/scripts/sw-swarm.sh +18 -6
- package/scripts/sw-team-stages.sh +13 -6
- package/scripts/sw-templates.sh +5 -29
- package/scripts/sw-testgen.sh +7 -1
- package/scripts/sw-tmux-pipeline.sh +4 -1
- package/scripts/sw-tmux-role-color.sh +2 -0
- package/scripts/sw-tmux-status.sh +1 -1
- package/scripts/sw-tmux.sh +3 -1
- package/scripts/sw-trace.sh +3 -1
- package/scripts/sw-tracker-github.sh +3 -0
- package/scripts/sw-tracker-jira.sh +3 -0
- package/scripts/sw-tracker-linear.sh +3 -0
- package/scripts/sw-tracker.sh +3 -1
- package/scripts/sw-triage.sh +2 -1
- package/scripts/sw-upgrade.sh +3 -1
- package/scripts/sw-ux.sh +5 -2
- package/scripts/sw-webhook.sh +3 -1
- package/scripts/sw-widgets.sh +3 -1
- package/scripts/sw-worktree.sh +15 -3
- package/scripts/test-skill-injection.sh +1233 -0
- package/templates/pipelines/autonomous.json +27 -3
- package/templates/pipelines/cost-aware.json +34 -8
- package/templates/pipelines/deployed.json +12 -0
- package/templates/pipelines/enterprise.json +12 -0
- package/templates/pipelines/fast.json +6 -0
- package/templates/pipelines/full.json +27 -3
- package/templates/pipelines/hotfix.json +6 -0
- package/templates/pipelines/standard.json +12 -0
- package/templates/pipelines/tdd.json +12 -0
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
# ║ convergence.sh — Build Loop Iteration Progress Quality Scorer ║
|
|
4
|
+
# ║ ║
|
|
5
|
+
# ║ Scores each build loop iteration's progress (0-100) and detects when ║
|
|
6
|
+
# ║ the loop has converged, diverging, oscillating, or needs escalation. ║
|
|
7
|
+
# ║ ║
|
|
8
|
+
# ║ Usage: Source from sw-loop.sh, call convergence_integrate() after ║
|
|
9
|
+
# ║ test evaluation. Returns exit codes: 0=continue, 1=stop-success, ║
|
|
10
|
+
# ║ 2=stop-fail, 3=escalate. ║
|
|
11
|
+
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
12
|
+
|
|
13
|
+
[[ -n "${_CONVERGENCE_LOADED:-}" ]] && return 0
|
|
14
|
+
_CONVERGENCE_LOADED=1
|
|
15
|
+
|
|
16
|
+
# ─── Defaults ────────────────────────────────────────────────────────────────
|
|
17
|
+
ARTIFACTS_DIR="${ARTIFACTS_DIR:-.claude/pipeline-artifacts}"
|
|
18
|
+
SCRIPT_DIR="${SCRIPT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
|
19
|
+
|
|
20
|
+
# ─── Score an iteration's progress (0-100) ───────────────────────────────────
|
|
21
|
+
#
|
|
22
|
+
# Evaluates:
|
|
23
|
+
# - Tests newly passing (+25 points)
|
|
24
|
+
# - Tests newly failing (-15 points)
|
|
25
|
+
# - Meaningful code changes (+20 points)
|
|
26
|
+
# - Same error repeated (-10 points per repeat)
|
|
27
|
+
# - New files created (+5 points each, capped at +15)
|
|
28
|
+
#
|
|
29
|
+
convergence_score_iteration() {
|
|
30
|
+
local iteration="${1:-0}"
|
|
31
|
+
local test_passed="${2:-false}"
|
|
32
|
+
local test_changed="${3:-false}"
|
|
33
|
+
local git_diff_lines="${4:-0}"
|
|
34
|
+
local error_count="${5:-0}"
|
|
35
|
+
local prev_score="${6:-50}"
|
|
36
|
+
|
|
37
|
+
local score=50 # Baseline
|
|
38
|
+
local signals=()
|
|
39
|
+
|
|
40
|
+
# Signal 1: Test transitions
|
|
41
|
+
if [[ "$test_passed" == "true" ]]; then
|
|
42
|
+
if [[ "$test_changed" == "true" ]]; then
|
|
43
|
+
# Newly passing tests — major progress
|
|
44
|
+
score=$((score + 25))
|
|
45
|
+
signals+=("newly_passing_tests:+25")
|
|
46
|
+
else
|
|
47
|
+
# Already passing — maintain score
|
|
48
|
+
signals+=("tests_already_passing:0")
|
|
49
|
+
fi
|
|
50
|
+
else
|
|
51
|
+
# Tests failing
|
|
52
|
+
if [[ "$test_changed" == "true" ]]; then
|
|
53
|
+
# Newly failing tests — regression
|
|
54
|
+
score=$((score - 15))
|
|
55
|
+
signals+=("newly_failing_tests:-15")
|
|
56
|
+
else
|
|
57
|
+
# Already failing — decay
|
|
58
|
+
score=$((score - 5))
|
|
59
|
+
signals+=("tests_still_failing:-5")
|
|
60
|
+
fi
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# Signal 2: Code changes (meaningful = >5 lines)
|
|
64
|
+
if [[ "$git_diff_lines" -gt 5 ]]; then
|
|
65
|
+
local change_bonus=20
|
|
66
|
+
# Scaling: cap at 50 line changes for max bonus
|
|
67
|
+
if [[ "$git_diff_lines" -gt 50 ]]; then
|
|
68
|
+
change_bonus=$((20 - (git_diff_lines - 50) / 10))
|
|
69
|
+
[[ "$change_bonus" -lt 5 ]] && change_bonus=5
|
|
70
|
+
fi
|
|
71
|
+
score=$((score + change_bonus))
|
|
72
|
+
signals+=("code_changes:+${change_bonus}")
|
|
73
|
+
elif [[ "$git_diff_lines" -gt 0 ]]; then
|
|
74
|
+
# Minimal changes — slight penalty
|
|
75
|
+
score=$((score - 5))
|
|
76
|
+
signals+=("minimal_changes:-5")
|
|
77
|
+
else
|
|
78
|
+
# No changes — stalled
|
|
79
|
+
score=$((score - 10))
|
|
80
|
+
signals+=("no_code_changes:-10")
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
# Signal 3: Error repetition (from error-log.jsonl)
|
|
84
|
+
if [[ "$error_count" -gt 2 ]]; then
|
|
85
|
+
local err_penalty
|
|
86
|
+
err_penalty=$((error_count - 2))
|
|
87
|
+
err_penalty=$((err_penalty * 5))
|
|
88
|
+
[[ "$err_penalty" -gt 20 ]] && err_penalty=20
|
|
89
|
+
score=$((score - err_penalty))
|
|
90
|
+
signals+=("repeated_errors:-${err_penalty}")
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# Clamp score to 0-100
|
|
94
|
+
[[ "$score" -lt 0 ]] && score=0
|
|
95
|
+
[[ "$score" -gt 100 ]] && score=100
|
|
96
|
+
|
|
97
|
+
# Determine trend vs previous score
|
|
98
|
+
local trend="stable"
|
|
99
|
+
local trend_delta=$((score - prev_score))
|
|
100
|
+
if [[ "$trend_delta" -gt 5 ]]; then
|
|
101
|
+
trend="improving"
|
|
102
|
+
elif [[ "$trend_delta" -lt -5 ]]; then
|
|
103
|
+
trend="declining"
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# Output JSON
|
|
107
|
+
cat <<JSON
|
|
108
|
+
{
|
|
109
|
+
"score": $score,
|
|
110
|
+
"trend": "$trend",
|
|
111
|
+
"trend_delta": $trend_delta,
|
|
112
|
+
"signals": [$(printf '"%s"' "${signals[@]}" | sed 's/" *"/, /g')],
|
|
113
|
+
"iteration": $iteration,
|
|
114
|
+
"test_passed": $test_passed,
|
|
115
|
+
"code_changed": $([ "$git_diff_lines" -gt 0 ] && echo "true" || echo "false"),
|
|
116
|
+
"code_lines": $git_diff_lines,
|
|
117
|
+
"errors": $error_count
|
|
118
|
+
}
|
|
119
|
+
JSON
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# ─── Detect convergence status ────────────────────────────────────────────────
|
|
123
|
+
#
|
|
124
|
+
# Reads convergence-history.json and determines the loop's state:
|
|
125
|
+
# - converged: Score >80 for 2+ consecutive iterations
|
|
126
|
+
# - diverging: Score dropping for 3+ consecutive iterations
|
|
127
|
+
# - oscillating: Score bouncing up/down for 4+ iterations
|
|
128
|
+
# - progressing: Score improving overall
|
|
129
|
+
# - stalled: Score unchanged for 3+ iterations
|
|
130
|
+
#
|
|
131
|
+
convergence_detect() {
|
|
132
|
+
local history_file="${ARTIFACTS_DIR}/convergence-history.json"
|
|
133
|
+
|
|
134
|
+
# If no history yet, can't detect
|
|
135
|
+
if [[ ! -f "$history_file" ]]; then
|
|
136
|
+
cat <<JSON
|
|
137
|
+
{
|
|
138
|
+
"status": "progressing",
|
|
139
|
+
"recommendation": "continue",
|
|
140
|
+
"reason": "first_iteration",
|
|
141
|
+
"confidence": 0.5
|
|
142
|
+
}
|
|
143
|
+
JSON
|
|
144
|
+
return 0
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
# Parse history (array of {score, trend, iteration})
|
|
148
|
+
local scores=()
|
|
149
|
+
local trends=()
|
|
150
|
+
local last_4=()
|
|
151
|
+
|
|
152
|
+
# Extract last 6 scores and trends
|
|
153
|
+
local history
|
|
154
|
+
history=$(jq -r '.[] | "\(.score),\(.trend)"' "$history_file" 2>/dev/null | tail -6)
|
|
155
|
+
|
|
156
|
+
if [[ -z "$history" ]]; then
|
|
157
|
+
cat <<JSON
|
|
158
|
+
{
|
|
159
|
+
"status": "progressing",
|
|
160
|
+
"recommendation": "continue",
|
|
161
|
+
"reason": "malformed_history",
|
|
162
|
+
"confidence": 0.5
|
|
163
|
+
}
|
|
164
|
+
JSON
|
|
165
|
+
return 0
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
while IFS=',' read -r s t; do
|
|
169
|
+
[[ -z "$s" ]] && continue
|
|
170
|
+
scores+=("$s")
|
|
171
|
+
trends+=("$t")
|
|
172
|
+
last_4+=("$s")
|
|
173
|
+
# Keep only last 4
|
|
174
|
+
[[ "${#last_4[@]}" -gt 4 ]] && last_4=("${last_4[@]:1}")
|
|
175
|
+
done <<< "$history"
|
|
176
|
+
|
|
177
|
+
local num_scores=${#scores[@]}
|
|
178
|
+
[[ "$num_scores" -lt 2 ]] && {
|
|
179
|
+
cat <<JSON
|
|
180
|
+
{
|
|
181
|
+
"status": "progressing",
|
|
182
|
+
"recommendation": "continue",
|
|
183
|
+
"reason": "insufficient_history",
|
|
184
|
+
"confidence": 0.5,
|
|
185
|
+
"scores": $(printf '[%s]' "$(printf '%s,' "${scores[@]}" | sed 's/,$//')")
|
|
186
|
+
}
|
|
187
|
+
JSON
|
|
188
|
+
return 0
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
local first_score="${scores[0]}"
|
|
192
|
+
local last_idx=$((num_scores - 1))
|
|
193
|
+
local last_score="${scores[$last_idx]}"
|
|
194
|
+
|
|
195
|
+
# Detection logic
|
|
196
|
+
|
|
197
|
+
# 1. Converged: Last 2 scores >= 80 and stable
|
|
198
|
+
if [[ "$num_scores" -ge 2 ]]; then
|
|
199
|
+
local last1_idx=$((num_scores - 1))
|
|
200
|
+
local last2_idx=$((num_scores - 2))
|
|
201
|
+
local last1="${scores[$last1_idx]}"
|
|
202
|
+
local last2="${scores[$last2_idx]}"
|
|
203
|
+
if [[ "$last1" -ge 80 && "$last2" -ge 80 ]]; then
|
|
204
|
+
local delta=$((last1 - last2))
|
|
205
|
+
[[ "$delta" -lt -5 ]] && delta=$((0 - delta))
|
|
206
|
+
if [[ "$delta" -le 5 ]]; then
|
|
207
|
+
cat <<JSON
|
|
208
|
+
{
|
|
209
|
+
"status": "converged",
|
|
210
|
+
"recommendation": "stop",
|
|
211
|
+
"reason": "high_score_stable",
|
|
212
|
+
"confidence": 0.95,
|
|
213
|
+
"last_scores": [$(printf '%s' "${last_4[@]}" | sed 's/ /, /g')],
|
|
214
|
+
"latest_score": $last1
|
|
215
|
+
}
|
|
216
|
+
JSON
|
|
217
|
+
return 0
|
|
218
|
+
fi
|
|
219
|
+
fi
|
|
220
|
+
fi
|
|
221
|
+
|
|
222
|
+
# 2. Diverging: 3+ consecutive declining iterations
|
|
223
|
+
if [[ "$num_scores" -ge 3 ]]; then
|
|
224
|
+
local diverging_count=0
|
|
225
|
+
for i in $(seq 1 $((num_scores - 1))); do
|
|
226
|
+
local prev="${scores[$((i - 1))]}"
|
|
227
|
+
local curr="${scores[$i]}"
|
|
228
|
+
[[ "$curr" -lt "$prev" ]] && diverging_count=$((diverging_count + 1))
|
|
229
|
+
done
|
|
230
|
+
if [[ "$diverging_count" -ge 3 ]]; then
|
|
231
|
+
cat <<JSON
|
|
232
|
+
{
|
|
233
|
+
"status": "diverging",
|
|
234
|
+
"recommendation": "stop",
|
|
235
|
+
"reason": "consistent_decline",
|
|
236
|
+
"confidence": 0.85,
|
|
237
|
+
"last_scores": [$(printf '%s' "${last_4[@]}" | sed 's/ /, /g')],
|
|
238
|
+
"declining_iterations": $diverging_count
|
|
239
|
+
}
|
|
240
|
+
JSON
|
|
241
|
+
return 0
|
|
242
|
+
fi
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
# 3. Oscillating: 4+ iterations with alternating up/down
|
|
246
|
+
if [[ "$num_scores" -ge 4 ]]; then
|
|
247
|
+
local oscillations=0
|
|
248
|
+
local last_dir=""
|
|
249
|
+
for i in $(seq 1 $((num_scores - 1))); do
|
|
250
|
+
local prev="${scores[$((i - 1))]}"
|
|
251
|
+
local curr="${scores[$i]}"
|
|
252
|
+
local direction=""
|
|
253
|
+
[[ "$curr" -gt "$prev" ]] && direction="up"
|
|
254
|
+
[[ "$curr" -lt "$prev" ]] && direction="down"
|
|
255
|
+
|
|
256
|
+
if [[ -n "$last_dir" && "$direction" != "$last_dir" ]]; then
|
|
257
|
+
oscillations=$((oscillations + 1))
|
|
258
|
+
fi
|
|
259
|
+
last_dir="$direction"
|
|
260
|
+
done
|
|
261
|
+
|
|
262
|
+
if [[ "$oscillations" -ge 3 ]]; then
|
|
263
|
+
cat <<JSON
|
|
264
|
+
{
|
|
265
|
+
"status": "oscillating",
|
|
266
|
+
"recommendation": "escalate",
|
|
267
|
+
"reason": "bouncing_scores",
|
|
268
|
+
"confidence": 0.80,
|
|
269
|
+
"last_scores": [$(printf '%s' "${last_4[@]}" | sed 's/ /, /g')],
|
|
270
|
+
"oscillations": $oscillations
|
|
271
|
+
}
|
|
272
|
+
JSON
|
|
273
|
+
return 0
|
|
274
|
+
fi
|
|
275
|
+
fi
|
|
276
|
+
|
|
277
|
+
# 4. Stalled: Score unchanged for 3+ iterations
|
|
278
|
+
if [[ "$num_scores" -ge 3 ]]; then
|
|
279
|
+
local stalled_count=0
|
|
280
|
+
local start_idx=$((num_scores - 3))
|
|
281
|
+
local end_idx=$((num_scores - 1))
|
|
282
|
+
for i in $(seq "$start_idx" "$end_idx"); do
|
|
283
|
+
[[ "$i" -lt 0 ]] && continue
|
|
284
|
+
local prev_idx=$((i - 1))
|
|
285
|
+
[[ "$prev_idx" -lt 0 ]] && continue
|
|
286
|
+
local prev="${scores[$prev_idx]:-0}"
|
|
287
|
+
local curr="${scores[$i]}"
|
|
288
|
+
local delta=$((curr - prev))
|
|
289
|
+
[[ "$delta" -lt 0 ]] && delta=$((0 - delta))
|
|
290
|
+
[[ "$delta" -le 3 ]] && stalled_count=$((stalled_count + 1))
|
|
291
|
+
done
|
|
292
|
+
if [[ "$stalled_count" -ge 2 ]]; then
|
|
293
|
+
cat <<JSON
|
|
294
|
+
{
|
|
295
|
+
"status": "stalled",
|
|
296
|
+
"recommendation": "change_strategy",
|
|
297
|
+
"reason": "no_progress",
|
|
298
|
+
"confidence": 0.75,
|
|
299
|
+
"last_scores": [$(printf '%s' "${last_4[@]}" | sed 's/ /, /g')]
|
|
300
|
+
}
|
|
301
|
+
JSON
|
|
302
|
+
return 0
|
|
303
|
+
fi
|
|
304
|
+
fi
|
|
305
|
+
|
|
306
|
+
# 5. Default: progressing
|
|
307
|
+
cat <<JSON
|
|
308
|
+
{
|
|
309
|
+
"status": "progressing",
|
|
310
|
+
"recommendation": "continue",
|
|
311
|
+
"reason": "improving_or_stable",
|
|
312
|
+
"confidence": 0.70,
|
|
313
|
+
"last_scores": [$(printf '%s' "${last_4[@]}" | sed 's/ /, /g')],
|
|
314
|
+
"trend": "$([ "$last_score" -gt "$first_score" ] && echo "up" || echo "down")"
|
|
315
|
+
}
|
|
316
|
+
JSON
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
# ─── Track history of scores across iterations ────────────────────────────────
|
|
320
|
+
#
|
|
321
|
+
convergence_history() {
|
|
322
|
+
local iteration="${1:-0}"
|
|
323
|
+
local score="${2:-50}"
|
|
324
|
+
local trend="${3:-stable}"
|
|
325
|
+
|
|
326
|
+
local history_file="${ARTIFACTS_DIR}/convergence-history.json"
|
|
327
|
+
mkdir -p "$ARTIFACTS_DIR"
|
|
328
|
+
|
|
329
|
+
# Initialize if doesn't exist
|
|
330
|
+
if [[ ! -f "$history_file" ]]; then
|
|
331
|
+
echo "[]" > "$history_file.tmp.$$"
|
|
332
|
+
mv "$history_file.tmp.$$" "$history_file"
|
|
333
|
+
fi
|
|
334
|
+
|
|
335
|
+
# Append new entry
|
|
336
|
+
local entry="{\"iteration\": $iteration, \"score\": $score, \"trend\": \"$trend\", \"ts\": \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\"}"
|
|
337
|
+
local tmp_hist="${history_file}.tmp.$$"
|
|
338
|
+
|
|
339
|
+
# Use jq to append safely
|
|
340
|
+
if jq ". += [$(echo "$entry" | jq '.') ]" "$history_file" > "$tmp_hist" 2>/dev/null; then
|
|
341
|
+
mv "$tmp_hist" "$history_file"
|
|
342
|
+
else
|
|
343
|
+
# Fallback: if jq fails, manually append JSON
|
|
344
|
+
{
|
|
345
|
+
cat "$history_file" | jq '.' 2>/dev/null | head -n -1
|
|
346
|
+
echo " $entry"
|
|
347
|
+
echo "]"
|
|
348
|
+
} > "$tmp_hist" 2>/dev/null
|
|
349
|
+
mv "$tmp_hist" "$history_file" || rm -f "$tmp_hist"
|
|
350
|
+
fi
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
# ─── Suggest action when stalled/diverging ────────────────────────────────────
|
|
354
|
+
#
|
|
355
|
+
convergence_suggest_action() {
|
|
356
|
+
local status="${1:-progressing}"
|
|
357
|
+
local iteration="${2:-0}"
|
|
358
|
+
local context="${3:-}"
|
|
359
|
+
|
|
360
|
+
case "$status" in
|
|
361
|
+
stalled)
|
|
362
|
+
cat <<ACTION
|
|
363
|
+
{
|
|
364
|
+
"action": "change_approach",
|
|
365
|
+
"reason": "score_unchanged_for_multiple_iterations",
|
|
366
|
+
"suggestions": [
|
|
367
|
+
"Break the remaining work into smaller, more granular steps",
|
|
368
|
+
"Try a completely different implementation strategy",
|
|
369
|
+
"Verify that dependencies are installed and working",
|
|
370
|
+
"Read error messages more carefully — focus on root cause, not symptoms"
|
|
371
|
+
],
|
|
372
|
+
"escalation": "if_still_stuck_after_next_iteration"
|
|
373
|
+
}
|
|
374
|
+
ACTION
|
|
375
|
+
;;
|
|
376
|
+
diverging)
|
|
377
|
+
cat <<ACTION
|
|
378
|
+
{
|
|
379
|
+
"action": "escalate_model",
|
|
380
|
+
"reason": "score_declining_consistently",
|
|
381
|
+
"suggestions": [
|
|
382
|
+
"Problem may be too complex for current model",
|
|
383
|
+
"Try a fundamentally different approach",
|
|
384
|
+
"Consider splitting into independent subproblems"
|
|
385
|
+
],
|
|
386
|
+
"escalation": "use_opus_model_or_manual_review"
|
|
387
|
+
}
|
|
388
|
+
ACTION
|
|
389
|
+
;;
|
|
390
|
+
oscillating)
|
|
391
|
+
cat <<ACTION
|
|
392
|
+
{
|
|
393
|
+
"action": "restart_session",
|
|
394
|
+
"reason": "context_pollution_or_conflicting_approaches",
|
|
395
|
+
"suggestions": [
|
|
396
|
+
"Context may be polluted from previous attempts",
|
|
397
|
+
"Previous approaches may be conflicting with new ones",
|
|
398
|
+
"Start fresh session with progress.md carrying over key learnings"
|
|
399
|
+
],
|
|
400
|
+
"escalation": "manual_review_recommended"
|
|
401
|
+
}
|
|
402
|
+
ACTION
|
|
403
|
+
;;
|
|
404
|
+
*)
|
|
405
|
+
cat <<ACTION
|
|
406
|
+
{
|
|
407
|
+
"action": "continue",
|
|
408
|
+
"reason": "progressing_normally",
|
|
409
|
+
"suggestions": []
|
|
410
|
+
}
|
|
411
|
+
ACTION
|
|
412
|
+
;;
|
|
413
|
+
esac
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
# ─── Integration hook for sw-loop.sh ──────────────────────────────────────────
|
|
417
|
+
#
|
|
418
|
+
# Called after test gate + quality gates. Scores iteration, detects convergence,
|
|
419
|
+
# and returns:
|
|
420
|
+
# 0 = continue
|
|
421
|
+
# 1 = stop-success (converged)
|
|
422
|
+
# 2 = stop-fail (diverging)
|
|
423
|
+
# 3 = escalate (oscillating)
|
|
424
|
+
#
|
|
425
|
+
convergence_integrate() {
|
|
426
|
+
local iteration="${ITERATION:-0}"
|
|
427
|
+
local test_passed="${TEST_PASSED:-false}"
|
|
428
|
+
local quality_passed="${QUALITY_GATE_PASSED:-false}"
|
|
429
|
+
|
|
430
|
+
# For first iteration, can't compute trend
|
|
431
|
+
if [[ "$iteration" -lt 1 ]]; then
|
|
432
|
+
return 0
|
|
433
|
+
fi
|
|
434
|
+
|
|
435
|
+
# ── LOOP_COMPLETE signal check ─────────────────────────────────────────
|
|
436
|
+
# If the agent explicitly signaled completion and tests pass, stop immediately.
|
|
437
|
+
# This prevents wasting iterations when the work is done but scores stay ~50.
|
|
438
|
+
local _log_file="${LOG_DIR:-}/iteration-${iteration}.log"
|
|
439
|
+
if [[ "$test_passed" == "true" ]] && grep -q "LOOP_COMPLETE" "$_log_file" 2>/dev/null; then
|
|
440
|
+
# Write a converged recommendation so the pipeline knows why we stopped
|
|
441
|
+
local rec_file="${ARTIFACTS_DIR}/convergence-recommendation.json"
|
|
442
|
+
mkdir -p "$ARTIFACTS_DIR"
|
|
443
|
+
cat > "$rec_file.tmp.$$" <<CONV_JSON
|
|
444
|
+
{
|
|
445
|
+
"status": "converged",
|
|
446
|
+
"recommendation": "stop",
|
|
447
|
+
"reason": "agent_signaled_complete_tests_pass",
|
|
448
|
+
"confidence": 0.95
|
|
449
|
+
}
|
|
450
|
+
CONV_JSON
|
|
451
|
+
mv "$rec_file.tmp.$$" "$rec_file"
|
|
452
|
+
if type emit_event >/dev/null 2>&1; then
|
|
453
|
+
emit_event "convergence.agent_complete" \
|
|
454
|
+
"iteration=$iteration" \
|
|
455
|
+
"test_passed=$test_passed" 2>/dev/null || true
|
|
456
|
+
fi
|
|
457
|
+
return 1 # Stop success
|
|
458
|
+
fi
|
|
459
|
+
|
|
460
|
+
# Compute test transition (newly passing/failing)
|
|
461
|
+
local test_changed="false"
|
|
462
|
+
if [[ -f "$LOG_DIR/iteration-$((iteration - 1)).log" ]]; then
|
|
463
|
+
local prev_test=""
|
|
464
|
+
# Try to extract previous test status from state
|
|
465
|
+
if [[ -f "${STATE_FILE:-}" ]]; then
|
|
466
|
+
prev_test=$(grep "TEST_PASSED=" "${STATE_FILE}" | tail -1 | cut -d'=' -f2 || echo "")
|
|
467
|
+
fi
|
|
468
|
+
if [[ -n "$prev_test" && "$prev_test" != "$test_passed" ]]; then
|
|
469
|
+
test_changed="true"
|
|
470
|
+
fi
|
|
471
|
+
fi
|
|
472
|
+
|
|
473
|
+
# Git diff stats
|
|
474
|
+
local diff_lines=0
|
|
475
|
+
if [[ "$iteration" -gt 0 ]]; then
|
|
476
|
+
diff_lines=$(git diff HEAD~1 --stat 2>/dev/null | tail -1 | grep -oE '[0-9]+ insertion|0 file' | grep -oE '[0-9]+' | head -1 || echo "0")
|
|
477
|
+
diff_lines="${diff_lines:-0}"
|
|
478
|
+
fi
|
|
479
|
+
|
|
480
|
+
# Count repeated errors from error-log.jsonl
|
|
481
|
+
local error_count=0
|
|
482
|
+
local error_log="${ARTIFACTS_DIR}/error-log.jsonl"
|
|
483
|
+
if [[ -f "$error_log" ]]; then
|
|
484
|
+
# Count unique errors in last 5 entries
|
|
485
|
+
error_count=$(tail -5 "$error_log" 2>/dev/null | jq -r '.error // .message // empty' 2>/dev/null | sort | uniq -c | sort -rn | head -1 | awk '{print $1}' || echo "0")
|
|
486
|
+
error_count="${error_count:-0}"
|
|
487
|
+
fi
|
|
488
|
+
|
|
489
|
+
# Get previous score (default 50)
|
|
490
|
+
local prev_score=50
|
|
491
|
+
if [[ -f "${ARTIFACTS_DIR}/convergence-history.json" ]]; then
|
|
492
|
+
prev_score=$(jq -r '.[-1].score // 50' "${ARTIFACTS_DIR}/convergence-history.json" 2>/dev/null || echo "50")
|
|
493
|
+
prev_score="${prev_score:-50}"
|
|
494
|
+
fi
|
|
495
|
+
|
|
496
|
+
# Score this iteration
|
|
497
|
+
local score_json
|
|
498
|
+
score_json=$(convergence_score_iteration "$iteration" "$test_passed" "$test_changed" "$diff_lines" "$error_count" "$prev_score")
|
|
499
|
+
local score
|
|
500
|
+
score=$(echo "$score_json" | jq -r '.score // 50' 2>/dev/null || echo "50")
|
|
501
|
+
local trend
|
|
502
|
+
trend=$(echo "$score_json" | jq -r '.trend // "stable"' 2>/dev/null || echo "stable")
|
|
503
|
+
|
|
504
|
+
# Track history
|
|
505
|
+
convergence_history "$iteration" "$score" "$trend"
|
|
506
|
+
|
|
507
|
+
# Detect convergence
|
|
508
|
+
local detect_json
|
|
509
|
+
detect_json=$(convergence_detect)
|
|
510
|
+
local status
|
|
511
|
+
status=$(echo "$detect_json" | jq -r '.status // "progressing"' 2>/dev/null || echo "progressing")
|
|
512
|
+
local recommendation
|
|
513
|
+
recommendation=$(echo "$detect_json" | jq -r '.recommendation // "continue"' 2>/dev/null || echo "continue")
|
|
514
|
+
|
|
515
|
+
# Write recommendation to artifact
|
|
516
|
+
local rec_file="${ARTIFACTS_DIR}/convergence-recommendation.json"
|
|
517
|
+
mkdir -p "$ARTIFACTS_DIR"
|
|
518
|
+
echo "$detect_json" > "$rec_file.tmp.$$"
|
|
519
|
+
mv "$rec_file.tmp.$$" "$rec_file"
|
|
520
|
+
|
|
521
|
+
# Log via emit_event if available
|
|
522
|
+
if type emit_event >/dev/null 2>&1; then
|
|
523
|
+
emit_event "convergence.scored" \
|
|
524
|
+
"iteration=$iteration" \
|
|
525
|
+
"score=$score" \
|
|
526
|
+
"trend=$trend" \
|
|
527
|
+
"status=$status" \
|
|
528
|
+
"recommendation=$recommendation" \
|
|
529
|
+
"test_passed=$test_passed" \
|
|
530
|
+
"code_lines=$diff_lines" 2>/dev/null || true
|
|
531
|
+
fi
|
|
532
|
+
|
|
533
|
+
# Return based on recommendation
|
|
534
|
+
case "$recommendation" in
|
|
535
|
+
stop)
|
|
536
|
+
return 1 # Stop success
|
|
537
|
+
;;
|
|
538
|
+
escalate)
|
|
539
|
+
return 3 # Escalate
|
|
540
|
+
;;
|
|
541
|
+
change_strategy)
|
|
542
|
+
# Suggest action but continue for one more iteration
|
|
543
|
+
local action_json
|
|
544
|
+
action_json=$(convergence_suggest_action "$status" "$iteration")
|
|
545
|
+
local action_file="${ARTIFACTS_DIR}/convergence-action.json"
|
|
546
|
+
echo "$action_json" > "$action_file.tmp.$$"
|
|
547
|
+
mv "$action_file.tmp.$$" "$action_file"
|
|
548
|
+
|
|
549
|
+
# Log suggestion
|
|
550
|
+
if type warn >/dev/null 2>&1; then
|
|
551
|
+
warn "Convergence stalled — $(echo "$action_json" | jq -r '.reason' 2>/dev/null || echo 'no progress')"
|
|
552
|
+
fi
|
|
553
|
+
return 0 # Continue but with warning
|
|
554
|
+
;;
|
|
555
|
+
*)
|
|
556
|
+
return 0 # Continue
|
|
557
|
+
;;
|
|
558
|
+
esac
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
# ─── Utilities ────────────────────────────────────────────────────────────────
|
|
562
|
+
|
|
563
|
+
# Force history flush (for testing)
|
|
564
|
+
convergence_clear_history() {
|
|
565
|
+
rm -f "${ARTIFACTS_DIR}/convergence-history.json" "${ARTIFACTS_DIR}/convergence-recommendation.json" "${ARTIFACTS_DIR}/convergence-action.json"
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
# Read current convergence status
|
|
569
|
+
convergence_read_status() {
|
|
570
|
+
local rec_file="${ARTIFACTS_DIR}/convergence-recommendation.json"
|
|
571
|
+
if [[ -f "$rec_file" ]]; then
|
|
572
|
+
local status
|
|
573
|
+
status=$(jq -r '.status // "unknown"' "$rec_file" 2>/dev/null || echo "unknown")
|
|
574
|
+
echo "$status"
|
|
575
|
+
else
|
|
576
|
+
# If no recommendation file, check history and detect
|
|
577
|
+
if [[ -f "${ARTIFACTS_DIR}/convergence-history.json" ]]; then
|
|
578
|
+
local detect_out
|
|
579
|
+
detect_out=$(convergence_detect 2>/dev/null || echo '{"status":"unknown"}')
|
|
580
|
+
echo "$detect_out" | jq -r '.status // "unknown"' 2>/dev/null || echo "unknown"
|
|
581
|
+
else
|
|
582
|
+
echo "unknown"
|
|
583
|
+
fi
|
|
584
|
+
fi
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
# Export for use by sw-loop.sh
|
|
588
|
+
export -f convergence_score_iteration
|
|
589
|
+
export -f convergence_detect
|
|
590
|
+
export -f convergence_history
|
|
591
|
+
export -f convergence_suggest_action
|
|
592
|
+
export -f convergence_integrate
|
|
593
|
+
export -f convergence_clear_history
|
|
594
|
+
export -f convergence_read_status
|