fix(#2086): gate each dev-lead retry payload on the target repo's pinned parser

Merged
DO
8d ago
+701-2

Problem

The dev-lead retry sweep runs scripts/ at a single channel: the one this repo's dev-lead.yml pins (#2050 / #2083). It then dispatches retries to every repo in the org, and each target parses the payload with dev-lead-intent.sh at its own pin. As a result, a target pinned to an older channel can receive a client_payload field its parser drops. That is the #2017 / #2050 skew, now between repos instead of over time.

The concrete case is comment_node_id: the v1 parsers don't read it, so a fix-bot-comment retry sent to a v1-stable repo would misroute.

Change

The first version of this PR skipped whole repos pinned older than the sweep. That would have stopped all retries for the fleet (stable / v1-stable), even though most payloads only carry fields every release reads. This version gates each payload separately instead.

scripts/lib/dispatch-channel-gate.sh works per target:

  • It finds the target's pin: the dev-lead.yml agent_ref, or the reusable's default main when there is none.
  • It reads the fields that pin's dev-lead-intent.sh reads: every .client_payload.<key> in the parser and in the lib/*.sh files it names, followed transitively, with comments stripped. This is the same reading check-retry-payload-fields.sh does in CI.
  • Both lookups are cached per sweep.

The payload is sent only when the target reads every non-informational field. Informational fields are the same ones the CI check uses (repo, attempt).

  • If the target pins the same channel as the sweep, no lookup is needed.
  • If the target has no dev-lead.yml (a 404 on a repo the token can read), the payload is held with no warning.
  • If the pin, the repo or the parser can't be read, the payload is held with a ::warning:: (fail closed).
  • If a field isn't read, the payload is held with one ::warning:: per repo and field set, naming the field.

Where the gate runs:

  • Every dispatcher calls dcg_payload_allowed on the payload it built. A new test enforces this for any function that POSTs to /dispatches.
  • The bot-comment scan checks BOT_COMMENT_RETRY_FIELDS before posting its retry marker. Otherwise a marker would block the comment for the whole pending window. A test pins this list to the dispatcher's payload.
  • main() no longer skips repos. It turns the gate on from SWEEP_AGENT_REF, which dev-lead-retry.yml now passes from its resolved channel.
    • An invalid SWEEP_AGENT_REF fails the sweep.
    • With SWEEP_AGENT_REF unset, as in unit tests or check-retry-payload-fields.sh sourcing the sweep, the gate is off and logs that it is.

Risk

Low to medium. The change only removes dispatches. A held payload leaves its markers in place, so the retry resumes once the target is repinned. Today the only held case is fix-bot-comment to repos whose pinned parser doesn't read comment_node_id. The extra API reads are cached: one stub read per repo, and a few file reads per distinct pin.

Test plan

  • shellcheck --severity=warning scripts/*.sh scripts/lib/*.sh: clean. bash scripts/dev-lead-lint.sh: clean.
  • bash scripts/check-retry-payload-fields.sh: OK at v139-next, ring0, ring1 and stable. The new lib introduces no dispatcher and no dispatches global.
  • tests/dev-lead/unit/test_dispatch_channel_gate.bats: 27/27. The mock gh fails on any unexpected call.
  • tests/dev-lead/unit/test_bot_comment_retry.bats: 67/67, including an end-to-end test where the target drops comment_node_id, so no marker is posted and nothing is dispatched.
  • Mutation checks: removing the early bot-comment gate, or any dispatcher's gate, fails a test.
  • These suites also pass: test_retry_payload_fields, test_delayed_rate_limit_retry, caller_stub_freeze, and test_{auto_rebase_retry,bot_thread_retry,dev_lead_retry,dev_lead_retry_channel,dev_lead_retry_pr_escalation,hold_notice,channel_stub_forward_guardrail}.

Rollback

Revert this PR. There are no non-revertible side effects.

Monitoring

The dev-lead-retry run logs [retry] sweep channel: dev-lead/v…. A held payload logs ::warning::[retry] holding retries for <repo>: it pins <ref>, whose scripts/dev-lead-intent.sh does not read client_payload field(s) … and [hold] <repo> ….

Closes #2086

🤖 Generated with Claude Code

https://claude.ai/code/session_019K6UsD7r25pAcRjJL3G6Kx