@plot-pm/board 0.15.0 → 0.16.1
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/dist/board-server.mjs +109 -107
- package/package.json +2 -1
- package/plot-agent-manifest.sh +80 -1
- package/plot-approve.sh +3 -1
- package/plot-budget.sh +378 -19
- package/plot-config.sh +61 -9
- package/plot-deliver.sh +3 -1
- package/plot-dispatch.sh +149 -46
- package/plot-fleet-scan.sh +61 -150
- package/plot-host.sh +227 -72
- package/plot-plan-meta.sh +58 -10
- package/plot-reap.sh +104 -8
- package/plot-resolve-artifact.sh +4 -2
- package/plot-tmp.sh +108 -0
- package/plot-transcript-quiet.sh +29 -1
- package/plot-worker-monitor.sh +66 -5
package/plot-fleet-scan.sh
CHANGED
|
@@ -133,9 +133,9 @@
|
|
|
133
133
|
# lifecycle state, verbatim from plot-plan-meta.sh, and the half of a
|
|
134
134
|
# row's phase git cannot answer. Which column a row reads is composed
|
|
135
135
|
# from it AND the branch state one layer up; this script decides nothing.
|
|
136
|
-
# The plan set also includes
|
|
137
|
-
#
|
|
138
|
-
#
|
|
136
|
+
# The plan set also includes every plan at `Delivered`, until it is
|
|
137
|
+
# `Released` (see "the release scope"), so DONE names what the next
|
|
138
|
+
# release ships.
|
|
139
139
|
# Plans are enumerated from REFS (`git ls-tree`/`git show`), NOT from
|
|
140
140
|
# the working tree — so the list describes committed state and does not
|
|
141
141
|
# change while rebases and worker commits rewrite the checkout
|
|
@@ -253,6 +253,8 @@ script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
|
253
253
|
# plot-dispatch.sh so a worker has ONE state, not one per reader.
|
|
254
254
|
# shellcheck source=plot-worker-state.sh
|
|
255
255
|
. "$script_dir/plot-worker-state.sh"
|
|
256
|
+
# Every temp path this scan creates, and its only EXIT/INT/TERM traps.
|
|
257
|
+
. "$script_dir/plot-tmp.sh"
|
|
256
258
|
cfg() { "$script_dir/plot-config.sh" get "$1" "${2:-}"; }
|
|
257
259
|
|
|
258
260
|
do_fetch=1
|
|
@@ -573,14 +575,15 @@ fi
|
|
|
573
575
|
# outlives the scan that fetched it — a stale `merged` read from a previous run
|
|
574
576
|
# is exactly the fabricated verdict the failure direction above forbids.
|
|
575
577
|
#
|
|
576
|
-
# Cleanup is
|
|
578
|
+
# Cleanup is registered rather than trailing: the script exits early in several
|
|
577
579
|
# places (--next with nothing to start, no active plans), and a temp directory
|
|
578
|
-
# left behind on those paths would accumulate one per poll.
|
|
580
|
+
# left behind on those paths would accumulate one per poll. It goes through
|
|
581
|
+
# `plot-tmp.sh`'s one registry: this cache had its own EXIT trap, and the
|
|
582
|
+
# `REF_TMP` trap below replaced it, so every scan left one `tmp.*` directory of
|
|
583
|
+
# ~955 files behind (measured 2026-09-30).
|
|
579
584
|
HOST_STATE_CACHE=""
|
|
580
585
|
if [ "$HOST_LOOKUP_OK" = 1 ]; then
|
|
581
|
-
HOST_STATE_CACHE
|
|
582
|
-
[ -n "$HOST_STATE_CACHE" ] \
|
|
583
|
-
&& trap 'rm -rf "$HOST_STATE_CACHE" 2>/dev/null || true' EXIT INT TERM
|
|
586
|
+
plot_tmpdir HOST_STATE_CACHE fleet-host-state 2>/dev/null || HOST_STATE_CACHE=""
|
|
584
587
|
fi
|
|
585
588
|
|
|
586
589
|
# The cache key. Shared by the join and by `host_pr_state`, because the two
|
|
@@ -2245,37 +2248,15 @@ pr_ready() {
|
|
|
2245
2248
|
}
|
|
2246
2249
|
|
|
2247
2250
|
# ---------------------------------------------------------------------------
|
|
2248
|
-
#
|
|
2251
|
+
# Delivered plans: the release scope
|
|
2249
2252
|
# ---------------------------------------------------------------------------
|
|
2250
2253
|
#
|
|
2251
|
-
#
|
|
2252
|
-
#
|
|
2253
|
-
#
|
|
2254
|
-
#
|
|
2255
|
-
#
|
|
2256
|
-
#
|
|
2257
|
-
#
|
|
2258
|
-
# A ROLLING 24 HOURS, not the calendar day. Literally "delivered today" is
|
|
2259
|
-
# easier to explain and wrong at exactly the wrong moment: a plan delivered at
|
|
2260
|
-
# 23:50 vanishes ten minutes later, mid-session, while the branches it names
|
|
2261
|
-
# are still on screen. 24 is also the one freshness bound this repo already
|
|
2262
|
-
# uses (`Claim stale after`), so it is one unit to learn rather than two.
|
|
2263
|
-
#
|
|
2264
|
-
# THE WINDOW FILTERS BEFORE THE PARSE. Measured: ~57 ms per plan through
|
|
2265
|
-
# plot-plan-meta.sh against a scan that already runs 500–1050 ms, so parsing
|
|
2266
|
-
# fourteen delivered plans to discard thirteen would roughly double the pulse —
|
|
2267
|
-
# and that cost grows with the archive, which only ever gets larger, while the
|
|
2268
|
-
# answer stays the size of a day's work. So the cheap signal comes first (the
|
|
2269
|
-
# delivered symlink's own mtime) and only the candidates it admits are parsed.
|
|
2270
|
-
#
|
|
2271
|
-
# The pre-filter may OVER-ADMIT AND PAY A PARSE; it may never exclude. A
|
|
2272
|
-
# checkout can freshen an old file, so the `Delivered:` record keeps the last
|
|
2273
|
-
# word — but nothing mtime rules out could have been delivered inside the
|
|
2274
|
-
# window. On a fresh clone or a CI worktree every file shares one checkout
|
|
2275
|
-
# timestamp and ALL of them are admitted: correct, merely slower, once. Reaching
|
|
2276
|
-
# for `git log` per plan to avoid that would spend a git call to save a parse.
|
|
2277
|
-
DELIVERED_WINDOW_HOURS=$(cfg "Claim stale after" "24")
|
|
2278
|
-
case "$DELIVERED_WINDOW_HOURS" in (*[!0-9]*|'') DELIVERED_WINDOW_HOURS=24 ;; esac
|
|
2254
|
+
# A plan at `Delivered` stays in the pulse until it is `Released`, whatever its
|
|
2255
|
+
# age. DONE holds the release scope (`done-holds-what-is-still-yours`): every
|
|
2256
|
+
# plan whose work has landed and whose version has not shipped. A 24-hour window
|
|
2257
|
+
# on the `Delivered:` record bounded this until 2026-10-01, when 25 plans awaited
|
|
2258
|
+
# 2.22.0 and DONE showed 10. Every plan file is parsed in one pass anyway, so the
|
|
2259
|
+
# window bought no parse time; it only hid work the next release ships.
|
|
2279
2260
|
|
|
2280
2261
|
|
|
2281
2262
|
# ---------------------------------------------------------------------------
|
|
@@ -2441,52 +2422,7 @@ changed_ago_of() { # $1=branch → "<seconds since>\t<epoch of>" for the newest
|
|
|
2441
2422
|
if [ "$newest" -gt "$now" ]; then printf '0\t%s' "$newest"; else printf '%s\t%s' "$((now - newest))" "$newest"; fi
|
|
2442
2423
|
}
|
|
2443
2424
|
|
|
2444
|
-
# A plan whose delivered symlink was touched inside the window. `find -newermt`
|
|
2445
|
-
# is not portable to every BSD find in the wild, so the cutoff is computed and
|
|
2446
|
-
# compared with `stat` — one stat per file, no parse.
|
|
2447
|
-
delivered_candidates() {
|
|
2448
|
-
local cutoff now link mtime
|
|
2449
|
-
now=$(date +%s)
|
|
2450
|
-
cutoff=$((now - DELIVERED_WINDOW_HOURS * 3600))
|
|
2451
|
-
for link in "$DELIVERED_DIR"*.md; do
|
|
2452
|
-
[ -e "$link" ] || continue
|
|
2453
|
-
# `stat` follows the symlink, which is what we want: the TARGET is the plan,
|
|
2454
|
-
# and a plan edited after delivery must still admit. An unreadable time
|
|
2455
|
-
# ADMITS rather than excludes — the pre-filter may only over-admit, and the
|
|
2456
|
-
# `Delivered:` record has the last word either way.
|
|
2457
|
-
mtime=$(file_mtime "$link") || { printf '%s\n' "$link"; continue; }
|
|
2458
|
-
[ "$mtime" -ge "$cutoff" ] && printf '%s\n' "$link"
|
|
2459
|
-
done
|
|
2460
|
-
}
|
|
2461
2425
|
|
|
2462
|
-
# Does this plan's `Delivered:` record fall inside the window? The RECORD
|
|
2463
|
-
# decides — mtime only chose who got asked.
|
|
2464
|
-
#
|
|
2465
|
-
# "No date, no row." A delivered plan with an empty record does not appear at
|
|
2466
|
-
# all: no date means no membership in any window, the same rule the waiting age
|
|
2467
|
-
# already follows. Showing it always would create the one row that can never
|
|
2468
|
-
# age out of DONE, and the missing record is a bookkeeping fault
|
|
2469
|
-
# plot-reconcile-scan.sh exists to report — a view that quietly compensates
|
|
2470
|
-
# for it makes the fault harder to see.
|
|
2471
|
-
#
|
|
2472
|
-
# A BARE DATE IS ANCHORED AT THE END OF ITS DAY, not at midnight, and this is
|
|
2473
|
-
# the one detail that makes "rolling, not the calendar day" true rather than
|
|
2474
|
-
# merely stated. Every `Delivered:` record in this repo is a bare date, which
|
|
2475
|
-
# names no time — so anchoring at 00:00 measures from up to a day BEFORE the
|
|
2476
|
-
# delivery, and the window collapses back into exactly the calendar boundary
|
|
2477
|
-
# the rolling window exists to avoid: a plan delivered at 23:50 would be an
|
|
2478
|
-
# hour from expiry the moment it was written, and gone ten minutes later
|
|
2479
|
-
# mid-session while the branches it names are still on screen.
|
|
2480
|
-
#
|
|
2481
|
-
# Anchoring at 23:59:59 over-admits by at most the length of the delivery day.
|
|
2482
|
-
# That is the same direction the mtime pre-filter is allowed to err in, and for
|
|
2483
|
-
# the same reason: showing a finished plan slightly too long costs a row, while
|
|
2484
|
-
# dropping one mid-session costs the reader the work they were looking at. A
|
|
2485
|
-
# record that DOES carry a time is honoured exactly, so the imprecision belongs
|
|
2486
|
-
# to the record rather than to the rule.
|
|
2487
|
-
# The rule itself now runs inside the ONE estate parse below (`in_window`),
|
|
2488
|
-
# because asking it per plan meant an interpreter per plan. What it decides is
|
|
2489
|
-
# unchanged; only the number of processes that decide it is.
|
|
2490
2426
|
|
|
2491
2427
|
# ---------------------------------------------------------------------------
|
|
2492
2428
|
# Plan enumeration: from the REF, not from the tree
|
|
@@ -2577,15 +2513,15 @@ ref_ls() { # $1=dir → newline-separated paths under it in origin/$MAIN
|
|
|
2577
2513
|
# `ref_plan_file` is called as `$(ref_plan_file ...)`, which runs it in a
|
|
2578
2514
|
# SUBSHELL. A lazy `[ -z "$REF_TMP" ] && REF_TMP=$(mktemp -d)` inside it
|
|
2579
2515
|
# assigns in the child and the parent never sees it — so every call made a
|
|
2580
|
-
# fresh directory, the parent's variable stayed empty, and the
|
|
2581
|
-
#
|
|
2516
|
+
# fresh directory, the parent's variable stayed empty, and the exit cleanup
|
|
2517
|
+
# removed nothing. Measured while writing this: three plans, three temp dirs,
|
|
2582
2518
|
# none removed. The lifetime is owned out here, where the trap can see it.
|
|
2583
2519
|
REF_TMP=""
|
|
2584
2520
|
if [ "$PLAN_SOURCE" = "ref" ]; then
|
|
2585
|
-
REF_TMP=$(mktemp -d "${TMPDIR:-/tmp}/plot-fleet-ref.XXXXXX") || REF_TMP=""
|
|
2586
2521
|
# The scan is read-only and short-lived, and the board polls it every 5 s —
|
|
2587
|
-
# a directory that outlives the run would accumulate one per poll
|
|
2588
|
-
|
|
2522
|
+
# a directory that outlives the run would accumulate one per poll, so the
|
|
2523
|
+
# helper removes it at exit.
|
|
2524
|
+
plot_tmpdir REF_TMP fleet-ref || REF_TMP=""
|
|
2589
2525
|
# No temp dir means no way to hand the parser a file, so the ref path cannot
|
|
2590
2526
|
# work. Falling back to the checkout is the honest answer, and it announces
|
|
2591
2527
|
# itself through `plan_source` exactly like an unreadable ref.
|
|
@@ -2890,7 +2826,7 @@ is_plan_phase() { # $1=normalized phase → 0 when this file is a plan
|
|
|
2890
2826
|
#
|
|
2891
2827
|
# `record` types, one per line, all tab-separated and all prefixed by the plan
|
|
2892
2828
|
# file they describe:
|
|
2893
|
-
# P <file> <phase>
|
|
2829
|
+
# P <file> <phase> one per parsed file
|
|
2894
2830
|
# W <file> <wave-idx> <branch> <deferred> <why> <wave-name> <claim>
|
|
2895
2831
|
#
|
|
2896
2832
|
# NO ASSOCIATIVE ARRAYS. `/bin/bash` on macOS is 3.2 and this script uses no
|
|
@@ -2908,7 +2844,7 @@ is_plan_phase() { # $1=normalized phase → 0 when this file is a plan
|
|
|
2908
2844
|
# gave when it failed.
|
|
2909
2845
|
plan_meta_files=()
|
|
2910
2846
|
plan_meta_phases=()
|
|
2911
|
-
|
|
2847
|
+
plan_meta_types=()
|
|
2912
2848
|
plan_meta_waves=()
|
|
2913
2849
|
|
|
2914
2850
|
# Parses every plan file given, filling the four arrays above. Called ONCE.
|
|
@@ -2919,33 +2855,6 @@ parse_plan_estate() { # $@=files to parse
|
|
|
2919
2855
|
| python3 -c '
|
|
2920
2856
|
import json, re, sys, time
|
|
2921
2857
|
|
|
2922
|
-
window = float(sys.argv[1]) * 3600
|
|
2923
|
-
now = time.time()
|
|
2924
|
-
|
|
2925
|
-
def in_window(raw):
|
|
2926
|
-
"""The `Delivered:` record against the rolling window — the same rule the
|
|
2927
|
-
per-plan test applied, moved into the one pass. A record whose date does
|
|
2928
|
-
not parse is dropped rather than coerced: Date-style leniency would turn a
|
|
2929
|
-
typo into a confident answer. A record with no time anchors at 23:59:59,
|
|
2930
|
-
so a plan delivered at 23:50 is not an hour from expiry the moment it is
|
|
2931
|
-
written. A FUTURE record is INSIDE (negative age), because hiding a live
|
|
2932
|
-
plan for a mistyped year costs more than showing one."""
|
|
2933
|
-
raw = (raw or "").strip()
|
|
2934
|
-
if not raw:
|
|
2935
|
-
return False
|
|
2936
|
-
m = re.match(r"(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2}))?", raw)
|
|
2937
|
-
if not m:
|
|
2938
|
-
return False
|
|
2939
|
-
y, mo, dy, hh, mi = m.groups()
|
|
2940
|
-
timed = hh is not None
|
|
2941
|
-
try:
|
|
2942
|
-
at = time.mktime((int(y), int(mo), int(dy),
|
|
2943
|
-
int(hh) if timed else 23, int(mi) if timed else 59,
|
|
2944
|
-
0 if timed else 59, 0, 0, -1))
|
|
2945
|
-
except (ValueError, OverflowError):
|
|
2946
|
-
return False
|
|
2947
|
-
return (now - at) <= window
|
|
2948
|
-
|
|
2949
2858
|
def clean(s):
|
|
2950
2859
|
return str(s).replace("\t", " ").replace("\n", " ")
|
|
2951
2860
|
|
|
@@ -2963,8 +2872,7 @@ for line in sys.stdin:
|
|
|
2963
2872
|
f = d.get("file")
|
|
2964
2873
|
if not f:
|
|
2965
2874
|
continue
|
|
2966
|
-
print("\t".join(["P", clean(f), clean(d.get("phase", "")),
|
|
2967
|
-
"1" if in_window(d.get("delivered_raw")) else "0"]))
|
|
2875
|
+
print("\t".join(["P", clean(f), clean(d.get("phase", "")), clean(d.get("type", ""))]))
|
|
2968
2876
|
for i, w in enumerate(d.get("waves", []) or []):
|
|
2969
2877
|
name = w.get("name")
|
|
2970
2878
|
for b in w.get("branches", []) or []:
|
|
@@ -2991,7 +2899,7 @@ for line in sys.stdin:
|
|
|
2991
2899
|
(b.get("deferred_reason") or "-"),
|
|
2992
2900
|
(b.get("waits_on") or "-"),
|
|
2993
2901
|
name or "-", b.get("claimed") or "-"]))
|
|
2994
|
-
'
|
|
2902
|
+
' 2>/dev/null) || records=""
|
|
2995
2903
|
|
|
2996
2904
|
local kind file rest
|
|
2997
2905
|
while IFS=$'\t' read -r kind file rest; do
|
|
@@ -2999,9 +2907,12 @@ for line in sys.stdin:
|
|
|
2999
2907
|
case "$kind" in
|
|
3000
2908
|
P)
|
|
3001
2909
|
plan_meta_files+=("$file")
|
|
3002
|
-
# `rest` is "<phase>\t<
|
|
3003
|
-
plan_meta_phases+=("${rest
|
|
3004
|
-
|
|
2910
|
+
# `rest` is "<phase>\t<type>", two tokens with no tabs inside either.
|
|
2911
|
+
plan_meta_phases+=("${rest%%$'\t'*}")
|
|
2912
|
+
case "$rest" in
|
|
2913
|
+
*$'\t'*) plan_meta_types+=("${rest#*$'\t'}") ;;
|
|
2914
|
+
*) plan_meta_types+=("") ;;
|
|
2915
|
+
esac
|
|
3005
2916
|
plan_meta_waves+=("")
|
|
3006
2917
|
;;
|
|
3007
2918
|
W)
|
|
@@ -3125,8 +3036,8 @@ else
|
|
|
3125
3036
|
# was buying less than it appeared to: it keyed off the `$DELIVERED_DIR`
|
|
3126
3037
|
# symlink's mtime, and a fresh checkout stamps every symlink at once — 56 of
|
|
3127
3038
|
# 56 delivered links admitted here, so the parse it was meant to avoid was
|
|
3128
|
-
# already being paid in full.
|
|
3129
|
-
#
|
|
3039
|
+
# already being paid in full. The `Delivered:` record's window was the filter
|
|
3040
|
+
# that actually decided until 2026-10-01, when the phase alone took over, and the pre-filter's own
|
|
3130
3041
|
# contract was that it may only ever OVER-admit. Removing it takes that
|
|
3131
3042
|
# contract to its limit — strictly more correct, and on this repo not even
|
|
3132
3043
|
# more expensive.
|
|
@@ -3374,7 +3285,7 @@ EOF
|
|
|
3374
3285
|
echo "$total $n"
|
|
3375
3286
|
}
|
|
3376
3287
|
|
|
3377
|
-
# WHAT WAS READ OF ONE BRANCH —
|
|
3288
|
+
# WHAT WAS READ OF ONE BRANCH — eleven tab-separated fields, and no decision.
|
|
3378
3289
|
#
|
|
3379
3290
|
# `branch_state()` UNTIL THIS SLICE, and every line of git archaeology below is
|
|
3380
3291
|
# its own, unchanged. What went is the `if` chain that merged these readings
|
|
@@ -3391,10 +3302,11 @@ EOF
|
|
|
3391
3302
|
# run of tabs collapses into one separator under `read`, so no field is ever
|
|
3392
3303
|
# empty. Nothing here is optional, so nothing can shift.
|
|
3393
3304
|
#
|
|
3394
|
-
# EIGHT FIELDS, NOT
|
|
3395
|
-
# what the host said about it — are appended by the caller, because reading
|
|
3396
|
-
# second costs a host round trip and the scan spends it only where it could
|
|
3305
|
+
# EIGHT FIELDS, NOT ELEVEN. The two the plan states — the prerequisite's name
|
|
3306
|
+
# and what the host said about it — are appended by the caller, because reading
|
|
3307
|
+
# the second costs a host round trip and the scan spends it only where it could
|
|
3397
3308
|
# change the answer. The rule reports which states those are; see the caller.
|
|
3309
|
+
# The caller appends the PR list's completeness last, once per run.
|
|
3398
3310
|
#
|
|
3399
3311
|
# THE DEFAULT BRANCH'S TIP IS READ ONCE PER RUN, not once per branch. It does
|
|
3400
3312
|
# not move while the scan runs — every fact below is derived from the ref batch
|
|
@@ -3743,26 +3655,19 @@ for plan in "${plans[@]}"; do
|
|
|
3743
3655
|
# is a judgment that belongs one layer up (Manifesto Principle 3).
|
|
3744
3656
|
plan_phase="${plan_meta_phases[$meta_i]}"
|
|
3745
3657
|
|
|
3746
|
-
#
|
|
3747
|
-
#
|
|
3748
|
-
#
|
|
3749
|
-
#
|
|
3750
|
-
#
|
|
3751
|
-
#
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
#
|
|
3756
|
-
#
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
# * the record's date has aged out of the window — ordinary expiry;
|
|
3760
|
-
# * there is NO record — "no date, no row". `reconcile-scan-accuracy.md` is
|
|
3761
|
-
# the live example; showing it would create the one row that can never
|
|
3762
|
-
# age out of DONE.
|
|
3763
|
-
# Both leave before a single git call is spent on the plan's branches.
|
|
3764
|
-
if is_terminal_phase "$plan_phase"; then
|
|
3765
|
-
[ "${plan_meta_inwindow[$meta_i]}" = "1" ] || continue
|
|
3658
|
+
# THE RELEASE SCOPE, decided by the PHASE alone. A terminal plan stays in the
|
|
3659
|
+
# pulse only while it is `delivered`: its work has landed and its version has
|
|
3660
|
+
# not shipped, which is what DONE holds. `released`, `rejected` and
|
|
3661
|
+
# `superseded` leave, whatever their age, and leave before a single git call
|
|
3662
|
+
# is spent on their branches. No date bounds it, so a plan delivered weeks
|
|
3663
|
+
# before a release still names the release's contents.
|
|
3664
|
+
if is_terminal_phase "$plan_phase" && [ "$plan_phase" != "delivered" ]; then
|
|
3665
|
+
continue
|
|
3666
|
+
fi
|
|
3667
|
+
# A docs or infra plan is live when it merges: /plot-release never records
|
|
3668
|
+
# `Released` for one, so `delivered` is its last phase and it leaves here.
|
|
3669
|
+
if [ "$plan_phase" = "delivered" ]; then
|
|
3670
|
+
case "${plan_meta_types[$meta_i]}" in docs|infra) continue ;; esac
|
|
3766
3671
|
fi
|
|
3767
3672
|
|
|
3768
3673
|
n_plans=$((n_plans + 1))
|
|
@@ -3860,12 +3765,17 @@ for plan in "${plans[@]}"; do
|
|
|
3860
3765
|
# deciding it — see pass 1c.
|
|
3861
3766
|
readings=""
|
|
3862
3767
|
order=""
|
|
3768
|
+
# THE ELEVENTH FIELD: whether the PR list held every PR (`.list-complete`,
|
|
3769
|
+
# written by `prefill_pr_states`). The rule reads a `NONE` for a ref behind
|
|
3770
|
+
# main as `open` only when it is true; a capped list may omit a merged PR.
|
|
3771
|
+
list_complete=false
|
|
3772
|
+
[ -n "$HOST_STATE_CACHE" ] && [ -f "$HOST_STATE_CACHE/.list-complete" ] && list_complete=true
|
|
3863
3773
|
while IFS=$'\t' read -r idx br deferred why waits wname claim; do
|
|
3864
3774
|
[ -n "$br" ] || continue
|
|
3865
3775
|
# "-" is the absent marker the shim writes, for the tab-collapse reason
|
|
3866
3776
|
# above. Normalized here so everything downstream tests emptiness.
|
|
3867
3777
|
[ "$waits" = "-" ] && waits=""
|
|
3868
|
-
readings+="$(branch_readings "$br" "$deferred") ${waits:--} ?"$'\n'
|
|
3778
|
+
readings+="$(branch_readings "$br" "$deferred") ${waits:--} ? $list_complete"$'\n'
|
|
3869
3779
|
order+="$idx $br $deferred $why ${waits:--} $wname $claim"$'\n'
|
|
3870
3780
|
done <<< "$wave_lines"
|
|
3871
3781
|
|
|
@@ -3920,7 +3830,8 @@ for plan in "${plans[@]}"; do
|
|
|
3920
3830
|
# list may legitimately omit: its plan may be delivered and its ref gone.
|
|
3921
3831
|
# `host_pr_state`'s run cache keeps this at one call per prerequisite per
|
|
3922
3832
|
# run, never one per pass.
|
|
3923
|
-
|
|
3833
|
+
# Field 10 is replaced; field 11 is carried.
|
|
3834
|
+
refill+="$(printf '%s' "$rd_line" | cut -f1-9) $(waits_pr_state "$waits_br") $(printf '%s' "$rd_line" | cut -f11)"$'\n'
|
|
3924
3835
|
else
|
|
3925
3836
|
refill+="$rd_line"$'\n'
|
|
3926
3837
|
fi
|