Hard-stool follow-up: user-reported symptoms with deterministic red-flag handoff #70

Closed
rockachopa wants to merge 0 commits from timmy/67-hard-stool-followup into main
pull from: timmy/67-hard-stool-followup
Member

Closes #67

What changed

A hard/lumpy (Bristol 1-2) entry now asks a short follow-up before saving: hard/difficult to pass, soreness or sharp pain afterward, visible red blood, truly black/sticky/tarry versus dark brown, and dehydration/dark urine. Every question starts unanswered, is user-reported, and is never inferred from the photo.

  • Confirmed visible blood writes the existing blood symptom flag; confirmed truly black/tarry writes the existing blackOrDarkRed flag. Both therefore trigger the same deterministic alert manual logging already uses, and clearing the answer clears the escalation.
  • Answering "dark brown" (or leaving the question unanswered) never escalates: a draft color value is not evidence.
  • A mild report gets concise non-diagnostic self-care (hydration, no straining, gentle cleaning, warm sitz bath, external barrier ointment, food-based fiber) plus an explicit "general self-care, not a diagnosis — your photo cannot rule out bleeding or another cause" note and a seek-care line for severe/worsening pain, faintness, very low urine, or any blood.
  • The follow-up appears only for Type 1-2 forms, on the existing safety step, next to the unchanged red-flag checkboxes. Non-hard forms are unaffected.
  • The photo suggestion surface is unchanged and provably symptom-free: the acceptance test asserts the suggestion never mentions soreness, hydration, tarry, difficulty passing, or blood.

Evidence (this branch, commit 4cc5f4f)

  • npm test — 104/104 pass from a clean checkout (was 92; +9 follow-up unit, +3 contract/release-demo assertions).
  • npm run test:followup — PASS (tests/followup.acceptance.mjs, real browser at 390x844).
  • npm run test:ui, test:photo, test:mobile-capture, test:sleek, test:manual-fallback, test:provisional — all PASS (no regressions).
  • npm run check:syntax — PASS, including src/followup.js and the new acceptance script.
  • scripts/record_release_demo.mjs — recorded end to end against the working app (/tmp/timmy-demo-verify.webm, 71.8s, no browser errors) exercising the follow-up block, the mild self-care output, and the black/tarry → deterministic alert handoff with synthetic data only.
  • Visual acceptance artifact: artifacts/followup-hard-stool-mobile.png.

RED/GREEN discipline

The unit contract was written first and observed failing (ERR_MODULE_NOT_FOUND: src/followup.js), then implemented to green. The browser acceptance was written next and observed failing (waiting for locator('[data-followup-block]') on the unwired app), then the app was wired to green. The suite also caught a real bug during that cycle: unchecking the mirrored red flag on a non-hard form left a stale alert because the refresh returned early when the follow-up block was absent.

Deliberate scope limit

Mild answers (difficulty passing, soreness, dehydration) drive the guidance but are not persisted: saving them would change the ledger schema, which #35/#61 already own. Follow-up as a separate ticket rather than a schema change on a safety PR.

Boundary

No change to the medical/non-diagnostic boundary: analysis still suggests visible form, broad color, and quality only; the follow-up adds user-reported answers, adds no new escalation keys, and reuses the canonical urgent copy from src/domain.js. No real medical images, no model weights, no infrastructure purchase, no production service change.

Closes #67 ## What changed A hard/lumpy (Bristol 1-2) entry now asks a short follow-up before saving: hard/difficult to pass, soreness or sharp pain afterward, visible red blood, truly black/sticky/tarry versus dark brown, and dehydration/dark urine. Every question starts unanswered, is user-reported, and is never inferred from the photo. - Confirmed **visible blood** writes the existing `blood` symptom flag; confirmed **truly black/tarry** writes the existing `blackOrDarkRed` flag. Both therefore trigger the same deterministic alert manual logging already uses, and clearing the answer clears the escalation. - Answering "dark brown" (or leaving the question unanswered) never escalates: a draft `color` value is not evidence. - A mild report gets concise non-diagnostic self-care (hydration, no straining, gentle cleaning, warm sitz bath, external barrier ointment, food-based fiber) plus an explicit "general self-care, not a diagnosis — your photo cannot rule out bleeding or another cause" note and a seek-care line for severe/worsening pain, faintness, very low urine, or any blood. - The follow-up appears only for Type 1-2 forms, on the existing safety step, next to the unchanged red-flag checkboxes. Non-hard forms are unaffected. - The photo suggestion surface is unchanged and provably symptom-free: the acceptance test asserts the suggestion never mentions soreness, hydration, tarry, difficulty passing, or blood. ## Evidence (this branch, commit 4cc5f4f) - `npm test` — 104/104 pass from a clean checkout (was 92; +9 follow-up unit, +3 contract/release-demo assertions). - `npm run test:followup` — PASS (`tests/followup.acceptance.mjs`, real browser at 390x844). - `npm run test:ui`, `test:photo`, `test:mobile-capture`, `test:sleek`, `test:manual-fallback`, `test:provisional` — all PASS (no regressions). - `npm run check:syntax` — PASS, including `src/followup.js` and the new acceptance script. - `scripts/record_release_demo.mjs` — recorded end to end against the working app (`/tmp/timmy-demo-verify.webm`, 71.8s, no browser errors) exercising the follow-up block, the mild self-care output, and the black/tarry → deterministic alert handoff with synthetic data only. - Visual acceptance artifact: `artifacts/followup-hard-stool-mobile.png`. ## RED/GREEN discipline The unit contract was written first and observed failing (`ERR_MODULE_NOT_FOUND: src/followup.js`), then implemented to green. The browser acceptance was written next and observed failing (`waiting for locator('[data-followup-block]')` on the unwired app), then the app was wired to green. The suite also caught a real bug during that cycle: unchecking the mirrored red flag on a non-hard form left a stale alert because the refresh returned early when the follow-up block was absent. ## Deliberate scope limit Mild answers (difficulty passing, soreness, dehydration) drive the guidance but are **not** persisted: saving them would change the ledger schema, which #35/#61 already own. Follow-up as a separate ticket rather than a schema change on a safety PR. ## Boundary No change to the medical/non-diagnostic boundary: analysis still suggests visible form, broad color, and quality only; the follow-up adds user-reported answers, adds no new escalation keys, and reuses the canonical urgent copy from `src/domain.js`. No real medical images, no model weights, no infrastructure purchase, no production service change.
rockachopa added 1 commit 2026-10-09 08:10:57 +00:00
Field finding #67: a hard/lumpy (Bristol 1-2) entry now asks a short follow-up
before saving — difficult to pass, soreness, visible red blood, truly
black/sticky/tarry versus dark brown, and dehydration. Every answer is
user-reported and starts unanswered; nothing is inferred from the photo, and
answering "dark brown" never silently escalates as black stool.

A confirmed red-flag answer writes the same symptom flag manual logging already
uses, so the existing deterministic escalation is the only escalation path.
Mild reports get concise non-diagnostic self-care that states the photo cannot
rule out bleeding, and it is withdrawn whenever a red flag is confirmed.

- src/followup.js: pure follow-up contract (9 unit tests, tests/followup.test.js)
- app.js + styles.css: follow-up block on the safety step for Type 1-2 entries only
- tests/followup.acceptance.mjs: browser acceptance incl. that the AI suggestion
  surface still contains no symptom wording
- CI + release builder gates: npm run test:followup, syntax checks, manifest gate
- scripts/record_release_demo.mjs: records the real follow-up, mild guidance, and
  the black/tarry alert handoff with synthetic data
rockachopa closed this pull request 2026-10-09 12:07:15 +00:00

Pull request closed

Please reopen this pull request to perform a merge.
Sign in to join this conversation.