Skip to content

fix(source-control): close the cost hold's billed and security probe gaps - #10583

Merged
kyle-sexton merged 24 commits into
mainfrom
work-resolve-issue/issue-9241-40621393295-1
Oct 29, 2026
Merged

kyle-sexton merged 24 commits into
mainfrom
work-resolve-issue/issue-9241-40621393295-1

Conversation

@melodic-automation-lanes

@melodic-automation-lanes melodic-automation-lanes Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Refs: #9241

Summary

The unattended merge gate's cost hold (babysit_risk.py) now catches the shapes from #9241 that can bill or that widen what a credential or an agent can reach. The model-setting shapes and the public-repository timeout shapes from the first version are dropped: under the owner's direction, cost means only what can bill, and model usage under the Claude subscriptions or prepaid API credits does not, nor do Actions minutes on a public repository. This PR no longer settles the #9241 rows for ANTHROPIC_MODEL, model: / max-turns: inputs and model flags in actions or scripts, so it refers to the issue and does not close it.

Kept:

  • A bare secrets context inside a ${{ }} expression outside a string (${{ secrets }}, contains(secrets, 'X')), secrets.*, and a reference split across a folded scalar rate HIGH secret-exposure-review.
  • A new model leg (claude-code* action or @anthropic-ai/claude-code run) in a composite .github/actions/**/action.yml rates HIGH agent-surface-review.
  • A runs-on expression's matrix values and input default: / options: count as runner values, since larger runners bill even on public repositories.
  • Off a public repository, where minutes bill, raised timeouts are compared at each job or step key path: one removed (the 360-minute default applies), set to an expression, in flow style, or on a workflow that calls a local workflow or action counts.

Dropped:

  • _MODEL_FLAG / _MODEL_SETTING and the widened _COST_WORKFLOW_LINE (ANTHROPIC_*MODEL, model: / max-turns: inputs) across workflows, composite actions and .github/scripts/; _COST_WORKFLOW_LINE is main's again.
  • The new local call cost-finding, and the no patch to read cost-finding for a .github/scripts/ file.
  • The new timeout shapes on a public repository: there the report is main's _raised_timeouts, unchanged.

Fix

  • plugins/source-control/skills/babysit-prs/scripts/babysit_risk.py: _billed_timeouts (the per-key-path comparison, _yaml_entries, _item_drops, _path_timeouts, _raises) runs only when public is not True; main's _raised_timeouts and _TIMEOUT stay for the public report. _composite_model_legs replaces _model_surface and returns new model legs only. The secret helpers (_expressions, _logical_lines, _secret_refs, _BARE_SECRETS) and runner helpers (_referenced_runners, _join_flow) are unchanged from the earlier head.
  • tests/test_babysit_risk_cost.py: removed test_model_settings_on_a_model_step_are_reported, test_a_new_local_workflow_or_action_call_is_reported, test_model_settings_in_actions_and_scripts_are_reported and test_other_action_and_script_lines_are_not_reported; assertCharge no longer asserts a public-repository report.
  • docs/conventions/pr-pipeline/README.md: the raised-timeout paragraph is scoped off a public repository; the cost-finding bullets are main's again.
  • .changes/source-control/ and .changes/pr-pipeline/ fragments reworded to the trimmed scope.

A composite action.yml or action.yaml with no patch to read rates HIGH agent-surface-review, since it may hide a new model leg (review thread on babysit_risk.py).

Verification

  • python3 -m unittest discover in plugins/source-control/skills/babysit-prs/scripts: 3433 tests, OK (5 skipped), on head a39253e. test_a_composite_action_with_no_patch_holds_for_security fails without the no-patch fix and passes with it.
  • python3 -I scripts/sync-hold-floor.py --check: exit 0.
  • scripts/check-changelog-fragments.sh --check: all fragments valid.
  • scripts/run-ruff.sh check on the changed Python: all checks passed.
  • The pre-push check-affected run passed after merging origin/main.

Related

🤖 Generated with Claude Code

Lane-Activity: work-resolve-issue/resolve-issue
@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

claude-security-review reviewed 82e7ac21667fa1b20ead0f986690bc671c8613fd: 0 finding(s), 0 as inline comments.

No security findings in the incremental change since e924f7d (babysit_risk.py and its test), so critical-findings: 0.

What changed. _SECRET_REF gained a fourth alternative. It matches a whole ${{ … secrets … }} span, built from _EXPRESSION_SECRET plus a tail that tolerates a lone }. This makes it detect a bare secrets context inside an expression, such as ${{ secrets }}, ${{ contains(secrets, 'A') }} and ${{ secrets || 'x' }}. The test change adds SecretRefReadsExpressions for those shapes and reflows one line.

Does it weaken a merge hold? No. _secret_refs feeds _net_added at babysit_risk.py:1879 and :1953, which compares whitespace-stripped, case-folded match strings between added and removed lines. _SECRET_REF now returns the whole expression span, not just secrets.X. That cuts two ways:

  • A secret reference moved into a different or longer expression is now a new match, so it is flagged. The base treated it as moved and let it through.
  • A match can offset only against an identical span on a removed line, so a span cannot hide a net-new reference. The change only widens what counts as a reference and tightens offsetting, so the hold is narrower on what it lets through, not wider.

Alternation order. At a ${{ start, only the new alternative can match, so findall returns the whole span. Earlier alternatives only match at their own start positions, so nothing else changes.

Unclosed expressions. A ${{ with no closing }} does not match the new alternative. It is still covered by the _expressions / _BARE_SECRETS / unclosed-literal path in _secret_refs, which this change leaves alone.

Spend. None. The change touches the risk-scoring script and its test only.

Not reported: on a very long line holding an unclosed ${{ followed by many repeated secrets tokens, the new greedy tail could make the scan quadratic. The only effect is a slow scorer on the author's own pull request. It does not bypass a hold and does not meet the severity bar.

@github-actions github-actions 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.

claude-review: 1 inline finding(s) on fbe89c59c07ce2a833fb9a152f71a2f810018ad0; the summary is in this lane's pull request comment.

claude-lane: claude-review

Comment thread plugins/source-control/skills/babysit-prs/scripts/babysit_risk.py Outdated
@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

claude-review reviewed 82e7ac21667fa1b20ead0f986690bc671c8613fd: 0 finding(s), 0 as inline comments.

Incremental review of babysit_risk.py and test_babysit_risk.py since e924f7d. I read the diff and the surrounding code, and I found nothing to block on. I did not run the tests.

What changed. _SECRET_REF (babysit_risk.py:1190-1195) gains a fourth alternative. It reuses _EXPRESSION_SECRET and consumes the rest of the ${{ … }} span, so a bare secrets context inside an expression now counts as a reference. Examples are ${{ secrets }}, ${{ contains(secrets, 'A') }} and ${{ secrets || 'x' }}. Before, only secrets.X, secrets['X'], secrets) and secrets: inherit matched.

Checks made.

  • Definition order. _EXPRESSION_SECRET is defined at line 420, before its use at line 1193.
  • Brace escaping. The rf"…" string escapes the braces correctly ([^}}], \}}).
  • Backtracking. The lazy prefix and the greedy tail are each deterministic per character ([^}] against \}(?!\})), so I expect no catastrophic backtracking. Cost is linear per secrets occurrence.
  • Matching. _SECRET_REF.findall is called only from _secret_refs (line 1362). Because the new alternative starts at ${{, a full expression like ${{ secrets.X }} is now returned as one whole-span token, and _BARE_SECRETS may also add secrets for the same body. Before and after patches use the same function and are compared as multisets, so offsets stay consistent. A cosmetic edit inside an expression, such as whitespace, now reads as a new reference. That fails toward holding, not toward missing a secret.
  • Unclosed expressions. A ${{ with no closing }} on the line does not match the new alternative, but the existing _BARE_SECRETS and unclosed-literal handling in _secret_refs still covers it.

Tests. SecretRefReadsExpressions fails on the old regex for the first three shapes. The fourth, ${{ secrets.X }}, would also match the older secrets.X alternative, so the comment "Only the ${{ }} alternative … matches these shapes" overstates it. That is cosmetic. The other test edit is a line-wrap only.

Spend. None of this change spends anything.

@melodic-automation-lanes

Copy link
Copy Markdown
Contributor Author

pr-unblock / address-feedback finished round 1 of max 4 on fbe89c5.

@melodic-automation-lanes melodic-automation-lanes Bot added the needs-human Human-in-the-loop required; autonomous sessions must not resolve items carrying this. label Oct 28, 2026
@melodic-automation-lanes

Copy link
Copy Markdown
Contributor Author

The pr-unblock lane stopped on this pull request and needs a person: a review finding is uncertain or deferred, so a person decides it.

  • Reason: feedback-uncertain
  • Resume key: fbe89c59c07ce2a833fb9a152f71a2f810018ad0 (the head the lane stopped on)

To resume, deal with the reason above, remove the needs-human label (and do-not-merge, if set), then dispatch the pr-unblock workflow with pr-number=10583 and activity=address-feedback:

gh workflow run pr-unblock.yml -R melodic-software/claude-code-plugins -f pr-number=10583 -f activity=address-feedback

kyle-sexton
kyle-sexton previously approved these changes Oct 28, 2026

@kyle-sexton kyle-sexton 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.

Owner approval of head fbe89c5 (hold path babysit_risk.py; cost rule loop-lane README:335-341).

@kyle-sexton kyle-sexton removed the needs-human Human-in-the-loop required; autonomous sessions must not resolve items carrying this. label Oct 28, 2026
Comment thread docs/conventions/pr-pipeline/README.md
Comment thread plugins/source-control/skills/babysit-prs/scripts/babysit_risk.py
@melodic-automation-lanes

melodic-automation-lanes Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor Author

Unattended merge: native auto-merge leaves this pull request unarmed at head 0ac52429b0b923e0d1249549b5c4757181750528. The lanes clear this reason on their own; the next scheduled run judges it again.

pr-merge's gate refuses its verifier verdict (verify:unreadable)

@github-actions github-actions 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.

claude-security-review: 1 inline finding(s) on 0ac52429b0b923e0d1249549b5c4757181750528; the summary is in this lane's pull request comment.

claude-lane: claude-security-review

Comment thread plugins/source-control/skills/babysit-prs/scripts/babysit_risk.py Outdated

@github-actions github-actions 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.

claude-review: 1 inline finding(s) on 0ac52429b0b923e0d1249549b5c4757181750528; the summary is in this lane's pull request comment.

claude-lane: claude-review

Comment thread plugins/source-control/skills/babysit-prs/scripts/babysit_risk.py
@melodic-automation-lanes

Copy link
Copy Markdown
Contributor Author

pr-unblock / address-feedback finished round 2 of max 4 on d826d5b.

@github-actions github-actions 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.

claude-security-review: 1 inline finding(s) on d826d5b19a98f272193f9869ded4799cc0bbbc6f; the summary is in this lane's pull request comment.

claude-lane: claude-security-review

Comment thread plugins/source-control/skills/babysit-prs/scripts/babysit_risk.py
@melodic-automation-lanes

Copy link
Copy Markdown
Contributor Author

pr-unblock / address-feedback finished round 3 of max 4 on 4017f44.

@melodic-automation-lanes

melodic-automation-lanes Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor Author

The pr-unblock lane stopped address-feedback on this pull request: review rounds are not converging: after the round cap and one fresh fixer round the code review still has open findings, so the fixer and the reviewer disagree on what the change should be. The lane sets no label: the run ends in a failing pr-unblock/address-feedback status on the head the lane gated.

The review rounds are not converging after the round cap and one fresh fixer round. Open review threads:

When a retry on this head can change the outcome, the lane runs one on its own, up to its per-head cap, and edits this comment if it stops again; a run that does not stop clears the status. When no retry can (a review thread the lane does not take or settle, or a failing check after the retries are spent), a person resolves the threads or gets the checks green, then dispatches the workflow below: that run finds the cause gone and clears the status. On a pull request the lanes App opened, the dispatchers start that run once per stop; on any other, a person does. A new commit to the branch starts clean. A reply on a thread, from an account with write access, starting PR-fix lane: please fix this finding grants one more round on that thread.

To run it again on this head now, dispatch the pr-unblock workflow:

gh workflow run pr-unblock.yml -R melodic-software/claude-code-plugins -f pr-number=10583 -f activity=address-feedback

@melodic-automation-lanes

Copy link
Copy Markdown
Contributor Author

Unattended merge: native auto-merge leaves this pull request unarmed at head 4017f446b8bc4341d55d20a25f0e7c8f7784af57. A person must clear this reason, so the pr-merge lane added the needs-human label: fix the reason, then remove the label, and the next scheduled run judges it again.

it changes plugins/source-control/skills/babysit-prs/scripts/babysit_risk.py, a hold path

@melodic-automation-lanes melodic-automation-lanes Bot added the needs-human Human-in-the-loop required; autonomous sessions must not resolve items carrying this. label Oct 28, 2026
kyle-sexton and others added 2 commits October 28, 2026 13:31
A quote a YAML comment leaves open in a `${{` could still move where
_logical_lines joins a split expression, so a bare `secrets` on the
next line escaped the secret-exposure hold. Every `${{` now starts a
fresh body with no literal open, keeping the body it cuts short, and a
body whose literal is still open where its read ends holds under its
own text, so only the same body removed elsewhere offsets it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@github-actions github-actions 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.

claude-review: 1 inline finding(s) on 09d6030909f54ddf335c477cb562a7f86ad96966; the summary is in this lane's pull request comment.

claude-lane: claude-review

Comment thread plugins/source-control/skills/babysit-prs/scripts/babysit_risk.py

@github-actions github-actions 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.

claude-review: 1 inline finding(s) on e1c59817cda1d5668a48ddb7b9f891b9b508dbb0; the summary is in this lane's pull request comment.

claude-lane: claude-review

Comment thread plugins/source-control/skills/babysit-prs/scripts/babysit_risk.py
kyle-sexton and others added 2 commits October 29, 2026 09:25
…imeout raises

The cost hold now reads a bare `secrets` in an `if:` value written without
`${{ }}` (job or step level, quoted, flow style or a block scalar) as a
secret-exposure-review reference.

At a timeout key path where the base has numbers and none is lost, a gained
number counts as raised only above the highest one there, and a
`leg-timeout-minutes` set where the base had none is a cap, not a raise. Job
`timeout-minutes` keeps its 360-minute baseline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@github-actions github-actions 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.

claude-review: 1 inline finding(s) on 64df6f92f5eaaf837c52a777be233a7202784619; the summary is in this lane's pull request comment.

claude-lane: claude-review

Comment thread plugins/source-control/skills/babysit-prs/scripts/babysit_risk.py Outdated
kyle-sexton
kyle-sexton previously approved these changes Oct 29, 2026

@kyle-sexton kyle-sexton 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.

Approved at 64df6f9.

…t their key path

On a workflow that names a model action, the line pass reported every
added timeout number when the patch removed none, so a cap added to a
model leg held the merge for cost. With base text to compare, the path
pass already judges each number at its key path, so the line pass now
keeps only added expressions. With no base text it still judges numbers.
Two fixtures whose base and head text omitted the timeout the patch
changed now carry it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kyle-sexton
kyle-sexton previously approved these changes Oct 29, 2026

@kyle-sexton kyle-sexton 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.

Approved at e42d913.

Lane-Activity: pr-unblock/resolve-conflicts
@melodic-automation-lanes

melodic-automation-lanes Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor Author

Unattended merge: native auto-merge leaves this pull request unarmed at head 82e7ac21667fa1b20ead0f986690bc671c8613fd. The lanes clear this reason on their own; the next scheduled run judges it again.

pr-merge's gate refuses its verifier verdict (verify:repeated-fail)

@melodic-automation-lanes melodic-automation-lanes Bot added the needs-human Human-in-the-loop required; autonomous sessions must not resolve items carrying this. label Oct 29, 2026
@kyle-sexton kyle-sexton removed the needs-human Human-in-the-loop required; autonomous sessions must not resolve items carrying this. label Oct 29, 2026
kyle-sexton
kyle-sexton previously approved these changes Oct 29, 2026

@kyle-sexton kyle-sexton 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.

Approved at e924f7d.

@kyle-sexton
kyle-sexton added this pull request to the merge queue Oct 29, 2026
@kyle-sexton
kyle-sexton removed this pull request from the merge queue due to a manual request Oct 29, 2026
@kyle-sexton kyle-sexton added the do-not-merge Hard merge gate: do not merge while applied. label Oct 29, 2026
…reference

The base merge at e924f7d resolved babysit_risk.py's conflict without
#10743's `${{ }}` alternative of _SECRET_REF, so `${{ secrets }}`,
`contains(secrets, 'A')` and `secrets || 'x'` inside an expression no
longer matched it. Restore the alternative and pin those shapes in a test
that fails without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton kyle-sexton removed the do-not-merge Hard merge gate: do not merge while applied. label Oct 29, 2026

@kyle-sexton kyle-sexton 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.

Approved at 82e7ac2.

@kyle-sexton
kyle-sexton added this pull request to the merge queue Oct 29, 2026
Merged via the queue into main with commit 1e54a3e Oct 29, 2026
83 of 86 checks passed
@kyle-sexton
kyle-sexton deleted the work-resolve-issue/issue-9241-40621393295-1 branch October 29, 2026 15:38
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.

1 participant