@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.
@@ -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 `**Phase:** Approved` → `Delivered` in the `## Status` section only.
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