DEV Community

Haley
Haley

Posted on

Show the First Refusal Before You Retry

Picture this review with me, as a teaching scenario. The design lead's cursor hovered over the Approve button. The second model pass agreed with our preferred label.

The first pass had refused that same recovery modal. I asked who owned the click if a user got stuck. The scenario says the design lead owned the decision.

I asked where we could still undo the approval. The room went quiet in that familiar review-room way. A cheap retry can make agreement feel like proof.

Agreement is not the same thing as evidence. Have you watched a second pass sand down a first no? It shows up inside this kind of teaching scenario.

The dissent vanishes, and the card looks clean. That is the decision I want to slow down. The owner is the design lead, not the model.

The consequence is a recovery path that hides refusal. The point of reversibility sits before the card is marked approved. After that click, engineering may treat the copy as settled.

Can you still walk that approval back after the click? Retry debates keep surfacing in public developer threads. I am not treating those posts as proof of anything.

I am treating the urge to retry as the design problem. A nod is a mood. A stored refusal is a record.

Name the decision before a model speaks

Start from zero, even if the file already exists. Write the decision in one sentence a stranger can read. Do not start this stage by prompting a model.

I use this sentence in the worked scenario. May we replace the refused recovery label with the team's preferred label? The owner, the user, and the harm must all appear.

Here is the card I ask the team to save first.

{
  "decision_id": "recovery-label-014",
  "owner": "design-lead",
  "user_moment": "account-lockout-recovery",
  "proposed_action": "replace refused label with team label",
  "harm_if_wrong": "user cannot tell refusal from success",
  "reversible_until": "review-card-status-approved",
  "first_refusal": {
    "kept_verbatim": true,
    "summary": "Label reads as success. Undo path missing. Reason missing.",
    "source_handle": "pass-1-output.txt",
    "overwritable": false
  },
  "stops": [
    "missing verbatim first refusal",
    "retry output drops the undo gap",
    "retry output drops the refusal reason",
    "no named decision owner"
  ],
  "noise": [
    "model confidence score",
    "a third wording variant",
    "praise for the team label"
  ],
  "evidence": ["stored first pass", "lockout flow notes"],
  "hypotheses": ["team label feels clearer", "accessible name wording"],
  "retry_allowed": false
}
Enter fullscreen mode Exit fullscreen mode

Verification for this stage is blunt on purpose. Read the sentence aloud without the model in the room. If you cannot name the owner, you stop.

If you cannot name the harm, you stop. A missing field should fail that command outright. A pretty sentence should not rescue a missing field.

Did the command exit clean, or did you shrug? A green exit is the only pass I accept here. A shrug means the stage is not done.

jq -e '.owner and .harm_if_wrong and .reversible_until' review-card.json
Enter fullscreen mode Exit fullscreen mode

Here is the flow I want on the wall, not in someone's head. The user is in lockout, then asks for a recovery path. The refusal is stored before any retry is even offered.

lockout
  -> recovery request
  -> first refusal stored and locked
  -> stop check
  -> optional retry challenge on a disposable run
  -> owner approves or hands back
Enter fullscreen mode Exit fullscreen mode

The optional run comes after the stop check, never before it. Approval sits with the owner at the end. A model nod is not a box on this flow.

Lock the first refusal

The first pass said the label sounded like success. It named a missing undo path and a missing reason. That dissent is the evidence, not a draft to polish.

I paste the refusal into the card before anyone hits retry. I do not paraphrase it into something kinder. Kind paraphrases are how eager agreement gets hired.

Now check that the refusal is present and locked. If that check fails, the retry is not a review. It is an eraser wearing a review badge.

jq -e '.first_refusal.overwritable == false and .first_refusal.kept_verbatim == true' review-card.json
Enter fullscreen mode Exit fullscreen mode

Would you approve a design that deletes its own critique? I would not. The deleted line is usually the one the user needed.

I separate evidence from the hunch in the next fields. Evidence is the stored first pass and the flow notes. The hunch is that the team label feels clearer.

Do not merge those two lines into one. A hunch can guide the next research question. A hunch cannot fill an empty evidence slot.

Set the stops before a cheap retry

Free retries tempt teams to sample until the model nods. I was told MonkeyCode offers free model access and a free server option. Disclosure: This article was prepared as part of MonkeyCode's product outreach.

I am not quoting a token total, a model list, or an uptime promise. I have not checked a primary source for those figures this week. Availability is not a quality certificate, and I will not pretend it is.

So the free lane is only the place we might rerun a bounded check. That lane is not the approver on this card. It is not allowed to overwrite the first refusal.

Here are the stops I want on the card. A missing verbatim refusal stops the retry cold. A second pass that drops the undo gap stops approval.

A confidence number without a cited flow is noise, not a key. Which missing evidence should stop this approval cold? For me, a dropped undo gap or a dropped reason stops it.

Which extra line would only add noise here? A score that flatters the preferred label is noise. A third adjective is noise too.

Verify the stops exist before you spend a free run. If you cannot say the stop out loud, do not open the model. Curiosity is not a stop condition by itself.

jq -e '.stops | length >= 3' review-card.json
Enter fullscreen mode Exit fullscreen mode

A deadline is not a stop condition either. Boredom is not a stop condition on this card. Only a named gap in the refusal can stop the run.

Save this checker beside the card. It is a proposed contract, not a lab result I already ran.

# Proposed checker. Not executed against a live product.
import json, sys
card = json.load(open(sys.argv[1]))
required = ["owner", "harm_if_wrong", "reversible_until", "first_refusal", "stops"]
missing = [k for k in required if not card.get(k)]
refusal = card.get("first_refusal") or {}
if refusal.get("overwritable", True):
    missing.append("locked_first_refusal")
if card.get("retry_allowed") and missing:
    sys.exit("retry allowed while evidence is missing")
if missing:
    sys.exit("stop: " + ", ".join(missing))
print("card has the minimum refusal record")
Enter fullscreen mode Exit fullscreen mode
python3 check_review_card.py review-card.json
Enter fullscreen mode Exit fullscreen mode

If the refusal is unlocked, the script should exit on a stop. If the minimum record is present, it should print the minimum line. Treat that print as the expected contract, not a lab result.

Challenge the label, do not rewrite the no

I point the second pass at the stored refusal, not at a blank page. The job is to challenge our label against that refusal. The job is not to make us feel right.

This command is a proposed check, not a live call I executed. Swap in whatever client your team already trusts. Keep the second output in a new file.

# Proposed sequence. Do not overwrite pass-1-output.txt.
cat review-card.json pass-1-output.txt > retry-brief.txt
test ! -e pass-2-output.txt && echo "no second pass yet"
# After your free model lane writes pass-2-output.txt:
diff -u pass-1-output.txt pass-2-output.txt
Enter fullscreen mode Exit fullscreen mode

Run the diff only after the second file exists. Until then, the test line should admit the gap. A missing second file is a stop, not a crash to ignore.

When a reply exists, I diff the claims, not the vibes. I then mark each surviving claim as evidence or hypothesis. If the second pass agrees and drops the undo gap, retry stays blocked.

Agreement with a hole is a failed retry. The jq line below is the failed-retry hand-back, not the happy path. The design lead still owns the actual decision.

jq '.retry_allowed = false' review-card.json > review-card.tmp
mv review-card.tmp review-card.json
Enter fullscreen mode Exit fullscreen mode

The model does not get a quieter vote. Can a free server make this safer by itself? No, a free server cannot make the decision safer alone.

A free server can make the run cheaper to discard. Cheap discard is useful only if the first no stays put. Otherwise the team only bought a cleaner mistake.

If the retry survives, the owner may edit the flag by hand. Do not let a script flip that flag to true. Automated approval is how eager agreement gets the last word.

Read the card the way we read the product

The review card is an interface in its own right. If dissent is only a red dot, some people will miss it. I want the word Refusal in text, early in the reading order.

I tab through the card before I trust the layout. The refusal block must be reachable without a pointer. The Approve control must expose why it is disabled.

I also read it with a screen reader in the scenario. Or I ask a teammate who will actually do it. The stop reason should be announced with the disabled button.

Color alone cannot carry a do-not-retry message. Here is the accessible name I expect on the blocked action. The wording is still a hypothesis until someone hears it.

<button type="button" disabled aria-describedby="stop-reason">
  Approve recovery label
</button>
<p id="stop-reason">
  Stopped. First refusal still names a missing undo path.
</p>
Enter fullscreen mode Exit fullscreen mode

Verify the association before you call the pattern done. If either grep is empty, the block is a visual hint. Visual hints fail quietly for a lot of people.

grep -n 'aria-describedby="stop-reason"' review-card.html
grep -n 'id="stop-reason"' review-card.html
Enter fullscreen mode Exit fullscreen mode

Quiet failure is how bad recovery copy ships. The stored refusal and the flow notes are the only evidence here. The accessible names are still a design hypothesis.

They stay a hypothesis until someone actually hears the stop. I also check the product flow, not only the card. The user hits lockout, asks for recovery, and must see refusal as refusal.

They need a reason, a next step, and a way back. If our preferred label skips any of those, the first no stands. A smoother sentence is not an accessible sentence.

Would you know you had just been refused? If the answer is a pause, the card is not ready. Smooth copy can still abandon someone.

Run three scenarios, then stop generalizing

I do not call this protocol a finding. It is a protocol you can run tomorrow. Run it on three scenarios before you generalize.

In the first scenario, the shorter label still lacks a reason. Success means the reason stays visible or approval stays blocked. Stop if the retry calls brevity a fix.

In the second scenario, the retry adds a reason but drops undo. Success means both the reason and the undo survive. Stop if the card celebrates a partial save.

In the third scenario, the two passes disagree on severity. Success means both statements stay on the card for the owner. Stop if you delete the harsher line to reduce noise.

Noise can look like a third decorative adjective. Noise can also look like a naked score. Noise can sound like a polished model compliment.

None of those tell the locked-out person what to do next. I would not use this approach on a live incident bridge. I would not use it if you cannot store the first pass.

I would not use it as a substitute for someone else's code fix. I would not use it to bless a quota or a chip. I would not use it to bless a forever-free claim.

I was not given those facts in a form I can check. Please do not invent them in the review notes either. Unchecked numbers age fast, and this card has no room for them.

Leave the no on the table

Leave the first refusal in the record even if you later reject it. A rejection is still a real human decision. Deletion looks like the model was right all along.

The flow is simple if you say it slowly. Capture the no, then lock that no in place. Name the stop before you challenge the label.

Challenge the label, and do not rewrite the no. Then let the owner approve or hand the work back. The model can speak twice and still not own the click.

If a free model lane already sits on your bench, use it here. If a free server is there too, keep the run disposable. Keep the first refusal in the file the retry cannot touch.

Then ask whether the missing undo path is still on the card. If it is gone, the retry failed, no matter how smooth the label sounds. Would you hand that card to the locked-out person?

Top comments (0)