DEV Community

Haley
Haley

Posted on

Show Recovery Evidence Before Approving Free-Model Empty States

I will walk a Friday review as a rehearsal. Treat the scene as a protocol, not a trophy. A billing empty state waits for a human yes.

The line looks calm on the board. "Payment failed. We will retry for you." Who is we in that quiet sentence? What recovery window does that retry occupy?

The designer on call owns the yes. Finance still owns the angry inbox later. Reversibility sits on a later receipt, not here.

So I ask two questions before any model speaks. Which missing evidence should stop approval? Which extra flourish is only noise?

An empty state is a fire exit sign. You do not let a poet rewrite the exit. Not without the floor plan in hand.

Stage 1: Freeze the decision in a file

Do not start by prompting a fluent model. Fluency still is not coverage. Have you felt that rush this week?

I write the act, the owner, and the stop. If the stop cannot be named, the copy cannot ship. A decision without a brake is only vibe.

mkdir -p review/billing-empty-state
cat > review/billing-empty-state/decision.md <<'EOF'
# Consequential decision (rehearsal)
Act: approve empty-state copy after a failed payment
Owner: designer on call
Consequence: the user infers that money is already moving
Reversibility: the next receipt, not this view
Stop if: retry actor, window, or cancel path is unnamed
Noise: tone words that do not change the recovery path
EOF
Enter fullscreen mode Exit fullscreen mode

Then I verify the stop exists in the file. A missing stop is a missing brake.

grep -q "Stop if" review/billing-empty-state/decision.md && echo "STAGE1_OK"
Enter fullscreen mode Exit fullscreen mode

If that command fails, I do not invite a model. Why ask a system to finish a sentence I cannot reverse?

Stage 2: Split evidence from hypotheses

Evidence is what the product already knows. Hypotheses are stories we hope users believe. Mix them, and the model writes fiction with a straight face.

I keep four evidence fields and nothing else. They are payment state, retry actor, retry window, and cancel. Everything else is a hypothesis wearing costume.

"Users want reassurance" is still a hypothesis. That line feels kind in a review room. Kindness without a mechanism is still a guess. Should a guess sit in the evidence block?

cat > review/billing-empty-state/coverage.yml <<'EOF'
evidence:
  payment_state: failed
  retry_actor: unknown
  retry_window: unknown
  cancel_control: unknown
  error_announced_before_promise: unknown
hypotheses:
  - users want a calmer sentence
  - retry in the background feels helpful
rules:
  approve_only_if:
    - retry_actor is named
    - retry_window is named
    - cancel_control is named
    - error_announced_before_promise is true
EOF
Enter fullscreen mode Exit fullscreen mode

Verify the unknowns. Unknowns are the point of this card. They are not shame, and they are not polish.

test "$(grep -c unknown review/billing-empty-state/coverage.yml)" -eq 4 && echo "STAGE2_OK"
Enter fullscreen mode Exit fullscreen mode

If that count is not four, I misfiled a wish as a fact. Would you let a wish sit beside payment_state?

Stage 3: Generate rivals in a sandbox, not a winner

I still want alternate sentences on the table. I do not want a crowned sentence. This is where a free-model sandbox earns a seat.

Disclosure: This article was prepared as part of MonkeyCode's product outreach. I use MonkeyCode's free model access to draft rival copy only. I use the free server option to pin one shared review board. I do not treat either as proof.

The sandbox is a studio, not a court. A free model can propose a sentence. A free server can remember the card. Neither can bless a billing line.

A proposed prompt stays boring on purpose. Boring prompts leak fewer hypotheses. Fancy prompts smuggle comfort into the evidence pile.

cat > review/billing-empty-state/prompt.txt <<'EOF'
Write three empty-state variants for a failed payment.
Each variant must name retry actor, retry window, and cancel path.
Do not invent a success. Do not hide the failure.
Return YAML only with id, copy, named_actor, named_window, named_cancel.
EOF
Enter fullscreen mode Exit fullscreen mode

I run that against the sandbox the team already uses. Your command will differ by tool. The check after it should not.

# Proposed check. Label: unexecuted example.
# Replace RUN_SANDBOX with your CLI against the free-model endpoint.
# RUN_SANDBOX --file review/billing-empty-state/prompt.txt > review/billing-empty-state/rivals.yml
python3 - <<'PY'
required = ["named_actor", "named_window", "named_cancel"]
print("STAGE3_OK if rivals.yml fills:", ", ".join(required))
PY
Enter fullscreen mode Exit fullscreen mode

If a variant cannot name the actor, it is not a candidate. It is noise wearing empathy. Would you ship empathy that hides the stop?

Stage 4: Score coverage, not charm

Charm is cheap on empty states. Coverage is a stubborn table. I read each rival against the four evidence fields.

I refuse to average a vibe across the room. Have you watched people vote for the friendliest lie? I have, in rehearsals like this one.

# Proposed scorer. Label: unexecuted example.
from pathlib import Path

text = Path("review/billing-empty-state/coverage.yml").read_text()
assert "approve_only_if" in text
for rule in [
    "retry_actor is named",
    "retry_window is named",
    "cancel_control is named",
    "error_announced_before_promise is true",
]:
    assert rule in text, rule
print("STAGE4_OK")
Enter fullscreen mode Exit fullscreen mode

Verify by reading the printout aloud with the owner. If you cannot say the rule in one breath, the rule is theater. Theater does not reverse a charge.

I keep discarded rivals in the same folder on purpose. A deleted almost-yes will haunt the next review. Scar tissue belongs in the record.

cat > review/billing-empty-state/discarded.md <<'EOF'
# Discarded rivals
- "We will retry for you." Reason: actor unnamed. Empathy without a mechanism.
- "Hang tight." Reason: hides failure. Adds noise, not coverage.
EOF
test -s review/billing-empty-state/discarded.md && echo "STAGE4_LOG_OK"
Enter fullscreen mode Exit fullscreen mode

Stage 5: Rehearse two research scenarios

Copy review is not user research. A scenario protocol can still stop a bad yes. I run two scenes only, then I stop adding questions.

Retry later succeeds. Retry is blocked. Success measure: the person names who retries and how to stop. Stop measure: if they cannot name the actor, the copy fails.

No extra adjectives get to vote in the room. Would a third mood question change the mechanism? That extra question is noise.

cat > review/billing-empty-state/scenarios.md <<'EOF'
# Scenario A — retry later succeeds
Setup: card declined, retry queued for 02:00 local, cancel is visible.
Ask: Who retries? When? How do you stop it?
Pass: actor, window, and stop are spoken back.
Fail: any of the three is guessed.

# Scenario B — retry is blocked
Setup: bank rejected the merchant, no retry will run.
Ask: What happens to the money? What can you do now?
Pass: no invented retry. Failure remains audible.
Fail: participant waits for a background save that will never come.
EOF
Enter fullscreen mode Exit fullscreen mode

Verify the fail lines exist before you recruit anyone. A protocol without fail lines is a compliment session. Compliments do not protect a payment.

test "$(grep -c '^Fail:' review/billing-empty-state/scenarios.md)" -eq 2 && echo "STAGE5_OK"
Enter fullscreen mode Exit fullscreen mode

I want that count at two. Two scenes. Two ways to lose. That is enough evidence for a copy yes.

Stage 6: Accessibility is a recovery check

Empty states fail in the ear first. Screen readers often hear the hopeful promise before the error. That order is a lie with good kerning.

I walk the view as a stop path, not a mood board. Error text first. Then recovery facts. Then cancel. Then any model-written comfort.

Comfort never leads the announcement. Comfort can even be missing. Missing comfort is not a failure of the pattern.

cat > review/billing-empty-state/a11y.md <<'EOF'
# A11y rehearsal (proposed)
Order: error, recovery facts, cancel, optional comfort.
Live region: retry actor and window travel with the error.
Tab: cancel is reachable before the comfort line.
Stop if: promise is read before the failure.
EOF
Enter fullscreen mode Exit fullscreen mode

Verify with a screen reader or an accessibility tree dump. I do not treat a visual screenshot as evidence. Eyes lie about order, especially on calm cards.

If the promise leads, I reject the copy on the spot. Would you accept an apology that hides the fire alarm?

grep -q "Stop if" review/billing-empty-state/a11y.md && echo "STAGE6_OK"
Enter fullscreen mode Exit fullscreen mode

Stage 7: Hand back with the discarded record

Approval is not a vibe in this flow. Approval is a hand-back to a named human. The model returns a card. A human records what was refused.

The product keeps the refuse list beside the shipped line. Future reviews need that scar tissue. Otherwise the same friendly lie returns on Monday.

flowchart TD
  A[Failed payment view] --> B{Coverage card complete?}
  B -->|No| C[Refuse copy]
  C --> D[Hand back to evidence owner]
  B -->|Yes| E[Sandbox rivals from a free model]
  E --> F{Actor window and cancel named?}
  F -->|No| C
  F -->|Yes| G[Two research scenarios]
  G --> H{Participant can stop the retry?}
  H -->|No| C
  H -->|Yes| I[A11y order check]
  I --> J{Error announced first?}
  J -->|No| C
  J -->|Yes| K[Human yes plus discarded log]

Verify the log still holds the almost-shipped line. Future you will thank present you. Memory is a product surface, not a chat aftertaste.

test -s review/billing-empty-state/discarded.md && test -f review/billing-empty-state/coverage.yml && echo "STAGE7_OK"
Enter fullscreen mode Exit fullscreen mode

What this rehearsal is not

This is not a claim that models write safer billing copy. This is not a benchmark against any paid model. This is not a substitute for counsel on payment language.

Do not use this flow when no human owns the yes. Do not use it for medical, legal, or credit decisions. Do not use a free sandbox as production truth.

A free server can host the board for the team. It cannot host accountability for the charge. Generation is cheap. Coverage is the job.

I still like generating rivals on a free model. I refuse to confuse generation with research. Evidence covers the act. Hypotheses stay in their pen.

Which missing field would stop your next empty state? Which sentence is only noise?

Top comments (0)