@giamme/forge 0.11.1 → 0.12.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/SKILL.md +4 -2
  3. package/VERSION +1 -1
  4. package/docs/artifacts.md +44 -11
  5. package/docs/commands.md +45 -7
  6. package/docs/decomposed-runs.md +43 -2
  7. package/docs/forge-ui.md +27 -6
  8. package/docs/reviews.md +31 -6
  9. package/package.json +1 -1
  10. package/references/dataset.md +3 -1
  11. package/references/decompose.md +159 -17
  12. package/references/harnesses.md +13 -1
  13. package/references/ui.md +14 -4
  14. package/scripts/forge-control.py +268 -25
  15. package/scripts/forge-dispatch.sh +80 -3
  16. package/scripts/forge-guard.py +156 -15
  17. package/scripts/forge-lib-plans.sh +9 -3
  18. package/scripts/forge-lib-review.sh +22 -3
  19. package/scripts/forge-lib-task.sh +51 -4
  20. package/scripts/forge-lib-verify.sh +137 -6
  21. package/scripts/forge-parallel-options.sh +59 -0
  22. package/scripts/forge-parallel.sh +212 -28
  23. package/scripts/forge-ripwire.py +1 -1
  24. package/scripts/forge-route.py +157 -5
  25. package/scripts/forge-schedule.py +687 -50
  26. package/scripts/forge-verify.py +878 -0
  27. package/scripts/forge_dataset/forecast.py +29 -4
  28. package/scripts/forge_dataset/harvest.py +10 -4
  29. package/scripts/forge_verify_reports.py +225 -0
  30. package/ui/README.md +18 -3
  31. package/ui/docs/api.md +126 -17
  32. package/ui/forge_ui/control.py +12 -2
  33. package/ui/forge_ui/export.py +20 -7
  34. package/ui/forge_ui/reader.py +307 -11
  35. package/ui/forge_ui/server.py +16 -0
  36. package/ui/forge_ui/static/app.js +2 -1
  37. package/ui/forge_ui/static/controls.js +217 -33
  38. package/ui/forge_ui/static/gantt.js +14 -9
  39. package/ui/forge_ui/static/index.html +1 -0
  40. package/ui/forge_ui/static/panel.js +2 -1
  41. package/ui/forge_ui/static/runmodel.js +92 -11
  42. package/ui/forge_ui/static/scene3d.js +9 -5
  43. package/ui/forge_ui/static/style.css +60 -0
  44. package/ui/forge_ui/static/verifyview.js +324 -0
@@ -471,6 +471,15 @@ turns it off):
471
471
  - **Stop:** when nothing is above (the model is in the top tier, or in no tier pool at
472
472
  all), the task stops with `tasks/<id>/split.suggested`: split it into smaller tasks, or
473
473
  name a model. `run --retry-failed` does not retry it, and a plain `retry` refuses (exit 2).
474
+ - **Effort step** (`--escalate-effort` on `run` or `retry`, kept in the plan as `escalate_effort`;
475
+ `--no-escalate-effort` removes it; off by default): when no higher tier has another model, the
476
+ same alias and harness is tried once more at the next effort its pairing accepts
477
+ (`forge-dispatch.sh efforts --spec <spec>` lists them, weakest first), counted from the effort
478
+ the last dispatch really ran after any clamp; a spec that names no effort counts as `medium`.
479
+ The streak then counts per effort, so the new effort starts from zero. At the pairing's highest
480
+ effort the task still stops, and `split.suggested` says that effort was tried too. Turning the
481
+ step on (flag or `forge control set escalate-effort on`) clears the stop markers, so tasks the
482
+ policy already stopped are decided again at their next retry.
474
483
  - **Overrides:** an explicit `retry --dwarf <spec>` is the human's choice and skips the policy, even
475
484
  with the same model. The dwarf column itself cannot tell a pin from a pool pick, so it does
476
485
  not; `--escalate-after 0` does.
@@ -488,8 +497,11 @@ changed spec passed, mostly effort bumps); the attempts this policy makes are wh
488
497
 
489
498
  **Live controls.** `forge control show|set|pin|unpin <plan>` changes a run while it works:
490
499
  `qa-threshold` (read once per attempt, so an attempt keeps the bar it started with; per task, `none`
491
- exempts it and `inherit` drops the override), `timeout`, `escalate-after`, `verify-retries`, and,
492
- only while a scheduler is live, `retry-budget` (plan or `--task`) and `infra-retries`. The budgets
500
+ exempts it and `inherit` drops the override), `timeout`, `escalate-after`, `escalate-effort`
501
+ (`on|off`), `verify-retries`, `infra-backoff` (a shape as for `--infra-backoff`; `none` drops it,
502
+ read at the next infrastructure wait), and, only while a scheduler is live, `retry-budget` (plan
503
+ or `--task`), `infra-retries` and `max-parallel` (1 to 64, at the next dispatch decision; a lower
504
+ limit lets running tasks finish and never stops one). The budgets
493
505
  keep the **per invocation** rule: the flags of `run` are not remembered, and a live scheduler only
494
506
  mirrors them into `retry_budget`, `infra_budget` and `tasks/<id>/retry_budget` (re-read at every
495
507
  decision, removed when it stops; `tasks/<id>/retries.used` stays), so a change dies with the
@@ -501,18 +513,55 @@ run. `pin <task> <spec>` sets the model the task's next full retry uses, once (c
501
513
  the pin stays and nothing is retried. A QA-only resume leaves it for the next full retry;
502
514
  Fractal plans never apply pins; a MERGED task refuses one.
503
515
  The last column of `escalations.tsv` says who decided: `policy`, `retry` (an explicit
504
- `--dwarf`) or `pin`; four-column rows read as `policy`. Every change goes to `control.log` (JSON lines:
505
- `at`, `actor` `cli|ui`, `action`, `task`, `key`, `value`, `previous`) and to the dataset as a
506
- `control` event. Exit 2 invalid input, 3 refused (not live, MERGED, Fractal, failing doctor,
507
- lock busy over 5 s). Report them; never set one yourself unless asked.
516
+ `--dwarf`), `pin` or `fallback` (`--infra-fallback`, below); four-column rows read as `policy`.
517
+ Every change goes to `control.log` (JSON lines: `at`, `actor` `cli|ui`, `action`, `task`, `key`,
518
+ `value`, `previous`) and to the dataset as a `control` event. Exit 2 invalid input, 3 refused (not
519
+ live, MERGED, Fractal, failing doctor, lock busy over 5 s). Report them; never set one yourself
520
+ unless asked.
521
+
522
+ Five more commands steer a live scheduler (exit 3 when none is live):
523
+
524
+ - `drain <plan>` starts nothing new: running tasks finish, the scheduler waits (it prints
525
+ `drained — N task(s) waiting` once) and infrastructure waits keep counting down. `resume <plan>`
526
+ ends the drain. This is the supported way to pause a run; nothing has to be signalled. A drain
527
+ holds only tasks still to start: when none is left (or waiting on infrastructure), the run
528
+ ends as it would without the drain, verification included.
529
+ - `stop <plan>` starts nothing new and ends the run once the running tasks are done: a waiting
530
+ infrastructure pause ends too, unstarted tasks stay `PENDING`, verification is skipped and `run`
531
+ exits 130. When every task has merged by then, nothing was cut short: the run ends as usual,
532
+ verifies and exits 0. `stop --now` interrupts the running tasks
533
+ as SIGTERM would.
534
+ - `retry <plan> <task> [--dwarf <spec>]` and `add <plan> <task>` only queue a request, one JSON
535
+ file in `control.requests/`. At its next pass the scheduler applies or refuses it and appends
536
+ an outcome line to `control.log` with the same `request` id (`actor` `scheduler`, `outcome`
537
+ `applied|refused`, `reason`). `retry` takes a `FAIL`, `UNKNOWN`, `TIMEOUT`, `ERROR`,
538
+ `BLOCKED` or `INTERRUPTED` task that is neither running nor waiting, prepares it exactly as
539
+ `forge-parallel.sh retry` does (a policy stop refuses it unless `--dwarf` names a model), and
540
+ spends no automatic retry budget; an `INFRA` task is the
541
+ scheduler's own to resume. `add` takes a row the human appended to `tasks.tsv` (with its
542
+ `tasks/<id>/prompt.md`) whose dependencies this run already knows, and refreshes `waves.tsv`.
543
+ A request never outlives the scheduler that was live when it was queued; one left behind is
544
+ refused, and every request is refused once the run is stopping.
508
545
 
509
546
  **Ordinary execution resume:** run `run` again. Tasks already `MERGED` are skipped. A saved PASS is fingerprint-checked and merged without
510
547
  redispatch. PENDING, `INFRA` (paused by an infrastructure failure) and newly unblocked tasks run
511
548
  when eligible. Failed or interrupted tasks
512
549
  require an explicit `retry` (or `--retry-failed N`); `run` never silently pays for them again. Worktrees and pinned
513
550
  baselines are retained. A per-plan advisory lock excludes concurrent run/retry/integrate
514
- operations. Interrupting the coordinator terminates its active process groups and preserves
515
- their work as INTERRUPTED.
551
+ operations.
552
+
553
+ **Interrupts.** A SIGINT or SIGTERM to `run` (or to its scheduler), and `forge control stop
554
+ --now`, first reap the tasks that already ended (a PASS still merges), then send TERM to each
555
+ running task's process group, wait up to `FORGE_INTERRUPT_GRACE` seconds (default 10) for the
556
+ group to empty and KILL what is left. Only those live tasks become `INTERRUPTED`; their worktree
557
+ keeps the partial work. Each guarded dispatch records its harness in `tasks/<id>/<role>.harness`
558
+ (pid and start time), and `forge-guard.py stop` ends a harness that outlived its dispatch, at
559
+ the interrupt and again at the next scheduler start (a scheduler killed outright leaves tasks
560
+ `RUNNING`; the next run stops their harnesses and marks them `INTERRUPTED`). An interrupted
561
+ `run` skips verification and its rerun, prints `forge-parallel.sh retry <plan> <id>` for each
562
+ interrupted task, releases the plan lock and exits **130**. A signal that arrives during
563
+ verification stops the verify command's whole process tree (TERM, then KILL after a few
564
+ seconds) and records `verification.status` `INTERRUPTED`; that verification is not rerun.
516
565
 
517
566
  **Fractal resume:** discover the managed ID with `<skill_dir>/forge fractal runs --repo
518
567
  "$REPO" --json`, then use `<skill_dir>/forge fractal resume RUN_ID`. Run-level resume
@@ -527,17 +576,52 @@ Pause, stop and inspection preserve both the work and diagnostics.
527
576
  Quota or usage limits, auth problems, rate limits, network errors and empty answers say nothing
528
577
  about the work. `forge-dispatch.sh` classifies them from the dispatch log (strong phrases and
529
578
  structured `is_error`/`turn.failed` results only; a reply that otherwise succeeded is never
530
- reclassified) and exits **8**, writing `<role>.infra` (`class=`, `retry_after=`, `detail=`).
531
- `do_task` then ends the task as **`INFRA`**:
579
+ reclassified) and exits **8**, writing `<role>.infra` (`class=`, `retry_after=`, `detail=`,
580
+ `harness=`, `model=`). `do_task` then ends the task as **`INFRA`**, copying that file to
581
+ `tasks/<id>/infra.txt` with `stage=dwarf|qa`:
532
582
 
533
583
  - a **dwarf-stage** stop refunds the attempt counter;
534
584
  - a **QA-stage** stop keeps the attempt (the dwarf's work is real and committed) and writes the
535
- `resume=qa` marker, so the next entry re-reviews without running the dwarf;
536
- - the scheduler stops dispatching, drains what is running, leaves unstarted tasks `PENDING`
537
- (never `BLOCKED`), and `run` exits `8` after printing what to fix. Verification is skipped.
538
- Running the same command again resumes; `run --infra-retries M` instead waits (the provider's
539
- retry-after if parsed, capped at 6 h, else 60 s doubling to 30 min; `FORGE_INFRA_BACKOFF` sets the
540
- base) and resumes in the same run, up to `M` times.
585
+ `resume=qa` marker, so the next entry re-reviews without running the dwarf.
586
+
587
+ The scheduler pauses only what the stop says something about. Its **scope** comes from the class:
588
+
589
+ | Class | Scope paused | Other tasks |
590
+ |---|---|---|
591
+ | `rate_limit` | that model on that harness (`model:<harness>/<model>`) | keep running, on any other model |
592
+ | `quota`, `auth` | that harness's account (`harness:<harness>`) | keep running on other harnesses |
593
+ | anything else (network, empty output, ...) | everything (`all`): running tasks drain, nothing new starts | wait with it |
594
+
595
+ A pending task whose next dispatch (its implementer and reviewer, or the reviewer alone before a
596
+ QA-only resume) needs a paused pair waits; the rest of the run continues. Unstarted tasks stay
597
+ `PENDING`, never `BLOCKED`. Without `--infra-retries`, `run` ends once nothing else can run, prints
598
+ what to fix and exits `8`; verification is skipped and running the same command again resumes.
599
+ `run --infra-retries M` waits out each scope instead, up to `M` waits **per scope** (a stop in a
600
+ scope already paused joins its wait and is not counted again), then resumes its tasks in the same
601
+ run with no attempt spent. A scope whose waits are used up stays paused while other tasks finish.
602
+
603
+ Each wait is the provider's retry-after when the log carried one (capped at 6 h), otherwise the
604
+ backoff shape for that wait number. `--infra-backoff <shape>` on `run` (kept in the plan as
605
+ `infra_backoff`, read at every wait, so `forge control set infra-backoff` applies to the next one)
606
+ takes `exp:<base>[,<cap>]` (base doubling, capped at `<cap>`, default the larger of the base and
607
+ 30 min), `linear:<start>,<step>[,<cap>]`, or `list:<a>,<b>,...` (a bare `<a>,<b>,...` is a list;
608
+ the last value repeats), all in positive seconds. The default is `exp:60`; `FORGE_INFRA_BACKOFF`
609
+ changes that base. The wait message names the source, for example `waiting 120s before resuming
610
+ (backoff exp:60, wait 2 of 3)`. While anything is paused, `infra.paused` (JSON `{"scopes": [...]}`
611
+ with `scope`, `class`, `harness`, `model`, `tasks`, `cycle`, `until`, `exhausted`) says what waits
612
+ and until when; `forge control show` reports it as `infra_paused`, and it is removed when nothing
613
+ is paused.
614
+
615
+ `--infra-fallback` on `run` (kept as `infra_fallback`; `--no-infra-fallback` turns it off, the
616
+ default) moves a **dwarf-stage `rate_limit`** stop to the next member of the task's own tier pool,
617
+ in pool order, that runs another model on a pair nobody paused. No attempt is spent; the move is
618
+ recorded in `escalations.tsv` with source `fallback` and in `tasks.tsv`, and the task is
619
+ dispatched again at once. A pinned task, a Fractal plan, a QA-stage stop and a pool with no
620
+ healthy member keep the ordinary pause.
621
+
622
+ A planned pause (a provider maintenance window, a quota reset) is `forge control drain`, not a
623
+ signal: running tasks finish, nothing new starts, and `forge control resume` continues the same
624
+ run.
541
625
 
542
626
  A dwarf that exits 0 with an empty final message *and* an empty diff is the same class
543
627
  (`class=empty`): it never really ran. Under Fractal a dwarf-side stop still ends as Fractal's own
@@ -647,7 +731,9 @@ The user's own branch and working tree are untouched for the whole run.
647
731
  | task status `TIMEOUT` | that role exceeded the dispatch timeout and was killed. It has usually left a partial edit in the worktree; `retry` continues from it |
648
732
  | task status `FAIL` with "produced no changes" | the dwarf ended without editing anything — usually an ambiguous prompt it could not resolve headlessly |
649
733
  | task status `UNKNOWN` | QA never emitted a verdict line; read `tasks/<id>/qa.last`. `retry` re-runs QA only, on the unchanged work |
650
- | task status `INFRA`, `run` exits `8` | quota/auth/rate limit/network/empty output stopped a dispatch; no attempt spent. See `tasks/<id>/infra.txt`, fix the cause, run again (or `--infra-retries M`) |
734
+ | task status `INFRA`, `run` exits `8` | quota/auth/rate limit/network/empty output stopped a dispatch; no attempt spent. See `tasks/<id>/infra.txt`, fix the cause, run again (or `--infra-retries M`, `--infra-fallback`) |
735
+ | task status `INTERRUPTED`, `run` exits `130` | the run was signalled or stopped with `forge control stop --now`; verification was skipped. The summary prints `forge-parallel.sh retry <plan> <id>` for each one; a plain `run` does not dispatch them |
736
+ | a run that sits idle with tasks `PENDING` | `forge control drain` is in effect (`forge control show` says `drained`; `forge control resume` continues), every scope a task needs is paused on infrastructure (`infra.paused`), or `max-parallel` is lower than you think |
651
737
  | a `FAIL` task merged anyway | an explicit `--qa-threshold` tolerated its findings; see `known_issues.md` and `qa.gate` |
652
738
  | task status `CONFLICT` | QA passed but the merge onto the integration branch conflicted — `files` was understated somewhere |
653
739
  | every wave has one task | `files` sets overlap across most tasks; the decomposition is not actually parallel |
@@ -688,6 +774,62 @@ in `verification.retry.log`, and prints a loud `FLAKY` line in `run`/`retry`/`in
688
774
  a command that changes the tree is never retried. This runs before Jev's optional model-assisted
689
775
  triage, which then does not.
690
776
 
777
+ **Verification lanes.** Instead of one command, verification can be several named **lanes**
778
+ that run side by side, each rerun on its own. A lane is a file whose name is the lane's name
779
+ (letters, digits, `_` and `-`, at most 64) and whose text is a Bash command, run from the
780
+ integration checkout like the single command; leading `#forge:` lines give it options. Declare
781
+ them in the repository as `.forge/verify.d/<name>` files, or for one plan with
782
+ `run --verify-lane 'name[opt,opt]=command'` (also `retry`; repeatable, written to
783
+ `<plan>/verify.d/`; the first one of an invocation replaces the plan's earlier lanes and its
784
+ `--verify`, as `--verify` replaces the plan's lanes). The first source that exists wins: the
785
+ plan's lanes, the plan's `--verify`, the repository's `.forge/verify.d/`, then
786
+ `.forge/verify` (ignored, with a note, when the repository also has lanes). `run` checks the
787
+ lanes before any model is paid for and exits 2 on an invalid one (an unknown or repeated
788
+ option, an empty command, a path outside the checkout, a missing baseline).
789
+
790
+ | Option | Means |
791
+ |---|---|
792
+ | `group=<name>` | lanes of one group never run at the same time (a shared database, a port) |
793
+ | `first` | runs before every other lane; if a `first` lane fails (after its reruns) or changes the tree, the rest are `skipped` |
794
+ | `retries=<0-9>` | reruns of this lane when it fails; otherwise `--verify-retries` (default `1`) |
795
+ | `reports=<dir>` | JUnit `*.xml` and TAP `*.tap` reports the lane writes under `<dir>` (inside the checkout). Only reports written during that run count; they give the lane's test counts and failing tests, and are re-read every 10 s while it runs |
796
+ | `baseline=<file>` | known failing test names, one per line (`#` comments), inside the checkout. A lane whose every failing test is in it ends `known`, which passes. A report that could not be read, or a failure no report names, is judged by the exit code |
797
+
798
+ Up to `--verify-parallel N` lanes run at once (`run`, `retry` and `integrate`, kept as
799
+ `verify_parallel`, 1 to 32, default 3), started in name order. A lane that fails is rerun alone,
800
+ after the round, up to its budget; a pass on a rerun makes it `flaky`, which passes but is named
801
+ in the run output (`FLAKY: lane <name> failed once and passed on rerun`), in
802
+ `verification.flaky` and in the whole-run review. Nothing reruns once the tree changed: the
803
+ checkout's fingerprint is compared after every round, and a change fails the verification
804
+ (`mutated`; with lanes in parallel it cannot be pinned to one of them). A lane's states are
805
+ `waiting`, `running`, `pass`, `fail`, `flaky`, `known`, `skipped` and `interrupted`. Lanes mode
806
+ replaces the whole-command rerun above and Jev's flake triage (a note says so when Jev's tests
807
+ site is on); the single-command mode is unchanged.
808
+
809
+ A lane may print `::forge step name=<step> state=start` and `::forge step name=<step>
810
+ state=end [exit=<n>]`, each on a line of its own, to say which step it is in; forge-ui shows the
811
+ open step live. A signal stops the running lanes' process groups (TERM, then KILL after 5 s),
812
+ marks them and the verification `INTERRUPTED`, and exits 130 like a single command.
813
+
814
+ The runner is `scripts/forge-verify.py` (`lanes` lists and checks a directory's lanes; `run`
815
+ runs them). In lanes mode `verification.command` holds one `<lane>: <first line>` per lane,
816
+ `verification.log` the runner's summary plus the last 200 lines of each lane that failed, and
817
+ `verification.lanes.json` the live state of every lane; each lane writes
818
+ `verification.lane.<name>.log` and keeps each finished run as `verification.lane.<name>.run-<k>.log`.
819
+
820
+ ```
821
+ .forge/verify.d/lint #forge: first
822
+ npm run lint && npm run typecheck
823
+ .forge/verify.d/unit #forge: reports=reports/unit
824
+ npm test -- --reporter=junit --outputFile=reports/unit/junit.xml
825
+ .forge/verify.d/db #forge: group=postgres retries=2 reports=reports/db baseline=.forge/known-db-failures.txt
826
+ docker compose up -d postgres && npm run test:db
827
+ ```
828
+
829
+ Here `lint` runs alone first; `unit` and `db` then run together, another lane in the
830
+ `postgres` group would wait for `db`, and a `db` run whose only failures are listed in the
831
+ baseline ends `known`.
832
+
691
833
  `scripts/forge-verify-coverage.py` reads the project's own test configuration and **warns**
692
834
  (never fails) about suites the command does not run — at `run` start before any model is paid
693
835
  for, again in `verify_result`, and at `integrate` — recording the lines in
@@ -53,7 +53,9 @@ looking for strong phrases (usage/session limit, quota, insufficient balance/cre
53
53
  429/529, unauthorized/401/403, invalid API key, not logged in, ECONNRESET/ENOTFOUND/…). An
54
54
  `empty` answer (exit 0, nothing said) is the fifth class. codex's non-fatal reconnect notices
55
55
  followed by a normal reply never classify. `<role>.infra` holds `class=`, `retry_after=` (parsed
56
- from "resets 3pm", "try again in 2h", `|<epoch>`, capped at 24 h), `detail=`, `rc=`, `role=`.
56
+ from "resets 3pm", "try again in 2h", `|<epoch>`, capped at 24 h), `detail=`, `rc=`, `role=`,
57
+ `harness=`, `model=` (the scheduler pauses by them: a rate limit pauses that model on that
58
+ harness, a quota or auth stop that harness).
57
59
  A dwarf that exits 0 with nothing said writes the file but still exits 0; callers consult it
58
60
  only when the diff is empty.
59
61
 
@@ -68,6 +70,16 @@ anything still alive in it (`<role>.orphans`). This is proven offline only; veri
68
70
  with one live dispatch before relying on it, and use the kill switch if a harness deliberately
69
71
  leaves a helper running.
70
72
 
73
+ On an interrupt the harness leads its own session, so the TERM a decomposed run's scheduler
74
+ sends to a task's process group reaches the dispatch, whose exit trap stops the harness. The
75
+ scheduler waits up to `FORGE_INTERRUPT_GRACE` seconds (default 10) for each task's group to
76
+ empty before KILLing it. As a backstop, a guarded dispatch records its harness right after
77
+ launch in `<role>.harness` (`<pid><TAB><start time>`, from `forge-guard.py identity`; removed when
78
+ the dispatch ends). For a task whose dispatch could not stop its harness, and at the next
79
+ scheduler start for tasks a killed scheduler left `RUNNING`, `forge-guard.py stop <record>`
80
+ signals the recorded group (TERM, about 2 s, KILL) only when the pid still has the recorded
81
+ start time, so a reused pid is left alone.
82
+
71
83
  Effort ladders, low to high:
72
84
 
73
85
  - codex — `low medium high xhigh max ultra`
package/references/ui.md CHANGED
@@ -24,9 +24,13 @@ run goes on, with the same exit code.
24
24
  - **Fractal child tasks:** the tree a Fractal task was split into, in the inspector and above
25
25
  the opened task in 3D, each node with its decision, cost, output and candidate diff.
26
26
  - **Controls:** change a live plan from its inspector: QA threshold, timeout, escalate-after,
27
- verify retries, and (while a scheduler is live) the retry and infra-retry budgets; per task, a
28
- threshold override (`None` exempts it), a retry budget and a one-shot model pin. Each is one
29
- `forge control` call; the change is confirmed first and logged in `control.log`.
27
+ the effort step, verify retries, the infrastructure backoff, and (while a scheduler is live)
28
+ the retry and infra-retry budgets, max parallel, Drain/Resume, Stop and Stop now; per task, a
29
+ threshold override (`None` exempts it), a retry budget, a one-shot model pin and, on a live run,
30
+ *Retry now* for a stopped or failed task (optionally on a chosen model), which shows its queued
31
+ request until the scheduler applies or refuses it. Each infrastructure pause gets a line: what
32
+ is paused, why, its tasks and the time left. Each is one `forge control` call; the change is
33
+ confirmed first and logged in `control.log`.
30
34
  - **Open findings and issues:** the findings a run (or all of its repository's runs) left open,
31
35
  filed as GitHub issues or dismissed through `forge issues`.
32
36
  - **Delete a run:** a dry run, then `forge runs rm`: the dataset keeps the traces, an archive is
@@ -44,7 +48,13 @@ run goes on, with the same exit code.
44
48
  as the run's verdict; changes on the user's own branch after an integrate are not tracked. The
45
49
  *Execution* view shows the same run on wall-clock time, one block per attempt in the lane it
46
50
  ran in, with projected blocks for what has not started and a strip of busy slots against
47
- `--max-parallel`.
51
+ `--max-parallel`; the verification follows on rows of its own, one per verification lane
52
+ with each run and rerun.
53
+ - **Verification** (its finish node): live, the elapsed time, a lane's announced step
54
+ (`::forge step`), its last line, a warning after five minutes without output and the live
55
+ output; after a failure, the failing tests first, by lane, new or known (from JUnit/TAP
56
+ reports), each opening the lane's log at its line, or a reportless lane's log tail; then every
57
+ lane's state, time, runs and tests; a lane's name opens its output.
48
58
  - **Opening a node** shows its attempts, dwarves and reviews, and an inspector with the prompt
49
59
  that launched each one, the model, timings by phase, tokens, verdicts and the P0–P3 findings
50
60
  of every review (the final review's included).