You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Since 2026-09-22 a pipeline-escalation keeps its row pending instead of parking it. That is right for a blocker a slot can fix itself, but one refusal class is provably not fixable by any slot session, and for that class the change turns a quiet park into an unbounded loop of full-effort sessions.
The refusal
phase-transition.py validate fails closed when a task worktree's submodule plumbing resolves into another tree, because a git submodule write there would corrupt a shared control. The refusal carries its own recipe, and the recipe is explicitly attended:
After quiescing the dispatcher, the slot crons AND the snapshot-refresh cron, and confirming this worktree is unleased with no live session: add --apply.
A slot session cannot quiesce the dispatcher or the slot crons, and it is itself the live session on that worktree. So the precondition is unsatisfiable from inside the lane, by construction. sweep-parked-escalations.py agrees and labels the remedy operator-foreign-submodules / "FLEET-INFRA, attended".
Under the old behavior the row parked and sat quiet for six weeks. Under the new one, the GitHub inbox promoted it to pending on a bot comment, and then:
01:04:50Z validate refuses, escalate writes the row back to pending
01:05:05Z dispatcher re-claims it for the same slot, generation 6 to 7
15 seconds, so the documented ~30 min escalation hold did not prevent the re-claim here. Every future slot firing repeats it: claim, refuse, escalate, re-claim. Each iteration is one opus/max session whose only possible output is the same measurement, and no amount of retrying can change the result.
Blast radius
report-foreign-submodules.py (read-only): 18 affected worktrees, 2351 foreign pointers, all aiming at /worktrees/.master.git/worktrees/master-debug-build, plus 2 snapshot module dirs pointed back into task worktrees. Every affected tree dates from 2026-04-20 to 2026-06-10, so this is a bounded set of stranded legacy trees rather than something still being produced.
17 of the 18 are invisible today only because they sit in waiting states (waiting_reply 8, on_review 6, monitoring_external 2, verifying 1). Mine is the only one in_progress, because it is the only one that got promoted. Any of the other 17 starts the same loop the moment a comment promotes it.
census summary
SUMMARY: affected worktrees=18 snapshot hijacks=2 quarantine entries=0
2351 pointer(s) -> /worktrees/.master.git/worktrees/master-debug-build
statuses: waiting_reply 8 on_review 6 monitoring_external 2 verifying 1 in_progress 1
3 of the 18 currently hold a live .scratch/.lock
obsolete gitlinks that must NOT be resurrected: contrib/spdlog, contrib/wasmedge, contrib/base64, contrib/curl
The decision I need
When a validate refusal's own remedy is operator-only, should the row park or stay pending?
A. Park that class again.escalate looks at its reason; the ones whose recipe is attended (operator-foreign-submodules and peers) park at a waiting status with the census as the proof artifact. Keeps "mechanical blockers stay pending" for everything a slot can actually fix. Cheapest change.
C. Neither. Repair the 18 trees now and accept that a recurrence loops again.
I would pick B, with A as the cheap version. Independently of which, the 18 stranded trees still need the attended repair, and one of them is blocking a p0 PR of mine that has 22 unpushed commits and an unanswered bot finding. I cannot run that repair myself:
fleet/bin/report-foreign-submodules.py # census, read-only
fleet/bin/repair-foreign-submodules.py --worktree <path> # dry run by default
One word on A, B or C unblocks me. @alex-clickhouse, this is the same area as #453 and #454, so you are the person I am asking.
Filed from a plan-lane session that could not proceed: validate rc=10, no lease, no lock, worktree untouched.
Since 2026-09-22 a
pipeline-escalationkeeps its rowpendinginstead of parking it. That is right for a blocker a slot can fix itself, but one refusal class is provably not fixable by any slot session, and for that class the change turns a quiet park into an unbounded loop of full-effort sessions.The refusal
phase-transition.py validatefails closed when a task worktree's submodule plumbing resolves into another tree, because agit submodulewrite there would corrupt a shared control. The refusal carries its own recipe, and the recipe is explicitly attended:A slot session cannot quiesce the dispatcher or the slot crons, and it is itself the live session on that worktree. So the precondition is unsatisfiable from inside the lane, by construction.
sweep-parked-escalations.pyagrees and labels the remedyoperator-foreign-submodules/ "FLEET-INFRA, attended".Before vs after, same task, same condition
Both manifests come from one row (my own open p0 PR ClickHouse/ClickHouse#105667):
park_statuswaiting_replypendingUnder the old behavior the row parked and sat quiet for six weeks. Under the new one, the GitHub inbox promoted it to
pendingon a bot comment, and then:01:04:50Zvalidate refuses, escalate writes the row back topending01:05:05Zdispatcher re-claims it for the same slot, generation 6 to 715 seconds, so the documented ~30 min escalation hold did not prevent the re-claim here. Every future slot firing repeats it: claim, refuse, escalate, re-claim. Each iteration is one opus/max session whose only possible output is the same measurement, and no amount of retrying can change the result.
Blast radius
report-foreign-submodules.py(read-only): 18 affected worktrees, 2351 foreign pointers, all aiming at/worktrees/.master.git/worktrees/master-debug-build, plus 2 snapshot module dirs pointed back into task worktrees. Every affected tree dates from 2026-04-20 to 2026-06-10, so this is a bounded set of stranded legacy trees rather than something still being produced.17 of the 18 are invisible today only because they sit in waiting states (
waiting_reply8,on_review6,monitoring_external2,verifying1). Mine is the only onein_progress, because it is the only one that got promoted. Any of the other 17 starts the same loop the moment a comment promotes it.census summary
The decision I need
When a
validaterefusal's own remedy is operator-only, should the row park or staypending?escalatelooks at its reason; the ones whose recipe is attended (operator-foreign-submodulesand peers) park at a waiting status with the census as the proof artifact. Keeps "mechanical blockers staypending" for everything a slot can actually fix. Cheapest change.pending, teach the dispatcher to skip. Dispatcher runs the ownership predicate before claiming and passes over a row whose worktree is foreign. The row stays visible and unparked but never burns a session. Better than A because nothing gets hidden, but it needs a counter so the skip cannot go silent the way the gate in Cron gates that fail open are silent: review-monitor's prci_delta gate has timed out on 19/19 passes since 2026-09-11, freezing 851 tasks #454 did.I would pick B, with A as the cheap version. Independently of which, the 18 stranded trees still need the attended repair, and one of them is blocking a p0 PR of mine that has 22 unpushed commits and an unanswered bot finding. I cannot run that repair myself:
One word on A, B or C unblocks me. @alex-clickhouse, this is the same area as #453 and #454, so you are the person I am asking.
Filed from a plan-lane session that could not proceed:
validaterc=10, no lease, no lock, worktree untouched.