# Deploy delivery reconciliation contract

A Git ref event and a Deploy delivery are separate idempotency boundaries.
Ordinary ref-event processing may create repository Action runs and fan the
result out to matching Deploy projects. Maintenance repairs only a missing
Deploy delivery; it never manufactures another ref event and never starts new
repository Actions.

The durable delivery identity is:

```text
delivery:deploy:repo:<repository-id>:project:<project-id>:config:<generation>:sha:<40-char-sha>
```

Maintenance copies the Action run IDs already attached to the latest matching
ref event into `author_config_json.required_workflow_run_ids`. A successful
required Action therefore remains successful and is not replayed. The release
gate continues to fail closed if any required run did not succeed.

Automatic reconciliation only fills a missing release. Any release for the
same repository, project, configuration generation, and SHA—including a failed
or cancelled release—blocks creation of a fresh repair release. This prevents a
maintenance pass from rerunning repository commands after they may have
started. Repeated maintenance calls recover the same queued claim or become a
no-op.

## Delivery acceptance

For the target repository/project/configuration/SHA tuple, acceptance requires
exactly one `deploy_release_runs` build and no queued or in-progress Site-kind
runner run without a `deploy_release_runs` owner. A green build alone is
insufficient; the ownership condition must also pass.

## Explicit retry boundary

Forge does not currently expose a post-command automatic retry. A future
API-owned retry must require all of the following in one request:

- failed release ID;
- exact 40-character source SHA;
- exact project ID and configuration generation;
- a fresh idempotency key; and
- explicit acknowledgement that repository commands may run again.

The API must verify those values against the failed immutable release before it
creates a new release. Adding that mutation remains a separately approved
change; reconciliation must not infer permission from a failed build.

## Build handoff recovery

The API gives each generalized build workflow a release-specific identity. If
the dispatcher stops after allocating the workflow but before linking
`deploy_release_runs`, recovery finds that exact workflow, completes any missing
job/step/link rows idempotently, and dispatches it only while its persisted
workflow status is still `queued`. An `in_progress` or completed workflow is
linked back to the release but never replayed by API maintenance.
