@biffo/cli 0.301.18 → 0.301.20
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.
|
@@ -50,6 +50,35 @@ export BIFFO_ORIGINAL_CWD
|
|
|
50
50
|
|
|
51
51
|
cd "$root"
|
|
52
52
|
|
|
53
|
+
# `claim` never depends on the TypeScript CLI's own logic -- it is a pure
|
|
54
|
+
# passthrough (`cli/src/commands/claim.ts`: `packagedScriptCommand({ script:
|
|
55
|
+
# 'scripts/claim.sh' })`, raw argv forwarded unchanged) to a POSIX shell
|
|
56
|
+
# script that needs only `gh` and `git`. Routing it through `tsx` anyway made
|
|
57
|
+
# RELEASING a claim depend on a toolchain the release itself has nothing to
|
|
58
|
+
# do with: in a worktree where `pnpm install` never completed in `cli/`, the
|
|
59
|
+
# final `exec` below hits a missing `tsx` binary, exits 127 with no
|
|
60
|
+
# reconciling action, and the caller sees a bare "not found" -- the claim/
|
|
61
|
+
# label is left stale, silently blocking re-dispatch of the same issue. Five
|
|
62
|
+
# confirmed instances since 2026-08-31 (biffo-fleet#1231), one costing ~1.25M
|
|
63
|
+
# tokens for zero progress.
|
|
64
|
+
#
|
|
65
|
+
# `scripts/claim.sh` only ships in the template checkout (satellites and
|
|
66
|
+
# instances get it via the published `@biffo/cli` package, never a root
|
|
67
|
+
# copy -- see the claim.ts doc comment), so this dispatches to it directly,
|
|
68
|
+
# unconditionally, whenever it is present at the repo root: eliminate the
|
|
69
|
+
# dependency for this command entirely rather than merely detecting its
|
|
70
|
+
# absence and failing louder. `sh` runs it explicitly rather than a bare
|
|
71
|
+
# `exec` on the path, so the script's own executable bit is irrelevant here
|
|
72
|
+
# too (`ensureExecutable` in packagedScriptCommand.ts exists to paper over
|
|
73
|
+
# exactly that on the tsx path; this path never needs it).
|
|
74
|
+
if [ "${1:-}" = "claim" ] && [ -f "$root/scripts/claim.sh" ]; then
|
|
75
|
+
shift
|
|
76
|
+
# Bare relative path, not "$root/scripts/claim.sh" -- interpreter-audit.sh
|
|
77
|
+
# can only statically resolve a literal `sh scripts/<name>.sh` target, and
|
|
78
|
+
# `cd "$root"` above already guarantees cwd is the repo root by this point.
|
|
79
|
+
exec sh scripts/claim.sh "$@"
|
|
80
|
+
fi
|
|
81
|
+
|
|
53
82
|
# Satellites (sibling apps, plugin repos, runner fleets) carry neither
|
|
54
83
|
# `biffo.core.json` nor `cli/`. They DO carry `.biffo-shared-version`, written
|
|
55
84
|
# by `scripts/shared-sync.sh` and naming the template core version their shared
|
|
@@ -50,6 +50,35 @@ export BIFFO_ORIGINAL_CWD
|
|
|
50
50
|
|
|
51
51
|
cd "$root"
|
|
52
52
|
|
|
53
|
+
# `claim` never depends on the TypeScript CLI's own logic -- it is a pure
|
|
54
|
+
# passthrough (`cli/src/commands/claim.ts`: `packagedScriptCommand({ script:
|
|
55
|
+
# 'scripts/claim.sh' })`, raw argv forwarded unchanged) to a POSIX shell
|
|
56
|
+
# script that needs only `gh` and `git`. Routing it through `tsx` anyway made
|
|
57
|
+
# RELEASING a claim depend on a toolchain the release itself has nothing to
|
|
58
|
+
# do with: in a worktree where `pnpm install` never completed in `cli/`, the
|
|
59
|
+
# final `exec` below hits a missing `tsx` binary, exits 127 with no
|
|
60
|
+
# reconciling action, and the caller sees a bare "not found" -- the claim/
|
|
61
|
+
# label is left stale, silently blocking re-dispatch of the same issue. Five
|
|
62
|
+
# confirmed instances since 2026-08-31 (biffo-fleet#1231), one costing ~1.25M
|
|
63
|
+
# tokens for zero progress.
|
|
64
|
+
#
|
|
65
|
+
# `scripts/claim.sh` only ships in the template checkout (satellites and
|
|
66
|
+
# instances get it via the published `@biffo/cli` package, never a root
|
|
67
|
+
# copy -- see the claim.ts doc comment), so this dispatches to it directly,
|
|
68
|
+
# unconditionally, whenever it is present at the repo root: eliminate the
|
|
69
|
+
# dependency for this command entirely rather than merely detecting its
|
|
70
|
+
# absence and failing louder. `sh` runs it explicitly rather than a bare
|
|
71
|
+
# `exec` on the path, so the script's own executable bit is irrelevant here
|
|
72
|
+
# too (`ensureExecutable` in packagedScriptCommand.ts exists to paper over
|
|
73
|
+
# exactly that on the tsx path; this path never needs it).
|
|
74
|
+
if [ "${1:-}" = "claim" ] && [ -f "$root/scripts/claim.sh" ]; then
|
|
75
|
+
shift
|
|
76
|
+
# Bare relative path, not "$root/scripts/claim.sh" -- interpreter-audit.sh
|
|
77
|
+
# can only statically resolve a literal `sh scripts/<name>.sh` target, and
|
|
78
|
+
# `cd "$root"` above already guarantees cwd is the repo root by this point.
|
|
79
|
+
exec sh scripts/claim.sh "$@"
|
|
80
|
+
fi
|
|
81
|
+
|
|
53
82
|
# Satellites (sibling apps, plugin repos, runner fleets) carry neither
|
|
54
83
|
# `biffo.core.json` nor `cli/`. They DO carry `.biffo-shared-version`, written
|
|
55
84
|
# by `scripts/shared-sync.sh` and naming the template core version their shared
|
package/package.json
CHANGED
package/scripts/claim.sh
CHANGED
|
@@ -642,6 +642,91 @@ claim_select_expr() {
|
|
|
642
642
|
"$_n" "$_slug" "$_n" "$_n"
|
|
643
643
|
}
|
|
644
644
|
|
|
645
|
+
# --- continuity-language guard for --release (biffo-fleet#1232) -------------
|
|
646
|
+
#
|
|
647
|
+
# `--reaffirm` (above) gives a session the right TOOL for "this PR promises
|
|
648
|
+
# the claim stays alive past this session" -- but nothing stopped the WRONG
|
|
649
|
+
# one, ordinary `--release`, from running anyway. PR #1848 said, in its own
|
|
650
|
+
# body and in a same-session comment on the issue it referenced, that it
|
|
651
|
+
# would not touch that issue's claim -- then the same session's routine
|
|
652
|
+
# end-of-session `--release` fired 21 seconds later and cleared it, exactly
|
|
653
|
+
# the "written promise vs. actual mechanism disagree" shape AGENTS.md's own
|
|
654
|
+
# `--guard` was built to close for the branch/PR half of collision
|
|
655
|
+
# prevention. This is that fix's sibling for release.
|
|
656
|
+
#
|
|
657
|
+
# It is necessarily a DETECT-tier guard (level 4, not higher): whether a PR
|
|
658
|
+
# or comment PROMISES continuity is a fact about free text, not a structural
|
|
659
|
+
# fact `gh` can hand back the way `closingIssuesReferences` can. So it
|
|
660
|
+
# pattern-matches a small set of phrases drawn from the one real corpus this
|
|
661
|
+
# estate has for it -- PR #1848's own body, and the comment posted on #1083
|
|
662
|
+
# quoted in biffo-fleet#1232 -- rather than inventing hypothetical wording.
|
|
663
|
+
#
|
|
664
|
+
# Deliberately errs toward FALSE positives, not false negatives: blocking an
|
|
665
|
+
# ordinary release that turns out to be fine costs one manual override
|
|
666
|
+
# (documented in the refusal message, same shape as the "genuinely stale
|
|
667
|
+
# claim" carve-out already below); missing a real continuity promise
|
|
668
|
+
# reproduces the exact bug this guard exists to close. A guard that only
|
|
669
|
+
# recognised PR #1848's literal wording would be exactly the "fixed the
|
|
670
|
+
# sentence, not the class" failure this estate has already paid for
|
|
671
|
+
# (biffo-fleet#711) -- so this matches the SHAPE of a continuity claim
|
|
672
|
+
# (claim/label + stays/remains/not-touched/reaffirm), not its exact words.
|
|
673
|
+
#
|
|
674
|
+
# GitHub markdown wraps PR/issue bodies at ~80 columns, and the one real
|
|
675
|
+
# instance of this phrase ("...under the Foreman's existing claim throughout
|
|
676
|
+
# the review window; nothing here touches that label or that claim.") spans
|
|
677
|
+
# three wrapped lines. A per-line grep would never see "touches" and "claim"
|
|
678
|
+
# on the same line, so the text is whitespace-collapsed before matching --
|
|
679
|
+
# deliberately, unlike the closing-keyword parser's cross-newline match
|
|
680
|
+
# AGENTS.md warns about, because collapsing here only widens what gets
|
|
681
|
+
# BLOCKED (safe direction: the escape hatch is a manual override, not an
|
|
682
|
+
# action taken on the estate), not what gets closed or released.
|
|
683
|
+
CONTINUITY_RE='(stays?|remains?) claimed|reaffirm|under the [^.]*claim|nothing[^.]*touch[^.]*(claim|label)|not[^.]*(touch|release)[^.]*(claim|label)|nothing[^.]*claim[^.]*touch'
|
|
684
|
+
|
|
685
|
+
# claim_continuity_promise <issue> -- tri-state: does an OPEN PR referencing
|
|
686
|
+
# $1 (via `claim_select_expr`'s same three signals: closes, branch name, or
|
|
687
|
+
# `Refs #N`), or $1's OWN issue comments, contain continuity language?
|
|
688
|
+
#
|
|
689
|
+
# 0 a continuity promise was found -- refuse the release
|
|
690
|
+
# 1 none found -- reads succeeded, nothing matched
|
|
691
|
+
# 2 cannot tell -- a read itself failed
|
|
692
|
+
#
|
|
693
|
+
# Requires BOTH reads to succeed before returning 1 ("none found"), same
|
|
694
|
+
# fail-closed shape as `claim_held_by`: a read failure that happened to hide
|
|
695
|
+
# a real promise must never be indistinguishable from "checked, and it's
|
|
696
|
+
# clear".
|
|
697
|
+
#
|
|
698
|
+
# Note for the ONE call site this has today (the `--release` block below):
|
|
699
|
+
# the comment read here is the identical `gh issue view --json comments`
|
|
700
|
+
# call `claim_held_by` already made, in the same process, just above it --
|
|
701
|
+
# so a genuinely unreadable comment list is caught there first, and this
|
|
702
|
+
# function's own comment-failure branch is unreachable through that path.
|
|
703
|
+
# It stays, fail-closed, for any future caller that does not check
|
|
704
|
+
# `claim_held_by` first.
|
|
705
|
+
claim_continuity_promise() {
|
|
706
|
+
_cp_issue="$1"
|
|
707
|
+
_cp_slug=$(repo_slug)
|
|
708
|
+
_cp_select=$(claim_select_expr "$_cp_issue" "$_cp_slug")
|
|
709
|
+
|
|
710
|
+
_cp_pr_bodies=$(gh_pr list --state open --limit 100 \
|
|
711
|
+
--json number,headRefName,closingIssuesReferences,body \
|
|
712
|
+
--jq "[.[] | select($_cp_select)] | .[].body // \"\"" 2>/dev/null)
|
|
713
|
+
_cp_pr_status=$?
|
|
714
|
+
|
|
715
|
+
_cp_issue_comments=$(gh_issue view "$_cp_issue" --json comments \
|
|
716
|
+
--jq '[.comments[]?.body // ""] | join("\n---\n")' 2>/dev/null)
|
|
717
|
+
_cp_comment_status=$?
|
|
718
|
+
|
|
719
|
+
if [ "$_cp_pr_status" -ne 0 ] || [ "$_cp_comment_status" -ne 0 ]; then
|
|
720
|
+
return 2
|
|
721
|
+
fi
|
|
722
|
+
|
|
723
|
+
_cp_text=$(printf '%s\n%s' "$_cp_pr_bodies" "$_cp_issue_comments" | tr '\n' ' ')
|
|
724
|
+
if printf '%s' "$_cp_text" | grep -Eiq "$CONTINUITY_RE"; then
|
|
725
|
+
return 0
|
|
726
|
+
fi
|
|
727
|
+
return 1
|
|
728
|
+
}
|
|
729
|
+
|
|
645
730
|
LABEL=in-progress
|
|
646
731
|
|
|
647
732
|
# --- holder identity (#1279) -------------------------------------------------
|
|
@@ -756,6 +841,31 @@ if [ -n "$RELEASE" ]; then
|
|
|
756
841
|
claim_held_by "$ISSUE" "$HOLDER"
|
|
757
842
|
_held_status=$?
|
|
758
843
|
if [ "$_held_status" -eq 0 ]; then
|
|
844
|
+
# Structural continuity check (biffo-fleet#1232) -- runs AFTER ownership
|
|
845
|
+
# is confirmed (a mismatched holder is refused below regardless) and
|
|
846
|
+
# BEFORE the label is actually removed, so an ordinary --release cannot
|
|
847
|
+
# silently contradict a promise an open PR or this issue's own comments
|
|
848
|
+
# have made about staying claimed. See claim_continuity_promise above.
|
|
849
|
+
claim_continuity_promise "$ISSUE"
|
|
850
|
+
_cp_status=$?
|
|
851
|
+
if [ "$_cp_status" -eq 0 ]; then
|
|
852
|
+
echo "${RED}claim: refusing to release #$ISSUE${OFF} — an open PR referencing it, or a" >&2
|
|
853
|
+
echo "${RED} comment on it, says the claim stays alive past this session.${OFF}" >&2
|
|
854
|
+
echo "${DIM} AGENTS.md §1: a PR that promises an issue stays claimed must REAFFIRM it,${OFF}" >&2
|
|
855
|
+
echo "${DIM} not just say so. If that promise still holds, run:${OFF}" >&2
|
|
856
|
+
echo "${DIM} sh scripts/biffo.sh claim $ISSUE --reaffirm $HOLDER${OFF}" >&2
|
|
857
|
+
echo "${DIM} If it has genuinely resolved (review window over, work finished), clear${OFF}" >&2
|
|
858
|
+
echo "${DIM} the label by hand and say so in a comment, rather than through --release:${OFF}" >&2
|
|
859
|
+
echo "${DIM} gh issue edit $ISSUE --remove-label $LABEL${OFF}" >&2
|
|
860
|
+
exit 1
|
|
861
|
+
fi
|
|
862
|
+
if [ "$_cp_status" -eq 2 ]; then
|
|
863
|
+
echo "${RED}claim: cannot tell whether #$ISSUE carries a continuity promise${OFF} — the" >&2
|
|
864
|
+
echo "${RED} referencing PR list or the issue's comments were unreadable.${OFF}" >&2
|
|
865
|
+
echo "${DIM} Not releasing on an unreadable read: that could clear a claim meant to${OFF}" >&2
|
|
866
|
+
echo "${DIM} stay held. Retry once gh/network is working.${OFF}" >&2
|
|
867
|
+
exit 2
|
|
868
|
+
fi
|
|
759
869
|
gh_issue edit "$ISSUE" --remove-label "$LABEL" >/dev/null 2>&1 || {
|
|
760
870
|
echo "${RED}claim: could not remove the '$LABEL' label.${OFF}" >&2
|
|
761
871
|
exit 2
|