Skip to content

fix(#479): add create-on-missing fallback for ready-for-review label - #481

Open
fullsend-ai-coder[bot] wants to merge 1 commit into
mainfrom
agent/479-label-create-on-missing
Open

fix(#479): add create-on-missing fallback for ready-for-review label#481
fullsend-ai-coder[bot] wants to merge 1 commit into
mainfrom
agent/479-label-create-on-missing

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Summary

Replace the fail-open ready-for-review label application pattern in post-code.src.sh with a create-on-missing fallback that addresses all three review findings from fullsend PR 5657:

  • Error gating (MEDIUM): The create fallback only fires when stderr contains missing-label indicators (404/422/not found). Transient errors (rate limit, network, permission) skip straight to error escalation without touching the label definition — no risk of --force clobbering admin-customized label metadata.
  • Exit 1 escalation (HIGH): All three error branches (create failed, created but retry apply failed, non-label-missing error) now exit 1, so broken review dispatch fails the step instead of producing a green job with only an annotation.
  • No --force (MEDIUM): gh label create is called without --force, preserving existing label metadata if the label somehow already exists.

Files changed

  • scripts/post-code.src.sh — label application block replaced with create-on-missing fallback
  • scripts/post-code.sh — re-bundled via make script-build
  • scripts/post-code-test.sh — 9 new test cases covering all label application paths

Testing

  • All 77 tests pass in post-code-test.sh (68 existing + 9 new)
  • New test cases cover: direct apply, create-then-apply (404 and 422), created-but-retry-fails, create-fails, transient error (rate limit), network error, permission error, early-return short-circuit
  • Secret scan passed
  • Bundle check passed (make check-bundle)

Note: pre-commit could not run in sandbox (network restriction). Post-script runs authoritative pre-commit on runner.


Closes #479

Post-script verification

  • Branch is not main/master (agent/479-label-create-on-missing)
  • Secret scan passed (gitleaks — 3760b3bb70de32596c1922332fb96e76cd9ebcd8..HEAD)
  • PR body secret scan passed (gitleaks — no-git)
  • Pre-commit hooks passed (authoritative run on runner)
  • Tests ran inside sandbox

@fullsend-ai-coder
fullsend-ai-coder Bot requested a review from a team as a code owner July 28, 2026 00:40

@ifireball ifireball left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure we want this at all (this is a retor-agent suggested change that should've been discussed IMO) - not having certain labels is a way for repo owners to avoid having fullsend set the labels on the repos, though perhaps we should have mandatory and optional labels, where the mandatory ones are the ones used to hand-off work between agents and may be auto-created

@maruiz93 maruiz93 self-assigned this Jul 29, 2026
@maruiz93
maruiz93 force-pushed the agent/479-label-create-on-missing branch from b15f90a to ee2de36 Compare July 30, 2026 08:41
@maruiz93
maruiz93 requested a review from ifireball July 30, 2026 09:12
Comment thread scripts/post-code.src.sh
@maruiz93
maruiz93 force-pushed the agent/479-label-create-on-missing branch 2 times, most recently from 81ce8c4 to 0b13210 Compare July 31, 2026 08:38
@maruiz93

Copy link
Copy Markdown
Contributor

We discussed this in the Jul 29 team sync. The team aligned on two clear groups:

  1. Mandatory labels (dispatch/handoff: ready-for-review, ready-to-code, ready-for-triage) — auto-created on the fly when missing
  2. Optional labels (agent suggestions: good-first-issue, question, etc.) — fail silently if missing

This PR implements that distinction. The shared ensure_label() in scripts/lib/labels.lib.sh handles the mandatory labels. For optional labels in post-triage.sh, the existing label_exists guard (line 505) still skips non-existent labels without creating them.

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 31, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:10 AM UTC · Completed 9:28 AM UTC
Commit: 0b13210 · View workflow run →

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review

Findings

Medium

  • [scope-exceeded] scripts/post-triage.sh:22 — The modifications to scripts/post-triage.sh exceed issue Port fullsend PR 5657 (ready-for-review label create-on-missing) to the live post-code.sh in this repo #479's explicitly authorized scope. The issue authorizes changes to scripts/post-code.src.sh, scripts/post-code-test.sh, and re-bundling scripts/post-code.sh only. Changes at lines 22–25 (sourcing labels.lib.sh), line 81 (adding ensure_label call in add_label), and the removal of the inline gh label create for pr-open (lines 309–311 in base) are not authorized. Per AGENTS.md rule 3: "Every changed line in your diff must trace directly to the issue scope."
    Remediation: Remove all changes to scripts/post-triage.sh and scripts/post-triage-test.sh. If these scripts need the same fix, file a separate issue.

  • [protected-path] scripts/ — All 6 changed files (scripts/lib/labels.lib.sh, scripts/post-code-test.sh, scripts/post-code.sh, scripts/post-code.src.sh, scripts/post-triage-test.sh, scripts/post-triage.sh) are under the scripts/ protected path. The PR links to issue Port fullsend PR 5657 (ready-for-review label create-on-missing) to the live post-code.sh in this repo #479 which authorizes changes to these scripts. Human approval is always required for protected-path changes regardless of context.

Low

  • [over-engineering] scripts/lib/labels.lib.sh:14_label_defaults defines defaults for 17 labels when only ready-for-review is needed for issue Port fullsend PR 5657 (ready-for-review label create-on-missing) to the live post-code.sh in this repo #479's authorized scope. The remaining 16 label definitions have no usage in authorized changes.

  • [inconsistent-label-metadata] scripts/lib/labels.lib.sh:16 — Label descriptions in _label_defaults add a "Fullsend:" prefix not present in existing inline gh label create calls. Since ensure_label does not use --force, existing labels will not be overwritten, creating divergence between new labels (with prefix) and existing ones (without prefix).

Previous run

Review

Findings

Medium

  • [scope-exceeded] scripts/post-triage.sh:81 — The modifications to scripts/post-triage.sh exceed issue Port fullsend PR 5657 (ready-for-review label create-on-missing) to the live post-code.sh in this repo #479's explicitly authorized scope. The issue scopes changes to scripts/post-code.src.sh and scripts/post-code-test.sh (plus re-bundling scripts/post-code.sh). Changes at lines 22–25 (sourcing labels.lib.sh), line 81 (adding ensure_label to add_label), and the removal of the inline gh label create for pr-open (lines 309–311 deleted) are not authorized. Per AGENTS.md rule 3: "Every changed line in your diff must trace directly to the issue scope."
    Remediation: Remove all changes to scripts/post-triage.sh and scripts/post-triage-test.sh from this PR. If consolidating label creation logic across scripts is desired, file a separate issue.

  • [protected-path] scripts/ — All 6 changed files (scripts/lib/labels.lib.sh, scripts/post-code-test.sh, scripts/post-code.sh, scripts/post-code.src.sh, scripts/post-triage-test.sh, scripts/post-triage.sh) are under the scripts/ protected path. The PR links to issue Port fullsend PR 5657 (ready-for-review label create-on-missing) to the live post-code.sh in this repo #479 which authorizes changes to these scripts. Human approval is always required for protected-path changes regardless of context.

Low

  • [API-contract-mismatch] scripts/lib/labels.lib.sh:43 — Issue Port fullsend PR 5657 (ready-for-review label create-on-missing) to the live post-code.sh in this repo #479 carries forward a review finding suggesting gating the create-on-missing fallback on 404/422 stderr from the apply step. The current implementation calls ensure_label preemptively instead. While this deviates from the suggested retry-on-failure pattern, the preemptive approach avoids --force (no metadata clobbering), handles "already exists" silently, and achieves the same functional outcome. The extra API call per run is minor overhead.

  • [over-engineering] scripts/lib/labels.lib.sh:14_label_defaults defines defaults for 17 labels when only ready-for-review is needed for issue Port fullsend PR 5657 (ready-for-review label create-on-missing) to the live post-code.sh in this repo #479's authorized scope. The remaining 15 label definitions have no usage in authorized changes.

  • [exit-code-contract-violation] scripts/post-code.src.sh:714 — Label application failure triggers exit 1 after the branch has been pushed and PR created. The script header documents exit code 1 as "validation failure or error (nothing pushed)." The exit 1 is required by issue Port fullsend PR 5657 (ready-for-review label create-on-missing) to the live post-code.sh in this repo #479's review findings, but the exit code documentation should be updated to reflect the new semantics.

  • [inconsistent-variable-naming] scripts/post-triage.sh:22 — Variable name SCRIPT_DIR_TRIAGE deviates from the established SCRIPT_DIR_POST convention used in other post-scripts. (Moot if post-triage.sh changes are removed per scope-exceeded finding.)

  • [inconsistent-label-metadata] scripts/lib/labels.lib.sh:17 — Label descriptions in _label_defaults add a "Fullsend:" prefix not present in existing inline gh label create calls. Since ensure_label does not use --force, existing labels will not be overwritten, but the descriptions diverge from the established pattern.

Previous run (2)

Review

Findings

High

  • [error-handling-gap] scripts/lib/labels.lib.sh:52ensure_label unconditionally swallows all gh label create failures via 2>/dev/null || true. If the token lacks permission to create labels, or if there is a network/auth error, the function silently returns 0. The caller in post-code.src.sh then proceeds to gh issue edit --add-label, which may also fail because the label was never created. While the gh issue edit failure now correctly exits 1, the root cause (label creation failure) is invisible in logs.
    Remediation: Capture stderr from gh label create and log it when the command fails. E.g.: if ! err=$(gh label create "${create_args[@]}" 2>&1); then echo "Warning: gh label create failed: ${err}" >&2; fi; return 0

Medium

  • [fail-open] scripts/lib/labels.lib.sh:52ensure_label fires unconditionally before every label application, not gated on stderr of a failed gh issue edit --add-label as issue Port fullsend PR 5657 (ready-for-review label create-on-missing) to the live post-code.sh in this repo #479 finding chore: update fullsend shim workflow #2 requires. The PR creates the label preemptively on every run rather than only when the apply step fails with a missing-label indicator (404/422). See also: [error-handling-gap] finding at this location.
    Remediation: Refactor to implement the issue-authorized pattern: attempt gh issue edit --add-label, capture stderr, inspect for missing-label indicators (404/422), only then call gh label create and retry.

  • [behavioral-escalation-risk] scripts/post-code.src.sh:696 — The gh issue edit --add-label call uses 2>/dev/null to suppress stderr, combined with the new exit 1 on failure. This makes the fatal error path uninformative — CI logs will only show "Failed to apply ready-for-review label" without the actual stderr from gh issue edit.
    Remediation: Capture stderr from gh issue edit instead of discarding it: if err=$(gh issue edit ... 2>&1); then ... else gha_echo error "Failed: ${err}"; exit 1; fi

  • [incomplete-migration] scripts/post-triage.sh:309ensure_label is called inside add_label, but an existing inline gh label create 'pr-open' --force at line ~312 still executes with --force and a different description ("An open PR already addresses this issue" vs "Fullsend: open PR addresses this issue" in _label_defaults). This creates redundant, conflicting label creation paths for pr-open.
    Remediation: Remove the direct gh label create 'pr-open' call since ensure_label inside add_label now handles label creation.

  • [scope-exceeded] scripts/post-triage.sh:81 — The modification to scripts/post-triage.sh exceeds issue Port fullsend PR 5657 (ready-for-review label create-on-missing) to the live post-code.sh in this repo #479's authorized scope. The issue explicitly scopes changes to scripts/post-code.src.sh and scripts/post-code-test.sh. Per AGENTS.md rule 3: "Modify only what the issue authorizes."
    Remediation: Remove the ensure_label call from scripts/post-triage.sh and the labels.lib.sh source statement. If post-triage.sh needs label creation, file a separate issue.

  • [protected-path] scripts/ — All 5 changed files (scripts/lib/labels.lib.sh, scripts/post-code-test.sh, scripts/post-code.sh, scripts/post-code.src.sh, scripts/post-triage.sh) are under the scripts/ protected path. The PR links to issue Port fullsend PR 5657 (ready-for-review label create-on-missing) to the live post-code.sh in this repo #479 which authorizes changes to these scripts. Human approval is always required for protected-path changes regardless of context.

Low


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

fullsend-ai-review[bot]

This comment was marked as outdated.

@maruiz93
maruiz93 force-pushed the agent/479-label-create-on-missing branch from 0b13210 to 50c786e Compare July 31, 2026 10:10
@maruiz93

Copy link
Copy Markdown
Contributor

[error-handling-gap] — Addressed. ensure_label now captures stderr and logs unexpected failures (silences only "already exists"). Commit 50c786e.

[fail-open] — By design. The preemptive ensure_label call is intentionally simpler than a retry-on-fail pattern. The extra gh label create is a no-op when the label exists. Parsing gh issue edit stderr for 404/422 codes would be fragile and couple the lib to caller error handling. The issue authorizes fixing the missing-label problem, not a specific implementation pattern.

[behavioral-escalation-risk] — Addressed. gh issue edit stderr is now captured and included in the error message instead of being suppressed. Commit 50c786e.

[incomplete-migration] — Addressed. Removed the redundant inline gh label create 'pr-open' --force call — ensure_label inside add_label now handles it. Commit 50c786e.

[scope-exceeded] — The post-triage.sh change is authorized by @ifireball's review feedback ("I would expect to see some label application library function here used in all agents") and the issue's scope (provisioning required labels in target repos). The shared lib was the reviewer's explicit request.

[protected-path] — Acknowledged. Human review is in progress via @ifireball.

[over-engineering] — The 17-label table is intentional. The shared lib covers all known fullsend labels so other scripts can migrate to it incrementally, per the reviewer's "used in all agents" request.

[issue-requirement-not-addressed] — The PUSH_TOKEN already documents issues:write in the workflow file. Runtime token-permission verification is not standard practice for these scripts.

[inconsistent-label-metadata] — The pr-open inconsistency was resolved by removing the inline call (see [incomplete-migration]). The remaining inline calls in other scripts (post-review, post-fix, pre-code) are not modified by this PR — migrating them is a follow-up.

[source-directive-variable] — Addressed. Renamed _TRIAGE_SCRIPT_DIRSCRIPT_DIR_TRIAGE to match the SCRIPT_DIR_POST convention. Commit 50c786e.

@maruiz93
maruiz93 force-pushed the agent/479-label-create-on-missing branch 2 times, most recently from 5cba679 to aca34a5 Compare July 31, 2026 10:21
@maruiz93
maruiz93 requested a review from ifireball July 31, 2026 10:30
@maruiz93

Copy link
Copy Markdown
Contributor

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 31, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:33 AM UTC · Completed 10:51 AM UTC
Commit: aca34a5 · View workflow run →

@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review July 31, 2026 10:51

Superseded by updated review

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Jul 31, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Marta Anon <manon@redhat.com>
@maruiz93
maruiz93 force-pushed the agent/479-label-create-on-missing branch from aca34a5 to 9f8c520 Compare July 31, 2026 11:03
@maruiz93

Copy link
Copy Markdown
Contributor

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 31, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 11:07 AM UTC · Completed 11:24 AM UTC
Commit: 9f8c520 · View workflow run →

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Squad — 3 agents (Claude x2, Grok), MEDIUM+ findings

Posting only findings that are new — deduped against the 8 existing comment threads and the 3 prior bot review rounds. Every finding below was verified against the code at 9f8c520 before posting; agent-reported findings that turned out to be false positives, already-resolved, or already-declared-intentional were dropped.

6 inline comments posted: 1 critical, 2 high, 3 medium.

The headline is the critical one on scripts/post-triage.sh:24: the source of scripts/lib/labels.lib.sh from a non-bundled runtime script violates the invariant this repo documents in its own README.md:55, and will abort triage on every enrolled repo. CI cannot catch it — check-bundle skips non-BUNDLE_SRCS scripts and shellcheck runs with -e SC1091. That finding and the post-triage.sh:81 one together mean the open CHANGES_REQUESTED is still unresolved: the code does not implement the mandatory/optional split described in the reply to it.

Two additional medium items without a clean inline anchor

[medium] premature-decision — the hardcoded colours contradict the live label set. Scoped to colours only; the description-prefix half of this is already settled as intentional and I'm not reopening it. Against gh api repos/fullsend-ai/agents/labels:

label live on this repo _label_defaults
ready-for-review a81af5 0E8A16
ready-to-code ededed 0e8a16
blocked / feature / needs-info / triaged all ededed e11d48 / a2eeef / d876e3 / c2e0c6

No issue, PR comment, or reviewer specified these values — they were chosen, not derived, and nothing pins them. Newly provisioned repos will also get mixed casing for the same green (0e8a16 vs 0E8A16 for the identical colour). Suggest deriving from the existing inline call sites, which are the de-facto spec, or recording where each value came from.

[medium] premature-decision — the token-permission answer cites the wrong token. Issue #479 step 3 asks to verify the coder-role token has Issues write for gh label create. The reply cited PUSH_TOKEN / the workflow permissions: block, but those govern the default GITHUB_TOKEN; the post-script actually receives steps.app-token.outputs.token, minted by .github/actions/mint-token with role: coder, whose permission set lives in the App configuration and cannot be granted or documented by a permissions: block. Probably fine in practice — POST /repos/{o}/{r}/labels and POST .../issues/{n}/labels share the same fine-grained Issues permission, and --add-label already works — but it was reported as verified when it wasn't, and ensure_label degrades a 403 to a warning, so if the coder role is read-only for label creation the symptom is the same silent non-dispatch #479 set out to fix.

Not re-flagged (settled in prior rounds)

scope-exceeded on post-triage.sh; preemptive ensure_label vs 404/422 gating; the 17-label count; the exit-code doc comment; SCRIPT_DIR_TRIAGE naming; the "Fullsend:" description prefix; the swallowed gh label create stderr (resolved in 50c786e).

Where a new finding lands near a settled thread, the inline comment says explicitly how it differs.

Verified as correct (no action)

local err on its own line followed by if ! err=$(...) correctly preserves exit status — no local-masking bug. if defaults=$(_label_defaults ...) is set -e-safe as an if condition. The include guard [[ -n "${LABELS_LIB_SH_LOADED:-}" ]] && return 0 is safe at top level of the bundle when the variable is unset (reproduced). The literal tabs in the %%/## splits are genuinely present and survive bundling intact. create_args is a proper array expanded as "${create_args[@]}", so repo and label names carry no injection risk. make check-bundle passes and post-code.sh is byte-exact.

Comment thread scripts/post-triage.sh

SCRIPT_DIR_TRIAGE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib/labels.lib.sh
source "${SCRIPT_DIR_TRIAGE}/lib/labels.lib.sh"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[critical] runtime-source-unavailable

post-triage.sh is a hand-maintained runtime script — it is not in BUNDLE_SRCS (Makefile:4 lists only pre-code, post-code, post-fix, post-prioritize). It now sources a file from scripts/lib/, which does not travel with it at runtime. This aborts triage on every enrolled repo, before the triage result is read, before any label, before the triage comment.

Four independent confirmations:

  1. This repo's own README documents the invariant. README.md:55"Harness fetches each runner script as an isolated blob, so post-scripts cannot source files from scripts/lib/ at runtime." And README.md:63"A custom post-script that tries to source a base lib at runtime will fail because the harness only fetches the single script blob."
  2. The resolver fetches exactly one file. fullsend/internal/harness/compose.go:881 fetchBaseFile fetches <baseURLDir> + <relPath> and caches it content-addressed at <workspace>/.fullsend-cache/<sha256>/content. At exec time dirname "${BASH_SOURCE[0]}" is that hash directory — there is no lib/ sibling. Sibling files are never fetched.
  3. The scaffold ships no lib/. internal/scaffold/fullsend-repo/scripts/ contains 12 files and no lib entry.
  4. Reproduced. Copying only post-triage.sh into an empty directory and running it:
    line 24: /private/tmp/blobsim/lib/labels.lib.sh: No such file or directory
    EXIT=1
    
    Under set -euo pipefail the script dies here.

Why CI is green: make check-bundle only covers BUNDLE_SRCS, so post-triage is skipped; post-triage-test.sh:10 sets POST_SCRIPT="${SCRIPT_DIR}/post-triage.sh" so tests always run with lib/ as a sibling; and shellcheck runs with -e SC1091, suppressing the unresolvable source. All three suites pass on this branch.

Suggested fix: Revert the post-triage.sh changes and confine ensure_label to the bundled post-code path — labels.lib.sh + post-code.src.sh already work correctly because they go through the bundler. The alternative (rename to post-triage.src.sh, add to BUNDLE_SRCS, make script-build, commit the generated post-triage.sh, update post-triage-test.sh to honour SCRIPT_TEST_TARGET) is the real fix but is a much larger change than #479 authorizes.

Independently, add a regression guard in scripts/bundle-sh-test.sh: assert that no scripts/*.sh lacking a matching .src.sh contains a source .*lib/.*\.lib\.sh line.

Distinct from the settled inconsistent-variable-naming thread on this line — that was about the SCRIPT_DIR_TRIAGE name, this is about the source on the next line failing at runtime.

Comment thread scripts/post-triage.sh

# add_label uses the labels API to avoid firing issues.edited.
add_label() {
ensure_label "${REPO}" "$1"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[high] mandatory-optional-split-not-implemented

Placing ensure_label at the top of add_label() auto-creates every label the action handlers apply, bypassing the label_exists guard entirely. That guard is pre-existing and only covers the label_actions loop (agent-suggested labels) at line ~505.

Labels now auto-created via direct add_label calls: needs-info (:131), duplicate (:146), blocked (:267), pr-open (:314), triaged (:403/432/436), bug (:409), documentation (:417), feature (:431), question (:449), not-planned (:460), ready-to-code (:539). Eleven labels — only one of which (ready-to-code) is in the declared mandatory set.

Driving the script under its own test mock confirms it:

action=question       -> gh label create question    --description Fullsend: issue is a question
action=not-planned    -> gh label create not-planned --description Fullsend: will not be implemented
action=insufficient   -> gh label create needs-info  --description Fullsend: issue needs more information
action=sufficient/bug -> gh label create bug         --description Fullsend: bug report
                         gh label create ready-to-code --description Fullsend: triggers code agent dispatch

question is the exact label cited in the PR discussion as an example of an optional label that should "fail silently if missing". It is auto-created.

The concrete consequence is the one raised in the open CHANGES_REQUESTED: ready-to-code is a dispatch trigger (labels.lib.sh:17 — "triggers code agent dispatch"). A repo owner who deleted ready-to-code to stop fullsend auto-dispatching the code agent will have it silently recreated and applied on the next triage run at line 539. Absent-label-as-opt-out is bypassed for all eleven.

Separately, ready-for-triage — one of the three labels designated mandatory — never reaches ensure_label at all; post-retro.sh:146 still creates it inline with --force.

Suggested fix: Make the split explicit in the library rather than implicit in add_label:

# Only these labels are auto-created; everything else must pre-exist.
_MANDATORY_LABELS="ready-for-review ready-to-code ready-for-triage"
ensure_label() {
  local repo="$1" label="$2"
  case " ${_MANDATORY_LABELS} " in *" ${label} "*) ;; *) return 0 ;; esac
  ...
}

Then call ensure_label only from the sites that need it rather than from generic add_label, and add a post-triage-test.sh case asserting add_label "question" emits no gh label create.

Distinct from the settled scope-exceeded thread on this line — that was about whether these changes are authorized, this is about whether they do what the PR says they do.

Comment thread scripts/post-code.src.sh
echo "Applied ready-for-review label to PR #${PR_NUMBER_FROM_URL}"
else
gha_echo error "Failed to apply ready-for-review label to PR #${PR_NUMBER_FROM_URL} — review agent will NOT be dispatched: ${label_err}"
exit 1

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[high] unreachable-assignment-and-silent-failure

exit 1 sits directly above maybe_assign_pr "${PR_NUMBER_FROM_URL}" (line 718), making it unreachable on this path. Previously the || gha_echo warning fallback let execution continue, so a PR whose label application failed still got assigned to a human via the invoker → issue assignee → issue author chain.

After this change, a label failure leaves a pushed branch and an open PR with no ready-for-review label and no assignee — strictly less discoverable than before. Issue #479's motivating incident is a PR sitting unreviewed for 24 days, so this inverts the intent of the fix on exactly the path it was meant to harden.

Second problem: exit 1 does not fire the trap 'report_post_failure_to_issue' ERR installed at line 61. Verified:

$ bash -c 'set -euo pipefail; trap "echo ERR-TRAP-FIRED" ERR; if x=$(false 2>&1); then echo ok; else exit 1; fi'; echo "rc=$?"
rc=1        # no ERR-TRAP-FIRED

Every other fatal path in this file routes through post_fail_to_issue <category> <detail> (15 call sites: :117, :131, :301, :312, :326, :444, :520, :694, ...), which posts a categorized comment on the source issue. This new path posts nothing — the only trace is an ::error:: annotation on a red job. Wiring the ERR trap would not help either: post-failure-report.lib.sh:287 defaults the detail to "Post-code script failed before push or PR creation completed", which is factually wrong here since both the push and the PR succeeded.

Suggested fix: Move the assignment above the label block (it does not depend on the label), and route the failure through the existing machinery:

PR_NUMBER_FROM_URL="${PR_URL##*/}"
maybe_assign_pr "${PR_NUMBER_FROM_URL}"

ensure_label "${REPO_FULL_NAME}" "ready-for-review"
if label_err=$(gh issue edit "${PR_NUMBER_FROM_URL}" --repo "${REPO_FULL_NAME}" \
     --add-label "ready-for-review" 2>&1); then
  echo "Applied ready-for-review label to PR #${PR_NUMBER_FROM_URL}"
else
  post_fail_to_issue label-apply-failed \
    "Failed to apply ready-for-review to PR #${PR_NUMBER_FROM_URL} — review agent will NOT be dispatched: ${label_err}"
fi

Distinct from the settled exit-code-contract-violation thread nearby — that was about updating the exit-code doc comment (addressed in 9f8c520), this is about the assignment being skipped and no issue-side signal being emitted.

Comment thread scripts/post-code-test.sh

# Bundled script must have labels.lib.sh inlined (ensure_label + _label_defaults)
BUNDLED_SCRIPT="${SCRIPT_DIR}/post-code.sh"
if grep -q '_label_defaults' "${BUNDLED_SCRIPT}" && grep -q 'ensure_label' "${BUNDLED_SCRIPT}"; then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[medium] no-behavioral-test-coverage

Both new assertions are text greps — grep -q 'ensure_label.*ready-for-review' and grep -q '_label_defaults' && grep -q 'ensure_label'. They assert that certain strings appear in a file. Neither ever executes ensure_label.

They would still pass if _label_defaults returned the wrong colour, if the tab-split produced a description containing the colour, if the *already exists* arm were inverted, or if ensure_label unconditionally exit 1'd. The second assertion is also partly redundant with make check-bundle, which already guarantees the lib is inlined.

This matters because the behaviour change in post-triage.sh's add_label() ships with zero coverage: post-triage-test.sh's mock gh (lines 21-43) always exits 0, so the already exists arm, the Warning: arm, and the new exit 1 branch are unexercised anywhere in the suite. And run_test only grep -qFs a single expected pattern, so the newly-emitted gh label create <control-label> line in ~90 other triage tests is entirely unasserted.

The repo has clear precedent for direct library unit tests — scripts/pr-assignee-test.sh, scripts/gitleaks-install-test.sh, and scripts/post-failure-report-test.sh each source their lib and are wired into make script-test. labels.lib.sh is the only lib in scripts/lib/ with no corresponding *-test.sh.

Suggested fix: Add scripts/labels-test.sh that sources the lib with a stub gh on PATH, and register it in the Makefile script-test list next to pr-assignee-test.sh. Minimum cases:

  • known label emits --description/--color with exactly the expected values
  • unknown label emits neither
  • stub gh exiting 1 with label with name "x" already exists... produces no stderr and returns 0
  • stub gh exiting 1 with HTTP 403: Resource not accessible by integration produces a Warning: and still returns 0
  • (once the mandatory gate exists) ensure_label repo question emits no gh call

Also note BUNDLED_SCRIPT is hardcoded to post-code.sh on line 1394, so under SCRIPT_TEST_TARGET=bundled — CI's second pass — that assertion tests the same file twice rather than the source.

Comment thread scripts/lib/labels.lib.sh
local err
if ! err=$(gh label create "${create_args[@]}" 2>&1); then
case "$err" in
*already\ exists*) ;;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[medium] premature-decision

The duplicate-detection contract is a match on gh's human-readable error prose. I verified the assumption holds today rather than assuming it — cli/cli pkg/cmd/label/create.go (v2.96.0):

func isLabelAlreadyExistsError(err api.HTTPError) bool {
	return err.StatusCode == 422 && len(err.Errors) == 1 &&
	       err.Errors[0].Field == "name" && err.Errors[0].Code == "already_exists"
}
...
if errors.Is(err, errLabelAlreadyExists) {
    return fmt.Errorf("label with name %q already exists; use `--force` to update its color and description", opts.Name)
}

So on a duplicate, gh exits non-zero and prints a message containing already existsthis glob currently matches, and the comment on line 39 is correct. This is not a live bug.

Two things are unverified, though:

  1. The fallback. The mapping is only applied when the 422 body has exactly one error item with field == "name" and code == "already_exists". Any other shape falls through to go-gh's generic HTTPError.Error(), which renders HTTP 422: Validation Failed (https://api.github.com/repos/o/r/labels)no already exists substring. In that case a benign duplicate is reclassified as a real failure and prints Warning: gh label create ... failed on every run, in every customer repo. That is exactly the log-noise-that-reviewers-learn-to-ignore failure mode Port fullsend PR 5657 (ready-for-review label create-on-missing) to the live post-code.sh in this repo #479 is trying to reduce.
  2. Durability. The contract is an English substring in a fmt.Errorf string with no stability guarantee, and there is no test, no minimum-gh-version assertion, and no comment recording which version was checked.

Suggested fix: Either stop parsing prose and use a documented status-code contract —

gh api "repos/${repo}/labels/${label}" --silent >/dev/null 2>&1 || gh label create ...

— or at minimum match the machine-readable forms too (*already\ exists*|*already_exists*|*HTTP\ 422*), record the verification in the comment (# Verified against gh 2.96.0: ...), and cover it in the new scripts/labels-test.sh so a wording drift surfaces as a test failure rather than as production log noise.

This is about how the create result is classified. It is distinct from the settled API-contract-mismatch thread (which was about when to create — preemptive vs. gating on 404/422) and from the error-handling-gap finding resolved in 50c786e (which was about the failure being swallowed entirely; this concerns the classification logic that fix introduced).

Comment thread scripts/lib/labels.lib.sh
requires-manual-review) printf '%s\t%s' 'Fullsend: review requires human judgment' 'FBCA04' ;;
rejected) printf '%s\t%s' 'Fullsend: approach rejected by review' 'B60205' ;;
needs-human) printf '%s\t%s' 'Fullsend: agent loop needs human input' 'D93F0B' ;;
pr-open) printf '%s\t%s' 'Fullsend: open PR addresses this issue' 'D4C5F9' ;;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[medium] premature-decision — incomplete migration leaves two --force writers that clobber these values

The PR summary lists "No --force: gh label create is called without --force, preserving existing label metadata" as one of three headline fixes, and the inline --force create for pr-open was removed from post-triage.sh. But pre-code.sh — which runs on every code-agent dispatch — still does:

# scripts/pre-code.src.sh:112-114  (bundled: scripts/pre-code.sh:139-141)
gh label create "pr-open" --repo "${REPO_FULL_NAME}" \
  --description "An open PR already addresses this issue" --color "D4C5F9" \
  --force 2>/dev/null || true

So the admin-customisation clobber this PR claims to have fixed is still live for pr-open on the highest-frequency path, and the two writers now disagree on the string: triage creates "Fullsend: open PR addresses this issue", pre-code force-rewrites it to "An open PR already addresses this issue". The description now oscillates depending on which agent ran last — a regression relative to the pre-PR state, where post-triage and pre-code used identical text.

The same flip applies to ready-for-triage via post-retro.sh:146, which also uses --force. That label is one of the three designated mandatory and is not wired to this library at all.

Six inline gh label create sites remain unmigrated:

Site Label --force?
pre-code.src.sh:112 pr-open yes
post-retro.sh:146 ready-for-triage yes
post-fix.src.sh:442 needs-human no
post-review.sh:414 ready-for-merge no
post-review.sh:425 requires-manual-review no
post-review.sh:432 rejected no

Note that post-review.sh, post-retro.sh, and post-fix.sh are non-bundled runtime scripts, so per the critical finding on post-triage.sh:24 they cannot migrate to this lib without being converted to .src.sh + BUNDLE_SRCS first — the "migrate incrementally" path does not currently exist.

Suggested fix: Drop --force from pre-code.src.sh:114 and re-run make script-build (a two-line change to an already-bundled script that actually delivers the stated fix), and align the pr-open description between the two writers. If touching pre-code is out of scope, remove the "No --force" bullet from the PR body and open a follow-up, so the claim isn't recorded as delivered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-review requires-manual-review Review requires human judgment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Port fullsend PR 5657 (ready-for-review label create-on-missing) to the live post-code.sh in this repo

3 participants