Skip to content

fix(release): refresh-aggregate exits 1 silently on a merged stale aggregation PR #34709

Closed

Description

@marcusquinn

fix(release): refresh-aggregate exits 1 silently on a merged stale aggregation PR

Pre-flight

Origin

Reproducer

Observed live on 2026-10-14 with v3.39.31 deployed (setup.sh at b44b336c42):

$ aidevops release refresh-aggregate 34690   # #34690 merged as b44b336c42; main then at c1db70b11d
(no output)
exit 1 after ~5s

Cause: full-loop-release-aggregate-recovery.sh:1667-1670 requires .state == "open" and returns 1 without a message.

What

aidevops release refresh-aggregate <PR> must never fail without a message. Every refusal in _full_loop_release_refresh_aggregate should print one Successor aggregation refused: <reason> line to stderr. A stale aggregation PR that was merged, and whose merge commit is still an ancestor of origin/main, must give a specific diagnostic. That diagnostic names the merge SHA and says to open a successor from that merge commit's trailers. Adopting merged stale PRs as successor input is an optional extension. Implement it only if it reuses the existing manifest helpers without new trust decisions.

Why

Operators and agents get no signal from a bare exit 1. In this session it cost a full investigation cycle, ending in a hand-built successor (#34701), while concurrent merges kept invalidating exact-tip aggregates.

Tier

Selected tier: tier:standard

Tier rationale: This is a known diagnostics pattern inside an already decided fail-closed boundary. The worker chooses wording and whether to extract a helper.

How (Approach)

Files to Modify

  • EDIT: .agents/scripts/full-loop-release-aggregate-recovery.sh:1642-1732 (_full_loop_release_refresh_aggregate). Replace each bare || return 1 that is a user-facing refusal with a stderr diagnostic followed by return 1. Model the wording on the existing refusals at lines 1677-1679 (Successor aggregation refused: ...) and _full_loop_successor_complete_manifest at 1546-1554.
  • EDIT: .agents/reference/release-aggregation-recovery.md:6-27. Document the merged-stale case.

Complete Write Surface

  • Callers/readers: full-loop-release-helper.sh:883-893 (refresh-aggregate dispatch). This is the only caller (rg -n "_full_loop_release_refresh_aggregate" .agents/scripts).
  • Writers/mutation paths: inside the function, release_lane_begin_aggregate_successor, the refs POST, PR creation and lane bind/finish. These are unchanged; only the messages on refusal paths before them change.
  • Existing verification/tests: .agents/scripts/tests/test-full-loop-release-aggregate-recovery.sh:1660-1690 calls the function with stderr discarded. Existing assertions must still pass.
  • Schemas/config: none affected. The change adds only printf ... >&2 lines. The lane JSON written by release_lane_begin_aggregate_successor (release-lane-helper.sh:596-636) and the PR body format from _full_loop_successor_body stay byte-identical, and no config key is read or added.
  • Generated/deployed mirrors: ~/.aidevops/agents/scripts/ via setup.sh. No generated files.
  • Migrations/backfills: none required. The new messages are emitted only on refusal paths, and no persisted lane, receipt or ref format changes, so existing lanes such as refs/heads/aidevops/release-lane need no backfill.
  • Cleanup/rollback paths: reverting the PR restores the silent exits in .agents/scripts/full-loop-release-aggregate-recovery.sh. The refusals happen before or instead of the existing writes, so a rollback leaves no orphaned branches, PRs or lane records beyond what the current code already leaves.

Implementation Steps

  1. At line 1666-1670, read .state/.merged_at before the open-state assertion. If the PR is merged, print Successor aggregation refused: #<PR> already merged as <merge_commit_sha>; open a successor from its trailers at the current main tip, then return 1. For other mismatches (closed unmerged, wrong base), print the specific reason.
  2. Add messages to the remaining silent refusals: fetch failure; stale head equals main; missing manifest in the body; lane read failure; branch/tree fence mismatch; PR lookup ambiguity.
  3. Update the reference doc paragraph.
  4. Run shellcheck and the existing recovery test.

Hazards and Compatibility

  • Concurrency/atomicity: unchanged. All new output happens before or instead of the existing returns, and no lane write is added.
  • Migration/rollback: none. Revert restores the old silent behaviour.
  • Mixed-version/backward compatibility: exit codes are unchanged (still 1), so scripts that check only the status are unaffected.
  • Idempotency/retry: unchanged.
  • Partial failure/recovery: refusals after release_lane_begin_aggregate_successor keep the existing retry-adoption semantics. Only add messages there.

Verification Before Dispatch

shellcheck .agents/scripts/full-loop-release-aggregate-recovery.sh
bash .agents/scripts/tests/test-full-loop-release-aggregate-recovery.sh
AIDEVOPS_FULL_LOOP_REPO=marcusquinn/aidevops bash .agents/scripts/full-loop-release-helper.sh refresh-aggregate 34690   # merged PR: expect exit 1 plus a "Successor aggregation refused" line naming b44b336c42
  • Surface mapping: the shellcheck run and the test cover the changed function. The live command against merged chore(release): aggregate v3.39.31 recovery after #34682 and #34688 #34690 proves the new merged-PR diagnostic and confirms no lane write happens; the merged check runs before release_lane_read.
  • Broad verification trigger: not required. One function, diagnostics only.

Files Scope

  • .agents/scripts/full-loop-release-aggregate-recovery.sh
  • .agents/reference/release-aggregation-recovery.md

Acceptance Criteria

  • refresh-aggregate against a merged aggregation PR exits 1 and prints a Successor aggregation refused line that names the merge SHA.
  • Every return 1 in _full_loop_release_refresh_aggregate that a user can reach through valid input has a preceding stderr diagnostic.
  • No exit code, lane write or fence check changes. The existing test-full-loop-release-aggregate-recovery.sh passes, and shellcheck is clean.

Context & Decisions


aidevops.sh v3.39.31 plugin for OpenCode v1.19.0 with claude-opus-5-5 spent 6h 55m and 411,972 tokens on this with the user in an interactive session.

Activity

  1. Repository owner locked as resolved and limited conversation to collaborators on Oct 14, 2026
  2. added
    auto-dispatchEligible for automated worker dispatch
    tier:standardStandard workload: implementation, bug fixes, refactors
    on Oct 14, 2026
  3. vladimirdulov commented on Oct 15, 2026

    @vladimirdulov
    Collaborator

    DISPATCH_CLAIM nonce=2cbba9343e64224f735562804b9a7fe3 runner=vladimirdulov ts=2026-10-15T00:27:51Z max_age_s=1800 version=3.39.31 opencode_version=1.19.0 lease_token=2cbba9343e64224f735562804b9a7fe3 device=device-1791387943-2519751-22989 session=issue-34709 phase=prelaunch expires_at=1792024193

  4. added
    status:queuedWorker dispatched, not yet started
    status:in-reviewNon-draft PR ready for review/merge
    and removed
    status:queuedWorker dispatched, not yet started
    on Oct 15, 2026
  5. vladimirdulov commented on Oct 15, 2026

    @vladimirdulov
    Collaborator

    CLAIM_RELEASED reason=blocked runner=vladimirdulov ts=2026-10-15T00:34:34Z aidevops_version=3.39.31 opencode_version=1.19.0

    Terminal blocker: reason=runner_capability_unmet owner=runner-recovery task=34709 attempt=f6a87ed294d725770a1577b6.
    Projected state: status:available.
    Next action: Release for a capable runner. Recheck declared secret and local-data requirements before claiming; do not impose a global issue hold or publish secret values.
    Raw evidence remains in protected worker telemetry.

  6. added
    solved:workerTask was solved by a headless worker
    and removed
    status:in-reviewNon-draft PR ready for review/merge
    on Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

auto-dispatchEligible for automated worker dispatchbugSomething isn't workingorigin:interactiveCreated by interactive user sessionsolved:workerTask was solved by a headless workerstatus:doneTask is completetier:standardStandard workload: implementation, bug fixes, refactors

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions