Skip to content

fix(devloop): a failed approval must not project success or completed #702

Closed

Description

@mashkovd

Problem

When the approve CWFT fails or refuses, the canonical DevLoop ends its we_ execution as Succeeded and the Temporal workflow ends as Completed. A refused approval is reported as a success, and it ends in a state that mctl-api will not restart.

Production incident (2026-10-18, mctlhq/.github#182)

  • 14:17:58Z mctl-agents-approve-6f23268b → Failed: the approve CWFT refused a stale grant (digest mismatch, mctl-agents#681 working as intended; see fix(devloop): reinvestigate must supersede the pending approval grant #701).
  • 14:18:26Z audit work_item.execution for we_dadf9c08-0156-4ffa-a306-a11e188ae921: phase: Succeeded.
  • mctl_get_dev_loop(dev-loop-mctlhq-.github-182) → status: Completed.
  • mctl-api restarts only executions that ended abandoned: … before implement (restartIfAbandoned, mctl-api#640), so this loop answers already_exists and cannot be retried on its id. The only clean way forward was a new carrier issue.

Cause (orchestrator/temporal/workflows/dev_loop.py@50e703d)

  • On not approve_result.succeeded the workflow returns a normal DevLoopResult(..., ended="approve flip ended Failed") (around line 3740).
  • The exit path then ends the canonical we_ with self._canonical_pending_phase or self._canonical_end_phase or "Failed" (lines 3183–3188). _canonical_end_phase is set from the investigator's result (lines 3344/3488), so a loop that ends "before any flip" ends with the investigator's phase, here Succeeded.

Expected

  • An approve CWFT that fails or refuses must not project Succeeded onto the WorkItem execution.
  • The loop ends with an explicit, typed terminal outcome, for example approval_refused (CWFT refused: digest mismatch or drift) or approval_failed (infrastructure failure), visible in the execution record, the DevLoopResult and mctl_get_dev_loop.
  • Retry semantics are defined for those outcomes: either the loop can request a fresh grant on the same we_ (preferred for approval_refused after a rewrite), or mctl-api treats the outcome as restartable the same way it treats abandoned: …. The choice belongs to the design, but a refused approval must not be a dead end.

Acceptance criteria

  • Test: canonical loop, approve CWFT returns Failed → we_ is not Succeeded; the terminal outcome is the typed refusal or failure.
  • Test: approve succeeds → unchanged (Succeeded, implementer runs).
  • The retry path for the new outcome is covered by a test (re-grant on the same we_, or restart via mctl-api).
  • The tests fail if the fix is reverted.

Refs mctlhq/.github#182, mctlhq/mctl-api#640.

Activity

  1. added
    bugSomething isn't working
    dev-loopTemporal dev-loop (DevLoopWorkflow) migration: phases, stages, governance
    on Oct 18, 2026
  2. mctl-agents commented on Oct 18, 2026

    @mctl-agents
    Contributor

    mctl-agents issue-investigator has analyzed this issue and created a proposal:

    https://github.com/mctlhq/mctl-gitops/tree/main/platform-gitops/agents-state/mctl-agents/proposals/issue-702-fix-devloop-a-failed-approval-must-not-p/

    Status: proposed — pending human approval. Review requirements.md, design.md and tasks.md, then approve: signal this issue's DevLoopWorkflow (mctl-api POST /api/v1/agents/dev-loop/dev-loop-mctlhq-mctl-agents-702/approve, or python -m orchestrator.temporal.cli approve dev-loop-mctlhq-mctl-agents-702 --approver <your-identity>) — the workflow flips .status.yaml to accepted via the mctl-agents-approve operation and runs the Tier 2 implementer. If no DevLoopWorkflow is running for this issue (pre-Temporal proposal), run the mctl-agents-approve operation directly with service=mctl-agents slug=issue-702-fix-devloop-a-failed-approval-must-not-p.

    The approver is recorded in .status.yaml and in the gitops commit, and the implementer refuses a proposal whose control.requires_human_approval carries no named approver — so prefer the mctl-api endpoint, which takes the approver from the authenticated caller rather than from what the caller types.

  3. added a commit that references this issue on Oct 18, 2026
  4. added a commit that references this issue on Oct 19, 2026
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

    bugSomething isn't workingdev-loopTemporal dev-loop (DevLoopWorkflow) migration: phases, stages, governance

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions