@biffo/cli 0.247.9 → 0.248.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/scripts/claim.sh +79 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biffo/cli",
3
- "version": "0.247.9",
3
+ "version": "0.248.1",
4
4
  "description": "Biffo project scaffolding CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/scripts/claim.sh CHANGED
@@ -91,6 +91,9 @@ set -u
91
91
 
92
92
  ISSUE=""
93
93
  REPO=""
94
+ HOLDER=""
95
+ RELEASE=""
96
+ HOLDER_MARK="claim-holder:"
94
97
  CHECK_ONLY=""
95
98
  GUARD_BRANCH=""
96
99
 
@@ -106,6 +109,15 @@ while [ $# -gt 0 ]; do
106
109
  shift 2
107
110
  ;;
108
111
  --check) CHECK_ONLY=1; shift ;;
112
+ --as)
113
+ HOLDER="${2:-}"
114
+ shift 2
115
+ ;;
116
+ --release)
117
+ HOLDER="${2:-}"
118
+ RELEASE=1
119
+ shift 2
120
+ ;;
109
121
  --guard)
110
122
  GUARD_BRANCH="${2:-}"
111
123
  shift 2
@@ -128,6 +140,23 @@ gh_issue() { if [ -n "$REPO" ]; then gh issue "$@" --repo "$REPO"; else gh issue
128
140
  gh_pr() { if [ -n "$REPO" ]; then gh pr "$@" --repo "$REPO"; else gh pr "$@"; fi; }
129
141
  gh_label() { if [ -n "$REPO" ]; then gh label "$@" --repo "$REPO"; else gh label "$@"; fi; }
130
142
 
143
+ # Does the newest claim comment on $1 carry holder token $2? (#1279)
144
+ #
145
+ # Reads the LAST claim comment rather than any, so a re-claim by a different
146
+ # session supersedes an older one rather than both matching for ever. Returns
147
+ # non-zero when it cannot tell -- an unreadable comment list must never read as
148
+ # "yours", or the flag becomes a way to steal a claim by guessing.
149
+ claim_held_by() {
150
+ _c=$(gh_issue view "$1" --json comments \
151
+ --jq "[.comments[]? | select(.body | contains(\"$HOLDER_MARK\"))] | last | .body // \"\"" \
152
+ 2>/dev/null) || return 1
153
+ [ -n "$_c" ] || return 1
154
+ case "$_c" in
155
+ *"$HOLDER_MARK$2"*) return 0 ;;
156
+ *) return 1 ;;
157
+ esac
158
+ }
159
+
131
160
  # This repo as `owner/name`, so a closing reference can be checked against it.
132
161
  # GitHub records a CROSS-REPO closing reference in the same list as a local one
133
162
  # -- tabsii-lms#43 closes tabsii-platform#553 -- so the number alone is not an
@@ -167,11 +196,51 @@ remote_branches() {
167
196
  }
168
197
 
169
198
  LABEL=in-progress
199
+
200
+ # --- holder identity (#1279) -------------------------------------------------
201
+ #
202
+ # A claim used to record WHEN and WHO, and every session on a workstation claims
203
+ # under the same GitHub actor. So a delegated agent could not distinguish
204
+ # "my orchestrator claimed this for me" from "a stranger claimed it 90 seconds
205
+ # ago", and the safe reading is to stop. On 2026-08-04 four agents were
206
+ # dispatched onto pre-claimed issues; one ran the check first and correctly
207
+ # refused to start, producing nothing. Whether a delegate works or stalls
208
+ # depended on whether it happened to check before starting.
209
+ #
210
+ # `--as <token>` makes the claim answerable: an existing claim carrying the same
211
+ # token reads as YOUR claim and returns free. `--release <token>` refuses to
212
+ # clear somebody else's.
213
+ #
214
+ # The token is opaque and never a secret -- it identifies a session, not a
215
+ # person, and appears in a public comment.
216
+
170
217
  TAKEN=0
171
218
  REASONS=""
172
219
 
173
220
  note() { REASONS="${REASONS} $1\n"; TAKEN=1; }
174
221
 
222
+ # --- --release <token>: only the holder clears it (#1279) --------------------
223
+ #
224
+ # A claim nobody can prove ownership of is one anybody can clear, and a stale
225
+ # label left behind by a crashed session is indistinguishable from a live one.
226
+ # Refusing a mismatched release is what makes the token worth recording.
227
+ if [ -n "$RELEASE" ]; then
228
+ if claim_held_by "$ISSUE" "$HOLDER"; then
229
+ gh_issue edit "$ISSUE" --remove-label "$LABEL" >/dev/null 2>&1 || {
230
+ echo "${RED}claim: could not remove the '$LABEL' label.${OFF}" >&2
231
+ exit 2
232
+ }
233
+ echo "${GREEN}Released.${OFF} ${DIM}(held by $HOLDER)${OFF}"
234
+ exit 0
235
+ fi
236
+ echo "${RED}claim: #$ISSUE is not held by '$HOLDER' — refusing to release it.${OFF}" >&2
237
+ echo "${DIM} Clearing somebody else's claim is how two sessions end up on one issue.${OFF}" >&2
238
+ echo "${DIM} If the claim is genuinely stale (AGENTS.md: over an hour, no branch, no PR),${OFF}" >&2
239
+ echo "${DIM} remove the label by hand and say so in a comment.${OFF}" >&2
240
+ exit 1
241
+ fi
242
+
243
+
175
244
  # --- --guard <branch>: the enforced pre-push gate -----------------------------
176
245
  #
177
246
  # Deliberately short-circuits before any of the four-signal machinery below —
@@ -290,7 +359,15 @@ fi
290
359
  case ",$labels," in
291
360
  *",$LABEL,"*)
292
361
  updated=$(gh_issue view "$ISSUE" --json updatedAt --jq .updatedAt 2>/dev/null)
293
- note "${YELLOW}label${OFF} carries '$LABEL' (issue last updated $updated)"
362
+ # Whose claim is it? (#1279) A claim carrying OUR token is not a collision --
363
+ # it is the orchestrator that dispatched us. Without this a delegated agent
364
+ # cannot tell its own reservation from a stranger's and correctly refuses to
365
+ # start, which is the failure this flag exists to remove.
366
+ if [ -n "$HOLDER" ] && claim_held_by "$ISSUE" "$HOLDER"; then
367
+ echo "${DIM}label carries '$LABEL', held by ${HOLDER} — that is you${OFF}"
368
+ else
369
+ note "${YELLOW}label${OFF} carries '$LABEL' (issue last updated $updated)"
370
+ fi
294
371
  ;;
295
372
  esac
296
373
 
@@ -388,7 +465,7 @@ gh_issue edit "$ISSUE" --add-label "$LABEL" >/dev/null 2>&1 || {
388
465
  exit 2
389
466
  }
390
467
  gh_issue comment "$ISSUE" \
391
- --body "Claimed at $(date -u +%FT%TZ) by \`$(git config user.name 2>/dev/null || echo agent)\`. Release it — remove the label — on merge, or if you stop." \
468
+ --body "Claimed at $(date -u +%FT%TZ) by \`$(git config user.name 2>/dev/null || echo agent)\`.${HOLDER:+ ${HOLDER_MARK}${HOLDER}} Release it — remove the label — on merge, or if you stop." \
392
469
  >/dev/null 2>&1
393
470
 
394
471
  echo "${GREEN}Claimed.${OFF}"