kaxil opened a new pull request, #74688: URL: https://github.com/apache/airflow/pull/74688
Since #73235, a task that asks for a human response again in the same Dag run (after a retry, a clear, or an operator that pauses more than once, such as common.ai's per-tool approval in #74686) replaces the request's subject, body and options and clears the earlier answer. The response endpoint still accepts any answer for the task instance, though. A reviewer who loaded the first request, or a second reviewer with that page still open, can answer the new request while reading the old one: approve refund 2 while the screen says refund 1. The response payload now takes an optional `request_created_at`, the `created_at` of the request the client read. The upsert resets `created_at` on every repeat request, so a mismatch means the request was replaced, and the endpoint returns 409. The Required Actions form captures `created_at` when it loads and sends it back; a REST API caller that leaves the field out answers whatever request is waiting, as before. ## Design rationale **Why `created_at` and not a new revision column?** The execution API upsert already sets `created_at` to the current time on every request (`execution_api/routes/hitl.py`), so it changes exactly when the content does. No migration, and the GET endpoints already return it. It is `AwareDatetime`, so a naive timestamp gets a 422 instead of a 409 that reloading can never fix; `UtcDateTime` keeps microseconds on all three backends, so the value from the GET compares equal when sent back unchanged. **The form remounts per request, and keeps the value it loaded.** The Required Actions modal polls, and its form was keyed on the task instance, so a repeat request swapped the body under an open form while keeping what the user had typed. Both places that render the form now key it on `created_at`, and the form sends the `created_at` captured at mount, so an answer can only ever name the request the user was shown. After any refused answer the form reloads the HITL queries, so the next attempt shows the request that is waiting. **An answered request still reports "already updated".** That check runs first, so a stale form submitted after the newer request was answered is told the request was answered, not to answer one that is no longer waiting. ## Gotchas - #73235 is in 3.4.0b1. If this lands only on main, 3.4.0 ships the request replacement without this guard; it is a candidate for `v3-4-test`, which is the release manager's call since it adds an optional field to the public API during the beta. - Clients other than the UI form get the guard only if they send the field. Links built with `HITLOperator.generate_link_to_ui`, which notifiers send, open the Required Actions page, so they are covered; airflow-ctl has no HITL command. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
