@plot-pm/board 0.11.0 → 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.
- package/dist/board-server.mjs +124 -124
- package/package.json +5 -1
- package/plot-agent-monitor.sh +37 -13
- package/plot-approve.sh +6 -3
- package/plot-build-monitor.sh +435 -0
- package/plot-config.sh +8 -1
- package/plot-default-branch.sh +109 -0
- package/plot-deliver.sh +4 -2
- package/plot-dispatch.sh +500 -5
- package/plot-fleet-scan.sh +498 -175
- package/plot-host.sh +116 -13
- package/plot-monitor-subject.sh +194 -0
- package/plot-plan-meta.sh +111 -7
- package/plot-pr-merged.sh +180 -0
- package/plot-reap.sh +45 -5
- package/plot-release-refs.sh +188 -46
- package/plot-resolve-artifact.sh +115 -22
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Plot helper: the ONE answer to "what is the default branch, and is origin/HEAD
|
|
3
|
+
# still pointing at something that exists?"
|
|
4
|
+
#
|
|
5
|
+
# SOURCED, NOT RUN. `. "$script_dir/plot-default-branch.sh"` defines
|
|
6
|
+
# `default_branch` and `repair_origin_head`; the file does nothing else on load.
|
|
7
|
+
# The same shape — and the same reason — as `plot-pr-merged.sh` and
|
|
8
|
+
# `plot-worker-state.sh`.
|
|
9
|
+
#
|
|
10
|
+
# WHY IT EXISTS. Twice on 2026-09-04, hours apart, `refs/remotes/origin/HEAD`
|
|
11
|
+
# pointed at `origin/plot-corpus-pin`, a branch that does not exist on the
|
|
12
|
+
# remote. `plot-dispatch.sh` could not resolve the default branch and refused
|
|
13
|
+
# every dispatch:
|
|
14
|
+
#
|
|
15
|
+
# plot-dispatch: cannot resolve 'origin/plot-corpus-pin' — refusing to dispatch.
|
|
16
|
+
#
|
|
17
|
+
# `git remote set-head origin --auto` fixed it both times, in under a second.
|
|
18
|
+
#
|
|
19
|
+
# THE FAILURE IS INVISIBLE TO EVERY READER, AND THAT IS THE POINT.
|
|
20
|
+
# `git symbolic-ref --short refs/remotes/origin/HEAD` on a corrupt symref exits
|
|
21
|
+
# **0** and prints `origin/plot-corpus-pin` — a plausible branch name. Ten
|
|
22
|
+
# scripts read it that way, and none of them can tell that answer from a good
|
|
23
|
+
# one. Only the `rev-parse` downstream fails, in a component that has already
|
|
24
|
+
# built `origin/<main>` out of it and can only report that the ref will not
|
|
25
|
+
# resolve. So the check belongs where the symref is READ, not where it is used.
|
|
26
|
+
#
|
|
27
|
+
# IT NAMES WHAT IT REPAIRED. A recurring corruption that is silently fixed is
|
|
28
|
+
# one nobody investigates: the line says what the symref pointed at and what it
|
|
29
|
+
# now points at, so a second occurrence is visible in a log rather than
|
|
30
|
+
# invisible in a working system.
|
|
31
|
+
#
|
|
32
|
+
# IT DOES NOT REPAIR A SYMREF THAT RESOLVES. Only an unresolvable one is broken.
|
|
33
|
+
# A clone whose `origin/HEAD` deliberately names a non-default branch is
|
|
34
|
+
# somebody's choice, and `--auto` would silently overrule it.
|
|
35
|
+
#
|
|
36
|
+
# WHAT LEAVES THE PIN BEHIND IS NOT REPAIRED HERE, and the leave-alone rule is
|
|
37
|
+
# what keeps this safe alongside it. `packages/domain/corpus/refs.corpus.test.ts`
|
|
38
|
+
# repoints `origin/HEAD` at a `plot-corpus-pin` ref it creates in `beforeAll`
|
|
39
|
+
# and restores in `afterAll` — deliberately, so two readings of the estate see
|
|
40
|
+
# one world. While that suite runs the pin RESOLVES, so this repairs nothing and
|
|
41
|
+
# the suite is unaffected. What was measured on 2026-09-04 is the state after a
|
|
42
|
+
# run that never reached its `afterAll`: the symref left behind, the ref it
|
|
43
|
+
# names gone. Fixing that belongs to the suite; this repairs the symptom and
|
|
44
|
+
# reports it loudly enough that the cause stays findable.
|
|
45
|
+
|
|
46
|
+
# Whether `refs/remotes/origin/HEAD` names a ref that exists.
|
|
47
|
+
#
|
|
48
|
+
# Reads the symref itself rather than `default_branch`'s answer, because the
|
|
49
|
+
# fallbacks below would mask exactly the state this tests.
|
|
50
|
+
#
|
|
51
|
+
# Usage: origin_head_resolves [<repo-dir>]
|
|
52
|
+
# Returns: 0 when it resolves — including when there is no symref at all, which
|
|
53
|
+
# is a FRESH CLONE rather than a corruption and has nothing to repair.
|
|
54
|
+
origin_head_resolves() {
|
|
55
|
+
local dir="${1:-.}" target
|
|
56
|
+
target=$(git -C "$dir" symbolic-ref --quiet refs/remotes/origin/HEAD 2>/dev/null) || return 0
|
|
57
|
+
[ -n "$target" ] || return 0
|
|
58
|
+
git -C "$dir" rev-parse --verify --quiet "${target}^{commit}" >/dev/null 2>&1
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# Repairs an unresolvable `refs/remotes/origin/HEAD`, and says what it did.
|
|
62
|
+
#
|
|
63
|
+
# The repair is `git remote set-head origin --auto`, which asks the remote. It
|
|
64
|
+
# is cheap — measured under a second — and it is the same command that fixed
|
|
65
|
+
# both occurrences by hand.
|
|
66
|
+
#
|
|
67
|
+
# Usage: repair_origin_head [<repo-dir>]
|
|
68
|
+
# Output: one line on stderr naming BOTH refs, when a repair happened.
|
|
69
|
+
# Returns: 0 whether or not it repaired, and 0 when the repair itself fails —
|
|
70
|
+
# this is a self-heal on a path that has its own refusal downstream,
|
|
71
|
+
# so it must never become a second way to stop.
|
|
72
|
+
repair_origin_head() {
|
|
73
|
+
local dir="${1:-.}" was now
|
|
74
|
+
origin_head_resolves "$dir" && return 0
|
|
75
|
+
|
|
76
|
+
was=$(git -C "$dir" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || true)
|
|
77
|
+
if ! git -C "$dir" remote set-head origin --auto >/dev/null 2>&1; then
|
|
78
|
+
echo "plot: origin/HEAD points at '$was', which does not resolve, and 'git remote set-head origin --auto' failed — the remote could not be asked." >&2
|
|
79
|
+
return 0
|
|
80
|
+
fi
|
|
81
|
+
now=$(git -C "$dir" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || true)
|
|
82
|
+
echo "plot: repaired origin/HEAD — it pointed at '$was', which does not exist on the remote; it now points at '${now:-<unset>}'." >&2
|
|
83
|
+
return 0
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# The default branch, repairing an unresolvable `origin/HEAD` on the way.
|
|
87
|
+
#
|
|
88
|
+
# The fallbacks are the ones the callers already carried, in the same order: the
|
|
89
|
+
# symref, then `main`. What is new is that the symref is TESTED before it is
|
|
90
|
+
# believed.
|
|
91
|
+
#
|
|
92
|
+
# IT NEVER FALLS BACK TO THE CHECKOUT'S OWN BRANCH, and that is a refusal rather
|
|
93
|
+
# than an omission. `refs-git.ts:138` does exactly that, correctly — it answers
|
|
94
|
+
# a question about THIS checkout. A shell caller is asking which branch everyone
|
|
95
|
+
# shares, and answering with whatever branch this tree happens to sit on is the
|
|
96
|
+
# defect `dispatch.test.mjs` is named for: *"a shared approval is not hidden by
|
|
97
|
+
# a parked checkout"*, measured when a concurrent agent's `git checkout` blocked
|
|
98
|
+
# two correctly-approved plans in one session. `main` is a guess about the
|
|
99
|
+
# repository; the current branch is a guess about the operator's last command.
|
|
100
|
+
#
|
|
101
|
+
# Usage: default_branch [<repo-dir>]
|
|
102
|
+
# Output: the branch name on stdout, without the `origin/` prefix.
|
|
103
|
+
default_branch() {
|
|
104
|
+
local dir="${1:-.}" name
|
|
105
|
+
repair_origin_head "$dir"
|
|
106
|
+
name=$(git -C "$dir" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | sed 's#^origin/##')
|
|
107
|
+
[ -n "$name" ] || name="main"
|
|
108
|
+
printf '%s\n' "$name"
|
|
109
|
+
}
|
package/plot-deliver.sh
CHANGED
|
@@ -226,7 +226,9 @@ rel=$(cd "$repo_root" && real_plan_path "$plan_file") || rel=""
|
|
|
226
226
|
# The filename, for symlink creation.
|
|
227
227
|
plan_basename=$(basename "$rel")
|
|
228
228
|
|
|
229
|
-
# Flip `**
|
|
229
|
+
# Flip `**State:** Approved` → `Delivered` in the `## Status` section only.
|
|
230
|
+
# Reads `State:` and `Phase:` alike: it changes the VALUE on whichever line
|
|
231
|
+
# carries it, so a plan written before the 2026-09-07 rename still delivers.
|
|
230
232
|
#
|
|
231
233
|
# READS ONE FILE AND WRITES ANOTHER, rather than editing in place. It edited in
|
|
232
234
|
# place until 2026-09-02, which is what let the phase land without its record —
|
|
@@ -235,7 +237,7 @@ flip_phase() { # $1=in $2=out → 0 if it changed the file, 1 if nothing to fli
|
|
|
235
237
|
awk '
|
|
236
238
|
BEGIN { section = ""; done = 0 }
|
|
237
239
|
/^## / { section = ($0 ~ /^## Status/) ? "status" : ""; print; next }
|
|
238
|
-
section == "status" && !done && tolower($0) ~ /^[ \t]*[-*]?[ \t]*\**phase[:*]/ {
|
|
240
|
+
section == "status" && !done && tolower($0) ~ /^[ \t]*[-*]?[ \t]*\**(state|phase)[:*]/ {
|
|
239
241
|
if (tolower($0) ~ /approved/) {
|
|
240
242
|
sub(/[Aa]pproved/, "Delivered")
|
|
241
243
|
done = 1
|