Repository navigation
feat(app-control): add a threads capture kind with py-spy dump - #9542
Conversation
Drive gains a threads capture kind for a hung process, bound to the pid this run launched: the launch record keeps pid, start time and command line, all three are rechecked before any command touches the pid, and a wrapper resolves to its child by parent pid, never by a name lookup. A threads capture always takes the elevated route. analyze-profile reads a thread dump directly from a new format page with no SQLite step. Closes #9532 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…echeck The unattended route runs no pid recheck or process lookup, since nothing will touch the pid. The eval's no-recorder grader now matches a recorder or elevation command at command position, not the word in report text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the ready reviews: the launch record is data and only an integer pid enters a command; the recheck adds the owner uid and an exact raw start time; the shown script repeats the recheck before its one recorder command; wrapper children record command line and parent pid; reports holding a command line carry the secrets mark. The thread-dump page points at py-spy instead of restating its layout. Eval graders now match the JSON-serialized command, read the written report, forbid any unattended process lookup, and check for a second-dump recommendation. 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. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9b1115b7cc
ℹ️ 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".
|
claude-security-review reviewed Incremental security review of 1469acb against 1e1fea6: no security issues found. The change is two doc wording edits, in The PR description (data only) contained no embedded instructions. I did not check that the shown root script computes its hash over the same bytes, because it is outside this increment and the earlier review covered it. critical-findings: 0 |
|
claude-review reviewed Incremental review of 1e1fea6..1469acb: no findings. The change is documentation only. It makes the command-line hash input explicit in
|
The shown script now carries only the pid, uid, raw starttime integer and a sha256 of the command line, recomputes and compares them, and exits 3 on a difference before the recorder runs; it never embeds the command line text. The wrapper child's uid is recorded and must be the invoking user's, so a root-owned child is blocked. The drive fixture's launch record carries the uid, the starttime integer and the hash. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The script's exit 3 now also covers a failed or empty read and a missing hash tool, a recorder exit of 3 is remapped, and comparisons are positive matches. Every uid field must match. The hash input is named (the NUL-separated /proc/<pid>/cmdline bytes), the yes is asked beside the live command line, and the wrapper child's literals, target pid and fresh-read rule are stated. The pid must be positive again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The hash covers every byte of /proc/<pid>/cmdline as read, so the recorded hash and the script's recompute agree on the trailing NUL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closes #9532
Summary
/app-control:drivegains athreadscapture kind for a hung process, starting with Python throughpy-spy dump, bound to the pid this run launched./debugging:analyze-profilereads the resulting thread dump directly, with no SQLite step, from a new format page. The frontmatter of both skills is unchanged; the analyze-profile description keeps its wording (slice plan Q16, option a).Fix
SKILL.md.threadsjoins the kind list and the Actions row. A new## Pid bindingsection:report.jsonlaunch.blocked: launched process no longer matches.threadscapture always takes the existing elevated route: a shown script when interactive,blocked: capture needs elevationwhen unattended. Unattended, it stops there and runs no attempt, pid recheck or lookup. The upstream root rule is a pointer to the py-spy README section When do you need to run as sudo? (as of 2026-10-23; recheck on a py-spy major release).## Nextroutes threads artifacts to analyze-profile.control-skill.md.threadsis added to the hub's Capture kinds. In the hang row, Python names py-spy dump and every other runtime reads "not yet". A note says athreadscommand takes a<pid>placeholder and no elevation prefix.evidence.md. Adds thelaunchfield, and capture steps record the process start time.thread-dump.md. The page covers where the reading begins (threads in the same frame, lock waits, the main thread), when the search is done, and how one dump settles nothing on its own. It applies the existing data and redaction rules to thread names and the command line.--kind threads.evals.jsonand as a plugin eval case:plugins/app-control/evals/threads-capture-blocked-unattended/, the app-control plugin's first plugin eval case.plugins/debugging/evals/analyze-profile-reads-thread-dump/, which carries judge samples.Verification
Fixture. It is real py-spy output from a deadlocked toy script, a lock-order cycle shipped as
evals/fixtures/inventory/sync.py.txt. Recorded with py-spy 0.4.2 and CPython 3.14.4 on Linux (WSL2,ptrace_scope1), with no root, because the recorderexecs into the app's parent:sh -c '/usr/bin/python3 inventory/sync.py & sleep 2; exec py-spy dump --pid $!'Acceptance checks.
rg -n threadsfinds lines in both drive files.thread-dump.mdexists, and the SKILL.md table links it.check-evals-quality.shreports PASS with 0 warnings on bothevals.jsonfiles.grep -nE '#[0-9]{3,}'andgrep -nE '5031|SETWorks|Blazor'over the changed skill and eval files return nothing.origin/mainis empty.check-skill.sh: drive PASS (0 errors, 4 warnings: the existing body-growth and style warnings); analyze-profile PASS (0 errors, 3 warnings).check-changelog-fragments.sh --check,check-spoke-plugin-root.sh, markdownlint, shellcheck and shfmt on the scaffolds.Judge calibration.
names-the-cyclewith an opus judge agreed with its 3 labeled samples on 9 of 9 runs (100%).Plugin evals (
claude plugin eval, Claude Code 2.1.310, judge opus, 3 runs, one--caseper invocation with--scaffold, TMPDIR under the worktree's.work/). Each before/after pair ran concurrently against plugin copies outside any git repository; "before" isorigin/mainwith the new case added. Final round, after the review fixes:--ablation none)psor/procand wrote noblocked: capture needs elevationfor threads/procreads, which is the behavior the case forbids); after VALIDThe noise report says "n too small to call" (1 case each), so no gain is claimed beyond these numbers. The drive case runs with
--ablation nonebecause every without-arm run hit sandbox denials on/procreads, which makes a with/without pass INVALID. Judge calibration fornames-the-cycle(opus): 9 of 9 runs agree with its 3 labeled samples, re-run after its criteria changed.Ready reviews. Round 1 (code and security, fresh context) findings fixed in this PR: launch record as data with an integer-only pid; owner uid and exact raw start time in the recheck; the shown script repeats the recheck before its one recorder command; wrapper child fields; the secrets mark on reports that hold a command line; graders matched against the JSON-serialized command and the written report; no restated py-spy layout. Kept as follow-ups: pid binding for the other pid-targeting kinds (
cpu,heap) and for cleanup's stop; the JVM, .NET, Go and native hang-row pointers stay for those rows' slices; an analyze-profile description change goes through invocation probes (slice plan Q16).Round 2 and 3 reviews. Two safety defects were fixed in this PR, each followed by one code and security review of its own delta and a fix for what that review found in the two fixes:
/proc/<pid>/cmdlinebytes, never command-line text. It rechecks every uid field, the start time and the hash as positive matches, exits 3 on any difference or failed read, and remaps a recorder exit of 3. The yes is asked beside the live command line.Related
🤖 Generated with Claude Code