Repository navigation
flow: approve an API step's exact request under per-item approval - #1355
Merged
Merged
Conversation
Under each_item and problems_only the API step used to fall back to the GUI steps, because the person approved a screenshot of a form the API step never fills. Flow 2 gates every saving run, so the API step never ran in the default profile. Now the run prepares the request first (reads only: form page, body, pre-checks) and the person approves that request. The approval request has form_state_kind "api_request", an api_request block (method, path, payload_sha256 over every byte sent) and an operation digest that binds the payload, the record the check reads back and the record check's contract hashes. After the approval the run prepares the request again; the last-point gate compares digests, and a changed or unpreparable request stops the item before the save, with no GUI redo. The run then sends exactly the request it checked, once. WebFormActuator splits into prepare() and send(); actuate() is both. An actuator without prepare() still falls back to the GUI under a gate. Governed, durable and multi-surface runs still use the GUI steps; those come in the next pull requests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 19, 2026
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First of the small PRs that put the API step, pipelined read-back and self-repair in one production profile. This one handles approval gating.
Why
The API step compiled from network capture (#1269,
docs/API_TIER.md) fell back to the GUI steps undereach_itemandproblems_only. The person approved a screenshot of a form the API step never fills. Flow 2 gates every saving run by default, so the default profile never used the API step.What changes
The person now approves the exact request the API step sends.
form_state_kind: "api_request"and anapi_requestblock: method, path, andpayload_sha256, the digest of every byte sent (session values included). Itsform_state_sha256is the operation digest, which binds the payload, the record the check reads back and the record check's contract hashes.form_changed_after_approval). A request that can't be prepared again also stops (form_state_unreadable). In both cases nothing is sent and the GUI steps don't redo it.WebFormActuatoris split intoprepare()andsend(), andactuate()calls both. An actuator withoutprepare()still falls back to the GUI under a gate, with a stated reason.Not in this PR
Governed (every CLI
run), durable and multi-surface runs still use the GUI steps. The next PRs cover durable checkpoints and reconciliation, the admitted-bundle binding, and the compatibility matrix test withdocs/COMPATIBILITY.md.Tests
tests/test_api_step_approval.py(new): an approved request is sent once and its digest equals the digest of the bytes sent; a session value that changes while the person looks means nothing is sent and the GUI doesn't redo it; a duplicate that appears after approval means nothing is sent; a decline sends nothing; a request that can't be prepared asks nobody and runs the GUI; the operation digest changes with the record, the record check or the body.tests/test_api_tier_web_form.py: the old "only an ungated run uses API steps" test now checks that a gated run uses API steps only when the actuator can prepare the request.ruff check,ruff format --checkand strict mypy on the four changed modules all pass.Synthetic data only.
🤖 Generated with Claude Code