Record the trigger type of directly dispatched runs

Merged
YU
2d ago
+180-21

Summary

A run started directly for a DAG with worker_selector went to the coordinator without saying how it was started, so its Trigger cell on Executions stayed blank. This adds trigger_type to the coordinator Task and records it on the run.

Changes

  • Add trigger_type = 42 to the coordinator Task, regenerated with make proto.

  • Add TriggerType to DispatchTask, an executor.WithTriggerType option, and the conversion both ways.

  • Pass it from each direct start: the API (dispatchStartToCoordinator), the CLI (dispatchToCoordinatorAndWait), the sub-DAG runner (buildStartTask) and the engine (runDistributed). Each uses the value its local path already records.

  • Write it in the coordinator's initial status, in the worker's agent options, and in the worker's load and init failure statuses.

  • A queued run reaches the worker as a retry, so the load and init failure statuses now read the trigger type through a remoteRun.triggerType() accessor that prefers run.retry.triggerType, the same value the agent uses. Before, a queued run that failed before its first step reported unknown (TestHandleRetryLoadErrorKeepsQueuedTrigger, following a review note).

Retry and queued dispatches are otherwise unchanged.

Evidence

Code:

  • The API and CLI dispatch functions passed triggerActor but not triggerType, and remote_handler.go set opts.TriggerType only on the retry branch.
  • ir.ParseTriggerType maps an empty string to TriggerTypeUnknown, so a peer that does not send the field keeps today's value.
  • A queued run is dispatched as a retry and keeps its type through queue.PreservedQueueTriggerType, which is why the queue path was never blank.

Executed:

  • Regression tests failed on assertions before the wiring: TestDispatch_RecordsTriggerType (a real run on worker-a ended with trigger 0, expected 2), TestDispatchTaskAttributionRoundTrips, TestHandler_Poll/WriteInitialStatusPreservesScheduleTime, TestRunnerRunDispatchesWorkflowRequest (expected 4), TestExecuteDAGRun_SuccessfulExecution, TestReportTaskLoadFailurePreservesProfileName, TestReportTaskInitFailurePreservesProfileName, TestDispatchTriggerType, TestManualStartDispatchCarriesTriggerType.
  • At commit a016a3fe on main 4a49538f, these packages pass in full: internal/proto/convert, internal/service/coordinator, internal/service/coordinator/subflow, internal/service/worker, internal/engine, internal/runtime/executor, internal/service/frontend/api/v1. Selected tests pass in internal/cmd (TestDispatch|TestStart) and internal/intg/distr (TestDispatch_, TestExecution_StartCommand, TestExecution_LabelsPropagation).
  • golangci-lint run --new-from-rev=HEAD~1 on the touched packages at the same commit: 0 issues (macOS only).

Inference, not run:

  • During a rolling upgrade, a new coordinator with an old worker, or the reverse, leaves the run with the blank value it has today.
  • A retry that was not queued records no trigger type, locally and on a worker alike, because both use PreservedQueueTriggerType. It is a separate gap, so this PR leaves it alone.

Notes for review

  • The internal/engine line has no test: runDistributed builds its client through a concrete method, so there is no seam to inject one. It mirrors the TriggerTypeManual on the local path in the same file.
  • TestExecution_StartCommand/directStartCommandExecution does not reach a worker. A probe printed worker="local" with a matching selector, so the new test uses dispatchDirect instead, which now takes task options.
  • Not run: the full test suite, make check, make conformance, and the Windows lint.

AI disclosure

This change was written with Claude Code, an AI coding agent, including the code, the tests and this description.

Related Issues

Closes #3625

Checklist

  • Code follows the project style guidelines
  • Self-review of the code has been performed
  • Tests have been added or updated as needed
  • Documentation has been updated as needed
  • Changes have been tested locally

🤖 Generated with Claude Code


Summary by cubic

Directly dispatched runs now carry their trigger type through the coordinator, so Executions shows how they started. Workers also preserve queued runs’ trigger type when loading or initialization fails by reading it from retry configuration. Older peers that omit the field retain the unknown value.

Summary by CodeRabbit

  • New Features
    • DAG run statuses now retain the trigger type across dispatch and execution, including manual and subworkflow-triggered runs.
    • Trigger type is carried through coordinator tasks and reflected in execution and failure statuses.