fix(lead): the duplicate banner covers a confirmed duplicate, in words that fit both verdicts - #1626
Merged
os-steve merged 1 commit intoSep 6, 2026
Conversation
The `record:alert` on the lead detail page was gated on `duplicate_status == "suspected"`, so the stronger state — a reviewer's `confirmed` verdict — got no banner at all. Since the conversion flow began refusing outright on `confirmed`, that was also the one state whose only announcement was the refusal dialog itself. Widen the predicate to any verdict the record actually carries, and rewrite the banner copy in all four locales so it names the fact both states share and points at Duplicate Status for the verdict, instead of calling every flagged lead "suspected". The guard stays: this call site is fail-soft, so an unevaluable predicate SHOWS the banner. `has()` alone is not enough either — it answers true for a present-but-null column, which is what driver-sql returns for a clean lead — so the comparison stays beside the guard. Both pinned boundaries in test/lead-duplicate-visibility.test.ts are updated deliberately, and a new pin holds the copy to naming neither verdict, reading the forbidden words from the locale packs themselves. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018xtjdpZFjgWh4Ad9Wcx68J
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
os-steve
marked this pull request as ready for review
September 6, 2026 02:07
This was referenced Sep 6, 2026
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.
Part of #1289 — deliberately not
Fixes. The card has two halves; this PRdelivers the record-page half, and the flow half's premise is gone. See
"What is not here" below. The card should be closed by a person who has read
that section, not by this merge.
Base measured at
ff433216.The banner now covers every verdict
src/pages/lead_detail.page.ts'srecord:alertwas gated onduplicate_status == "suspected", so the stronger state got no banner atall: a
confirmedduplicate is a reviewer's verdict, and the record page'salarm stayed silent on it while firing on the machine's guess.
That is worse now than when #1207 set the boundary. Since #1288 a
confirmedduplicate cannot be converted —
Convertopens a refusal instead of theconversion form. So the one state the page was quiet about was also the one
state that stops the rep's next click: they pressed Convert and met a refusal
dialog with no warning on the record behind it.
Both guards are still here, and they still fail in opposite directions
Unchanged and load-bearing, restated because it is the property easiest to lose
while editing the comparison beside it:
lead_detail.page.tsrecord:alertvisibleuseConditionlead-conversion.flow.tsedgese21/e22/e25The flow's guards are untouched by this PR. The page's guard is untouched in
substance:
has(...)still stands in front of the comparison, andtest/lead-duplicate-visibility.test.tsstill proves it is not decorative byderiving the unguarded spelling from the shipped predicate itself and watching
it fault.
has(...), and that was measuredThe obvious spelling of "any set value" —
has(record.duplicate_status)alone,which is what the card asks for in as many words — is wrong on this surface.
has()is true for a key that is present and null, and a clean lead comesback from
driver-sqlas exactly that. Measured on@objectstack/formula:So dropping the comparison reaches the same cry-wolf banner as dropping the
guard, just by the other road. Ablation, run against the committed tree with the
mutation proved on disk by blob hash and the restore proved the same way:
replacing the shipped predicate with the bare
has(...)turns two pins red— the
driver-sqlclean lead starts showing the banner, and the load-bearingpin reports the guard has become the whole predicate.
Copy: it may name neither verdict
One banner now covers two states that mean different things, with one title and
one body and no per-state channel (
record:alertcarries a singlevisible,and
pickLocalizedpicks by language, not by row). So the copy names the factboth states share and points at Duplicate Status for the verdict and
Duplicate Management for the record it repeats, instead of asserting one.
Keeping #1207's "Intake flagged this lead" would have told every reviewer that
their finished verdict was a machine's guess.
All four locales. A new pin holds the copy to naming neither verdict and reads
the forbidden words out of the locale packs rather than hard-coding them, so
renaming an option re-aims the assertion instead of quietly retiring it.
What is not here — the flow half's premise is gone
The card's ruling asks to widen a second predicate: the per-run warning line on
lead-conversion.flow.ts. I did not, and it should not be done. The cardmeasured against PR #1286 /
413d9640; atff433216the flow no longer has theshape the ruling describes. #1288 — which the card lists as an open decision —
has been decided, implemented and documented. The decision node now has
three out-edges, not two:
e21suspected→warn_duplicate(warn, allow)e25confirmed→refuse_confirmed_duplicate(refuse)e22everything else →no_duplicate_warningWidening
e21to any set value would make aconfirmedlead matche21ande25. A decision node that declares noconfig.conditionstakes everyout-edge whose condition holds, in parallel — the flow's own comment records
this being measured, and records that the same non-partition once "would have
shown the refusal and converted the lead in the same run". So the literal edit
would have re-opened conversion on confirmed duplicates: it would have silently
reverted #1288, implementing the block-versus-warn question the card puts
explicitly out of scope, in the reverse direction. It also contradicts AGENTS.md
metadata-semantics rule 8.
The hazard the card names is closed on that surface regardless: a
confirmedduplicate at conversion now gets a refusal, which is strictly more than the
warning the ruling asked for. Nothing is left undone there — but confirming that
a refusal discharges the ruling is the PM's call, which is why this is
Part ofand notFixes.Gates
pnpm verifygreen at the tip of this branch — all eight stages(
validate typecheck lint lint:i18n-gate hygiene hygiene:tokens build test),160test files,3395passed /1skipped. i18n gate: 0i18n/missing-*.Token ratchet clean;
interaction layer~37,572 of ~40,000.test/lead-duplicate-visibility.test.tsis the suite that judges this change,and it was run red first: with the two pins updated and the predicate still
suspected-only, it failed exactly twice —confirmedansweringfalse, andthe en copy still saying "Suspected". Both went green on the widening and on
nothing else.
Generated by Claude Code