- pipeline: MOCK mode picks bill vs scam mock by md5 fingerprint of input
PIL vs bundled assets/samples/{bill,scam}.png. Previously every image
returned the bill mock, masking the scam example's expected `warning`.
- app + ui_text: amber "MOCK MODE - fake data" banner shown when
GUARDIAN_MOCK=1. Localized in en/hi/es/ja; rebuilds on language change.
- render, safety: dict-lookup .get() fallback so an unknown worry_level
degrades to caution instead of KeyError on result-card render.
- speak: np.nan_to_num before peak-normalize so a corrupted TTS waveform
fails gracefully instead of crashing the int16 cast.
- ui_text: extract _localized() helper; doc_type_label + signal_label
collapse to one-liners.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Deploy gate NO-GO: es-UI scam run paraphrased '24 hours' as 'within a
set time' and dropped to one signal -> caution. Urgency pattern now
matches within-N-hours and set/short/limited-time paraphrases (en+es),
TDD. Re-running the gate.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix(prompts): read non-English letters in original script or refuse honestly
Probe evidence (modal_artifacts/ml_probe.json): ja letters are readable
but the model summarizes scams blandly in English (no quotable scam
phrases -> no signals); hi letters are under-read and misclassified.
Prompt now demands original-script quotes (feeds the multilingual
heuristics) and readable=false over guessing (honest retake path).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat(triage): Devanagari + Japanese scam vocabulary in heuristics
Modal ML-eval found hi/ja gift-card scams rated low (model under-reads
non-Latin letters, no signals fire). Heuristic taxonomy now carries
gift-card/urgency/arrest/credentials patterns in both scripts (TDD);
modal_probe.py dumps raw model output for the non-Latin letters so the
next iteration can pick the right deeper mitigation with evidence.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat(eval): Hindi + Japanese letter-text coverage in the forge
Space SSR drops launch(head=) scripts and Gradio 6.17 never executes
launch(js=) or js-only load deps (all verified against rendered pages)
-> head= script + ssr_mode=False for local/Space parity.
Modal eval intel (48 letters x 2 prompts): worry_reasons now feed the
heuristic scan - phishing letters were landing at caution because the
model's reasons (which paraphrase signals) were unscanned. Pinned by
an offline test. 96 tests pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat(training): letter forge + labeled dataset + Modal prompt-A/B eval
- third AA-tested palette 'softclub' (pastel Y2K); html class toggled by
a MutationObserver when 日本語 is selected; re-maps our tokens AND
Gradio's theme vars (scoped past :root.dark re-declarations, found in
browser); chrome-gradient wordmark, pill buttons, glow sheets, sparkle
- analyzing state: generic progress bar + timer hidden, bigger envelope
flap + breathing localized text instead
- read-aloud button hidden until an analysis succeeds (single-component
visibility update; a wrapper Group's toggle got stomped when its child
was updated in the same event - documented in code)
- example-letter thumbnails get real tap-target styling
95 tests pass; browser-verified: egg on/off round trip, read-aloud
show/hide across analyze + language switch, screenshot in docs/ui.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix(safety): strip letter contact details from facts and reasons too
The Modal validation matrix caught the lottery scammer's reply address
surfacing through key_facts - stripping only covered action steps.
sanitize_fact/sanitize_reason now strip contacts and drop entries that
were essentially nothing but the contact. 91 tests pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
7 analyses (4 letters incl. the land-letter regression; scam in all
four languages) + 3 photo-robustness degradations (honest refusal
passes, wrong analysis fails) + safety-contract assertions per case +
speech in en/hi/es/ja. Letter texts consolidated in guardian/samples.py.
Build/test-time only - the deployed runtime stays 100% local.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix(live): single progress overlay + drop prompt-echo 'facts'
User-reported on real mail: (1) progress bars stacked one per output
component -> show_progress_on=out_what, speak progress on its button,
hidden on language change; stale card hidden while pending. (2) model
parroted schema field descriptions ('who sent it', 'money mentioned') as
key facts -> normalized-echo filter in validate_extraction (TDD), prompt
now demands real copied values, heuristic scan widened to explanation
text so junk extractions cannot mute the second-pair-of-eyes check.
87 tests pass; loader verified in-browser (one overlay, envelope intact).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat(ui): localized analyzing state with envelope-flap loader
guardian/samples.py is the single source for synthetic letters;
modal_validate.py imports it; gr.Examples(cache_examples=False) so no
GPU runs at build time. Browser-verified example->analyze flow.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat(ui): 1-2-3 postmark step strip, localized in all four languages
step1/2/3 + samples_label keys x4 (parity-test guarded); numbers are
styled rings in HTML, not baked into strings. 85 tests pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat(ui): postage-stamp verdict badge in worry card
Polite desu/masu register, short sentences, no katakana jargon.
Parity tests extended to ja now green (81 passed).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat(ui): design tokens with WCAG AA contrast tests + forced-dark theme
Live model emitted a stray period between fields; json_repair (pure
Python, offline) covers the long tail of small-model JSON glitches.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Salvage truncated model JSON; surface signal labels when reasons absent
Live failure root cause: generation hit the token cap mid-explanation,
leaving unterminated JSON. Close open strings/brackets best-effort, raise
MAX_NEW_TOKENS to 1600, demand a compact explanation object in the prompt,
and log full raw output on parse failure.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The live MiniCPM-V output used \' (invalid JSON escape) and collapsed the
explanation object into prose. Repair invalid escapes, keep prose
explanations, and deterministically synthesize summary/facts/steps from
extracted fields so no result card is ever empty. Adds live-Space check.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mystery-Mail Guardian: complete app for the Build Small Hackathon