@giamme/forge 0.9.0 → 0.11.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 +56 -0
- package/SKILL.md +22 -3
- package/VERSION +1 -1
- package/docs/artifacts.md +28 -2
- package/docs/commands.md +55 -0
- package/docs/dataset.md +22 -0
- package/docs/decomposed-runs.md +54 -2
- package/docs/forge-ui.md +48 -7
- package/docs/memory.md +4 -2
- package/docs/reviews.md +87 -2
- package/forge +31 -1
- package/package.json +1 -1
- package/references/dataset.md +103 -4
- package/references/decompose.md +41 -5
- package/references/harnesses.md +43 -1
- package/references/ui.md +31 -6
- package/scripts/forge-config.py +350 -0
- package/scripts/forge-control.py +649 -0
- package/scripts/forge-dispatch.sh +235 -121
- package/scripts/forge-issues.py +1134 -0
- package/scripts/forge-lib-plans.sh +6 -2
- package/scripts/forge-lib-review.sh +158 -3
- package/scripts/forge-lib-task.sh +358 -41
- package/scripts/forge-parallel-options.sh +3 -1
- package/scripts/forge-parallel.sh +99 -13
- package/scripts/forge-prompt.py +61 -4
- package/scripts/forge-route.py +245 -28
- package/scripts/forge-runs-rm.py +840 -0
- package/scripts/forge-runs.py +19 -1
- package/scripts/forge-runtime.py +47 -1
- package/scripts/forge-schedule.py +227 -11
- package/scripts/forge-ui.py +85 -19
- package/scripts/forge_dataset/__init__.py +84 -8
- package/scripts/forge_dataset/capture.py +5 -2
- package/scripts/forge_dataset/cli.py +44 -1
- package/scripts/forge_dataset/export.py +36 -3
- package/scripts/forge_dataset/forecast.py +355 -0
- package/scripts/forge_dataset/harvest.py +45 -2
- package/scripts/forge_dataset/importer.py +431 -17
- package/scripts/forge_dataset/reviewers.py +145 -0
- package/scripts/forge_dataset/routing.py +1 -0
- package/scripts/forge_jev/__init__.py +7 -3
- package/scripts/forge_registry.py +183 -26
- package/ui/README.md +41 -7
- package/ui/docs/api.md +259 -21
- package/ui/forge_ui/__main__.py +10 -2
- package/ui/forge_ui/control.py +587 -0
- package/ui/forge_ui/export.py +17 -3
- package/ui/forge_ui/reader.py +210 -12
- package/ui/forge_ui/server.py +125 -4
- package/ui/forge_ui/static/app.js +104 -18
- package/ui/forge_ui/static/cloud3d.js +4 -1
- package/ui/forge_ui/static/cloudmodel.js +1 -1
- package/ui/forge_ui/static/cloudui.js +13 -4
- package/ui/forge_ui/static/controls.js +619 -0
- package/ui/forge_ui/static/findingsview.js +438 -0
- package/ui/forge_ui/static/graph.js +6 -5
- package/ui/forge_ui/static/index.html +7 -1
- package/ui/forge_ui/static/leaderboard.js +18 -0
- package/ui/forge_ui/static/modelstats.js +19 -1
- package/ui/forge_ui/static/panel.js +76 -14
- package/ui/forge_ui/static/rundelete.js +388 -0
- package/ui/forge_ui/static/runmodel.js +128 -13
- package/ui/forge_ui/static/scene3d.js +32 -15
- package/ui/forge_ui/static/settings.js +399 -0
- package/ui/forge_ui/static/style.css +184 -5
- package/ui/forge_ui/static/uikit.js +161 -0
|
@@ -130,7 +130,7 @@ plans_make_integration() { # plans_make_integration <repo> <branch> <commit> <wt
|
|
|
130
130
|
|
|
131
131
|
# Per-plan option files carried to a derived plan. The cost-bearing choices a user made
|
|
132
132
|
# for this plan apply to the plan made from it; `planner` does not (nothing plans again).
|
|
133
|
-
PLANS_OPTION_FILES="no_ripwire no_memory yolo_dwarf yolo_qa qa_threshold timeout verify_retries verify_cmd setup_cmd final_qa shadow_qa shadow_rate explore escalate_after fractal-routing.json fractal-selection.json jev-selection.json"
|
|
133
|
+
PLANS_OPTION_FILES="no_ripwire no_memory yolo_dwarf yolo_qa reviewer_session qa_threshold timeout verify_retries verify_cmd setup_cmd final_qa shadow_qa shadow_rate explore escalate_after fractal-routing.json fractal-selection.json jev-selection.json"
|
|
134
134
|
|
|
135
135
|
# --- accept ---------------------------------------------------------------------
|
|
136
136
|
# A reviewer's FAIL is the QA gate; overriding it is the user's call and never forge's or
|
|
@@ -505,7 +505,7 @@ Retry or accept them (or split them off into their own plan) first." 3
|
|
|
505
505
|
[ "$ok" = 1 ] || { plans_undo; trap - INT TERM; die "could not write the combined plan" 3; }
|
|
506
506
|
|
|
507
507
|
# Options: a plan-wide choice carries over when every source made it identically; the
|
|
508
|
-
#
|
|
508
|
+
# permission flags and the reviewer-session experiment only when every source chose them.
|
|
509
509
|
local goals="" g
|
|
510
510
|
for p in "${SRC[@]}"; do
|
|
511
511
|
if [ -s "$p/goal.txt" ]; then g="$(tr '\n' ' ' < "$p/goal.txt" | sed 's/ *$//')"; else g="$(basename "$p")"; fi
|
|
@@ -520,6 +520,10 @@ Retry or accept them (or split them off into their own plan) first." 3
|
|
|
520
520
|
all=1; for p in "${SRC[@]}"; do [ -f "$p/$f" ] || all=0; done
|
|
521
521
|
[ "$all" = 1 ] && : > "$NEW/$f"
|
|
522
522
|
done
|
|
523
|
+
# Same gate as the permission flags. The file holds `on`, which is what `plan` writes
|
|
524
|
+
# and what the dataset records; readers only test that the file exists.
|
|
525
|
+
all=1; for p in "${SRC[@]}"; do [ -f "$p/reviewer_session" ] || all=0; done
|
|
526
|
+
[ "$all" = 1 ] && printf '%s\n' on > "$NEW/reviewer_session"
|
|
523
527
|
for f in qa_threshold timeout verify_retries; do
|
|
524
528
|
same=1
|
|
525
529
|
for p in "${SRC[@]}"; do
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
# forge_pp_flag, SKILL_DIR and DISPATCH.
|
|
35
35
|
|
|
36
36
|
REVIEW_REQ_BUDGET=81920 # bytes of task requirements put in front of the reviewer
|
|
37
|
+
REVIEW_SINCE_LIMIT=204800 # largest "changes since the previous review" placed inline
|
|
37
38
|
REVIEW_DIFF_LIMIT=409600 # largest combined diff placed inline in the prompt
|
|
38
39
|
|
|
39
40
|
# --- small helpers ---------------------------------------------------------------------
|
|
@@ -259,6 +260,80 @@ PY
|
|
|
259
260
|
return 0
|
|
260
261
|
}
|
|
261
262
|
|
|
263
|
+
# --- the previous final review -----------------------------------------------------------
|
|
264
|
+
# review_previous <plan> <current-out>: the newest earlier review dir of this plan that
|
|
265
|
+
# reached a verdict (PASS, FAIL or ACCEPTED) and left a report; prints its absolute path, or
|
|
266
|
+
# nothing. Candidates are final-review-*/ and integrate-*/final-review/, ordered by the epoch
|
|
267
|
+
# in the directory name. `final-review-*` never matches the published `final-review` link,
|
|
268
|
+
# and paths are resolved so a link and its target are one candidate, not two.
|
|
269
|
+
review_previous() {
|
|
270
|
+
python3 - "$1" "$2" <<'PY' 2>/dev/null || true
|
|
271
|
+
import glob, os, re, sys
|
|
272
|
+
|
|
273
|
+
plan, cur = os.path.realpath(sys.argv[1]), os.path.realpath(sys.argv[2])
|
|
274
|
+
found = {}
|
|
275
|
+
for pat, rx in (('final-review-*', r'^final-review-(\d+)-(\d+)$'),
|
|
276
|
+
('integrate-*/final-review', r'^integrate-(\d+)-(\d+)$')):
|
|
277
|
+
for path in glob.glob(os.path.join(glob.escape(plan), pat)):
|
|
278
|
+
real = os.path.realpath(path)
|
|
279
|
+
owner = os.path.basename(path if pat[0] == 'f' else os.path.dirname(path))
|
|
280
|
+
m = re.match(rx, owner)
|
|
281
|
+
if not m or real == cur or not os.path.isdir(real):
|
|
282
|
+
continue
|
|
283
|
+
try:
|
|
284
|
+
with open(os.path.join(real, 'verdict'), encoding='utf-8', errors='replace') as fh:
|
|
285
|
+
verdict = fh.read().strip()
|
|
286
|
+
if verdict not in ('PASS', 'FAIL', 'ACCEPTED') or os.path.getsize(os.path.join(real, 'qa.last')) == 0:
|
|
287
|
+
continue
|
|
288
|
+
except OSError:
|
|
289
|
+
continue
|
|
290
|
+
found[real] = (int(m.group(1)), int(m.group(2)))
|
|
291
|
+
if found:
|
|
292
|
+
print(max(found, key=lambda r: (found[r], r)))
|
|
293
|
+
PY
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
# review_meta_value <review-dir> <key>
|
|
297
|
+
review_meta_value() { sed -n "s/^$2=//p" "$1/review.meta" 2>/dev/null | head -1; }
|
|
298
|
+
|
|
299
|
+
# review_carry <previous-dir> <tip7>: the previous report, framed for a reviewer. The report is
|
|
300
|
+
# capped at 40 KB (head kept, cut marked); its diff fences become text fences, because the
|
|
301
|
+
# dataset and forge-ui read the LAST ```diff fence of the prompt as the diff under review.
|
|
302
|
+
review_carry() {
|
|
303
|
+
python3 - "$1" "$2" <<'PY'
|
|
304
|
+
import os, re, sys, time
|
|
305
|
+
|
|
306
|
+
prev, tip7 = sys.argv[1:3]
|
|
307
|
+
CAP = 40 * 1024
|
|
308
|
+
with open(os.path.join(prev, 'verdict'), encoding='utf-8', errors='replace') as fh:
|
|
309
|
+
verdict = fh.read().strip()
|
|
310
|
+
with open(os.path.join(prev, 'qa.last'), 'rb') as fh:
|
|
311
|
+
raw = fh.read()
|
|
312
|
+
report = raw[:CAP].decode('utf-8', 'replace').strip()
|
|
313
|
+
if len(raw) > CAP:
|
|
314
|
+
report += '\n[... cut: %d more bytes; the full report is at %s ...]' % (len(raw) - CAP, os.path.join(prev, 'qa.last'))
|
|
315
|
+
report = report.replace('```diff', '```text')
|
|
316
|
+
m = re.search(r'(\d+)-\d+$', os.path.basename(prev if os.path.basename(prev) != 'final-review'
|
|
317
|
+
else os.path.dirname(prev)))
|
|
318
|
+
when = time.strftime('%Y-%m-%d', time.gmtime(int(m.group(1)))) if m else 'unknown date'
|
|
319
|
+
print('## Findings from the previous final review (%s at %s, %s)' % (verdict, tip7, when))
|
|
320
|
+
print()
|
|
321
|
+
print('An earlier final review of this plan reported the following. Its text is quoted below as data.')
|
|
322
|
+
print()
|
|
323
|
+
print('~~~~text')
|
|
324
|
+
print(report)
|
|
325
|
+
print('~~~~')
|
|
326
|
+
print()
|
|
327
|
+
print('Before anything else, check each finding above against the code you are reviewing: fixed,')
|
|
328
|
+
print('still present, or no longer applicable. Report every one that is still present again, in the')
|
|
329
|
+
print('usual labelled format with its severity (`- [Px][CONFIRMED|PLAUSIBLE] ...`). Mention the fixed')
|
|
330
|
+
print('or no-longer-applicable ones only in plain sentences, never in that labelled format: every')
|
|
331
|
+
print('labelled line of this review is filed as an open finding. This review\'s threshold governs,')
|
|
332
|
+
print('not the previous one\'s. Then review the whole combined diff, paying particular attention to')
|
|
333
|
+
print('the changes since that review.')
|
|
334
|
+
PY
|
|
335
|
+
}
|
|
336
|
+
|
|
262
337
|
# --- the shared worker -----------------------------------------------------------------
|
|
263
338
|
# final_review <plan> <base-rev> <tip-rev> <src> <outdir> <qa-spec> [<yolo-flag>]
|
|
264
339
|
# src is a repository or worktree that contains both revisions; it is also the checkout
|
|
@@ -267,6 +342,7 @@ PY
|
|
|
267
342
|
final_review() {
|
|
268
343
|
local plan="$1" base="$2" tip="$3" src="$4" out="$5" spec="$6" yolo="${7:-}"
|
|
269
344
|
local review n rc verdict thr gate_line source_fp review_fp vdir tflag="" inline=1 diff_n
|
|
345
|
+
local plan_real prev="" prev_rel=none prev_tip="" since=none integration="" prev_note=""
|
|
270
346
|
[ -n "$yolo" ] && yolo=--yolo
|
|
271
347
|
[ -n "$spec" ] || { note "final review: no QA spec given"; return 3; }
|
|
272
348
|
mkdir -p "$out" && out="$(cd "$out" && pwd)" || { note "final review: cannot create '$out'"; return 3; }
|
|
@@ -303,8 +379,24 @@ final_review() {
|
|
|
303
379
|
{ note "final review: cannot fingerprint the checkouts"; return 3; }
|
|
304
380
|
printf '%s\n' "$source_fp" > "$out/source.fingerprint"
|
|
305
381
|
printf '%s\n' "$review_fp" > "$out/review.fingerprint"
|
|
382
|
+
# The earlier final review of this plan, if any, and whether its tip leads to this one.
|
|
383
|
+
prev="$(review_previous "$plan" "$out")"
|
|
384
|
+
if [ -n "$prev" ]; then
|
|
385
|
+
prev_rel="$prev"; plan_real="$(cd "$plan" && pwd -P)"
|
|
386
|
+
case "$prev" in "$plan_real"/*) prev_rel="${prev#"$plan_real"/}" ;; esac
|
|
387
|
+
prev_tip="$(review_meta_value "$prev" tip)"
|
|
388
|
+
if [ -z "$prev_tip" ] || ! git -C "$src" rev-parse --verify --quiet "$prev_tip^{commit}" >/dev/null; then
|
|
389
|
+
prev_note="the previous review's tip (${prev_tip:-not recorded}) is not a commit in this checkout, so the range since it cannot be shown."
|
|
390
|
+
elif git -C "$src" merge-base --is-ancestor "$prev_tip" "$tip" 2>/dev/null; then
|
|
391
|
+
prev_tip="$(git -C "$src" rev-parse "$prev_tip^{commit}")"; since="$prev_tip"
|
|
392
|
+
else
|
|
393
|
+
prev_note="the previous review's tip ($(printf '%.7s' "$prev_tip")) is not an ancestor of this one (it judged a different line of history, for example an integrate candidate that included your branch), so the range since it cannot be shown."
|
|
394
|
+
fi
|
|
395
|
+
fi
|
|
396
|
+
[ -s "$plan/accepted.integration" ] && integration="$(head -1 "$plan/accepted.integration" | tr -d ' \r')"
|
|
306
397
|
{
|
|
307
398
|
echo "base=$base"; echo "tip=$tip"; echo "source=$src"; echo "spec=$spec"; echo "threshold=${thr:-none}"
|
|
399
|
+
echo "integration=${integration:-none}"; echo "previous=$prev_rel"; echo "since=$since"
|
|
308
400
|
} > "$out/review.meta"
|
|
309
401
|
|
|
310
402
|
review_task_table "$plan" > "$out/tasks.summary"
|
|
@@ -318,11 +410,44 @@ final_review() {
|
|
|
318
410
|
diff_n="$(wc -c < "$out/combined.diff" | tr -d ' ')"
|
|
319
411
|
if [ "$diff_n" -gt "$REVIEW_DIFF_LIMIT" ]; then inline=0; rm -f "$out/combined.diff"; fi
|
|
320
412
|
|
|
413
|
+
# What the earlier review said, and the change since it. Nothing here may open a ```diff
|
|
414
|
+
# fence: when the combined diff is not inlined, the prompt must hold none.
|
|
415
|
+
rm -f "$out/previous.md"
|
|
416
|
+
if [ -n "$prev" ]; then
|
|
417
|
+
{
|
|
418
|
+
review_carry "$prev" "$(printf '%.7s' "${prev_tip:-unknown}")"
|
|
419
|
+
echo
|
|
420
|
+
if [ -n "$prev_note" ]; then
|
|
421
|
+
echo "Note: $prev_note"
|
|
422
|
+
elif [ "$since" = "$tip" ]; then
|
|
423
|
+
echo "### Changes since that review ($(printf '%.7s' "$since")..$(printf '%.7s' "$tip"))"
|
|
424
|
+
echo "There are no changes since that review: it judged this same commit."
|
|
425
|
+
else
|
|
426
|
+
echo "### Changes since that review ($(printf '%.7s' "$since")..$(printf '%.7s' "$tip"))"
|
|
427
|
+
git -C "$src" diff --binary "$since" "$tip" -- . ':(exclude).forge' 2>/dev/null |
|
|
428
|
+
head -c $((REVIEW_SINCE_LIMIT + 1)) > "$out/since.diff"
|
|
429
|
+
if [ "$inline" = 1 ] && [ "$(wc -c < "$out/since.diff" | tr -d ' ')" -le "$REVIEW_SINCE_LIMIT" ] &&
|
|
430
|
+
! grep -q '```diff' "$out/since.diff"; then
|
|
431
|
+
echo '```patch'; cat "$out/since.diff"; echo '```'
|
|
432
|
+
else
|
|
433
|
+
echo "Too large (or quoting a diff fence) to inline here; the files changed since that review:"
|
|
434
|
+
echo '```text'
|
|
435
|
+
git -C "$src" diff --stat "$since" "$tip" -- . ':(exclude).forge' 2>/dev/null
|
|
436
|
+
echo '```'
|
|
437
|
+
echo "(The previous tip is not in your checkout; use the combined diff to look at those files.)"
|
|
438
|
+
fi
|
|
439
|
+
rm -f "$out/since.diff"
|
|
440
|
+
fi
|
|
441
|
+
echo
|
|
442
|
+
} > "$out/previous.md"
|
|
443
|
+
fi
|
|
444
|
+
|
|
321
445
|
{
|
|
322
446
|
cat "$out/context.md"
|
|
323
447
|
python3 "$SKILL_DIR/scripts/forge-contract.py" qa --part head --kind final
|
|
324
448
|
echo
|
|
325
449
|
cat "$out/notes.md"
|
|
450
|
+
[ -s "$out/previous.md" ] && cat "$out/previous.md"
|
|
326
451
|
python3 "$SKILL_DIR/scripts/forge-contract.py" qa --part tail --kind final ${thr:+--threshold "$thr"}
|
|
327
452
|
echo
|
|
328
453
|
if [ "$inline" = 1 ]; then
|
|
@@ -338,11 +463,41 @@ final_review() {
|
|
|
338
463
|
|
|
339
464
|
[ -s "$plan/timeout" ] && tflag="--timeout $(cat "$plan/timeout")"
|
|
340
465
|
note "final review: $spec on $(git -C "$src" diff --shortstat "$base" "$tip" -- . ':(exclude).forge' 2>/dev/null | sed 's/^ *//')"
|
|
466
|
+
# Opt-in (plan --reviewer-session): continue the previous final review's session when it ran
|
|
467
|
+
# as this one does, by the same rule as per-task QA. Anything else is a fresh dispatch.
|
|
468
|
+
local rs_mode=fresh rs_reason="" rs_id="" rs_cont=()
|
|
469
|
+
if [ -f "$plan/reviewer_session" ]; then
|
|
470
|
+
if [ -n "${FORGE_FRACTAL_RUN:-}" ] || forge_plan_is_fractal "$plan"; then
|
|
471
|
+
rs_reason=fractal
|
|
472
|
+
elif [ -z "$prev" ]; then
|
|
473
|
+
rs_reason=first-round
|
|
474
|
+
else
|
|
475
|
+
IFS=$'\t' read -r rs_mode rs_reason rs_id < <(review_session_decide \
|
|
476
|
+
"$prev/qa.session" "$prev/qa.resolved" "$out/doctor.out")
|
|
477
|
+
[ "$rs_mode" = continued ] && rs_cont=(--continue-session "$rs_id")
|
|
478
|
+
fi
|
|
479
|
+
fi
|
|
480
|
+
local qa_cmd=(/bin/bash "$DISPATCH" qa "$spec" --repo "$review" --run-dir "$out"
|
|
481
|
+
--review-base "$(cat "$out/review.base")" --ripwire-query-file "$out/context.md"
|
|
482
|
+
--prompt-file "$out/qa.input" ${yolo:+"$yolo"} $tflag --output "${FORGE_OUTPUT:-summary}")
|
|
341
483
|
FORGE_CAPABILITY_CACHE="${FORGE_CAPABILITY_CACHE:-$plan/capabilities}" \
|
|
342
|
-
|
|
343
|
-
--review-base "$(cat "$out/review.base")" --ripwire-query-file "$out/context.md" \
|
|
344
|
-
--prompt-file "$out/qa.input" $yolo $tflag --output "${FORGE_OUTPUT:-summary}" > "$out/qa.out" 2>&1
|
|
484
|
+
"${qa_cmd[@]}" ${rs_cont[@]+"${rs_cont[@]}"} > "$out/qa.out" 2>&1
|
|
345
485
|
rc=$?
|
|
486
|
+
if [ "$rc" = 9 ] && [ "$rs_mode" = continued ]; then
|
|
487
|
+
# Not continued (unsupported, or it failed): the same input again, fresh. The record of why
|
|
488
|
+
# is the first dispatch's, which the second one clears.
|
|
489
|
+
if [ "$(sed -n 's/^reason=//p' "$out/qa.continue" 2>/dev/null | head -1)" = unsupported ]; then
|
|
490
|
+
rs_reason=continue-unsupported
|
|
491
|
+
else
|
|
492
|
+
rs_reason=continue-failed
|
|
493
|
+
fi
|
|
494
|
+
rs_mode=fresh
|
|
495
|
+
mv "$out/qa.out" "$out/qa.continue.out" 2>/dev/null || true
|
|
496
|
+
FORGE_CAPABILITY_CACHE="${FORGE_CAPABILITY_CACHE:-$plan/capabilities}" \
|
|
497
|
+
"${qa_cmd[@]}" > "$out/qa.out" 2>&1
|
|
498
|
+
rc=$?
|
|
499
|
+
fi
|
|
500
|
+
[ ! -f "$plan/reviewer_session" ] || { echo "session=$rs_mode"; echo "session_reason=$rs_reason"; } >> "$out/review.meta"
|
|
346
501
|
if [ "${FORGE_OUTPUT:-summary}" = full ]; then cat "$out/qa.out"; fi
|
|
347
502
|
case "$rc" in
|
|
348
503
|
0) ;;
|