From 3c51308a9ea2907d80c2368b05827a0e923eccdd Mon Sep 17 00:00:00 2001 From: Timmy Date: Thu, 20 Aug 2026 08:32:16 +0000 Subject: [PATCH] docs: fix product boundary and release authority --- README.md | 1 + docs/PRODUCT-DECISIONS.md | 51 +++++++++++++++++++++++++++++++++ package.json | 2 +- scripts/record_release_demo.mjs | 3 +- tests/product-decisions.test.js | 17 +++++++++++ tests/release-demo.test.js | 2 ++ 6 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 docs/PRODUCT-DECISIONS.md create mode 100644 tests/product-decisions.test.js diff --git a/README.md b/README.md index 06b0021..32e1928 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ A working mobile-first bowel diary with an optional **photo-first AI assist**: - [Product roadmap and live Gitea epic map](ROADMAP.md) - [Product and safety boundary](PRODUCT.md) +- [Product boundary and release authority decision](docs/PRODUCT-DECISIONS.md) - [Self-hosted stool-vision decision](research/SELF-HOSTED-STOOL-VISION.md) ## Build a gated review release diff --git a/docs/PRODUCT-DECISIONS.md b/docs/PRODUCT-DECISIONS.md new file mode 100644 index 0000000..34beef4 --- /dev/null +++ b/docs/PRODUCT-DECISIONS.md @@ -0,0 +1,51 @@ +# Product Boundary and Release Authority Decision + +- **Status:** Accepted +- **Date:** 2026-08-20 +- **Scope:** Photo-assisted bowel logging and release governance + +## Context + +Timmy can make a conservative visual suggestion from a user-selected image, but the available general-purpose model is not clinically validated. The product must preserve manual logging, deterministic red-flag escalation, and user control regardless of model availability or output. + +## Decision + +### Observable AI fields + +AI may suggest only: + +- visible Bristol form (Types 1–7); +- broad color; +- image quality. + +Every suggestion is provisional, may abstain, and requires user review or correction before it can enter the journal. Symptoms remain user-entered, and journal saving is separate from training contribution consent. + +### Prohibited inferences + +AI must not infer or claim disease, bleeding, pain, urgency, fever, vomiting, treatment, causation, or whether a food is safe. It must not replace professional care, suppress deterministic red-flag escalation, or present a suggestion as a diagnosis. + +### Release authority + +Hermes/Timmy is the release authority for routine engineering, test, build, deployment, and release-candidate preparation. Automated evidence must remain reproducible and must not overstate model accuracy or safety. + +### Human gates + +Human gates are limited to: + +1. clinical and privacy review when language or policy crosses those boundaries; +2. beta consent before participation; +3. RC approval before shipping a release candidate. + +Routine provisioning, deployment, label sweeps, and release-candidate assembly are not human-operated gates. + +## Consequences + +- Model output is assistive and fail-closed, never authoritative. +- A generic open-weight VLM can support bootstrap experiments but cannot become the production Bristol classifier without the documented calibration and evaluation evidence. +- Release evidence and demonstrations must use fictional or synthetic data and state the non-diagnostic boundary honestly. + +## Evidence and governing references + +- [Product brief and safety boundary](../PRODUCT.md) +- [AI execution evidence](../AI-EVIDENCE.md) +- [Sovereign vision decision and measured limitations](../research/SELF-HOSTED-STOOL-VISION.md) diff --git a/package.json b/package.json index cae5094..38c7f68 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "scripts": { - "test": "node --test tests/domain.test.js tests/analysis.test.js tests/vision-service.test.js tests/vision-config.test.js tests/training-ingest.test.js tests/ci-workflow.test.js tests/release-demo.test.js", + "test": "node --test tests/domain.test.js tests/analysis.test.js tests/vision-service.test.js tests/vision-config.test.js tests/training-ingest.test.js tests/ci-workflow.test.js tests/product-decisions.test.js tests/release-demo.test.js", "test:ui": "node tests/ui.acceptance.mjs", "test:photo": "node tests/photo-first.acceptance.mjs", "check:syntax": "node --check app.js && node --check server.mjs && node --check service-worker.js && node --check src/analysis.js && node --check src/domain.js && node --check src/vision-config.js && node --check src/vision-service.js && node --check scripts/record_release_demo.mjs && bash -n scripts/run_selfhost_smolvlm.sh && python3 -m py_compile scripts/ingest_training_photo.py scripts/build_release.py", diff --git a/scripts/record_release_demo.mjs b/scripts/record_release_demo.mjs index 5c9c4e7..8a2ff43 100644 --- a/scripts/record_release_demo.mjs +++ b/scripts/record_release_demo.mjs @@ -97,10 +97,11 @@ await caption('The photo stays unsaved until explicit consent', 1300); await page.locator('#ai-consent').check(); await tap('#analyze-photo', 500); await page.getByText(/83% confidence/i).waitFor(); -await caption('Timmy suggests only visible form and broad color', 1500); +await caption('AI may suggest visible form, broad color, and image quality — never symptoms or diagnosis', 1900); await tap('#use-suggestion', 600); await caption('Nothing persists until the user reviews or corrects it', 1500); await page.locator('[data-type="4"]').scrollIntoViewIfNeeded(); +await caption('Release gates: clinical/privacy review, beta consent, and RC approval', 1800); await sleep(700); await page.evaluate(() => { document.querySelector('#demo-caption')?.remove(); diff --git a/tests/product-decisions.test.js b/tests/product-decisions.test.js new file mode 100644 index 0000000..4b4832c --- /dev/null +++ b/tests/product-decisions.test.js @@ -0,0 +1,17 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; + +const decisionPath = new URL('../docs/PRODUCT-DECISIONS.md', import.meta.url); + +test('product decision record fixes the AI boundary and release authority', async () => { + const decision = await readFile(decisionPath, 'utf8'); + + assert.match(decision, /Observable AI fields[\s\S]*Bristol form[\s\S]*broad color[\s\S]*image quality/i); + assert.match(decision, /Prohibited inferences[\s\S]*disease[\s\S]*bleeding[\s\S]*pain[\s\S]*urgency[\s\S]*fever[\s\S]*vomiting[\s\S]*food/i); + assert.match(decision, /Hermes\/Timmy[\s\S]*release authority/i); + assert.match(decision, /Human gates[\s\S]*clinical and privacy review[\s\S]*beta consent[\s\S]*RC approval/i); + assert.match(decision, /\.\.\/PRODUCT\.md/); + assert.match(decision, /\.\.\/AI-EVIDENCE\.md/); + assert.match(decision, /\.\.\/research\/SELF-HOSTED-STOOL-VISION\.md/); +}); diff --git a/tests/release-demo.test.js b/tests/release-demo.test.js index a2bd688..8c3d6d4 100644 --- a/tests/release-demo.test.js +++ b/tests/release-demo.test.js @@ -9,5 +9,7 @@ test('release demo visibly explains the CI-protected browser path without overst assert.match(demo, /Automated checks replay this synthetic path before review/); assert.match(demo, /tests\/fixtures\/synthetic-type4\.jpg/); + assert.match(demo, /AI may suggest visible form, broad color, and image quality — never symptoms or diagnosis/); + assert.match(demo, /Release gates: clinical\/privacy review, beta consent, and RC approval/); assert.match(demo, /Visual assistance — never a diagnosis\./); });