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 --> Dtrigger-preflight, whichtrigger-flowruns before dispatch, takes the goal and calls the flow'svalidate_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-flowthen sends the goal the check returns, so a merge request URL forcode_review/v1goes 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_eventsis printed as information when it names events. It restricts flow triggers only, and the pipeline page startsfix_pipeline/v1directly with the pipeline's URL as its goal.- When the instance lacks
validate_goalor 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 whattrigger-flowleaves out when it startsfix_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-flowdoes 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.