That sounds harmless until you ask what would have happened if someone had added one more version before approval. My choice would still have been 30. The newest version would have been 31. The approval record could then have pointed at text I had not selected, with no error to announce that the two choices had parted company. In this case, the result would have been right. The mechanism was wrong. That is luck disguised as a control.
The failure was not that the choice went unrecorded. It was that the operation making the consequential decision never read the recorded choice. In the command path, approval had no option for a variant identifier. The state machine took the last item from a list ordered by identifier. In the review page, the selected identifier arrived with the request and was used to redraw the page, but approval passed an empty set of options to the state transition. The page could show the version I chose and still approve a different one. A reassuring screen is not evidence that its action used what it displayed.
The content hash did its job, but that was part of the trap. It protected the text the mechanism selected. If that file changed afterward, the system could send the post back to review. It could not tell whether the protected text was what I had chosen in the first place. Every later check could be green while guarding the wrong starting point. More review effort from me would not fix a missing link in that data path.
We guarded that link in stages; we have not yet repaired it. The first fix bound each review gate to the exact variant on which the review was earned. Existing gate rows were backfilled without pretending we knew more than the record showed. That made a mismatch visible, but visibility alone did not stop approval or scheduling. The next step made both actions refuse a required review gate bound to a different variant. That refusal was live by September 22. Approval still takes the newest version and still does not read my choice; what changed is that a mismatch with the reviewed version is now refused. The wrong approval is blocked, not prevented. A record that is not checked is a note; a gate must sometimes say no.
Then the refusal fired for the first time, and it exposed a second problem. On September 22, I passed voice review for posts 18 and 19 on variants 40 and 41. Eight minutes later, approved hashtags were appended, creating variants 42 and 43. The change was small and legitimate. It still made the earlier review inapplicable to the new text. The check correctly refused to treat the old review as authority for the new versions. But the review page offered no way to pass the gate again on the current variant. The only available button sent the post back to drafting. The refusal was correct and, through that page, unrecoverable. Two days later, on September 24, the review page gained a way to pass the gate again on the current version.
That matters because the answer is not to copy an approval forward and hope the edit was minor enough. The approval belonged to the version the person saw. If the version changes, the person needs to see the change and make a fresh decision. In this instance the command path could clear the block, but the page used for review could not. We had built the right stop without the right way through it. The sequence also became clear: finish content edits before asking for a review gate, or expect to obtain a new review afterward.
There are three questions I would ask of any approval pipeline, including one that appears to work perfectly.
Which identifier did the person select? In my example, the answer was variant 30, chosen by name from three options. “The current draft” is not precise enough if drafts can change between selection and approval. Write down the identifier of the exact object the person saw.
Which identifier did the next step read? The approval step reads the highest identifier in the list, not my selection. It still does. With only variants 28 through 30, those answers matched. Add variant 31 and they separate. Trace the value actually passed into the state transition, not just the value displayed on the review page.
Which object do the later checks protect? The hash protected the file tied to the variant the mechanism approved. The later gate check compared the review's variant with the one headed toward approval or scheduling. Those checks are valuable only after the original selection and the action are bound to the same object. Otherwise they can prove the integrity of the wrong version with impressive precision.
This is what “near the loop” looks like in practice: a person makes a real decision, a screen acknowledges it, and a later action quietly substitutes a convenient default. Being in the loop requires a continuous path from that choice to the action, and a refusal when the path breaks. It also requires a way to make a fresh choice after a legitimate change. The human is not in the loop just because the interface has an approval button. The decision has to survive the trip through the data.