feat(cards): a refusal says refused; a crash still says failed - #13
Closed
lucy-wolfe wants to merge 2 commits into
Closed
feat(cards): a refusal says refused; a crash still says failed#13lucy-wolfe wants to merge 2 commits into
lucy-wolfe wants to merge 2 commits into
Conversation
The word is a claim about whose fault the failure was, and the two invite opposite recoveries: refused invites a corrected call, failed invites a retry. Calling a crash a refusal is the worse direction, because it sends somebody to fix a call that was never wrong. The verb follows the classification rather than a list, so a refusal added next month gets the right word without anyone remembering. One predicate in one place: three renderers build a failure title, and two of them are bespoke titles that would otherwise have kept saying failed for a refusal -- the both-arms property again, found by sweeping for the word rather than trusting the default path to be the only one. One case was not what the classification said. A partial hire carries a status so the card can name the people already hired, which a retry needs so it does not double-hire them -- but the error it wraps may be a genuine crash. A status carried for CONTEXT is not a claim about fault. That path now asks the wrapped error's own type, and a producer in the same position marks itself rather than being enumerated in the renderer, because a list is where the next case is missed. Pinned by a discriminating pair: a classified refusal is refused, an unclassified failure is not, and a context-carried status with fault:true is not. Verified by mutation -- making the predicate return true fails both negative cases. Checked the collapsed card: the verb is one character longer and the title is not what gets truncated, so no card's summary window moves. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The verb moved to the fault marker and the tag did not. The tag still measured only the ABSENCE of a status, so a partial batch carrying one for context while wrapping a real crash said "failed" -- correctly -- and then dropped the crash marker. Somebody debugging that would see a list of people already hired, no fault marker, and reasonably conclude they had passed bad input. One classification, two surfaces. The tag now reads the same marker, and a test asserts the two are complementary rather than merely each correct: nothing may be both a refusal and a system fault, which fails if either rule moves without the other. Verified by mutation -- putting the tag back on the old instrument fails the crash case and only that case. This was the scope note I flagged rather than fixed in the first pass. Review ruled it fix-here, and that was right: shipping the verb on the new instrument and the tag on the old one is a divergence nobody would find again except by hitting it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
lucy-wolfe
force-pushed
the
feat/refusal-verb
branch
from
August 28, 2026 16:26
6765161 to
357540b
Compare
Contributor
Author
|
Superseded by #16, which merges this branch together with the other two changes so |
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.
A card headline for a caller refusal now reads refused; a genuine system fault keeps failed.
Why the word matters
It is a claim about whose fault the failure was, and the two invite opposite recoveries. "Refused" invites a corrected call. "Failed" invites a retry. Using either for the other sends the reader to the wrong action — and calling a crash a refusal is the worse direction, because it sends somebody to fix a call that was never wrong.
This is only implementable because the classification already exists to follow. The verb reads that, rather than matching on message text, which this file explicitly bans.
The split follows the classification, not a list
One predicate, in one place. That matters more than it looks: three renderers build a failure title, and two of them are bespoke ones for the bench/recall and start/stop batches. Those would have gone on saying "failed" for a refusal, because they never reach the default path.
They were found by sweeping for the word rather than trusting the default renderer to be the only producer — the same both-arms property that has cost this codebase several rounds. A rule copied into three renderers is three rules waiting to disagree at the first bespoke card.
One case was not what the classification said it was
A partial hire carries
status: "hire_partial"so the card can name the people already hired — a retry needs that, or it hires them twice and fails on the wrong person.But that status is data a retry needs, not an assertion about fault, and the error it wraps may be either kind: a mid-batch caller refusal (an unknown department on person four), or a genuine crash. So "carries a status" and "is the caller's fault" are not the same property, and treating them as one would have labelled a real crash a refusal.
That path now asks the wrapped error's own type, exactly as every other site does. And a producer in the same position marks itself with
fault: truerather than being enumerated in the renderer — a marker, not a list, because a list is where the next case is missed.Pinned by a discriminating pair
Per the rule this codebase keeps re-learning: a test asserting only "a refusal says refused" is satisfiable by returning
truefor everything, which would relabel every crash a refusal and delete the distinction rather than use it.fault: trueis not.Verified by mutation: making the predicate return
trueunconditionally fails both negative cases.The collapsed card
"Refused" is one character longer than "failed". The 120-character window applies to the summary tag rather than the title, and the title is not what gets truncated, so no card's truncation point moves. Checked rather than assumed, since it was asked.
Scope note
The
(system fault)tag keeps the rule it was given previously: it is driven by the absence of a status. Ahire_partialresult that wraps a real crash therefore says "failed" but does not carry the tag. That is pre-existing behaviour and not changed here — flagged as an observation rather than fixed silently, because it is a separate decision about what the tag means.Checks
bun run typecheck,test,lint,lint:reactive,knip, all 98 repository guards, and CI.