How PII is protected in the AI pipeline
This page answers the question worth settling before anyone types a real child's details into the assistant: what actually happens to that information once it is typed?
It describes what the assistant's own code does, not what a diagram of the product says it does. Where the two disagree, what follows is the code.
The seven steps, as they actually run
One part of PTRS handles every question that goes through the assistant, start to finish. Its own steps, matched against what it actually does:
- Strip out personal details from both the question and the live data gathered for it, separately, then combine the two.
- Decide the model and how carefully it should stick to the facts, for the kind of question this is.
- Build the wording sent to the model, entirely from the already-stripped text.
- Send it to whichever model is configured.
- Check the answer for personal details slipping through.
- Put the real values back.
- Keep a record of the kind of question, the model, how long it took and whether the check found anything, never the words themselves.
Two things about this design are worth stating plainly, because they are what make it work at all.
The placeholders made up for one question are thrown away the moment that question finishes. Nothing carries over to the next one.
The same real value always gets the same placeholder within one question, so the model can reason about "the same child" without ever being told who they are.
That second point has a limit worth knowing. The part of PTRS that creates placeholders is built fresh for each of four different jobs within a single question, so one question can end up with several separate sets of placeholders, each numbering from the start. Within the wording that actually reaches the model this does not matter, because that one wording is built from one consistent set. Between what reaches the model and what gets saved afterward, it does. See what is stored.
What is stripped out
Eight kinds of information, checked in a fixed order, most specific first, so part of an email address is not mistaken for someone's name.
| Kind | Placeholder looks like | What it catches |
|---|---|---|
| Social security number | [SSN_001] | A number in the standard nine-digit format |
| [EMAIL_001] | Any address | |
| Phone number | [PHONE_001] | US formats, with or without a country code, in any common style |
| Street address | [ADDR_001] | A number, one to three capitalised words, a street type, an optional unit |
| Date of birth | [DOB_001] | Any date written in a common numeric form |
| A child's name | [CHILD_001] | The word Child, Member, Student, Youth or Minor, followed by a colon and a name |
| A staff member's name | [STAFF_001] | The word Staff, Teacher, Employee, Instructor, Counselor, Director or Worker, followed by a colon and a name |
| A guardian's name | [GUARDIAN_001] | The word Guardian, Parent, Caregiver, Emergency contact, Pickup person or Authorized pickup, followed by a colon and a name |
The name checks are narrower than they look
All three name checks require both a labelling word right before the name and a name written as two or more capitalised words in a row.
That means the following are not stripped out:
A name with no label in front of it. "Write up the incident where Marcus hit Devon" sends both names to the model exactly as typed, because neither is preceded by a label like Child.
A single-word name. The check needs two or more capitalised words side by side.
A name in all capitals, a name with a hyphen, or a name with an apostrophe.
There is a second effect worth understanding. The live data PTRS gathers for a question never contains a personal name at all; it is counts, site names, dates and scores. So in the ordinary chat, the name checks have almost nothing to act on, and the free text that does contain names, what a person actually types, is exactly the input the labelling requirement misses.
What is checked on the way out
Four kinds are checked for on the way back: a social security number, a phone number, an email address and a date of birth. Each match is replaced with a marker saying what was found, and the exchange is flagged.
There is no check for a name and no check for an address. The two kinds the stripping step exists for are the two the outbound check cannot catch. If a model produced a name of its own, or repeated one that slipped through the labelling requirement above, nothing on the way out would notice.
The chat and everything else check in opposite order
There are two ways a question reaches this pipeline, and they run two of the seven steps in opposite order.
| Everything except the chat | The chat | |
|---|---|---|
| Order | Check first, then restore | Restore first, then check |
| What the check sees | The answer with placeholders still in it | The answer with real values already back in |
| Result | A value the pipeline itself restored is never looked at again | A value the pipeline itself restored is caught and blanked out again |
So on the chat, the path most people actually use, an email address, a phone number or a date that was correctly stripped out on the way in and correctly restored on the way out is then caught by the outbound check and replaced with a marker, and treated as though it had leaked.
The date check makes this visible constantly: it matches any date written in year-month-day order, including dates PTRS itself writes into the live data, such as a compliance snapshot's own date. The phone number check has no requirement that it start at the beginning of a number, so it can match the last ten digits of any longer run of numbers.
This leans toward blanking out too much rather than too little, which is the safer direction to lean, and the practical effect is that the assistant cannot show a date in the chat without it being blanked out.
Is the streamed answer checked before it reaches you?
Yes, two pieces at a time. The chat holds back each piece of the answer until the next one has also arrived, checks the pair together, and only then shows you the first one. The reason given in the code is to catch a value split across the boundary between two pieces, such as a social security number cut in half.
So nothing reaches you unchecked, and a value split across two neighbouring pieces is caught. A value split across three or more pieces is not, and when a match is found, the arithmetic that trims the piece can cut a blanked-out marker in half, because blanking something out changes how long the text is.
This was flagged internally as an unresolved issue. Measured against what the code actually does, it is partly resolved: the two-piece holding-back exists and works up to that depth. What remains unresolved is the trimming arithmetic above, and the panel described next.
What never goes through this pipeline at all
This is the section worth reading most carefully. Four things bypass it completely.
1. The chart, table or cards under the answer
After the words finish, a separate step builds whatever chart, table or set of cards belongs with that kind of question, and sends it straight to your screen. It never passes through the stripping-and-checking pipeline at all: not stripped going in, not checked coming out, because it is built directly from your organisation's live records rather than from anything the model wrote.
Two of the nineteen possible panels carry personal information: the absent member cards write each absent child's first and last name, and the streak leaderboard writes the full names of the ten children with the most attendance days.
The person who sees either panel is already a signed-in Staff member or above, so this is not a leak to anyone outside your organisation. It is a gap: the structured half of an answer sits entirely outside the pipeline built to protect the words.
2. What the data question box returns
The data question box turns your words into an instruction and runs it directly, returning whatever comes back exactly as it arrived: not stripped, not checked.
Worse, that instruction runs outside the boundary that keeps every other screen in PTRS inside your own organisation's own records. The screen checks that you belong to an organisation and never uses that fact again. The approved list of record types the instruction may touch admits several sensitive ones, and the wording the model is told to use for a record type's name happens to slip past the part of the check meant to catch anything outside that list, so in practice nothing is actually checked against it either.
The Sanitized indicator on that screen reports whether the model's own written-out answer triggered the outbound check, not whether the rows themselves were ever checked for anything.
3. Quick Scan
Quick Scan never uses this pipeline, and never has. It lives in the Members module, keeps its own separate connection to an outside service, and sends the raw uploaded file, exactly as uploaded, to that service directly. It never touches any part of the pipeline described on this page.
A completed enrolment form can carry a child's name, date of birth, home address, school, allergies, medications, physician, hospital, insurance policy number, the family's pickup code, and a guardian's employer and contact details. None of it is stripped, checked or blanked out, on the way there or on the way back.
Any statement that PTRS strips personal details before sending something to a model is false for Quick Scan specifically. It is the one place in the product where a complete child record is sent to an outside service in a single request.
4. The regulation library
A regulation document is deliberately not kept separate by organisation, since regulation text is the same for everyone, and the search behind a regulation question adds no such separation either. The loading form accepts any text over 100 characters with no check on what it actually contains.
Treating regulations this way makes sense; the consequence worth stating is that anything loaded is retrievable by every organisation on the installed copy of PTRS, including anything loaded by mistake.
Where a question actually ends up
Everything above is about what is sent. This is about where.
One setting picks between two destinations: your organisation's own equipment, running an open model locally, or an outside service reached over the open internet with a private access key.
What each of PTRS's own configuration files selects:
| File | Selection |
|---|---|
| The checked-in default configuration | The outside service |
| The development environment | The outside service |
| The production environment template | Your organisation's own equipment |
| The cloud staging environment | The outside service |
So: a production installation, set up from the template as given, keeps a question on its own equipment. The development and cloud staging environments checked into this project do not.
The model that turns text into a numeric fingerprint, used for sorting a question into one of the 23 kinds and for searching the regulation library, always runs on your own equipment, whichever destination is chosen for the words themselves, because the outside service offers no equivalent of its own. The text sent for that fingerprinting is your question and any regulation document loaded, and neither one is stripped of personal details first, since that part works entirely outside the seven-step pipeline described above.
The two switches on the model status screen do not switch anything
The model status screen shows two rows, PII Enforced and RAG Enabled. Both simply read a setting; nothing else in the whole pipeline ever looks at either one.
Stripping out personal details always happens, with no setting able to turn it off. Searching the regulation library for a regulation question always happens too. Turning the first setting off would change the word on the screen and nothing else; turning it on, which every environment file already does, turns nothing on that was not already on.
Read those two rows as a statement of intent, not as something you can adjust.
What is kept, and what the record claims
Sixteen of the 27 actions in this module keep a record of the exchange. That record's own description claims every field on it holds only stripped, placeholder text.
That is true of two of the sixteen. The other fourteen save the question exactly as typed, including a free-text incident description, which is the field in the whole product most likely to contain a child's name.
The answer side is never stripped in any of the sixteen. All of them save the answer after the real values have already been put back into it.
And this record is kept in a table with no organisation boundary of its own. Nothing in the product currently reads that table, other than the data question box, which can, and which runs outside that same boundary in any case.
Separately, sending a message into a conversation saves the question exactly as typed into that conversation's own record. That is appropriate; it is your own record of your own conversation. It is worth knowing it exists.
Summary for an evaluator
What is genuinely protected: every question that goes through the seven-step pipeline has eight kinds of personal information replaced with placeholders first. No placeholder outlives the question that created it. The answer is checked before any of it reaches you, including while it is still streaming in, and a match is blanked out and logged as a security event. A carried-over conversation, if one were ever sent, would be re-stripped message by message.
What the protection does not cover:
The structured chart or table under an answer, which never goes through the pipeline at all; two of the nineteen it can produce carry children's full names.
What the data question box returns, which also crosses the organisation boundary.
Quick Scan, which sends complete enrolment forms to an outside service and never touches this pipeline.
Names and addresses on the way out; a name with no label in front of it, or written as one word, on the way in.
The exchange record, whose two text fields are named for a guarantee fourteen of sixteen writers do not honour.
The question worth settling before this module is used for real is not a technical one. It is whether a real child's information may leave your organisation's own equipment at all. If the answer is no, the model must be set to run on your own equipment everywhere, and Quick Scan must be turned off, which today does not fully work either, for the same reason a blank access key does not disable the outside model connection.
Checked against PTRS on 7 September 2026.