Repository navigation
feat(source-control): hold a PR that could add or raise a charge for an owner decision - #9224
Conversation
…an owner decision The unattended merge scorer adds a HIGH cost-review-required reason in both lanes, whatever high-path-floor says: Pulumi, Terraform and Bicep files; a workflow patch adding a cron line or a larger-runner label, or, off a public repository, a runs-on or macos line; and a pr-pipeline.yaml patch adding a model, effort or max-turns line. The gate clears it only when the body links an unedited **Decision recorded.** comment by an admin User on the closing issue or the pull request, and reads that comment only for a held PR. Closes #9221 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…leg timeouts and pipeline spend limits Adds the four detections Security & Cost asked for to cost-review-required: a workflow that gains a secrets reference to a model credential or a run-claude-cli or claude-code-action leg (net of removed lines, so a move or repin is not new); a timeout-minutes or leg-timeout-minutes raised on a workflow whose head text starts a model leg, which the gate now reads; and pr-pipeline.yaml lines on spend keys only (rounds, restarts, turn caps, per-run and WIP caps, admission, pause-from, leg timeouts, credential route, model, effort, max-turns). The README names azure-iac's C# program and its deploy gate as the backstop for Pulumi program edits. Refs: #9221 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # plugins/source-control/skills/babysit-prs/scripts/babysit_merge.py # plugins/source-control/skills/babysit-prs/scripts/babysit_risk.py
poteto's review of the cost hold found ways around it; each now holds, with a test that failed before the change: - a file renamed or copied into a workflow or pr-pipeline.yaml path; - a cron key in flow style or JSON spelling; - a runner that is not a free literal: the workflow's head text is compared with its base text, so an expression, a group: mapping, a labels: entry and a larger runner hold on a public repository, and every new runner off one; the check reads runs-on values only, and Windows or macOS matrix lines hold off a public repository; - --model and --max-turns flags in a workflow; - any file under a directory holding a Pulumi.yaml at the base. The hold stays independent of an adopted author's floor exemption. Tests cover a null decision node, a node without lastEditedAt, and a record with no repository visibility. Refs: #9221 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A cost decision's first line must start `**Decision recorded.** Cost:`, the gate's self logins are excluded with the agent identity, and a link inside an HTML comment in the body cites nothing. - pr-pipeline.yaml holds on any change except blank, comment-only, description: and title: lines, or an edit to trailing comments only. - A workflow holds on any net-new secrets reference (dotted, bracket, toJSON(secrets), secrets: inherit), any claude-code* action or image and run-claude-cli in any case, and a run line invoking @anthropic-ai/claude-code. - plugins/multi-agent/reference/defaults.yaml is a cost path, and an unlisted base tree (pulumi_dirs None) holds every file. Refs: #9221 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A cost decision comment clears the hold only when its created_at is after the pull request's created_at, which the author cannot forge the way commit dates can. A missing time on either side clears nothing. A push after the decision stays an accepted residual. Refs: #9221 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ssue A decision comment on the closing issue counts only when created after the pull request; one on the pull request itself postdates it already and counts as it is. Refs: #9221 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
claude-security-review reviewed Security review: incremental, head 2795674No security findings. I reviewed only the five files changed since 3b0a63a, and read the surrounding code in
Manifest and instruction-surface lens: no |
|
claude-review reviewed Incremental review of 2795674 (since 3b0a63a)No findings. I read the incremental diff and the surrounding code. I did not run the tests: the sandbox refused What I checked
Minor, not counted as a finding The module docstring at |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3b0a63a660
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…re pipeline edits in place Review findings on #9224 at 3b0a63a: - DECISION_LINE took `\s+` between the decision marker and `Cost:`, so a `Cost:` on the next line matched. It now takes spaces or tabs only. - A pr-pipeline.yaml value swap between two profiles cancelled out in the file-wide multiset compare. Each run of removed lines and the added lines after it now passes only when they match in order once trailing comments are stripped. Refs: #9221 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closes #9221
Summary
The owner's rule (2026-10-22): anything that would increase cost or spending, or affect billing,
stops for the owner unless a human-recorded decision already covers it. The unattended merge
scorer now holds such a pull request as HIGH
cost-review-requiredin both lanes, whatevermerge.diff-check.high-path-floorsays, and clears it only on a cited owner decision.Fix
babysit_risk.py_cost_reasonsaddscost-review-required. It is HIGH but not ahigh-code, so
_floor_heldstays false, the reasons comment says the PR waits for a person, andneeds-humanlands aftermerge.risk-escalation-minutes. It runs outside the floor gates, thelane switches and
floor_exempt, because adoption is not an owner decision. It catches:Pulumi.yaml,Pulumi.*.yaml,*.tf,*.tfvars,*.bicep,*.bicepparam),plugins/multi-agent/reference/defaults.yaml, and any file under a directory that holds aPulumi.yamlat the base (RiskInput.pulumi_dirs, read bypulumi_dirs_at). A base treethat can't be listed whole (
None) holds every file;pr-pipeline.yamlpath from outside one;cronkey in any spelling, or a--modelor--max-turnsflag;secretsreference (secrets.X,secrets['X'],toJSON(secrets),secrets: inherit), a net-newrun-claude-cliorclaude-code*action or image(case-insensitive), or a run line invoking
@anthropic-ai/claude-code;runs-on::on a public repository only when it is not a free literal (standard labels, managed
self-hosted labels, a
self-hostedlist), and on any other repository every one. Off apublic repository, Windows and macOS lines also hold;
pr-pipeline.yamlchange unless every added and removed line is on the safeallowlist: blank lines, comment-only lines, and
description:ortitle:values. Achange that only edits trailing comments also passes. Everything else holds, including
flow style, quoted keys and new list items;
babysit_merge_policy.cost_decision: the first body link to a comment on the closing issue orthe PR itself, in this repository, whose first line starts
**Decision recorded.** Cost:, byan admin User who is not the agent identity or a gate self login, not posted through an App,
never edited. A comment on the closing issue must also be created after the PR's
created_at, which the author cannot forge the way they can commit dates. A comment on thePR itself counts as it is. A link inside an HTML comment (closed or not) counts for nothing. Any failed
read clears nothing.
with_capability_contentsalso reads workflow text.pulumi_dirs_atlistsPulumi.yamldirectories from the base tree.babysit_merge.score_unattendedscores once, then reads decisions only when the cost reasonfires. It reads workflow text whenever a workflow changed.
reference/safety.md, the scorer docstringand two changelog fragments.
Limits, stated in the README:
cost-review-required. In this repository every workflow file anddocs/conventions/pr-pipeline.yamlis also on the HIGH path floor, so such a PR still waitsfor a person to merge it.
merge.risk-escalation-minutespasses, or a person clears the label.azure-iac's Pulumi program is C#, and its deploy gate (azure-iac#75) stops any Azure change for
the owner at deploy time.
select-trusted-text.mjshas the same limit.lifetime, not to a head.
Verification
uv run --with pytest python -m pytest -q testsinplugins/source-control/skills/babysit-prs/scripts: 2601 passed, 2077 subtests passed in 67.87stests/test_babysit_risk_cost.py(each class holds with the floor off, a citeddecision clears it, standard runners on a public repository and removed lines do not hold),
tests/test_babysit_merge_cost.py(each decision condition), andCostHoldintests/test_babysit_merge_unattended.py(a cost hit refusesrisk:HIGH, a decision clears it,a non-cost pull request never reads a decision, a raised timeout holds only on a workflow
whose read text starts a model leg). Each S&C detection and each finding from poteto's review landed with a test
that failed before the code (22, then 15, then 43 failures, then green).
test_babysit_risk.py::test_rename_into_a_high_path_is_highnow also expectscost-review-required, which is the new rename rule firing.tests/test_babysit_verify_evidence.py:test_a_lane_pr_touching_the_merge_gate_merges_on_a_passgave the workflow file no patch, which now holds as unreadable. It now carries a patch with no
cost line, so it still proves a non-cost workflow change merges with the floor off.
scripts/run-ruff.sh checkon the scripts directory and markdownlint on the changed docs: clean.Related
NEVER_FLOOR_EXEMPTconstant does not exist on
main, so the reason sits in the scorer besidemanifest-review-required.plugins/source-control/skills/babysit-prs/**, which is on the HIGHfloor, so it is itself held for a human merge.
🤖 Generated with Claude Code