# Validate a Local Workflow Change (https://www.shipfox.io/docs/how-to/run-and-troubleshoot/validate-local-workflow-change)

Description: Use a coding agent and create_dev_run with dry_run enabled to check unpushed workflow YAML without starting a run.

Use this guide to validate unpushed workflow YAML before you start a run. An
integration trigger needs a retained event. Manual and cron triggers do not.

## Before you begin [#before-you-begin]

You need:

* A coding agent [connected to the Shipfox MCP server](https://www.shipfox.io/docs/how-to/set-up-work/connect-mcp-client).
* A Shipfox project with its repository configured.
* A workflow YAML file in your working tree.
* For an integration trigger, its integration connection and a matching event
  received within the last 30 days.

For integration triggers, Shipfox retains events for 30 days by default. Use a
newer event when the event you need no longer appears.

## Choose the trigger input [#choose-the-trigger-input]

Use the workflow trigger type to choose the input:

| Trigger type | Input                                                                                            |
| ------------ | ------------------------------------------------------------------------------------------------ |
| Integration  | Use a matching retained event.                                                                   |
| Manual       | Omit `replay_event_id`. Add `inputs` only when you need to override the trigger's `with` values. |
| Cron         | Omit `replay_event_id` and `inputs`.                                                             |

For an integration trigger, ask the coding agent to follow these steps:

### Find a replayable event

Call `list_trigger_events` with `replayable: true`. Choose an event whose
source and event name match the workflow trigger.

### Review the event payload

Call `get_trigger_event` with the event ID. Confirm that the payload has the
fields and values the trigger filter expects.

## Validate the local YAML [#validate-the-local-yaml]

Call `create_dev_run` with these values:

* `project_id`: the Shipfox project ID.
* `config_path`: the workflow file path in the repository.
* `trigger`: the trigger key from the YAML.
* `replay_event_id`: the selected event ID for an integration trigger. Omit it
  for manual and cron triggers.
* `inputs`: optional values for a manual trigger. Omit them for integration and
  cron triggers.
* `content`: the complete local YAML file.
* `dry_run`: `true`.

A successful call returns `dry_run: true` and `check_passed: true`. It confirms
these facts for every trigger type:

* The definition resolved and passed validation.
* The trigger exists.

For an integration trigger, it also confirms these facts:

* The selected event matches the trigger source and event name.
* The trigger filter passed for the selected event.

The check does not cover admission, run creation, or execution. It does not
start a run or execute workflow steps, and a real call can still fail after a
successful check.

Only the YAML in `content` is uploaded. Scripts, prompts in separate files, and
other working-tree changes are not part of the check.

## Fix a refusal [#fix-a-refusal]

Use the returned details to decide what to change:

| Error                      | What to inspect                                                                                                                                           |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `invalid-definition`       | Read each validation error's `message` and any `path` or `reason`. `total` counts all errors. `truncated` shows whether the response omitted some errors. |
| `inputs-not-allowed`       | Remove request `inputs`. Only manual triggers accept them.                                                                                                |
| `replay-event-required`    | Select a retained event for the integration trigger and provide its ID.                                                                                   |
| `replay-event-not-allowed` | Remove `replay_event_id`. Manual and cron triggers do not use a retained event.                                                                           |
| `trigger-filtered`         | Read `reason`, then compare the filter with the event payload.                                                                                            |
| `trigger-not-found`        | Read `available_trigger_keys`, `total`, and `truncated`. Use a key defined in the YAML.                                                                   |
| `replay-event-mismatch`    | Compare the event source and name with the selected trigger.                                                                                              |

Edit the YAML and repeat the dry-run call until it passes.

`content-too-large` means the YAML exceeds 256 KiB of UTF-8 content. A
transport error means the complete request exceeded the server body limit.
Reduce the request before trying again.

## Verify the validation [#verify-the-validation]

Confirm these results:

* The response contains `dry_run: true` and `check_passed: true`.
* The response does not contain a `run_id`.
* Any returned warnings have been reviewed.
* No workflow steps ran and no external resource changed.

When an integration check passes, [test the local workflow change](https://www.shipfox.io/docs/how-to/run-and-troubleshoot/test-local-workflow-change)
against the selected event to inspect its real behavior.

See the [Shipfox MCP server reference](https://www.shipfox.io/docs/reference/mcp-server#tool-catalog) for
the complete `create_dev_run` input, result, and limit tables.