@giamme/forge 0.0.0-stage → 0.8.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/CHANGELOG.md +181 -0
- package/LICENSE +21 -0
- package/README.md +1854 -2
- package/SKILL.md +122 -0
- package/VERSION +1 -0
- package/forge +74 -0
- package/package.json +26 -4
- package/references/dataset.md +329 -0
- package/references/decompose.md +667 -0
- package/references/fractal-dashboard.md +21 -0
- package/references/fractal.md +307 -0
- package/references/harnesses.md +353 -0
- package/references/jev.md +865 -0
- package/references/memory.md +228 -0
- package/references/planning.md +42 -0
- package/references/ripwire.md +47 -0
- package/references/runtime.md +64 -0
- package/references/solo.md +113 -0
- package/references/ui.md +112 -0
- package/registry.tsv +81 -0
- package/scripts/forge-artifact.sh +78 -0
- package/scripts/forge-contract.py +430 -0
- package/scripts/forge-dataset.py +6 -0
- package/scripts/forge-dispatch.sh +665 -0
- package/scripts/forge-fail-first.py +195 -0
- package/scripts/forge-fractal-dispatch.sh +4 -0
- package/scripts/forge-fractal-options.sh +54 -0
- package/scripts/forge-fractal.py +6 -0
- package/scripts/forge-guard.py +266 -0
- package/scripts/forge-install-fractal.sh +4 -0
- package/scripts/forge-install-ripwire.sh +52 -0
- package/scripts/forge-install.sh +135 -0
- package/scripts/forge-jev-options.sh +259 -0
- package/scripts/forge-jev.py +6 -0
- package/scripts/forge-lib-plans.sh +785 -0
- package/scripts/forge-lib-review.sh +494 -0
- package/scripts/forge-lib-task.sh +302 -0
- package/scripts/forge-lib-verify.sh +30 -0
- package/scripts/forge-memory.sh +575 -0
- package/scripts/forge-metrics.sh +39 -0
- package/scripts/forge-parallel-options.sh +76 -0
- package/scripts/forge-parallel.sh +1717 -0
- package/scripts/forge-plan-check.py +118 -0
- package/scripts/forge-prompt.py +45 -0
- package/scripts/forge-ripwire.py +136 -0
- package/scripts/forge-route.py +344 -0
- package/scripts/forge-runtime.py +533 -0
- package/scripts/forge-schedule.py +312 -0
- package/scripts/forge-shadow.py +305 -0
- package/scripts/forge-solo.sh +347 -0
- package/scripts/forge-ui-options.sh +42 -0
- package/scripts/forge-ui.py +435 -0
- package/scripts/forge-update.sh +71 -0
- package/scripts/forge-verify-coverage.py +956 -0
- package/scripts/forge-version.py +198 -0
- package/scripts/forge_dataset/__init__.py +439 -0
- package/scripts/forge_dataset/capture.py +330 -0
- package/scripts/forge_dataset/checks.py +109 -0
- package/scripts/forge_dataset/cli.py +233 -0
- package/scripts/forge_dataset/export.py +1009 -0
- package/scripts/forge_dataset/harvest.py +716 -0
- package/scripts/forge_dataset/importer.py +412 -0
- package/scripts/forge_dataset/outcomes.py +275 -0
- package/scripts/forge_dataset/routing.py +278 -0
- package/scripts/forge_fractal/__init__.py +83 -0
- package/scripts/forge_fractal/bridge.py +46 -0
- package/scripts/forge_fractal/cli.py +202 -0
- package/scripts/forge_fractal/dashboard.css +138 -0
- package/scripts/forge_fractal/dashboard.html +41 -0
- package/scripts/forge_fractal/dashboard.js +687 -0
- package/scripts/forge_fractal/decomposition.py +262 -0
- package/scripts/forge_fractal/execution.py +659 -0
- package/scripts/forge_fractal/inspection.py +261 -0
- package/scripts/forge_fractal/install.py +158 -0
- package/scripts/forge_fractal/invocation.py +94 -0
- package/scripts/forge_fractal/pipeline.py +124 -0
- package/scripts/forge_fractal/selection.py +179 -0
- package/scripts/forge_jev/__init__.py +376 -0
- package/scripts/forge_jev/backtest.py +322 -0
- package/scripts/forge_jev/cli.py +980 -0
- package/scripts/forge_jev/client.py +418 -0
- package/scripts/forge_jev/corpus.py +156 -0
- package/scripts/forge_jev/coupling.py +244 -0
- package/scripts/forge_jev/features.py +24 -0
- package/scripts/forge_jev/gates.py +100 -0
- package/scripts/forge_jev/memory.py +163 -0
- package/scripts/forge_jev/outcome.py +49 -0
- package/scripts/forge_jev/predict.py +204 -0
- package/scripts/forge_jev/questions.py +661 -0
- package/scripts/forge_jev/review.py +73 -0
- package/scripts/forge_jev/routing.py +271 -0
- package/scripts/forge_jev/subset.py +123 -0
- package/scripts/forge_jev/verify.py +453 -0
- package/scripts/forge_registry.py +140 -0
- package/ui/README.md +287 -0
- package/ui/THIRD_PARTY_NOTICES.md +69 -0
- package/ui/docs/api.md +888 -0
- package/ui/forge_ui/__init__.py +20 -0
- package/ui/forge_ui/__main__.py +135 -0
- package/ui/forge_ui/diffs.py +454 -0
- package/ui/forge_ui/executions.py +436 -0
- package/ui/forge_ui/export.py +204 -0
- package/ui/forge_ui/findings.py +132 -0
- package/ui/forge_ui/fractal.py +583 -0
- package/ui/forge_ui/models.py +216 -0
- package/ui/forge_ui/reader.py +1336 -0
- package/ui/forge_ui/server.py +303 -0
- package/ui/forge_ui/static/app.js +924 -0
- package/ui/forge_ui/static/cloud3d.js +646 -0
- package/ui/forge_ui/static/cloudmodel.js +375 -0
- package/ui/forge_ui/static/cloudui.js +394 -0
- package/ui/forge_ui/static/diffview.js +216 -0
- package/ui/forge_ui/static/favicon.svg +1 -0
- package/ui/forge_ui/static/files.js +776 -0
- package/ui/forge_ui/static/gantt.js +97 -0
- package/ui/forge_ui/static/graph.js +266 -0
- package/ui/forge_ui/static/index.html +55 -0
- package/ui/forge_ui/static/iso.js +765 -0
- package/ui/forge_ui/static/leaderboard.js +315 -0
- package/ui/forge_ui/static/map3d.js +45 -0
- package/ui/forge_ui/static/modelstats.js +249 -0
- package/ui/forge_ui/static/notify.js +179 -0
- package/ui/forge_ui/static/panel.js +526 -0
- package/ui/forge_ui/static/runmodel.js +651 -0
- package/ui/forge_ui/static/scene3d.js +996 -0
- package/ui/forge_ui/static/search.js +119 -0
- package/ui/forge_ui/static/style.css +618 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Per-task helpers for forge-parallel.sh: attempt accounting, infrastructure stops,
|
|
3
|
+
# QA-only resume, retry preparation, implementer guards, the severity gate and the
|
|
4
|
+
# report blocks printed under the results table.
|
|
5
|
+
#
|
|
6
|
+
# Sourced by forge-parallel.sh, which provides die/note/field/all_ids/task_status and
|
|
7
|
+
# SKILL_DIR. Functions only; nothing runs at source time.
|
|
8
|
+
|
|
9
|
+
# --- attempt log ----------------------------------------------------------------
|
|
10
|
+
# One row per do_task entry: epoch, attempt number, resulting status, infra class, and the
|
|
11
|
+
# implementer spec the attempt ran (the escalation policy counts failures per model, and
|
|
12
|
+
# the dwarf column is rewritten before the next attempt begins).
|
|
13
|
+
# `attempt` itself only counts attempts that were really spent (an infrastructure stop
|
|
14
|
+
# refunds it), so this file is the audit trail of what happened, including the refunds.
|
|
15
|
+
record_attempt() { # record_attempt <plan> <id>
|
|
16
|
+
local tdir="$1/tasks/$2" cls=""
|
|
17
|
+
[ "$(task_status "$1" "$2")" = BLOCKED ] && return 0 # never started: nothing to audit
|
|
18
|
+
[ "$(task_status "$1" "$2")" = INFRA ] && cls="$(sed -n 's/^class=//p' "$tdir/infra.txt" 2>/dev/null | head -1)"
|
|
19
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "$(date +%s)" "$(cat "$tdir/attempt" 2>/dev/null || echo 0)" \
|
|
20
|
+
"$(task_status "$1" "$2")" "$cls" "$(field "$1/tasks.tsv" "$2" dwarf)" >> "$tdir/attempts.tsv"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# --- infrastructure stops -------------------------------------------------------
|
|
24
|
+
# A dispatch that failed because of quota, auth, a rate limit, the network or empty
|
|
25
|
+
# output says nothing about the work. The task is marked INFRA and the attempt is not
|
|
26
|
+
# spent. A dwarf-stage stop refunds the counter; a QA-stage stop leaves the dwarf's
|
|
27
|
+
# committed work intact, so a `resume` marker makes the next entry re-run QA only.
|
|
28
|
+
infra_stop() { # infra_stop <tdir> <id> <role> <attempt>
|
|
29
|
+
local tdir="$1" id="$2" role="$3" attempt="$4" cls
|
|
30
|
+
cp "$tdir/$role.infra" "$tdir/infra.txt" 2>/dev/null || : > "$tdir/infra.txt"
|
|
31
|
+
printf 'stage=%s\n' "$role" >> "$tdir/infra.txt"
|
|
32
|
+
cls="$(sed -n 's/^class=//p' "$tdir/infra.txt" | head -1)"
|
|
33
|
+
echo INFRA > "$tdir/status"
|
|
34
|
+
if [ "$role" = dwarf ]; then
|
|
35
|
+
[ "$attempt" -gt 0 ] && echo $((attempt - 1)) > "$tdir/attempt"
|
|
36
|
+
rm -f "$tdir/resume"
|
|
37
|
+
else
|
|
38
|
+
echo qa > "$tdir/resume"
|
|
39
|
+
fi
|
|
40
|
+
note "$id: $role dispatch hit an infrastructure failure (${cls:-unknown}) — no attempt spent; re-run to resume"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# A QA-only resume is trusted only while the worktree is still exactly what was
|
|
44
|
+
# reviewed. Anything else clears the marker and the full path runs.
|
|
45
|
+
resume_valid() { # resume_valid <tdir> <wt>
|
|
46
|
+
local tdir="$1" wt="$2"
|
|
47
|
+
[ "$(cat "$tdir/resume" 2>/dev/null)" = qa ] || return 1
|
|
48
|
+
[ -s "$tdir/reviewed.commit" ] && [ -s "$tdir/source.fingerprint" ] && [ -s "$tdir/changes.diff" ] || return 1
|
|
49
|
+
[ -e "$wt/.git" ] || return 1
|
|
50
|
+
[ "$(git -C "$wt" rev-parse HEAD 2>/dev/null)" = "$(cat "$tdir/reviewed.commit")" ] || return 1
|
|
51
|
+
[ "$(forge_fingerprint "$wt")" = "$(cat "$tdir/source.fingerprint")" ]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# --- retry preparation ----------------------------------------------------------
|
|
55
|
+
# Shared by `retry` (a human asked) and the scheduler's automatic retries. The findings
|
|
56
|
+
# are the whole point of retrying rather than re-running: the dwarf is told what was
|
|
57
|
+
# wrong with its own previous attempt.
|
|
58
|
+
prepare_retry() { # prepare_retry <plan> <id> [new-dwarf]
|
|
59
|
+
local PLAN="$1" id="$2" newdwarf="${3:-}" T="$1/tasks.tsv" tdir="$1/tasks/$2" st verdict
|
|
60
|
+
st="$(task_status "$PLAN" "$id")"
|
|
61
|
+
verdict="$(forge_verdict "$tdir/qa.last" 2>/dev/null)"
|
|
62
|
+
if [ "$st" = INFRA ]; then
|
|
63
|
+
# Nothing was judged, so there is nothing new to carry forward; the stop already
|
|
64
|
+
# recorded where to resume (a `resume` marker for a QA-stage stop).
|
|
65
|
+
note "$id: resuming after an infrastructure stop"
|
|
66
|
+
elif [ "$st" = UNKNOWN ] && [ -s "$tdir/reviewed.commit" ]; then
|
|
67
|
+
# The reviewer never reached a verdict, so the code was not rejected. Re-asking QA is
|
|
68
|
+
# cheaper and more honest than paying an implementer to rewrite unjudged work; the
|
|
69
|
+
# marker is revalidated against the worktree before it is believed.
|
|
70
|
+
echo qa > "$tdir/resume"
|
|
71
|
+
note "$id: the reviewer gave no verdict — re-running QA on the unchanged work"
|
|
72
|
+
# Only a real review replaces the findings. The runner also writes explanatory text
|
|
73
|
+
# into qa.last when it fails a task without dispatching QA ("produced no changes at
|
|
74
|
+
# all"), and copying that over would throw away the actual findings the previous
|
|
75
|
+
# reviewer gave — the one thing a retry exists to carry forward.
|
|
76
|
+
elif [ "$verdict" = PASS ] || [ "$verdict" = FAIL ]; then
|
|
77
|
+
cp "$tdir/qa.last" "$tdir/retry_findings.md"
|
|
78
|
+
elif grep -q 'FORGE_VERDICT' "$tdir/qa.last" 2>/dev/null; then
|
|
79
|
+
cp "$tdir/qa.last" "$tdir/retry_findings.md"
|
|
80
|
+
elif [ -s "$tdir/retry_findings.md" ]; then
|
|
81
|
+
note "$id: no new review since the last retry — reusing the previous findings"
|
|
82
|
+
else
|
|
83
|
+
note "$id: no previous QA findings to pass on (last status was $st)"
|
|
84
|
+
: > "$tdir/retry_findings.md"
|
|
85
|
+
fi
|
|
86
|
+
# The retry policy (forge-route.py escalation): after escalate_after failed attempts on
|
|
87
|
+
# one model (default 3), the next attempt goes a tier up in the user's own pools instead
|
|
88
|
+
# of repeating the dispatch whose fourth try passes least often, or the task stops with a
|
|
89
|
+
# split suggestion. An explicit --dwarf is the human's call and skips the policy.
|
|
90
|
+
local cur next rc
|
|
91
|
+
cur="$(field "$T" "$id" dwarf)"
|
|
92
|
+
if [ -z "$newdwarf" ]; then
|
|
93
|
+
next="$(python3 "$SKILL_DIR/scripts/forge-route.py" escalation --plan "$PLAN" --task "$id")"; rc=$?
|
|
94
|
+
if [ "$rc" = 3 ]; then
|
|
95
|
+
note "$(cat "$tdir/split.suggested" 2>/dev/null)"
|
|
96
|
+
return 3
|
|
97
|
+
fi
|
|
98
|
+
if [ -n "$next" ]; then
|
|
99
|
+
note "$id: escalating from $cur to $next after $(cat "$PLAN/escalate_after" 2>/dev/null || echo 3) failed attempts on one model (--escalate-after 0 turns this off)"
|
|
100
|
+
newdwarf="$next"
|
|
101
|
+
fi
|
|
102
|
+
else
|
|
103
|
+
rm -f "$tdir/escalation.stop" "$tdir/split.suggested" "$tdir/escalation.next"
|
|
104
|
+
fi
|
|
105
|
+
if [ "$newdwarf" = "$cur" ]; then newdwarf=""; fi # the same spec again is no escalation
|
|
106
|
+
if [ -n "$newdwarf" ]; then
|
|
107
|
+
# Written into tasks.tsv rather than kept in a side file: the table then shows the
|
|
108
|
+
# model that will actually be spent, and plan's rule that an explicit column value
|
|
109
|
+
# survives a re-plan protects the escalation for free. Rewritten first, so a failed
|
|
110
|
+
# rewrite is never recorded as an escalation that happened.
|
|
111
|
+
awk -F'\t' -v OFS='\t' -v id="$id" -v dw="$newdwarf" \
|
|
112
|
+
'$0 !~ /^#/ && $1==id { $5=dw } { print }' "$T" > "$PLAN/.tasks.tsv.retry" \
|
|
113
|
+
&& mv "$PLAN/.tasks.tsv.retry" "$T" \
|
|
114
|
+
|| { note "$id: could not write $newdwarf into tasks.tsv; nothing was retried"; return 1; }
|
|
115
|
+
# The rewrite loses which model failed first, and an escalation is exactly the record
|
|
116
|
+
# of "this tier was too weak for this task".
|
|
117
|
+
printf '%s\t%s\t%s\t%s\n' "$(date +%s)" "$(cat "$tdir/attempt" 2>/dev/null || echo 0)" \
|
|
118
|
+
"$cur" "$newdwarf" >> "$tdir/escalations.tsv"
|
|
119
|
+
# Recorded at the rewrite itself: it happens before the retried attempt begins, so no
|
|
120
|
+
# attempt capture would ever see it as something that attempt produced.
|
|
121
|
+
forge_dataset_event escalation --dir "$PLAN" --task "$id" --set "from=$cur" \
|
|
122
|
+
--set "to=$newdwarf" --set "after_attempt=$(cat "$tdir/attempt" 2>/dev/null || echo 0)"
|
|
123
|
+
rm -f "$tdir/escalation.next"
|
|
124
|
+
fi
|
|
125
|
+
rm -f "$tdir/noretry"
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# The scheduler's `_prepare_retry` target: prepare, then clear the status so the task
|
|
129
|
+
# reads as PENDING again. `retry` clears it itself, after its own preflight.
|
|
130
|
+
retry_for_scheduler() { # retry_for_scheduler <plan> <id>
|
|
131
|
+
prepare_retry "$1" "$2" || return $?
|
|
132
|
+
rm -f "$1/tasks/$2/status"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# --- implementer guards ---------------------------------------------------------
|
|
136
|
+
# Observed facts, never self-reports. They warn and are recorded in guard.txt; they do
|
|
137
|
+
# not change a task's status, because the diff and the reviewer still decide that.
|
|
138
|
+
guard_before_dwarf() { # guard_before_dwarf <wt> <tdir>
|
|
139
|
+
rm -f "$2/guard.txt"
|
|
140
|
+
git -C "$1" rev-parse HEAD > "$2/dwarf.head.before" 2>/dev/null || rm -f "$2/dwarf.head.before"
|
|
141
|
+
git -C "$1" symbolic-ref -q HEAD > "$2/dwarf.ref.before" 2>/dev/null || echo detached > "$2/dwarf.ref.before"
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
guard_after_dwarf() { # guard_after_dwarf <wt> <tdir> <id>
|
|
145
|
+
local wt="$1" tdir="$2" id="$3" h0 h1 r0 r1 n g="$2/guard.txt" line
|
|
146
|
+
: > "$g"
|
|
147
|
+
h0="$(cat "$tdir/dwarf.head.before" 2>/dev/null)"; h1="$(git -C "$wt" rev-parse HEAD 2>/dev/null)"
|
|
148
|
+
r0="$(cat "$tdir/dwarf.ref.before" 2>/dev/null)"; r1="$(git -C "$wt" symbolic-ref -q HEAD 2>/dev/null || echo detached)"
|
|
149
|
+
if [ -n "$h0" ] && [ -n "$h1" ] && [ "$h0" != "$h1" ]; then
|
|
150
|
+
n="$(git -C "$wt" rev-list --count "$h0..$h1" 2>/dev/null || echo '?')"
|
|
151
|
+
printf 'self-commit: the implementer committed (%s new commit(s)) although it was told not to; the cumulative diff against the task base is still what gets reviewed\n' "$n" >> "$g"
|
|
152
|
+
fi
|
|
153
|
+
if [ -n "$r0" ] && [ "$r0" != "$r1" ]; then
|
|
154
|
+
printf 'branch switch: the implementer changed the checked-out ref from %s to %s; the work is rejected as INVALIDATED unless it ends on the task branch\n' "$r0" "$r1" >> "$g"
|
|
155
|
+
fi
|
|
156
|
+
if [ -s "$tdir/dwarf.orphans" ]; then
|
|
157
|
+
printf 'orphan processes: %s process(es) the implementer left running were stopped (listed in %s)\n' \
|
|
158
|
+
"$(wc -l < "$tdir/dwarf.orphans" | tr -d ' ')" "$tdir/dwarf.orphans" >> "$g"
|
|
159
|
+
fi
|
|
160
|
+
if [ -s "$tdir/dwarf.last" ]; then
|
|
161
|
+
python3 "$SKILL_DIR/scripts/forge-guard.py" promises "$tdir/dwarf.last" 2>/dev/null |
|
|
162
|
+
while IFS= read -r line; do
|
|
163
|
+
printf 'promised later work: %s\n' "$line"
|
|
164
|
+
done >> "$g"
|
|
165
|
+
fi
|
|
166
|
+
if [ -s "$g" ]; then
|
|
167
|
+
while IFS= read -r line; do note "$id: guard — $line"; done < "$g"
|
|
168
|
+
else
|
|
169
|
+
rm -f "$g"
|
|
170
|
+
fi
|
|
171
|
+
return 0
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# --- QA severity gate -----------------------------------------------------------
|
|
175
|
+
qa_threshold_for() { # per-task file wins over the plan-level default; prints nothing if unset
|
|
176
|
+
local f
|
|
177
|
+
for f in "$1/tasks/$2/qa_threshold" "$1/qa_threshold"; do
|
|
178
|
+
[ -s "$f" ] && { head -1 "$f" | tr -d ' \r'; return 0; }
|
|
179
|
+
done
|
|
180
|
+
return 0
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
# With an explicit threshold, a reviewer's FAIL whose confirmed findings are all labelled
|
|
184
|
+
# and less severe than the threshold is accepted as PASS-with-known-issues. The literal
|
|
185
|
+
# status stays PASS, so the merge path, the scheduler and Fractal need no new vocabulary.
|
|
186
|
+
# Anything unlabelled stays blocking: a finding the gate cannot place is not tolerated.
|
|
187
|
+
qa_gate() { # qa_gate <plan> <id> <tdir> <verdict>
|
|
188
|
+
local plan="$1" id="$2" tdir="$3" verdict="$4" thr line
|
|
189
|
+
rm -f "$tdir/qa.gate" "$tdir/known_issues.md"
|
|
190
|
+
thr="$(qa_threshold_for "$plan" "$id")"
|
|
191
|
+
[ -n "$thr" ] || return 0
|
|
192
|
+
line="$(python3 "$SKILL_DIR/scripts/forge-contract.py" gate --verdict "${verdict:-UNKNOWN}" \
|
|
193
|
+
--qa-last "$tdir/qa.last" --threshold "$thr" --known-issues "$tdir/known_issues.md" 2>/dev/null)" || return 0
|
|
194
|
+
[ -n "$line" ] || return 0
|
|
195
|
+
printf '%s\t%s\n' "$thr" "$line" > "$tdir/qa.gate"
|
|
196
|
+
if [ "$(printf '%s' "$line" | awk -F'\t' '{print $1}')" = accept ]; then
|
|
197
|
+
echo PASS > "$tdir/status"
|
|
198
|
+
note "$id: the reviewer said FAIL, but every confirmed finding is less severe than the $thr threshold — accepted with known issues ($tdir/known_issues.md)"
|
|
199
|
+
fi
|
|
200
|
+
return 0
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
# --- report blocks ----------------------------------------------------------------
|
|
204
|
+
report_infra() { # report_infra <plan>
|
|
205
|
+
local plan="$1" id n=0 cls
|
|
206
|
+
for id in $(all_ids "$plan/tasks.tsv"); do
|
|
207
|
+
[ "$(task_status "$plan" "$id")" = INFRA ] || continue
|
|
208
|
+
[ "$n" = 0 ] && echo "paused on an infrastructure failure (not a task failure, no attempt spent):"
|
|
209
|
+
n=$((n+1))
|
|
210
|
+
cls="$(sed -n 's/^class=//p' "$plan/tasks/$id/infra.txt" 2>/dev/null | head -1)"
|
|
211
|
+
printf ' %-14s %s (%s stage)\n' "$id" "${cls:-unknown}" "$(sed -n 's/^stage=//p' "$plan/tasks/$id/infra.txt" 2>/dev/null | tail -1)"
|
|
212
|
+
done
|
|
213
|
+
if [ "$n" -gt 0 ]; then
|
|
214
|
+
echo " fix the cause (quota, login, network), then re-run: forge-parallel.sh run $plan"
|
|
215
|
+
echo
|
|
216
|
+
fi
|
|
217
|
+
return 0
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
report_known_issues() { # report_known_issues <plan>
|
|
221
|
+
local plan="$1" id n=0
|
|
222
|
+
for id in $(all_ids "$plan/tasks.tsv"); do
|
|
223
|
+
[ -s "$plan/tasks/$id/known_issues.md" ] || continue
|
|
224
|
+
[ "$n" = 0 ] && echo "known issues accepted (findings below the QA threshold, or accepted by a human):"
|
|
225
|
+
n=$((n+1))
|
|
226
|
+
printf ' %-14s %s finding(s) %s\n' "$id" "$(grep -c '^- ' "$plan/tasks/$id/known_issues.md" 2>/dev/null || echo 0)" "$plan/tasks/$id/known_issues.md"
|
|
227
|
+
done
|
|
228
|
+
[ "$n" -gt 0 ] && echo
|
|
229
|
+
return 0
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
report_guard_notes() { # report_guard_notes <plan>
|
|
233
|
+
local plan="$1" id n=0 line
|
|
234
|
+
for id in $(all_ids "$plan/tasks.tsv"); do
|
|
235
|
+
[ -s "$plan/tasks/$id/guard.txt" ] || continue
|
|
236
|
+
[ "$n" = 0 ] && echo "implementer guard notes (observed by forge, not self-reported):"
|
|
237
|
+
n=$((n+1))
|
|
238
|
+
while IFS= read -r line; do printf ' %-14s %s\n' "$id" "$line"; done < "$plan/tasks/$id/guard.txt"
|
|
239
|
+
done
|
|
240
|
+
[ "$n" -gt 0 ] && echo
|
|
241
|
+
return 0
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
report_verify_notes() { # report_verify_notes <dir holding verification.*>
|
|
245
|
+
local d="$1"
|
|
246
|
+
if [ -s "$d/verification.flaky" ]; then
|
|
247
|
+
echo "FLAKY verification: the command failed once and passed on rerun — read $d/verification.log"
|
|
248
|
+
echo " before trusting this result; an intermittent failure can be a real bug."
|
|
249
|
+
echo
|
|
250
|
+
fi
|
|
251
|
+
if [ -s "$d/verification.coverage.txt" ]; then
|
|
252
|
+
echo "verification coverage (suites the project defines that the verify command does not run):"
|
|
253
|
+
sed 's/^/ /' "$d/verification.coverage.txt"
|
|
254
|
+
echo
|
|
255
|
+
fi
|
|
256
|
+
return 0
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
# Everything worth saying under the results table, in one call.
|
|
260
|
+
# Advisory notes under the approval table: tasks large enough that their first attempt
|
|
261
|
+
# usually fails review, and prompts with no acceptance criteria (forge-plan-check.py).
|
|
262
|
+
report_size() { # report_size <plan>
|
|
263
|
+
python3 "$SKILL_DIR/scripts/forge-plan-check.py" size --plan "$1" 2>/dev/null || true
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
# The retry policy's verdicts: a task that stopped with a split suggestion, or whose next
|
|
267
|
+
# retry would go a tier up. Decided after every attempt, so a spent budget still says it.
|
|
268
|
+
report_escalations() { # report_escalations <plan>
|
|
269
|
+
local plan="$1" id n=0 head
|
|
270
|
+
head="retry policy (after $(cat "$plan/escalate_after" 2>/dev/null || echo 3) failed attempts on one model):"
|
|
271
|
+
for id in $(all_ids "$plan/tasks.tsv"); do
|
|
272
|
+
if [ -s "$plan/tasks/$id/split.suggested" ]; then
|
|
273
|
+
[ "$n" = 0 ] && echo "$head"
|
|
274
|
+
n=$((n+1)); printf ' %-14s split suggested: %s\n' "$id" "$(cat "$plan/tasks/$id/split.suggested")"
|
|
275
|
+
elif [ -s "$plan/tasks/$id/escalation.next" ] && [ "$(task_status "$plan" "$id")" != MERGED ]; then
|
|
276
|
+
[ "$n" = 0 ] && echo "$head"
|
|
277
|
+
n=$((n+1)); printf ' %-14s its next retry goes to %s\n' "$id" "$(cat "$plan/tasks/$id/escalation.next")"
|
|
278
|
+
fi
|
|
279
|
+
done
|
|
280
|
+
[ "$n" -gt 0 ] && echo
|
|
281
|
+
return 0
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
run_report_extras() { # run_report_extras <plan> [<verification dir>]
|
|
285
|
+
report_infra "$1"
|
|
286
|
+
report_escalations "$1"
|
|
287
|
+
report_known_issues "$1"
|
|
288
|
+
report_guard_notes "$1"
|
|
289
|
+
report_verify_notes "${2:-$1}"
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
# Integrating a partial run is allowed (a human may want what passed), but never silently:
|
|
293
|
+
# say which tasks are NOT in the result.
|
|
294
|
+
report_unmerged() { # report_unmerged <plan>
|
|
295
|
+
local plan="$1" id st list=""
|
|
296
|
+
for id in $(all_ids "$plan/tasks.tsv"); do
|
|
297
|
+
st="$(task_status "$plan" "$id")"
|
|
298
|
+
[ "$st" = MERGED ] || list="$list $id($st)"
|
|
299
|
+
done
|
|
300
|
+
[ -z "$list" ] || note "WARNING: not merged, so NOT part of this integration:$list"
|
|
301
|
+
return 0
|
|
302
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Verification helpers for forge-parallel.sh. Sourced; functions only.
|
|
3
|
+
|
|
4
|
+
# The verify command a plan would run: --verify (persisted in the plan) first, then the
|
|
5
|
+
# repository's own .forge/verify. Prints nothing when neither is configured.
|
|
6
|
+
verify_command_for() { # verify_command_for <plan> <repo>
|
|
7
|
+
if [ -s "$1/verify_cmd" ]; then cat "$1/verify_cmd"
|
|
8
|
+
elif [ -s "$2/.forge/verify" ]; then cat "$2/.forge/verify"
|
|
9
|
+
fi
|
|
10
|
+
return 0
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
# "Verify is whatever you hand it": a gate that skips a suite the project defines lets a
|
|
14
|
+
# regression in that suite reach the branch with no warning. Read the project's own test
|
|
15
|
+
# configuration and say which suites the command does not run. Advisory by design — it
|
|
16
|
+
# prints and records, it never changes an outcome, and a failure of the checker is silent.
|
|
17
|
+
verify_coverage_report() { # verify_coverage_report <checkout> <command> <out-file>
|
|
18
|
+
local dir="$1" cmd="$2" out="$3" line
|
|
19
|
+
[ -f "$SKILL_DIR/scripts/forge-verify-coverage.py" ] || return 0
|
|
20
|
+
rm -f "$out"
|
|
21
|
+
if [ -n "$cmd" ]; then
|
|
22
|
+
python3 "$SKILL_DIR/scripts/forge-verify-coverage.py" check --repo "$dir" --command="$cmd" --out "$out" >/dev/null 2>&1
|
|
23
|
+
else
|
|
24
|
+
python3 "$SKILL_DIR/scripts/forge-verify-coverage.py" check --repo "$dir" --none --out "$out" >/dev/null 2>&1
|
|
25
|
+
fi
|
|
26
|
+
if [ -s "$out" ]; then
|
|
27
|
+
while IFS= read -r line; do note "$line"; done < "$out"
|
|
28
|
+
fi
|
|
29
|
+
return 0
|
|
30
|
+
}
|