daily-fix: single-flight pgrep probes self-match and are not
kind: infra#wf-fix#wf-fix-fp:836ad04b03d3#daily-auto-filed
track:Highlight text on any card (body, plan, or an event) to anchor a comment, or leave a whole-task comment here. Mention @claude to summon a reply.
No comments yet.
Overview / Motivation
Auto-filed by the /daily 2026-07-26 problem sweep (route 2). Surfaced by 4 independent miner group(s) over the 2026-07-26 session transcripts.
Goal
Make every Step 9c / Step 10d single-flight pgrep probe self-exclusion-safe and
exact-issue-scoped, so a probe can neither match its own harness wrapper nor a sibling
session's gate.
Workflow gap
- Bug observed: Single-flight probes fired three times on 2026-07-26 against the wrong process — twice self-matching the probe's own wrapper argv (one of which printed
GATE STILL RUNNING; skip compareand exited 0, silently skipping the Step 9c compare leg behind a green background-Bash exit code) and once matching a sibling session's Step 10d lint gate. - Why it is a workflow gap: The committed
[.]-bracket idiom protects only against the pattern TEXT appearing in the prober's own argv; it does not survive a command line that also passes the artifact PATH as a real argument, and two committed probe recipes carry an issue-agnosticscripts/workflow_lint[.]pyalternate that matches every session's gate by construction. - Confidence (emitter): high
- verified-at-filing:
grep -n 'pgrep' .claude/skills/issue/SKILL.md→ 18 hits; the un-issue-scoped alternatescripts/workflow_lint[.]pyis present at L11716 (Step 10d gate-and-land probe) and L12023 (missing-sentinel recovery probe), both carrying a WAIT-never-kill mitigation but no issue scoping.grep -rn 'grep -v -e "bash -c"\|PPID' .claude/rules/ .claude/skills/issue/SKILL.md→ 0 hits (the self-exclusion form is absent from the workflow surface).grep -n 'bracket' .claude/rules/gotchas.md→ the ownership-probe entry at L373 states the bracket idiom as the DEFAULT shape with no artifact-path-in-argv caveat.grep -n 'pkill' .claude/rules/crash-fix-rounds.md→ 6 hits; the broad-pattern ban is already present at L180-183.git log --oneline --since='7 days ago' -- .claude/skills/issue/SKILL.md .claude/rules/gotchas.md→ no commit adding a self-exclusion form (2026-07-26)
Evidence
- Session
2b779905, 12:24:08Z: the Step 9ccompareleg was launched in background with an inlinepgrep -af 'step9c-junit-issue-1699[.]xml'guard; the launch shell's own argv contained the unbracketed--junitxml /tmp/step9c-junit-issue-1699.xml, so the probe matched itself. Output:"GATE STILL RUNNING; skip compare", then"FATAL: compare rc file missing"and"ls: cannot access '/tmp/step9c-compare-issue-1699.*': No such file or directory". The background Bash reported completion with exit code 0 — a false DONE with no compare output, the same class as the #825 empty-dir false DONE. - Same session's own diagnosis:
"Compare bg-call self-matched its own argv on the probe pattern (the launch shell contains step9c-junit-issue-1699[.]xml in its argv)." - Session
891b2cc6, 15:30:43Z and 15:30:53Z: a lint-gate probe usingscripts/workflow_lint[.]pymatched three sibling sessions' gates (issues 1702/1709/1711); the issue-scoped retry then self-matched on two UNbracketed alternates (issue-1704-lint-verdict,issue-1704-surgical-outcome) present verbatim in its own wrapper —"Self-match false positive (the probe's own shell wrapper contains the pattern)." - Session
c0319d9e, 13:37:54Z: the pre-compare probepgrep -af 'step9c-junit-issue-1701[.]xml|scripts/workflow_lint[.]py'matched a Step 10d lint gate running in.claude/worktrees/issue-1698. The session reasoned its way out —"Other sessions' step10d gates are running (not mine). Launching the step 1d compare."— one wasted reasoning step, with the standing risk that a less careful session waits on, or reads the verdict file of, a foreign gate. - Session
5c5a89e8, 05:31:51Z (pod-side liveness probe, same failure family): an UNbracketedpgrep -af 'issue1689'self-matched its own SSH command line, andLADDER_PIDS=$(pgrep -f 'issue1689_fit_ladder.py' | head -3); ps -p $LADDER_PIDSproduced"error: process ID list syntax error"— one wasted SSH round-trip inside a four-hour hang diagnosis. The working retry iterated per pid. - Session
0e2c3b21, 09:27:12Z (kill side): an implementer cancelling one redundant background job of its own ranpkill -f 'workflow_lint.py'with no scoping while it had already observed concurrent gates from issues 1694, 1696 and 1697 on the box (exit 144). No sibling is known to have been killed; the blast radius was fleet-wide by construction. The same subagent scoped its pytest kill correctly 56 minutes later (grep -v issue-1694 | grep -v issue-1696 | grep -v issue-1697). - Measured cost: roughly 7 wasted tool calls across
2b779905and891b2cc6plus a compare relaunch; the false-DONE skip is the material harm, since it would have read as PASS had the rc-file guard not been present.
Proposed change
- In
.claude/rules/gotchas.md(SSH-remote / ownership-probe entry, L373): state that bracketing protects only the pattern text, and FAILS whenever the same command line also passes the artifact path as an argument or uses multiple alternates. Add the self-exclusion form as the recipe for those cases — pipe the probe through a filter that drops the prober's own wrapper (| grep -v -e "bash -c" -e "^$$ ") — and add the multi-pid iteration form (for p in $(pgrep -f 'patter[n]'); do ps -p "$p" -o pid,etime,pcpu,stat; done), neverps -p $LISTon an unquoted multi-line capture. - In
.claude/skills/issue/SKILL.md, issue-scope both un-scoped alternates: L11716 and L12023 currently matchscripts/workflow_lint[.]pyfleet-wide. Replace with an issue-keyed pattern (the existingissue-<N>-lint-gate-tre[e]shape used at L10309 / L10683), and keep the WAIT-never-kill disposition for any residual ambiguous hit. - In
.claude/skills/issue/SKILL.mdStep 9c, extend the existing "SEPARATE FOREGROUND call — never inside the launch call itself" statement (L8875, landed 2026-07-23 in1f98e924dc0d3c1de34ce91bec70cd29024f605a) with the observed consequence: a self-match inside a background launch turns into a silent exit-0 skip of the leg, which the harness reports as a successful completion. The rule exists; the 2026-07-26 firing shows it needs the failure mode named to bind. - Add the artifact-path caveat wherever the bracket idiom is cited as sufficient —
.claude/rules/crash-fix-rounds.md§ Kill-before-relaunch step 1 references it as the DEFAULT probe shape. - Kill side: the broad-pattern
pkillban is ALREADY LANDED at.claude/rules/crash-fix-rounds.mdL180-183 (pkill -f python,pkill -f run_,pkill -f uvnamed as BANNED, "kill by explicit PID from the listing instead of pkill"). Do NOT re-add it. The residual gap is applicability scope: that section's opening line binds it to "EVERY re-run of a smoke / launch / dispatch command", and the 2026-07-26 firing was a session cancelling its OWN redundant background job — not a re-run. Widen the applicability sentence to cover any kill targeting a workload pattern, whatever the reason.
Scope / surfaces
- Primary target:
.claude/skills/issue/SKILL.md .claude/rules/gotchas.md(ownership-probe entry, L373),.claude/rules/crash-fix-rounds.md(§ Kill-before-relaunch applicability line)
Constraints / invariants
- Workflow-surface only — never experiment code,
configs/, ortasks/. uv run python scripts/workflow_lint.pypasses (no-flags); ruff clean on touched files.- This session runs under a
workflow_fix_target:Provenance line — it MUST NOT auto-route its own subagents' workflow-fix candidates (recursion guard).
Provenance
-
fingerprint: 836ad04b03d3
-
workflow_fix_target: .claude/skills/issue/SKILL.md
-
fingerprint: PENDING
/daily 2026-07-26 route-2 filing. Miner refs: F-P3, C-P8, A-P12, H-P7.
Activity