Skip to content

Persona opt-out label sync must not fail open - #765

Merged
don-petry merged 2 commits into
mainfrom
fix/persona-label-fail-open
Jul 17, 2026
Merged

Persona opt-out label sync must not fail open#765
don-petry merged 2 commits into
mainfrom
fix/persona-label-fail-open

Conversation

@don-petry

Copy link
Copy Markdown
Contributor

Fixes the regression that shipped in #757. Flagged in review there, not addressed, merged anyway — it is live on main now.

ids=$(gh api .../contents/personas ...) || {
  warn "  Could not list persona manifests ... — skipping opt-out labels"
  return 0
}

A transient API error emits no labels and returns success. The repo gets "All repos processed successfully" while the <id>:hands-off family is silently absent — and the escape hatch §4 rule 4 mandates doesn't exist there. Nobody finds out until someone tells a persona to leave an issue alone and it ignores them.

That's a fifth instance of the systemic pattern #755 names as its headline finding: reading an error as a negative answer.

Keeps both halves — that's the actual requirement

  • Resilience — the static 7 labels still land. A persona hiccup must not block unrelated label work. The original rationale was right and is preserved.
  • Honesty — the run can no longer claim success. persona_opt_out_label_configs returns non-zero, apply_labels records it, and both main() exit paths (--all and single-repo) fail the run.

Why it survived review

The caller used mapfile < <(persona_opt_out_label_configs). Process substitution runs the function in a subshell whose exit code is unreachablemapfile reports its own status — so a failure there literally could not be observed. Switched to $( ): also a subshell, but its status propagates.

That's worth noting beyond this PR: the fail-open wasn't just an oversight, it was structurally unobservable.

Also: the guessed-label path

<id>:hands-off is only a convention (§4 rule 4) — the schema lets a persona declare any opt_out_label. If the manifest is unreadable we may create a label nobody uses while the real one stays absent, leaving opt-out silently broken. The guess is still emitted (it's the best available), but the run now says so.

Two tests encoded the defect as intended behaviour

was now
"unavailable manifest listing degrades to no opt-out labels (returns 0)" "... returns NON-ZERO (does not fail open)"
"apply_labels still succeeds when persona derivation is unavailable" split: the resilience half (static 7 still land) + the honesty half (failure flag set)

The gh stub always exited 0 even for a missing fixture, so the fetch-failure path was untestable — a real gh api on an absent manifest exits non-zero (404). Made the stub faithful; that's what let the new guessed-label test fail first, then pass.

Verification

  • bats test/scripts/apply-repo-settings/apply-repo-settings.bats14/14 (11 + 3 new)
  • shellcheck --severity=warning -x — clean
  • Live check against the real public manifests: derives qa-lead:hands-off|ededed|..., exits 0 on the happy path

Process note

This regression shipped because my finding was a PR comment, which is advisory — only CHANGES_REQUESTED or a red check stops the pipeline. Findings on agent PRs should land as blocking reviews from here on.

🤖 Generated with Claude Code

#757 shipped with the fail-open flagged in review (#755). It is live on main:

  ids=$(gh api .../contents/personas ...) || {
    warn "  Could not list persona manifests ... — skipping opt-out labels"
    return 0
  }

A transient API error emits no labels and returns success, so a repo gets
"All repos processed successfully" while the <id>:hands-off family is silently
absent — and the escape hatch §4 rule 4 MANDATES does not exist there. Nobody
finds out until someone tells a persona to leave an issue alone and it ignores
them. That is a fifth instance of the systemic pattern #755 names as its
headline finding: reading an error as a negative answer.

Keeps both halves, which is the actual requirement:
- RESILIENCE — the static 7 labels still land; a persona hiccup must not block
  unrelated label work (the original rationale, and it was right).
- HONESTY — the run can no longer CLAIM success. persona_opt_out_label_configs
  returns non-zero, apply_labels records it, and both main() exit paths (--all
  and single-repo) fail the run.

Why it survived review: the caller used `mapfile < <(persona_opt_out_label_configs)`.
Process substitution runs the function in a subshell whose exit code is
unreachable — mapfile reports its own status — so a failure there literally
could not be seen. Switched to $( ), also a subshell but its status propagates.

Also marks the GUESSED-label path as a failure. <id>:hands-off is only a
CONVENTION (§4 rule 4); the schema lets a persona declare any opt_out_label. If
the manifest is unreadable we may create a label nobody uses while the real one
stays absent — opt-out silently broken. The guess is still emitted (it is the
best available), but the run says so.

Two tests ENCODED the defect as intended behaviour and are rewritten:
- "unavailable manifest listing degrades to no opt-out labels (returns 0)"
  -> "... returns NON-ZERO (does not fail open)"
- "apply_labels still succeeds when persona derivation is unavailable"
  -> split into the resilience half (static 7 still land) and the honesty half
     (the failure flag is set)

The gh stub always exited 0, even for a missing fixture, so the fetch-failure
path was untestable — a real `gh api` on an absent manifest exits non-zero (404).
Made the stub faithful; that is what let the new guessed-label test fail first.

Verification
- bats test/scripts/apply-repo-settings/apply-repo-settings.bats: 14/14 (11 + 3
  new: non-zero on listing failure, non-zero on unreadable manifest, flag unset
  on a healthy derivation).
- shellcheck --severity=warning -x: clean.
- Live check against the real public manifests: derives
  'qa-lead:hands-off|ededed|...' and exits 0 on the happy path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@don-petry
don-petry requested a review from a team as a code owner July 17, 2026 11:58
Copilot AI review requested due to automatic review settings July 17, 2026 11:58
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@don-petry, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 23 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 10c6d36f-c1c0-4798-9036-0491f6c8a1e0

📥 Commits

Reviewing files that changed from the base of the PR and between 1e94d91 and f7e2191.

📒 Files selected for processing (2)
  • scripts/apply-repo-settings.sh
  • test/scripts/apply-repo-settings/apply-repo-settings.bats
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/persona-label-fail-open

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — review-changes (no-changes)

No changes were needed for this PR.

@don-petry
don-petry enabled auto-merge (squash) July 17, 2026 12:00

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request improves error handling and resilience when syncing persona opt-out labels in apply-repo-settings.sh. It ensures that if persona manifests cannot be listed or read, the script tracks the failure using a _PERSONA_OPT_OUT_SYNC_FAILED flag and exits with a non-zero status, rather than failing silently. The process substitution was replaced with command substitution to properly propagate the exit status. Corresponding unit tests were added and updated to verify this behavior. There are no review comments, so I have no feedback to provide.

@don-petry
don-petry disabled auto-merge July 17, 2026 12:01
@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — fix-reviews (no-changes)

Agent reasoning
Addressed 0 threads:
(no open threads)
Test verification: skipped — no code changes made
Files changed: none
Notes:
- All CI checks pass (no Tier 1 blockers)
- No CHANGES_REQUESTED reviews
- gemini-code-assist[bot] left a COMMENTED review with no actionable feedback ("no feedback to provide")
- PR is clean and unblocked
```

@don-petry
don-petry enabled auto-merge (squash) July 17, 2026 12:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a regression in scripts/apply-repo-settings.sh where persona opt-out label derivation failures were treated as success (fail-open), causing runs to report “All repos processed successfully” even when the required <id>:hands-off opt-out label family was missing or incomplete.

Changes:

  • Make persona_opt_out_label_configs return non-zero on manifest listing/read failures while still emitting best-effort label configs where possible.
  • Record derivation failure via _PERSONA_OPT_OUT_SYNC_FAILED and fail the run in both --all and single-repo paths so success can’t be claimed dishonestly.
  • Update bats tests and the gh stub to make fetch failures observable/testable and to validate both resilience (static labels still apply) and honesty (run fails).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
scripts/apply-repo-settings.sh Makes persona opt-out label derivation failures observable and ensures the script exits non-zero when the opt-out family is incomplete/guessed.
test/scripts/apply-repo-settings/apply-repo-settings.bats Updates stubs and tests to encode the new “must not fail open” behavior and verify failure recording.

Comment thread scripts/apply-repo-settings.sh Outdated
Comment thread scripts/apply-repo-settings.sh
Comment thread test/scripts/apply-repo-settings/apply-repo-settings.bats
Comment thread test/scripts/apply-repo-settings/apply-repo-settings.bats
donpetry-bot
donpetry-bot previously approved these changes Jul 17, 2026

@donpetry-bot donpetry-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated review — APPROVED ✓

Risk: MEDIUM
Reviewed commit: 1bc439c512da464a1f2137aa541033c400cffbfc
Review mode: triage-approved (single reviewer)

Summary

Fixes the fail-open regression from #757 in scripts/apply-repo-settings.sh: persona opt-out label derivation failures previously returned 0 and let the run claim "All repos processed successfully" while the mandated :hands-off escape hatch was silently absent. The fix preserves resilience (the static 7 labels still land on a persona hiccup) while adding honesty (persona_opt_out_label_configs returns non-zero, apply_labels records it in _PERSONA_OPT_OUT_SYNC_FAILED, and both --all and single-repo exit paths fail the run). Tests updated: 2 tests that encoded the defect rewritten, 3 new tests added, and the gh stub made faithful (non-zero on missing manifest) so the failure path is actually testable.

Linked issue analysis

No formal closing issue. The PR fixes a regression introduced by merged PR #757 and addresses the systemic pattern ("reading an error as a negative answer") documented in open issue #755 (persona framework pre-rollout review findings). Verified: #757 is merged (the fail-open is live on main), #755 is open, and the removed code matches the fail-open quoted in the PR body. The fix substantively addresses the stated defect.

Findings

No blocking findings. Verified in detail:

  • The root-cause claim is correct: mapfile < <(fn) runs fn in a process-substitution subshell whose exit code is unreachable (mapfile reports its own status). Switching to $( ) makes the status propagate — the fix is structurally sound, not cosmetic.
  • warn/err write to stderr (confirmed at head SHA), so the command substitution captures only label-config lines, never warning text.
  • The empty-output guard before mapfile <<< "$persona_out" correctly avoids the one-empty-element pitfall that would create a bogus "||" label config.
  • Both exit paths (--all and single-repo) check _PERSONA_OPT_OUT_SYNC_FAILED before reporting success; resilience of the static label set is preserved.
  • Guessed-label path (<id>:hands-off convention fallback when a manifest is unreadable) now sets rc=1 while still emitting the best guess — consistent with the honesty goal.
  • Test stub change (exit 1 on missing fixture) matches real gh api 404 behavior; new tests fail-first validated per PR description.
  • Minor, non-blocking: in --all mode the derivation is cached after the first failure, so later repos share the degraded set and one failure fails the whole run — this matches the intended semantics and the prior caching behavior.
  • Secret scan: run_secret_scanning MCP tool not available in this environment; gitleaks CI check passed and the diff contains no secret-like content.

CI status

All checks green at 1bc439c: Lint and bats (14/14), ShellCheck, Lint, Agent Security Scan, Secret scan (gitleaks), CodeQL, SonarCloud Quality Gate (0 new issues, 0 hotspots), AgentShield, dependency audits (SUCCESS or SKIPPED where ecosystem absent). CodeRabbit/Codex were rate-limited (advisory only); gemini-code-assist reviewed with no feedback. No unresolved review threads.


Reviewed automatically by the PR-review agent (single-reviewer mode: fable 5). Reply if you need a human review.

@donpetry-bot

donpetry-bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor
Superseded by automated re-review at 1bc439c512da464a1f2137aa541033c400cffbfc — click to expand prior review.

Review — fix requested (cycle 1/3)

The automated review identified the following issues. Please address each one:

Findings to fix

Automated review — NEEDS HUMAN REVIEW

Risk: MEDIUM
Reviewed commit: 1bc439c512da464a1f2137aa541033c400cffbfc
Review mode: triage-approved (single reviewer)

Summary

Fix is correct and well-tested, but 4 unresolved Copilot review threads landed after the dev-lead pass and were never addressed. Escalating so they can be resolved; core change verified sound.

Linked issue analysis

No formally linked (closing) issues. The PR substantively addresses the fail-open regression it cites from #757 and the systemic "error read as negative answer" pattern from #755: persona_opt_out_label_configs now returns non-zero on listing/manifest failures, apply_labels records the failure in _PERSONA_OPT_OUT_SYNC_FAILED, and both main() exit paths (--all and single-repo) turn the flag into a non-zero exit. Verified against the full file at the head SHA: apply_labels is called directly in main's shell (not a subshell/pipeline), so the flag genuinely reaches the exit guards; the switch from mapfile < <(...) to $( ) correctly makes the subshell exit status observable; and the empty-output mapfile guard prevents a bogus "||" label config.

Findings

Blocking (procedural): 4 unresolved review threads from copilot-pull-request-reviewer, submitted at 12:03 — after the dev-lead fix-reviews pass ran at 12:01 (which correctly reported "no open threads" at that time). None invalidate the fix, but they are unaddressed:

  1. awk '{print $1}' truncates free-form opt_out_label values at whitespace (scripts/apply-repo-settings.sh:120). Note: this parsing is pre-existing from feat: implement issue #756 — Provision <id>:hands-off labels so persona opt-out actually works #757, not introduced here — but the thread deserves a reply.
  2. _PERSONA_OPT_OUT_CONFIGS_CACHED=true is set even on failed derivation, so a transient error on the first repo in --all mode skips retry for all later repos (scripts/apply-repo-settings.sh:172). A legitimate improvement suggestion — the run does still exit non-zero, so failure is not silent, but caching only on success would leave fewer repos without the opt-out family.
    3–4. || true in the two new bats tests masks unexpected non-zero returns from apply_labels (apply-repo-settings.bats:180, :187). Minor test-robustness nits.

Verified sound (no action needed): flag propagation to both exit paths, subshell exit-code observability fix, empty-mapfile guard, test stub corrected to exit non-zero on missing manifest (making the failure path testable), and the resilience half preserved (static 7 labels still land on a persona hiccup).

Secret scan MCP tool not available in this environment — relying on the green gitleaks CI check.

CI status

All required checks green: Lint and bats, ShellCheck, CodeQL, SonarCloud (quality gate passed), gitleaks secret scan, Agent Security Scan, agent-shield, npm audit. Remaining checks skipped (not applicable ecosystems). mergeStateStatus BLOCKED pending review only.


Reviewed automatically by the PR-review agent (single-reviewer mode: fable 5). Reply if you need a human review.

Additional tasks

  1. Resolve all unresolved review thread comments from other reviewers
  2. Ensure all CI checks pass after your changes
  3. Rebase on the target branch if behind
  4. Do NOT modify files unrelated to the findings above

The review cascade will automatically re-review after new commits are pushed.

@donpetry-bot
donpetry-bot dismissed their stale review July 17, 2026 12:07

Superseded by automated re-review at 1bc439c.

@donpetry-bot

donpetry-bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor
Superseded by automated re-review at f7e21915817af44aa6aa5b13d4c2e0dc48cbb95b — click to expand prior review.

Review — fix requested (cycle 2/3)

The automated review identified the following issues. Please address each one:

Findings to fix

Automated review — NEEDS HUMAN REVIEW

Risk: MEDIUM
Reviewed commit: 1bc439c512da464a1f2137aa541033c400cffbfc
Review mode: triage-approved (single reviewer, cycle 1/3)

Summary

Re-review at the same head SHA as the prior review (1bc439c). The core fix remains verified sound, but the 4 unresolved review threads from copilot-pull-request-reviewer that blocked the prior cycle are still unresolved and no new commits have been pushed. All prior findings are carried forward unchanged; escalating again.

Linked issue analysis

No formally linked (closing) issues. The PR substantively addresses the fail-open regression from #757 and the "error read as a negative answer" systemic pattern from #755: persona_opt_out_label_configs now returns non-zero on listing/manifest failures, apply_labels records the failure in _PERSONA_OPT_OUT_SYNC_FAILED, and both main() exit paths (--all and single-repo) turn the flag into a non-zero exit. The switch from mapfile < <(...) to $( ) makes the subshell exit status observable, the empty-output guard prevents a bogus || label config, and the resilience half (static 7 labels still land on a persona hiccup) is preserved and tested.

Findings

Blocking (procedural, carried forward from prior review — unchanged): 4 unresolved review threads from copilot-pull-request-reviewer remain open. No commits have landed since the prior review requested they be addressed. None invalidate the fix, but they are unaddressed:

  1. awk '{print $1}' truncates free-form opt_out_label values at whitespace (scripts/apply-repo-settings.sh:120) — pre-existing from feat: implement issue #756 — Provision <id>:hands-off labels so persona opt-out actually works #757, but the thread deserves a reply.
  2. _PERSONA_OPT_OUT_CONFIGS_CACHED=true is set even on failed derivation, so a transient error on the first repo in --all mode skips retry for all later repos (scripts/apply-repo-settings.sh:172). The run still exits non-zero, so the failure is not silent, but caching only on success would be an improvement.
    3–4. || true in the two new bats tests masks unexpected non-zero returns from apply_labels (apply-repo-settings.bats:180, :187). Minor test-robustness nits.

New issues: none found.

Verified sound (no action needed): flag propagation to both exit paths, subshell exit-code observability fix, empty-mapfile guard, test stub corrected to exit non-zero on a missing manifest, and the resilience half preserved (14/14 bats green in CI).

Secret scan MCP tool not available in this environment — relying on the green gitleaks CI check.

CI status

All required checks green: Lint and bats, ShellCheck, Lint, CodeQL, SonarCloud (quality gate passed), Secret scan (gitleaks), Agent Security Scan, agent-shield, npm audit. Remaining checks skipped (non-applicable ecosystems). mergeStateStatus BLOCKED pending review only.


Reviewed automatically by the PR-review agent (single-reviewer mode: fable 5). Reply if you need a human review.

Additional tasks

  1. Resolve all unresolved review thread comments from other reviewers
  2. Ensure all CI checks pass after your changes
  3. Rebase on the target branch if behind
  4. Do NOT modify files unrelated to the findings above

The review cascade will automatically re-review after new commits are pushed.

…t whitespace

Both from Copilot's review of this PR; both correct.

1. A failed derivation was cached (my bug, introduced here)

_PERSONA_OPT_OUT_CONFIGS_CACHED was set to true even on failure. In --all mode
the cache is shared across all 11 repos, so ONE transient API blip on the first
repo denied opt-out labels to every LATER repo too, even after the API
recovered — turning a hiccup into a fleet-wide gap. Now only a GOOD derivation
is cached; a failure retries on the next repo. The retry costs a few API calls
on the failure path only.

_PERSONA_OPT_OUT_SYNC_FAILED stays STICKY on purpose: the repo that failed went
without its labels, so the run did not do what it claims even if later repos
recover. Resilience and honesty are separate concerns and both hold.

2. awk '{print $1}' truncated opt_out_label at the first word

opt_out_label is free-form in the schema and GitHub label names may contain
spaces, so a persona declaring "needs human review" would have provisioned a
label named "needs" — leaving the real opt-out absent and the hatch silently
broken, which is the exact class of bug this PR exists to fix. Now takes the
whole scalar and strips a trailing YAML comment, trailing space, and
surrounding quotes.

3. Tests used `|| true`, which masks failures

`|| true` would let the flag assertions pass even if apply_labels started
returning non-zero or tripped errexit. Replaced with an explicit status capture
under `set +e`. The first attempt used $(...) to return the status — which
defeated itself, because command substitution is a subshell and the flag
mutations being asserted happen in the current shell. The helper now records
the status in a global instead.

Verification
- bats test/scripts/apply-repo-settings/apply-repo-settings.bats: 18/18 (14 + 4
  new: failure not cached / success cached / spaces preserved / trailing comment
  stripped).
- shellcheck --severity=warning -x: clean.
- Live check against the real public manifests: still derives
  'qa-lead:hands-off|ededed|...' and exits 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@don-petry
don-petry disabled auto-merge July 17, 2026 12:28
@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — review-changes (no-changes)

No changes were needed for this PR.

@sonarqubecloud

Copy link
Copy Markdown

@don-petry
don-petry enabled auto-merge (squash) July 17, 2026 12:29
@don-petry
don-petry disabled auto-merge July 17, 2026 12:29
@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — fix-bot-comment (no-changes)

Agent reasoning
Issues addressed: 0
- Quality Gate Passed: no actionable issues reported
Files changed: none
Skipped (informational): 0
```
The PR is healthy — all quality gates passing, no code issues identified, and CI checks green. No changes needed.

@don-petry
don-petry enabled auto-merge (squash) July 17, 2026 12:30

@donpetry-bot donpetry-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated review — APPROVED ✓

Risk: MEDIUM
Reviewed commit: f7e21915817af44aa6aa5b13d4c2e0dc48cbb95b
Review mode: triage-approved (single reviewer)

Summary

Fixes the fail-open regression from #757 in scripts/apply-repo-settings.sh: persona opt-out label derivation failures previously returned 0, letting the run claim success while the mandated :hands-off escape hatch was silently absent. The fix preserves resilience (static 7 labels still land) while making the run honest (non-zero exit in both --all and single-repo paths). All 4 Copilot review threads that blocked cycles 1-2 are now resolved by commit f7e2191: failed derivations are no longer cached, space-containing opt_out_label values are no longer truncated, and test-side || true masks were replaced with explicit status capture. 18/18 bats tests, shellcheck clean, all CI gates green.

Linked issue analysis

No formally linked closing issue. The PR remediates a regression introduced by #757 (merged despite a review flag) and is motivated by the systemic "error read as negative answer" pattern documented in #755. The PR body substantively documents the defect, the structural reason it survived review (process-substitution subshell swallowing the exit code), and live verification against the real manifests.

Findings

Prior-cycle blockers — all resolved (cycles 1-2 escalated over 4 unresolved Copilot threads; commit f7e2191 addresses each, and all 4 threads are now marked resolved):

  1. Failed derivation was cached, denying opt-out labels to later repos in an --all sweep → now only successful derivations set _PERSONA_OPT_OUT_CONFIGS_CACHED; failure flag stays sticky by design. Covered by new tests.
  2. awk '{print $1}' truncated free-form opt_out_label at the first word → replaced with whole-scalar parsing that strips trailing YAML comment, trailing space, and surrounding quotes. Covered by two new tests (spaces preserved, comment stripped).
    3-4. Test-side || true masked unexpected non-zero from apply_labels → replaced with a set +e / APPLY_RC status-capture helper that asserts status explicitly.

Core fix verified sound: persona_opt_out_label_configs returns non-zero on listing failure or unreadable manifest (guess still emitted); $( ) capture replaces mapfile < <(...) so the exit status is observable; empty-output mapfile guard prevents a bogus "||" label config; both main() exit paths fail the run when _PERSONA_OPT_OUT_SYNC_FAILED is set; the gh stub now faithfully exits non-zero on a missing manifest, making the failure path testable.

Non-blocking nit: the sed pipeline strips a trailing comment before removing surrounding quotes, so a quoted label containing ' # ' (e.g. "stop # now") would be mangled. Vanishingly rare edge; fine as a follow-up if ever relevant.

Secret scan: run_secret_scanning MCP tool not available in this session; gitleaks CI check is green. No secrets, auth, or credential-adjacent changes in the diff.

CI status

All quality gates green: ShellCheck, Lint and bats, CodeQL (actions), SonarCloud Quality Gate, Secret scan (gitleaks), Agent Security Scan, AgentShield, npm audit, CodeRabbit — all SUCCESS. Two CANCELLED dev-lead orchestration runs (dispatch/ci-relay) are concurrency-superseded duplicates; the parallel runs of the same jobs completed SUCCESS/SKIPPED seconds later. Ecosystem audits not applicable to this change were SKIPPED. mergeStateStatus BLOCKED reflects only the pending review requirement.


Reviewed automatically by the PR-review agent (single-reviewer mode: fable 5). Reply if you need a human review.

@don-petry
don-petry merged commit 290df3b into main Jul 17, 2026
23 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants