fix: escalate ordinary fever and pain phrasing without allowlist narrowing
Quality gates / quality (pull_request) Successful in 1m59s

Hostile verification of the fever continuation-word allowlist found ordinary
symptom false negatives at the authoritative zero-Hermes-call boundary:
'I have a fever right now', 'my fever is 103', 'fever started this morning',
plus 'severe pain in/around the abdomen' against the hard-coded 'my' in the
pain pattern.

- fever: drop the continuation-word allowlist entirely; the bare clinical
  word escalates with any continuation. Nonclinical controls become
  structured exclusions (malaria/yellow/dengue/cabin compounds, fever tree,
  figurative 'feverish about'/'feverish with excitement'), so future natural
  symptom wording can never be narrowed again.
- severePain: accept any determiner or none plus location qualifiers
  (lower/upper/left/right) between preposition and organ.
- blood: differential audit against origin/main caught the same regression
  class in the PR's rewrite: 'poop/stool with blood' (escalated on main)
  was lost while 'my stool has been normal' / 'the poop contains seeds'
  became false positives. Restored explicit blood shapes with bounded verbs.

RED regressions first: matrix grows 56 -> 82 pinned positives (fever +20,
severePain +6 with case/punctuation/contraction variants), a pinned
preserved-behavior list for main's blood shapes, and two boundary suites
proving every review phrase and variant escalates at the detector AND
intercepts chat at the service layer with exactly zero Hermes calls.

Gates: npm test 90/90, staging-deploy 20/20 OK, test:ui/test:photo/
test:sleek pass, check:syntax clean, check:diff clean, npm audit 0
vulnerabilities, diff secret scan clean.
This commit is contained in:
timmy committed 2026-08-22 21:46:11 +00:00
1 parent e05d7d1e1a
commit 1aadca91c6
3 files changed
+125 -5

No files matched your search

+20 -3
View File
@@ -3,12 +3,29 @@ const URGENT_MESSAGE = 'These reported symptoms can need prompt medical care. Co
// Deterministic red-flag language table. Every match escalates and nothing that
// consumes this table can weaken it; the regression suite pins both the positive
// phrases and the idiomatic non-medical phrasings that must stay out.
//
// Context-handling contract (see tests/symptom-escalation.regression.test.js):
// positives key on the clinical vocabulary itself, and nonclinical controls are
// expressed as structured exclusions around it (named-infection compounds,
// figurative idioms, concrete objects). Continuation wording after a symptom
// word must NEVER be narrowed through an allowlist — arbitrary natural
// continuations ("right now", "is 103", "started this morning") keep escalating.
const URGENT_TEXT_PATTERNS = Object.freeze([
['blood', /\b(?:rectal bleeding|bleeding from (?:the |my |his |her |their )?(?:rectum|bottom)|rectal bleed(?:ing)?|blood(?:y)? (?:in|on|with) (?:my |the )?(?:stool|poop|bowel movement)|(?:stool|poop|bowel movement) (?:has|contains|is bloody|looks bloody)|bloody (?:stool|poop|bowel movement))\b/i],
// Blood: explicit clinical objects/shapes only, so ordinary stool sentences
// ("my stool has been normal", "the poop contains seeds") stay silent while
// every blood-mention shape (in/on/with/has/contains/looks bloody) escalates.
['blood', /\b(?:rectal bleeding|rectal bleed(?:ing)?|bleeding from (?:the |my |his |her |their )?(?:rectum|bottom)|blood(?:y)? (?:in|on|with) (?:my |the |his |her |their )?(?:stool|poop|bowel movement|rectum)|(?:bloody|blood-streaked) (?:stool|poop|bowel movement)|(?:stool|poop|bowel movement) (?:with blood|has blood|contains blood|has (?:blood clots|bloody streaks)|is bloody|looks bloody|looks like blood))\b/i],
['blackOrDarkRed', /\b(?:(?:black|dark[- ]?red) (?:stool|poop|bowel movement)|(?:stool|poop|bowel movement) (?:is|looks?(?: like)?|appears?|seems?|was) (?:a )?(?:very )?(?:really )?(?:black|dark[- ]?red))s?\b/i],
['severePain', /\b(?:(?:severe|constant|unrelenting) (?:abdominal|stomach|belly) pain|(?:severe|unrelenting) pain (?:in|around) my (?:abdomen|belly))\b/i],
// Severe pain: severity adjective + organ noun, with any determiner (or none)
// and optional location qualifiers between the preposition and the organ —
// never a specific possessive.
['severePain', /\b(?:(?:severe|constant|unrelenting|intense|excruciating) (?:abdominal|stomach|belly) pain|(?:severe|constant|unrelenting|intense|excruciating) pain (?:in|around) (?:the |my |his |her |your |their |this )?(?:(?:lower|upper|left|right) )*(?:abdomen|belly))\b/i],
['vomiting', /\b(?:vomit(?:ing|ed|s)?|throw(?:ing|s)? up|threw up|thrown up|puk(?:e|ed|ing|es)|barf(?:ed|ing|s)?|upchuck(?:ed|ing|s)?|toss(?:ed|ing|es)? (?:my|your|his|her|our|their|the) cookies|los(?:e|t|ing|es) (?:my|your|his|her|our|their|the) lunch|(?:i|we|you|he|she|they|someone) (?:(?:have|had|just|already|recently|am|are|was|were|kept) )?(?:hurl(?:s|ed|ing)?|spew(?:s|ed|ing)?)(?=\s*(?:[.!?]|$|again\b|twice\b|all night\b))|emesis)\b(?!\s+(?:my|your|his|her|our|their)\s+(?:hands|arms)|\s+confetti\b|\s+(?:a |the )?(?:scaffold|wall|barricade|tent)\b)/i],
['fever', /\bfever(?:ish)?\b(?:$|[.!?]|\s+(?:of|and|or|since|all|last|this|for|with|tonight|today|yesterday|again|plus|but)\b|\s*$)/i],
// Fever: the bare clinical word escalates with any continuation. Exclusions
// are structured nonclinical contexts only — topic compounds
// (malaria/yellow/dengue/cabin), the fever tree, and figurative
// "feverish about"/"feverish with excitement".
['fever', /(?<!\b(?:malaria|yellow|dengue|cabin)\s)\bfever(?:ish)?\b(?!\s+tree\b)(?!\s+about\b)(?!\s+with\s+excitement\b)/i],
['cannotPassGas', /\b(?:cannot|can['’]?t|cant|unable to|not able to) pass (?:any )?gas\b/i],
]);