Skip to content

Add an experimental Claude PR review CI check - #26272

Open
jkmassel wants to merge 42 commits into
trunkfrom
jkmassel/claude-blockers-pr-prototype
Open

jkmassel wants to merge 42 commits into
trunkfrom
jkmassel/claude-blockers-pr-prototype

Conversation

@jkmassel

@jkmassel jkmassel commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Adds an experimental Claude PR Review CI check. On every internal PR, Claude reviews the change on a Mac agent: it builds the app, runs tests, creates WordPress test sites and taps through the change in a simulator. The check fails only when Claude proves a bug caused by the PR, with a test or a screenshot. Everything Claude does on the machine is logged to one archive, audit-log.tar.gz.

How it works

  • Runs on every PR that isn't from a fork. It doesn't wait for other steps.

  • Claude gets this prompt, plus CI notes in .buildkite/claude/pr-review-ci-context.md:

    Please adversarially review and /code-review the PR. Run it in simulators as needed to validate the fix. If you find potential bugs caused by the fix, prove them with a test or a screenshot. If this PR has already been reviewed, evaluate responses to your comments and determine if the issue is resolved.

  • Exit status: 0 = no proven bugs, 1 = a proven bug caused by the PR, 2 = anything else went wrong and the review didn't complete (retried once).

  • Results go in three places:

    • a comment-only review with an inline comment per finding
    • one summary comment, updated on every run
    • a Buildkite annotation
  • Screenshots are published to the jkmassel-screenshots Spacefast Space so GitHub can show them. Test output is a Buildkite artifact, and the transcript is in audit-log.tar.gz.

  • On later runs, Claude reads replies to its own threads, checks the current code, replies, and resolves the thread when the issue is fixed.

  • claude-opus-5-5 at --effort medium.

What Claude can use

  • Simulators: simctl, and AXe for tapping and reading the accessibility tree. Xcode MCP needs a desktop session, which the reviewer user doesn't have.
  • Test sites: .buildkite/claude/wp-test-site.sh creates a fresh self-hosted WordPress on http://localhost:<port> in about 30 s, with an application password. It's based on wordpress-rs's native test server (Add a test server that runs without Docker, for macOS CI Automattic/wordpress-rs#1665). There is no WordPress.com account.
  • Builds and tests: plain xcodebuild. Swift packages are restored from the build jobs' cache before Claude starts, which adds about 1 minute to every run. A cold Jetpack build then takes about 4 minutes, and one unit test run about 3 more.

Limits on what Claude can do

  • It can't approve or request changes. Claude has no GitHub token. The script fetches the PR data for it and posts its result, always as a COMMENT review. It only replies to and resolves threads that wpmobilebot started with the review marker.

  • It runs as a separate macOS user. The script creates claude-reviewer: not an admin, with its own group. It gets a copy of the checkout and only the Anthropic key. Run as the build user, Claude could have read the job's secrets:

    • files in the build user's home
    • the Buildkite job token, through ps -E
    • the AWS keys and bot SSH key that the host mounts into every VM
  • The shared credentials mount is unmounted before the reviewer starts, because every user on the VM can read it (AINFRA-3517).

  • Checked before Claude runs. As claude-reviewer, the script checks that it can't read:

    • the build user's .aws, .ssh, .config or keychains
    • the shared mount
    • any build-user process environment

    It also checks that it can't signal those processes or use sudo. If any check fails, Claude doesn't run.

  • Evidence comes back without root. The reviewer archives its evidence, and the build user unpacks it into a fresh folder and keeps only plain files. The artifact upload doesn't follow symlinks.

  • No app secrets. a8c-secrets isn't installed, so builds use the example credentials.

  • The reviewer stops before secrets are used. Its processes can outlive its session and it can read other users' command lines, so the script takes down its launchd domains and kills everything it left running before any step that uses a secret. If anything survives, nothing is posted. The Anthropic key and Spacefast token are never passed as command arguments.

Audit log

Everything claude-reviewer does is logged from places it can't change. audit-log.tar.gz holds the results and the raw logs.

Log How it's recorded Why the reviewer can't change it
Claude's transcript Claude appends to a file marked sappnd The file is root-owned and append-only, in a root-owned folder
Every tool call Managed-settings hooks send each call to a listener The hooks can't be turned off, and the build user runs the listener. A tool call that can't be logged is blocked
Process launches, file opens, changes and deletes eslogger, as root It runs as root, and the logs are in folders only the build user can read
Outgoing connections and DNS A pf rule for the reviewer's user ID, and tcpdump Both run as root

What's in the archive:

  • claude-audit/: audit-checks.txt, processes.txt, files-read.txt, files-written.txt.gz, network-summary.txt, privileged.txt, the transcript, the hook log, and the inputs (prompt, schema, PR data, versions and script checksums).
  • claude-audit-live/: the raw event, network, hook and transcript logs, in parts.

Keeping it usable:

  • eslogger environments are removed, and this job's secrets are redacted from every event. Every file is then checked for those secrets before upload.
  • Read-only opens of system files, Xcode, Homebrew and the reviewer's own folders are counted in es-dropped.json instead of listed. Reads anywhere else are listed. This took files-read.txt from 120 MB to 30 KB.
  • The parts are also uploaded every 2 minutes as separate artifacts, because a cancelled or timed-out job loses anything still on the VM.
  • Each run checks itself: a known command run as the reviewer must appear in the process, file and network logs, and the tool call counts in the hook log and transcript must match. The results are in audit-checks.txt.

Claude Code and the Spacefast CLI are pinned to checksummed binaries, and installer output is kept.

Known gaps

  • Cancelled or timed-out jobs don't produce the archive, only the parts uploaded so far. They may also leave the wpmobilebot review request pending, which then has to be removed by hand.
  • The audit logs are best-effort. If one fails to start, the review still runs, and the failure is only noted in audit-checks.txt.
  • Claude's network access isn't restricted. Connections are logged, not blocked.

Found along the way

  • eslogger exec events include each process's whole environment, and eslogger ignores SIGINT.
  • Xcode's compilation cache hit 100% of compile tasks at the same path (91 s), but only 32% from a different path (280 s, against 397 s with no cache), and prefix mapping didn't help. Reusing it needs the reviewer at the build jobs' path, so it isn't used here.
  • Mac VMs only get the job's BUILDKITE_* variables (hostmgr copies only those), so a step's env: settings never arrive. The review's options are command arguments for that reason.
  • AINFRA-3517: every user in a Mac VM can read the mounted mac-ci-vm AWS keys and wpmobilebot SSH key.
  • The S3 secrets hook couldn't parse 48 of the 101 env files, because upload-secrets-to-s3.rb wrote multi-line secrets as heredocs. While a file doesn't parse, its values aren't redacted from build logs. The script now writes them single-quoted (in mobile-secrets), and wordpress-ios/env parses again. 4 Tumblr files still don't, because they contain shell code.

Test plan

  • The script, tested locally with stubbed claude, gh, buildkite-agent, sudo and curl: pass, fail, inline-comment fallback, no result, unexpected failures (exit 2), sudo that won't go away, fake review markers, the environment allowlist, and hostile evidence archives
  • Isolation checks pass as claude-reviewer on a real VM (builds 35450 onwards)
  • --validate-setup passes end to end on a real VM (builds 35517 and 35521):
    • builds Jetpack
    • creates a test site
    • signs in to it by tapping with AXe, confirmed by the site's access log
    • runs a unit test
  • Known bug: Handle WordPress.com accounts whose token is missing or rejected #26132's changes (test PR [Test, do not merge] Claude review of #26132 #26293, build 35534) → fail. Claude proved the Reader re-sign-in regression (Reader menu and Reader links stop working on iPhone after signing in to WordPress.com again #26228) with a test that fails with the change and passes without it. It also found two compile errors against current trunk.
  • Clean control: Sort Posts All tab by post date in the SwiftUI list #26061 (test PR [Test, do not merge] Claude review control: #26061 #26294, build 35544) → pass. Claude built the app, ran CustomPostTabTests, made a test site with XML-RPC off, and checked the new sort order in the simulator, with a screenshot on Spacefast.
  • Fork PR (test PR [Test, do not merge] Claude review fork check #26295): no Buildkite build, so no review. A fork build started by hand in the Buildkite UI is skipped by the script's own check; that path is tested locally only.
  • Earlier review threads: later runs replied and resolved fixed threads (builds 35408 to 35498)
  • Spacefast publishing: screenshots published and every URL checked (builds 35521 and 35544), and again with the token in the environment (build 35648)
  • Audit logs: the known command appears in every log, and the tool call counts match (build 35668: 12 and 12)
  • Secrets: nothing removed by the secret check, and the Anthropic key isn't in Claude's launch arguments (builds 35645 and 35650)
  • Parts uploaded during the run (builds 35644 onwards), and audit-log.tar.gz uploaded (build 35650)
  • Reviewer processes stopped before posting (builds 35648 onwards)
  • --validate-setup with the packages resolved up front: Jetpack build 240 s, signed in by tapping with AXe, unit test 168 s (build 35668)
  • The review request is cleared after a real early exit. Not done: tested only with a stubbed gh, because it needs a job to fail on CI. The normal path passed in build 35662.
  • A real cancelled job keeps its uploaded parts. Not done: no job has been cancelled since the uploads were added.

@jkmassel jkmassel added [Type] Tooling Tooling Build, Release, and Validation Tools labels Oct 28, 2026
@jkmassel jkmassel self-assigned this Oct 28, 2026
@jkmassel jkmassel added this to the 27.6 milestone Oct 28, 2026
@dangermattic

dangermattic commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator
1 Warning
⚠️ This PR is larger than 500 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.

Generated by 🚫 Danger

@wpmobilebot wpmobilebot 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 CI review – superseded by the run on c3fd877b07. The current verdict is in the Claude CI review comment.

Comment thread .buildkite/commands/claude-pr-review.sh Resolved

@wpmobilebot wpmobilebot 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 CI review – superseded by the run on c3fd877b07. The current verdict is in the Claude CI review comment.

Comment thread .buildkite/commands/claude-pr-review.sh Resolved
@wpmobilebot

wpmobilebot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

🤖 Claude CI review: ✅ pass

Commit 3bc1f2032b. Test output and the transcript are on the Buildkite build.

No proven bugs

Commit 3bc1f2032b. Since my last run (a6cbd62599), the PR has one new commit, 3bc1f2032b. It makes .buildkite/commands/claude-pr-review.sh call shared-set-up.sh --artifacts DerivedData/SourcePackages, so the Swift package cache from the build jobs is restored before the resolve step. The resolve step stays as a fallback.

What I checked:

  • The call matches build-for-testing.sh. That script saves the cache with the same --package-resolved Modules/Package.resolved --artifacts DerivedData/SourcePackages arguments. shared-set-up.sh passes the arguments on to restore_swiftpm_cache.
  • The updated comment now describes what the step does.
  • On this run the reviewer's checkout has DerivedData/SourcePackages, about 9.2 GB with artifacts, checkouts and repositories. So the setup worked.

What I did not check: this PR changes only CI scripts, not the app. I did not build the app, run unit tests or use the simulator. I also did not time a run against the previous setup.

Other people's threads: AliSoftware's thread about Mac queue load is still open. It's a policy question for the maintainers. This commit partly addresses it by reusing the package cache.

Earlier findings

  • ✅ Resolved: Resolved. 3bc1f2032b now calls .buildkite/commands/shared-set-up.sh --artifacts DerivedData/SourcePackages, the same call as build-for-testing.sh, which saves this cache. The resolve step stays as a fallback for anything the cache is missing. The comment now matches what the step does. On this run, DerivedData/SourcePackages (about 9.2 GB) was already in place when I started.

@wpmobilebot

wpmobilebot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

🤖 Build Failure Analysis

This build has failures. Claude has analyzed them - check the build annotations for details.

@wpmobilebot wpmobilebot 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 CI review – superseded by the run on c3fd877b07. The current verdict is in the Claude CI review comment.

Comment thread .buildkite/commands/claude-pr-review.sh Resolved

@wpmobilebot wpmobilebot 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 CI review – superseded by the run on c3fd877b07. The current verdict is in the Claude CI review comment.

Comment thread .buildkite/commands/claude-pr-review.sh Resolved

@wpmobilebot wpmobilebot 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 CI review – superseded by the run on c3fd877b07. The current verdict is in the Claude CI review comment.

@wpmobilebot wpmobilebot 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 CI review – superseded by the run on c3fd877b07. The current verdict is in the Claude CI review comment.

Comment thread .buildkite/claude/wp-test-site.sh Resolved

@wpmobilebot wpmobilebot 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 CI review – superseded by the run on c3fd877b07. The current verdict is in the Claude CI review comment.

Comment thread Scripts/ci/check-secrets-env/main.go Resolved
Claude reviews each internal PR on a Mac CI agent with full shell access, so it
can build, run tests and use simulators. The step fails only when Claude proves
a bug caused by the PR.

Claude runs with an allowlisted environment (the Anthropic key and the test
site sign-in) and has no GitHub or Buildkite agent token. The script fetches
the PR data for it and posts its result as a comment-only review.
Headless Xcode would ask before Claude can use it, and nobody can answer on CI,
so the script enables it for every agent on the throwaway VM. Claude falls back
to xcodebuild and simctl when that fails.
Claude builds the app itself when it needs to. Also pins the effort level to
medium, the current default, so an update can't change it silently.
- Any unexpected failure now exits 2 (retried once), so exit 1 only ever
  means a fail verdict. Annotating the build is best-effort.
- A review thread only counts as Claude's if wpmobilebot started it, not just
  if it carries the hidden marker, which anyone can type.
- sudo is used to set up headless Xcode, then denied for the build user, so
  Claude doesn't get root. Claude doesn't run if sudo still works.
One comment per PR, updated in place on every run: the verdict, the summary,
each new finding in full, and the status of earlier findings. Screenshots
become links to the Buildkite build, since GitHub can't show its artifacts.
When a Spacefast key and Space are configured, the script publishes the
review's screenshots additively under wordpress-ios/pr-<n>/build-<n>/, checks
each URL loads, and embeds them in the PR comments. Otherwise, or if anything
fails, it links to the Buildkite build as before. Claude never sees the key.
As the build user, Claude could read the job's secrets: the AWS keys and bot
SSH key mounted into every Mac VM, files in the build user's home, and the
Buildkite job token in the environment of the build user's processes.

The script now creates a non-admin claude-reviewer user with its own group and
a copy of the checkout, and runs Claude as that user over SSH to the VM (or
with launchctl asuser). Before Claude starts, the reviewer checks that it
can't read those files or process environments, signal the build user's
processes, or use sudo. If any check fails, Claude doesn't run. It also
reports whether simulators and Xcode MCP work for the second user.
The host mounts its AWS keys and the bot's SSH key into every Mac VM, and the
mount is readable by every user, so the separate reviewer user could read
them. Nothing in this step needs the mount after setup, so unmount it before
the reviewer starts. The reviewer's checks still stop Claude if it's there.
The reviewer controls its evidence folder, so a symlink there made root's
ditto, and then the artifact upload, copy files the reviewer can't read. The
reviewer now archives what it can read, and the build user unpacks it into a
fresh folder and deletes anything that isn't a plain file or folder. The step
also tells the artifact upload not to follow symlinks.

Also enables headless Xcode MCP for the reviewer user: the setting is per
user, and mcp-server takes the user from the SUDO_* variables.
mcp-server takes the user from the SUDO_* variables but the settings folder
from $HOME, so it tried to write into the build user's home and failed.
Setting HOME and the SUDO_* variables wasn't enough: mcp-server still wrote
into the build user's settings. The reviewer now runs the two mcp-server
commands itself, through a sudo rule that allows only those commands and is
deleted again before Claude starts.
mcp-server enable stores an integrity hash in the user's keychain. The new
reviewer user had none, so it failed with -25308 (user interaction is not
allowed).
The permission check passed, but the bridge couldn't launch Xcode Service
from the reviewer's SSH-only session, so Claude was told to use tools that
never loaded. The reviewer script now sends an MCP initialize request and
only offers the tools if it gets a reply.
- Drops Xcode MCP: it needs a desktop session, which the separate reviewer
  user doesn't have. Claude taps around with AXe instead (pinned, checksum
  verified, installed into the reviewer's home), not WebDriverAgent.
- Adds .buildkite/claude/wp-test-site.sh, based on wordpress-rs's native test
  server (Automattic/wordpress-rs#1665): Homebrew PHP 8.4, MariaDB 11.4 and
  Apache, one fresh WordPress 7.1.4 site per call on http://localhost:<port>,
  with an admin user and an application password. The build user installs
  the Homebrew packages; the reviewer creates and runs the sites.
- The reviewer checks AXe and creates one site before Claude starts, and
  reports both in isolation-checks.txt.
- The reviewer no longer gets the shared test site's credentials.
- wp-test-site.sh: a start that fails partway now removes the site's folder,
  database and database users, so the name can be used again.
- The reviewer's AXe check keeps AXe's errors in axe-check.log, retries while
  the simulator finishes starting, and no longer stops the review if the
  download fails.
- The CI notes give Claude its time budget and the Jetpack build time, so it
  waits for a build before finishing.
- The build failure analysis ignores the review step, which explains its own
  failures.
@jkmassel
jkmassel requested a review from a team as a code owner October 29, 2026 14:52
## Build and test

- The XCFrameworks are in place, but Swift packages may resolve again on your first build. App secrets aren't installed: builds use the example credentials, as for external contributors.
- You have up to about 90 minutes in total. A cold Jetpack build takes about 5 minutes on this VM, and a unit test run about 2 more. When the PR changes how the app behaves, build it and check the change in the simulator. Run builds and tests in the foreground: a single command may take up to an hour. Return your result only after every check you started has finished.

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.

Given the current state of our Mac queue recently and how it's been struggling to keep up with the demand especially due to more and more repos running very long jobs (and @mokagio recently trying to find workarounds to reduce the demand until we get more capacity)… I fear that doing an experiment that adds a 1h30 job to the queue will have quite a heavy impact our overall Mac CI fleet (across all repos) at this moment… 😓

Is there a particular reason to ask the agent to build the code itself, instead of reusing the Build Product artifacts of other jobs in the pipeline which also already build the app too (and also do it with cache mechanisms in place, like for SwiftPM resolution etc, while Claude won't use those speedup benefits when compiling again on its end)? Can't we instead share the build artifacts to reduce the time used by this Claude job drastically so it only focuses on testing the already-built app?

Anything that could help avoid clogging the queue for everyone else given the current situation, at least until we manage to add more agents and capacity to our Mac fleet 🙏

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Is there a particular reason to ask the agent to build the code itself

The agent will almost certainly have to rebuild the code, even if it receives a built binary because a lot of the work is fault injection, or writing tests to reproduce an issue, or injecting fake data/timeouts/???.

The point of this isn't to test the built binary, it's to prove issues in the code. For that it needs to be able to compile it – otherwise it's just assuming based on the code, which isn't very useful.

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.

Right. That still means it will rebuild it from scratch (including SwiftPM dependencies and project framework) instead of relying on local cache of build products (which could help making it an incremental build)

Btw, as a side note, @twstokes noted that we also still need to tighten our security posture inside VMs on some edge cases and recent changes before being ready to let agents roam free in them (AINFRA-3517).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I opened that issue :)

It explains that this PR has a workaround that avoids agents being able to read secrets by unmounting the secrets volume prior to running. In general though, it'd be nice if we didn't need the workaround and this stuff was scoped down to a single user without each project needing to handle it.

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.

I opened that issue :)

Ah, nice, thanks for that 👍

It explains that this PR has a workaround that avoids agents being able to read secrets by unmounting the secrets volume prior to running. In general though, it'd be nice if we didn't need the workaround and this stuff was scoped down to a single user without each project needing to handle it.

Totally!

Each run posted a review that said "verdict: fail" or "pass", and those
piled up, so a fixed PR still looked failing. A review can't be deleted once
submitted, so new reviews no longer state a verdict, and each run rewrites the
first line of the earlier runs' reviews to say they were superseded. Findings
and their threads are kept. The summary comment, updated on every run, is the
one place with the current verdict.
A GitHub Action on review_requested retries the Claude PR Review step on the
PR's latest Buildkite build, using the same reusable workflow that retries
Danger when labels change. The step allows retrying after a pass, and the
review script removes the request when it finishes, so it can be requested
again.

@wpmobilebot wpmobilebot 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 CI review – superseded by the run on af0642533d. The current verdict is in the Claude CI review comment.

Comment thread .buildkite/commands/claude-pr-review.sh Resolved
@jkmassel
jkmassel requested a review from wpmobilebot October 29, 2026 17:21
@wpmobilebot
wpmobilebot removed their request for review October 29, 2026 17:30
The reviewer user's activity is logged from places it can't change:

- Its transcript streams back to the build user instead of a file it owns.
- Managed-settings hooks send every tool call to a listener run by the
  build user. A tool call that can't be logged is blocked.
- eslogger (as root) records the reviewer's process launches, file opens,
  changes and deletes.
- A pf rule logs the reviewer's outgoing connections, with DNS traffic to
  name the addresses.

A known command run as the reviewer checks that each log records it. The
prompt, PR data and versions are uploaded, installer output is kept, and
Claude Code and the Spacefast CLI are pinned to checksummed binaries.
The test site's database and web server inherited the file descriptor
the reviewer script writes Claude's transcript to. They kept the SSH
session open after Claude finished, so the job hung until it timed out.
Only Claude's process now gets it.
The transcript only reached the log at the end, so a stuck or slow run
looked the same from outside. Each tool call and the final result are
now printed while the transcript is saved.
Streaming it back over SSH left the session open after Claude finished
(build 35633), so the job hung until it timed out. Claude now appends to
a root-owned file with the system append-only flag, which the reviewer
can add to but not change or delete. The reviewer's checkout no longer
includes the audit folder.
Build 35640 hung while collecting the audit logs. Each logger now gets a
SIGINT and, after 10 seconds, a SIGKILL, and the wait for the Endpoint
Security log is capped. The log shows how long stopping and summarizing
took and how big each file is.

@wpmobilebot wpmobilebot 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 CI review – superseded by the run on 3663687c08. The current verdict is in the Claude CI review comment.

Comment thread .buildkite/claude/audit.sh Resolved
eslogger's exec events include each process's whole environment, so the
Endpoint Security log carried the reviewer's ANTHROPIC_API_KEY and was
uploaded as an artifact (found by the Claude review in build 35643). The
environment is now removed before the log is written, and any audit or
evidence file that still contains one of the job's secrets is deleted
before upload. The audit folder is no longer readable by the reviewer.
Unchanged files' close events are dropped, halving the log.
A cancelled or timed-out job lost every audit log, because the VM shuts
down before artifacts are uploaded. The logs are now uploaded in parts
every two minutes to build/claude-audit-live/, each checked for the
job's secrets first.

The Endpoint Security log was 266 MB after six minutes, mostly reads of
system files, Xcode and the reviewer's own folders. Those are now
counted instead of logged; reads anywhere else are still listed.

eslogger ignores SIGINT, so it now gets SIGTERM next, and the signal
that stopped each logger is recorded.
Lists of paths compress well, and files-read.txt alone was 120 MB.
Summaries over 1 MB are gzipped after the checks; the verdict is read
from the original transcript.

@wpmobilebot wpmobilebot 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 CI review – superseded by the run on 56389e156f. The current verdict is in the Claude CI review comment.

Comment thread .buildkite/claude/audit-es-filter.py Resolved
… log

Claude was started with env -i ... ANTHROPIC_API_KEY=... claude, so the
key was in a command's arguments, visible to ps and recorded by the
Endpoint Security log (found by the Claude review in build 35644).
Claude now gets an exported, filtered environment instead.

The secret check before upload missed matches in large compressed parts:
grep -q exits at the first match, gzip gets SIGPIPE, and under pipefail
the pipeline failed, which read as no secret. The ES filter also now
redacts the job's secrets anywhere in an event.

@wpmobilebot wpmobilebot 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 CI review – superseded by the run on afaa27ce2a. The current verdict is in the Claude CI review comment.

Comment thread .buildkite/commands/claude-pr-review.sh Resolved
The test sites, the simulator and anything Claude started outlive the
reviewer's session, and the reviewer can read other users' command lines.
Spacefast's token was then passed as an argument (raised by the Claude
review in build 35645). The reviewer's launchd domains are now taken
down and its processes killed before posting, and the run stops if any
survive. sf reads the token from SPACEFAST_TOKEN instead of --token.
build/audit-log.tar.gz holds every audit result and the log parts
uploaded during the run, so an audit needs one download. It's built
last, from the EXIT trap, after the posting steps add their logs, and
checked for the job's secrets first. The parts are still uploaded
during the run, for jobs that are cancelled or time out.
The request was only removed after posting, so a run that exited early
left wpmobilebot as a pending reviewer, and asking again couldn't start a
new run. It's now cleared from the EXIT trap, and a failure to clear it
is logged.
Its first Xcode build otherwise resolves them again, including 6.5 GB of
binaries, which took 187 seconds locally. They're now resolved into
DerivedData/SourcePackages before the checkout is copied. A copied folder
needed no fetching in a build from a different path. The prompt's test
command now uses the same DerivedData.

@wpmobilebot wpmobilebot 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 CI review – superseded by the run on a6cbd62599. The current verdict is in the Claude CI review comment.

Comment thread .buildkite/pipeline.yml Resolved

@wpmobilebot wpmobilebot 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 CI review – superseded by the run on 3bc1f2032b. The current verdict is in the Claude CI review comment.

Comment thread .buildkite/commands/claude-pr-review.sh Resolved
trunk now caches the packages' binaries, so restore them instead of
resolving everything from the network (suggested by the Claude review in
build 35673). Resolving stays, to fill in anything the cache is missing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Tooling Build, Release, and Validation Tools [Type] Tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants