Skip to content

Operator-only validate refusals no longer park, so an unclaimable worktree hot-loops full-effort slot sessions (18 trees stranded) #455

Description

@groeneai

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".

Before vs after, same task, same condition

Both manifests come from one row (my own open p0 PR ClickHouse/ClickHouse#105667):

escalation park_status
2026-08-12 waiting_reply
2026-09-24 pending

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.
  • B. Keep 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.
  • 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.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions