Refuse a goal the flow cannot use before trigger-flow starts it

trigger-flow now judges a goal the way GitLab's own start path does, and refuses one the flow cannot use before it creates a workflow. It starts flows through CreateAndStartWorkflowService, which runs no goal check, so it accepted goals the REST start path refuses, and goals that name no pipeline for fix_pipeline/v1. The workflow then failed later, in a way that reads as a flow or model fault.

Closes #13 (closed)

flowchart TD
    T["trigger-flow PROJECT GOAL BRANCH FLOW"] --> V["flow.validate_goal<br/>the check the REST start path runs"]
    V -->|"error"| X1["refused with the flow's own message"]
    V -->|"success or no check"| P["flow.resolve_source_pipeline_for<br/>when the flow declares a pipeline lookup"]
    P -->|"no pipeline"| X2["refused, asking for the pipeline's URL"]
    P -->|"a pipeline, or no lookup"| U{"both methods<br/>on this GitLab?"}
    U -->|"yes"| D["CreateAndStartWorkflowService<br/>with the goal the check returned"]
    U -->|"no"| N["goal marked not judged<br/>and named in the summary"]
    N --> D
  • trigger-preflight, which trigger-flow runs before dispatch, takes the goal and calls the flow's validate_goal, the method the REST create path calls before it creates a workflow. A refusal fails the preflight with the flow's own message. trigger-flow then sends the goal the check returns, so a merge request URL for code_review/v1 goes out as its IID, as it does from the REST path.
  • GitLab 19.4.1 has no goal check for fix_pipeline/v1. The flow finds its pipeline by reading /-/pipelines/<id> from the goal, and GitLab links no pipeline when the goal names none. For a flow that declares that lookup, the preflight runs it and refuses a goal naming no pipeline in the project, saying the flow needs the pipeline's URL. This rule is the simulator's own: it uses the flow's lookup and keys on the flow declaring one, not on the flow's name.
  • supported_events is printed as information when it names events. It restricts flow triggers only, and the pipeline page starts fix_pipeline/v1 directly with the pipeline's URL as its goal.
  • When the instance lacks validate_goal or the pipeline lookup, or no goal is given, the goal check reads ? and the summary names it as not judged. A check that can run still refuses.
  • trigger-preflight PROJECT [FLOW] ["GOAL"] judges a goal on its own. SKILL.md says what trigger-flow leaves out when it starts fix_pipeline/v1, and the CHANGELOG records the change.

What the simulation showed

One caproni instance on GitLab 19.4.1-ee with a staging license, the mock model and the default lane, torn down afterwards. GitLab's own starts went through POST /api/v4/ai/duo_workflows/workflows, with the parameters and additional context the pipeline page sends for fix_pipeline/v1.

The checks
goal GitLab's REST start trigger-flow on main this branch
fix_pipeline/v1, a sentence accepted with no pipeline linked; failed after five checkpoints, its pipeline tools called with the sentence as their URL the same refused before any workflow, naming the pipeline URL the flow needs
fix_pipeline/v1, a failed pipeline's URL pipeline linked; pipeline tools ran without error; failed after five checkpoints with "the agent did not produce the expected tool call" on the mock model not run the same as GitLab's start
code_review/v1, a sentence 400: "The goal for code reviews must be a merge request IID or a full merge request URL" accepted; failed after four checkpoints refused with the same message
code_review/v1, a merge request URL sent as the IID; finished sent as the URL; failed after four checkpoints with the URL as merge_request_iid sent as the IID; finished
developer/v1, a sentence not run not run finished; the preflight says the flow has no goal check

Known limits

  • The pipeline page also sends the pipeline's source branch as additional context, which trigger-flow does not. On the mock model both starts ended at the same point, so what that context changes later in a run is not measured here.
  • The pipeline lookup, like GitLab's, does not ask that the pipeline failed.
  • A GitLab without these methods is covered by the engine self-test only, since 19.4.1 has both.

Testing

just ci passes. The engine self-test covers the preflight, the dispatch and the engine wiring against stubbed flows with and without each method, and fails against main.

Claude Code wrote this change, its tests and this description, and we ran the checks above in a simulated environment. Each commit carries the AI-Assisted: yes and AI-Tools: Claude Code trailers that CONTRIBUTING.md asks for.

Merge request reports

Loading
Loading