Skip to content

ci: enforce egress-block on the last three AI jobs, closing the audit group - #604

Merged
allxsmith merged 7 commits into
mainfrom
ci/578-egress-block-branch-verifiable-jobs
Aug 30, 2026
Merged

allxsmith merged 7 commits into
mainfrom
ci/578-egress-block-branch-verifiable-jobs

Conversation

@allxsmith

Copy link
Copy Markdown
Owner

Second half of #578. Flips review (claude-review.yml) and fix/verify (claude-pr-loop.yml)
to egress-policy: block with the rule 10 assertion, which empties rule 10's "audit, deliberately"
group.

Stacked on #602. Based on ci/578-egress-block-default-branch-jobs rather than main,
because both PRs edit the same rule 10 inventory lines and counts. Retarget to main once #602
merges. Review this diff on its own; #602's three jobs are not part of it.

verify is measured — and #593's premise turned out to be avoidable

#593 concluded that nothing could be done but wait for a natural blocking deep-review finding.
That is true of the PENDING_VERIFY path. It is not true of the gate, which is an OR:

elif [ "$PENDING_VERIFY" -gt 0 ] || [ "$REBUTTABLE" -gt 0 ]; then MODE=verify

REBUTTABLE (claude-pr-loop.yml:230-234) wants an unresolved claude-opened thread whose last
comment is from a non-claude author with ccount < 2. A maintainer replying to the inline
finding satisfies it directly — no fix round, no push, no iteration consumed.

Measurement run: 33284876176

decision: verify (fail=0 pend=0 actionable=0 verify=0 rebut=1 cr=success iter=0)
gate: success   verify: success   (fix / handoff / halt all skipped)

The session was representative, not a stub — which is the only thing that makes its endpoint
list worth anything. It checked out, ran pnpm install --frozen-lockfile, and the Claude session
swept every <Button>+<Icon> pair across docs/docs/** and skills/**, read Icon.tsx:195 to
confirm the ariaLabel default, replied on the thread and resolved it. Full protocol end to end.

The four-step recipe is written up in #593 for whoever needs to re-measure this job.

Endpoints

All three jobs produced the same six application hosts as the other three — api.anthropic.com,
api.github.com, claude.ai, downloads.claude.ai, github.com, registry.npmjs.org — so all
six of #578's jobs get the same eight-host list, the one already enforcing on ai-scan/scan,
ai-triage/triage and claude-repro/author.

verify matching fix is the expected result rather than an assumed one. #578 was right to refuse
to derive one job's list from another's; the derivation happened to hold, and now it is checked.

TURBO_TELEMETRY_DISABLED: '1' joins CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC on all three.
It matters most on this loop: its sessions run pnpm test/pnpm run on nearly every iteration, so
turbo is invoked constantly, and turbo samples its telemetry — an intermittent that would otherwise
redden the loop weeks after the flip.

The Actions cache blob host stays off, and verify supplied fresh evidence for why:
productionresultssa13 appeared, joining sa3/6/7/9/11 from the earlier runs. Six runs, six
different names.

fix gets one extra comment

Its measured pre-step-to-Initialized time was 9.97s, past the ~9s the pre-step gives up
after. So on this job the polling loop is not defensive programming — a single-shot check would
have failed it. That is recorded next to the assertion rather than left in an issue thread.

Docs

  • Rule 10's inventory: nineteen enforcing jobs, empty audit group. Counts re-derived with the
    greps the rule prescribes (19 key-form, 20 plain-form, 0 audit), not incremented.
  • The audit group is kept rather than deleted. Audit is still the right starting point for an
    existing live job whose egress has never been observed; what it is not is a resting place, which
    is the whole reason [Security] Flip the six audit-mode AI jobs to egress-policy: block #578 existed.
  • I1's egress clause now covers all five code-executing jobs, and restates the scope rule 10 sets
    for it — it bounds where data can go, and api.github.com is necessarily allow-listed, so it
    does not bound what a session does through a tool.

Verification

Local: prettier --check, check:conformance (19 green), both rule 1 pin greps, and a js-yaml
pass over every workflow asserting that no block job lacks an assertion and that all six of
#578's assertions are byte-identical to auto-close-duplicates.yml's apart from the credential
each names. (The seven pre-existing ai-scan/ai-triage variants differ by design — the fail()
wrapper rule 10 documents.)

Before merge, unlike #602's three, all three of these can be exercised on the branch:

  • fix / verify: gh workflow run claude-pr-loop.yml --ref ci/578-egress-block-branch-verifiable-jobs -f branch=<a claude/ PR>
  • review: label a PR whose head is this branch. Note the session itself will skip (the OIDC
    exchange requires the workflow to match the default branch when the PR modifies it), so that
    exercises the policy and the assertion, not the session's egress — acceptable, since the session
    egress is already measured.

Watch for: assertion green, "egress_policy":"block" in the config, Initialized with no
Reverted changes or timed out, and the session completing real work.

Closes #578
Closes #593

… group

Flips `review` (claude-review.yml) and `fix`/`verify` (claude-pr-loop.yml) from
`egress-policy: audit` to `block`, each with the rule 10 assertion. Stacked on
the branch that flips the other three; together they close #578 and empty rule
10's "audit, deliberately" group.

`verify` was the blocker, and #593 is why: it fires only on `PENDING_VERIFY` or
`REBUTTABLE`, both of which need a claude-opened review thread, and blocking
deep-review findings are rare enough that no run had happened since #577 gave
it a harden-runner step. Run 33284876176 is the measurement. It was reached
through `REBUTTABLE` rather than `PENDING_VERIFY` -- a maintainer replying to a
deep-review inline finding satisfies that counter directly, with no fix round,
no push and no iteration consumed. That route was available the whole time; the
issue had only considered the `Fixed in <sha>` path. #593 carries the recipe.

The session was representative rather than a stub, which is what makes the list
worth anything: it checked out, ran `pnpm install --frozen-lockfile`, swept
every Button+Icon pair across docs/ and skills/, read Icon.tsx to confirm a
prop default, replied on the thread and resolved it.

All three jobs came out as the same six application hosts as the other three,
so all six of #578's jobs get the same eight-host list -- the one already
enforcing on ai-scan/scan, ai-triage/triage and claude-repro/author. `verify`
matching `fix` was the expected result, not an assumed one; #578 was right to
refuse to derive one from the other, and the derivation happened to hold.

`TURBO_TELEMETRY_DISABLED: '1'` joins the existing
`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` on all three. It matters most on this
loop, whose sessions run `pnpm test`/`pnpm run` on nearly every iteration, so
turbo is invoked constantly and samples its telemetry.

`fix`'s assertion comment records why the poll is load-bearing on that job
specifically: its measured pre-step-to-`Initialized` time was 9.97s, past the
~9s the pre-step gives up after, so a single-shot check would have failed it.

Rule 10's inventory now lists nineteen enforcing jobs and an empty audit group,
with the counts re-derived rather than incremented (19 key-form, 20 plain-form,
0 audit). The group is kept rather than deleted, because audit is still the
right starting point for an existing job with unobserved egress -- what it is
not is a resting place. I1's egress clause now covers all five code-executing
jobs and restates the scope rule 10 sets for it.

Closes #578
Closes #593
@coderabbitai

coderabbitai Bot commented Aug 30, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • main

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2c11cd68-6c54-44e9-8d1d-0792f6a609f9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

Same correction as on the base branch, for the two jobs that live here, plus the
evidence that produced it.

Run 33286967625 exercised `claude-review` at `egress-policy: block` from this
branch (`deep-review` label on PR #604, so the head's workflow ran). The
assertion step passed, the config read `"egress_policy":"block"`, the agent
reported `Initialized` with no `Reverted changes`, and -- the part that matters
here -- `setup-node` restored the pnpm cache and `pnpm install
--frozen-lockfile` completed in 15.7s, with neither the Actions cache blob host
nor `results-receiver.actions.githubusercontent.com` allow-listed.

So the "a cache miss is accepted" line these comments carried was wrong.
harden-runner does not gate the runner's own control-plane traffic and the cache
works normally at block. `claude-pr-loop`'s `fix` and `verify` run the same
checkout/setup/install sequence, so that result covers them too.

The session itself skipped, as expected for a PR that modifies
`claude-review.yml` (the OIDC exchange requires the file to match the default
branch), so this verifies the policy, the assertion and the install path rather
than the session's egress. The session egress was already measured in audit.
@allxsmith

Copy link
Copy Markdown
Owner Author

Branch verification done for review, and it corrected the PR

Applied deep-review to this PR, so claude-review.yml ran from this branch's version at
egress-policy: block: run 33286967625.

Check Result
Assert egress policy is enforced success
effective config "egress_policy":"block"
agent status Initialized, no Reverted changes, no timed out
Checkout PR head → Setup pnpm → Setup Node.js → Install dependencies all success
pnpm install --frozen-lockfile completed, 15.7s

The Claude session skipped, as expected on a PR that modifies claude-review.yml (the OIDC
exchange requires the file to match the default branch). So this verifies the policy, the
assertion and the whole install path, not the session's egress — which was already measured in
audit on runs 33230210532 / 33230112308.

It falsified a claim these comments made

I had written that omitting the Actions cache hosts costs a cache miss that "degrades to a
registry.npmjs.org refetch". That was a prediction, and it was wrong. setup-node logged
Cache hit and Cache restored successfully at block, with neither
productionresultssa<N>.blob.core.windows.net nor results-receiver.actions.githubusercontent.com
allow-listed. harden-runner does not gate the runner's own control-plane traffic, so leaving those
hosts out costs nothing at all.

Corrected in ec968f3 here and on the base branch, and rule 10's measurement section now says
plainly that the prediction was wrong — the point of that section is to read a run rather than
reason about what a policy will cost.

The run also produced productionresultssa13, a sixth distinct blob host name across six runs,
which strengthens the "cannot be pinned" argument rather than changing it.

fix / verify on the branch

Not separately exercised. Doing so needs an open claude/… PR with ai-loop, and the fixture PR
(#603) is closed and its branch deleted. They run the identical
checkout → pnpm → setup-node → pnpm install --frozen-lockfile sequence that just passed at
block above, and their session egress is measured (33233097667 and 33284876176). The remaining
gap is their assertion step under a real dispatch, which the next ai-loop PR after merge will
exercise on its own.

Same gap Copilot flagged on the base branch, and the same reason: these jobs
took the eight-host Claude-session list, but none of the three jobs that list
came from runs `actions/setup-node`, so it never needed nodejs.org. All three
jobs here do, and setup-node v7 falls back to nodejs.org/dist when the toolcache
misses and the actions/node-versions lookup fails.

The six audit runs could not have caught this: the toolcache hit every time, so
the fallback never executed and left no endpoint in any log. A measurement only
covers the paths the run took, which is worth remembering the next time a list
is described as measured.

Comments updated to name all three toolcache-miss hosts and what each one
serves, rather than implying the two githubusercontent hosts covered the whole
path.
Same defect Copilot found on #602, in the three jobs that live here. Each
session step's telemetry comment still said "On this `audit` job the point is
the MEASUREMENT: audit mode exists to produce the endpoint list a block flip
will use" -- true when written, false as of this flip.

Rewritten for the enforced state in all three, matching the base branch: the
allowlist refuses the Datadog host and the setting stops the CLI attempting it,
so a denied call stays out of the run rather than filling the log with refused
traffic. Audit-phase rationale kept as history, since it is why the measured
list was clean.

Worth noting where this one came from. The loop cannot review these PRs (they
touch .github/**, so its gate halts on protected-path), so the review side is
Copilot and CodeRabbit plus an opt-in deep review. Copilot found two real
defects here that six measured runs could not have: an unexercised setup-node
fallback, and prose. Neither leaves an endpoint in a log.
Base automatically changed from ci/578-egress-block-default-branch-jobs to main August 30, 2026 03:05
@allxsmith allxsmith self-assigned this Aug 30, 2026
@allxsmith
allxsmith merged commit e5ed56b into main Aug 30, 2026
3 checks passed
@allxsmith
allxsmith deleted the ci/578-egress-block-branch-verifiable-jobs branch August 30, 2026 03:06
@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 5.11.7 🎉

The release is available on:

Your semantic-release bot 📦🚀

@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 4.2.4 🎉

The release is available on:

Your semantic-release bot 📦🚀

@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 2.2.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 1.2.3 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Projects

None yet

1 participant