The staff have their assistant. Now the patient at the kiosk and the patient on the phone get theirs, and the difference between the two is the whole design: the patient assistant sees public documents only, calls no tools, and is asked its questions through a rate-limited endpoint that never accepts an audience from the client. This part builds it on the kiosk and in the season-three app, and the screenshot that matters is the one where it refuses.
Kiosk, a public question
Kiosk, a staff-only questionOne endpoint, with the audience decided by the server
The Pocket API from season three gains one route, a POST that takes a question and streams the answer back as plain text. The endpoint writes the audience itself, "public", and the request contract has no field for it; a client cannot ask to be staff. No tool is ever attached. Questions must be between 3 and 300 characters, and the built-in rate limiter allows ten per minute per address, which is generous for a person and enough to stop a script from turning the clinic's GPU into a public utility. The tenth request in a minute passed and the eleventh returned 429 in the live check.
The kiosk: giant type, one question, then reset
The kiosk runs inside the main application, so it calls the assistant service directly, with the public audience, below the check-in card. The panel follows the kiosk rules from season two: type large enough to read standing up, one action per screen, an "Ask another" button, and a reset after sixty seconds of nothing so the next patient does not read the last one's question. The footer says, in words a patient can act on, that the answer comes from the clinic's own computer and is not medical advice.
The app: a screen and a longer timeout
The shared Razor library gets an "Ask the clinic" screen reached from Home; the tab bar turned out to be five fixed links rather than a data-driven list, so a button on Home was the honest change. The typed API client reads the streamed text as it arrives. One detail that would have bitten a real patient: the app's HTTP client has a ten-second timeout for its other calls, and a model answer can take longer, so the assistant call uses its own two-minute client. If the request fails, the screen says the assistant needs a connection.
The rule, and the test that proves it
docs/private.md says the patient assistant never sees a staff chunk, enforced in SQL and tested in xUnit. The test uses the fake embedding generator so the exact text of a staff chunk is the question; with the public audience the query returns nothing, and the service returns the refusal without calling a model. That is the test. The kiosk screenshot is the same fact at human scale. Seventy-three tests pass, thirteen of them new.
What went wrong
The first live refusal on the kiosk came with two source lines under it. The model had written the refusal sentence and then, obediently, a Sources line, and the client's "is this the refusal" check read the raw text rather than the text the patient sees. The rule says citation or refusal, never both. Both clients now strip the Sources line before deciding, and a regression test pins the shape. A rule in a document is a wish until a test reads it the way a user would.
Perishable facts, as of September 2026: ASP.NET Core's built-in rate limiter with a fixed window; the Pocket API remains unauthenticated by design, with the confirmation code as the credential for visit data and no credential needed for public questions. The sixty-second kiosk reset mirrors the success screen's proven pattern and was not itself photographed.
Model pick: the visibility rule, the server-decided audience and the rate limit were in the brief at high effort; the cheaper model built three surfaces in twenty minutes, and found the double-citation bug by looking at its own screenshot.
What the AI got wrong: the local model appended a source line to a refusal; the coding assistant checked the wrong text. Neither reached a patient.
The meter: a patient question costs the same as a staff one, one embedding and one generation, and the rate limit caps the worst case at ten a minute per address. The build meter accumulates for the retro.
Checkpoint: tag private-09 in
the repo. Open the kiosk,
ask what to bring, then ask for the internal home-collection rate; the second answer
must be the fixed sentence. Next: does it actually work, in numbers.