← All privacy impact assessments
PIA: the on-device task assistant (Device-only groups)
Amended 1 November 2026 — Device-only retired (privacy-levels §3E). This assistant exists only for Device-only groups, and no group can choose Device-only any more. It keeps working for the groups that have it until they move to Standard, and is removed from the app after that.
Status: Draft — written before this feature was built, as required by section 38(3) of the exposure draft Children's Online Privacy Code. Written: 10 September 2026 · Last revised: 10 September 2026 Nothing here is legal advice.
This assessment was written before the feature and is a precondition of it.
1. Purpose and scope
A member of a Device-only group opens the Assistant tab and types a request in their own words. The model built into their phone (Apple Foundation Models, or Gemini Nano via ML Kit) classifies it into one of three intents and copies out a span of what they typed. Everything after that — which task the span refers to, the answer to a question, the card on screen, the write — is ordinary deterministic JavaScript.
The feature exists because Device-only is the level with no assistant at all. The privacy table already tells that household the phone's own model may write their daily brief; this extends the same bargain to the three things people actually do with a task list.
In scope — the list is closed. Four writes: tick a task off, add a task, put something on the calendar, cancel an event. And read-only questions: what is on your list, what is due, what is overdue, what you finished today or this week, what is next, what is on the shopping list, what is on the calendar, and what is for dinner (with who is cooking). Out of scope, by construction: anything else. See §5's closed union.
The list was widened from three to six before the code shipped, on 2026-09-10, and the widening is deliberately more of the same shape rather than more capability: every new arm is still the model emitting one keyword and copying words out of the member's own sentence, with a deterministic resolver deciding what — if anything — those words refer to. Nothing gave the model a tool to call, a row to choose, or a date to compute.
Not offered to child accounts in v1. §7 says why, and why that is not a claim about the app's position under the under-16 rule.
2. Data involved
| Item | Whose | Where it comes from | Strictly necessary? (s 38(2)(b)) |
|---|---|---|---|
| The typed utterance | The member's own | Typed by them, this second | Yes — it is the request. Held in device RAM for the length of one turn and not written anywhere. |
| Task titles, due dates, assignees, completion state | The group's | Already in the store, already decrypted on this device for the Tasks tab | Yes, and not collected for this feature — it reads what the app already holds to render a list the member is looking at anyway. |
| A dinner's assignee, as a first name | The group's | The roster already in the store | Yes — "who is cooking?" is the question. Resolved by lookup against the roster, never a name a model produced. It is a coordination fact, not a report on how anybody responded to anything (§5.6). |
| Event titles, start instants, recurrence | The group's | Already in the store, already expanded and decrypted for the Schedule tab | Same answer: read, never collected. Nothing new is stored about an event either — a created one is an ordinary row the member could have typed themselves. |
| A member's own first name | The group's | Already in the roster | Only to say "yours" vs "Sam's" in an answer. Not sent anywhere. |
| A date and a time of day parsed out of the member's own typed words (added 11 October 2026) | The member's own | Parsed on the device, from the sentence they just typed — "due Friday at 5pm" becomes a due_date and a due_time on the group's clock |
Yes, and nothing new leaves the device. The task arm reads the words a member typed into this phone and resolves them against the group's timezone, never the handset's, so the deadline a household sees is the one they meant. No model is involved in the parse and no new field is stored: due_date and due_time are the ordinary task columns a member could have filled in by hand. |
Nothing is collected. There is no new column, no new table, no new row of any kind. The feature adds no field to the schema.
Revision, 11 October 2026 (decision 25). The task arm now parses a due date and a time of
day out of the member's own typed sentence and writes them to the existing due_date /
due_time columns. It is recorded here rather than left silent because the arm reads more of the
sentence than it used to — but the answer to every question in this assessment is unchanged: the
parse runs on the device, no model sees it, nothing is sent anywhere, no field is added to the
schema, and the time is resolved against the group's timezone rather than the handset's. A phone
whose database does not yet have the two columns is detected and writes neither, rather than
failing the save.
What is deliberately NOT written, each an absence a future change would have to add back:
- No transcript. The exchange lives in component state and is gone when the screen unmounts. It is not written to the chat history. See §5's "an ephemeral conversation is not time-limited content" for why this is not the retention question it superficially resembles.
- No stored proposal card. The proposal is local state, never a row. The database table that would carry it is deliberately server-readable, so a card carrying a task title would put that title in plaintext in a database this level promises cannot read it.
- No telemetry, no counters, no "how often was the local model right". Tempting, and it would be a per-member record of how the app judged their requests.
3. Flows and locations
member types ──▶ phone's own model ──▶ one keyword + one span
(RAM) (RAM, on-device) e.g. "COMPLETE: the bins"
│
▼
deterministic resolver (JS, RAM)
matches the span against the tasks
already in the store on this device
│
┌─────────┴──────────┐
no/ambiguous match one match
│ │
a question a card
back to the the member
member confirms
│
▼
existing store action
(completing or
creating a task)
│
▼
Supabase — title SEALED
under the group key
Egress attributable to this feature: none. The module imports no network client. The only bytes that leave the phone are the ones the existing store actions already send when the member taps a checkbox by hand, and at this level the title in them is ciphertext.
The model is never shown the task list. This is the single most important line in this document and it is a design choice, not a consequence. The extraction function takes one argument — the member's own utterance — so another member's task title, notes, or beneficiary never enters a prompt. That removes the prompt-injection surface entirely rather than fencing it: there is nothing untrusted in the context to fence. It also means the model cannot leak a title it was never given.
4. Legal basis and consents
A new consent purpose, on_device_assistant, subject user, twelve-month currency like
every other, added in two places at once — the app's own list of consent purposes and the
database's check on that list, as part of the same database change — because our rule is that a
purpose added in only one place fails at the database rather than silently disagreeing with it.
Unbundled from on_device_brief, deliberately (s 14(3)(b)). Reusing that purpose would be
cheaper by one database change and wrong twice over: current_consents is the latest act per
(subject, purpose), so withdrawing the brief would read as withdrawing this too, and a person
who agreed to a briefing on their home screen has not thereby agreed to an assistant that
changes their group's task list.
Consent-class, and therefore excluded from stance presets. Nothing leaves the device, and
that is not what makes a consent a consent — the same correction we made to
dashboard_insight_on_device in an earlier review. Concretely it means the twelve-month sweep
turns the switch off and the row shows the member an expiry prompt, and no preset is allowed to
switch it back on for somebody. A stance called "Reserved" would want to enable this, which is
exactly the situation our rule keeps off-limits: a preset may not switch on a consent-class
setting on somebody's behalf.
What the member reads, from 10 October 2026. The first "on" asks with the prompt: what the assistant on this phone does, that what you type stays on this phone and nothing is sent to us, and that a change you confirm is saved to your group (shown); how to turn it off (shown); and, one tap away under "How it's handled", that the conversation is not saved, that the model is small, and that we ask again in twelve months (one tap). Its wording version moved to 1.1 and an earlier yes still counts. The year-on question now repeats the prompt's own shown words and records the prompt's version, as the on-device briefing's already did; before, it repeated the switch's longer description and recorded the general default wording version. The switch's description in Settings is now short (what it does, that it runs on this phone and sends nothing to us, and that it works in a Device-only group), with the list of questions and changes, the confirmation step and the model's limits one tap away.
From 15 October 2026 there is no separate prompt (owner: "there should just be the toggle"). The switch's description is now the prompt's shown words, verbatim, with its one-tap words under "How it's handled", and turning the switch on records the grant under the same 1.1 version. Nobody is asked again, and what the member reads before choosing is unchanged; only the separate "Turn on" and "Not now" buttons are gone.
Opt-in. A missing row is off — the user_tool_settings default is ON for an ordinary tool
and OFF here, matching dashboard_insight_on_device and improve_app_traces, because an absent
row means nobody has been asked.
The switch is the gate, never the ledger. Our rule is that a consent check reads the switch itself, not the log of when somebody agreed to it — that log is a network read that can fail, and a consent check that fails open is worse than none.
Fair and lawful means (s 38(2)(c)): the member types the request themselves, sees the proposal in their own words, and taps to confirm. Nothing is inferred from behaviour, nothing is collected in the background, and the feature does not exist until a person switches it on.
5. Risks and mitigations
5.1 The model names a task that does not exist
This is the measured failure mode, not a hypothetical. Our own testing notes from 7 September 2026 record the on-device brief completing from the worked examples in its own prompt — inventing petrol, mince and school runs for a household that had none. A model that does that to a sentence would do it to a task name.
Mitigation, and it is the design rather than a filter: the model's span is never treated as an identifier. The deterministic resolver described in §3 matches it against the titles actually in the store using a scoring rule. No match above threshold produces a question back to the member, never a guess. Nothing the model emits can name a row.
5.2 The model resolves the wrong task
Two close titles ("clean the kitchen", "clean the bathroom") is the ordinary case, not the adversarial one.
Mitigation: the scorer refuses on ambiguity — a runner-up within a small margin of the best match yields a "which did you mean" rather than a pick — and the card shows the resolved title verbatim before any write. Wrongness is visible at the moment it matters.
5.3 The model invents a title on "add a task"
There is nothing to match a new title against, so §5.1's mitigation does not reach it.
Mitigation: the proposed title must share content words with the member's own utterance — the same safeguard we use to test the on-device brief, and for the same reason. A title that came from nowhere is refused rather than shown.
5.3a A date the model got wrong
The model does not compute dates, and this is the calendar arms' equivalent of §5.1. It copies the words the speaker used ("tomorrow at seven") and a separate, deterministic date-parsing step turns them into a day and a clock in the group's zone. We already record why elsewhere: a model handed a date to work out mis-grounds relative time confidently and silently, and that was a reported bug on a surface that only had to describe a time. An event carries a time somebody turns up for.
Everything that date-parsing step understands is enumerated and tested; what it cannot read it refuses, because a time nobody stated written onto a calendar is a wrong answer that looks exactly like a right one. A day with no time becomes an all-day event rather than a guessed hour. The one inference it makes — a bare time is today if it has not passed, tomorrow if it has — is the one a person makes themselves, and it is rendered on the card before anything is written.
5.3b Cancelling the wrong thing, or more than was meant
Cancelling is the only irreversible arm, and it carries two hazards that are not obvious:
- A repeating event has no per-occurrence exception in this schema. Cancelling one Tuesday's swimming cancels the series. The proposal carries that warning and the card says so in words; outright deletion of a task remains refused by design, precisely because it has no equivalent ask that would justify the same risk.
- A cross-group mirror belongs to the host. The matching step excludes mirrors entirely, so a guest is never offered the chance to cancel another family's party — which the database would refuse anyway, after the member had confirmed a card.
Past events are excluded too: there is nothing to cancel, and including them would let a stale title out-match a live one.
5.4 The write itself
Ticking a task off is not free: it writes a task_completions row, and un-ticking deletes
one — by design, task_completions is not an append-only log. Points and a Chore Lotto ticket
hang off it.
Mitigation: the confirm card. Nothing is written from the turn itself — the same standard we set for spoken baby logs on 5 September 2026, and for the same reason: a card the household can read before it lands is stronger than an assistant's confidence. Direct execution with an undo toast was considered and rejected; it would have gated the write on the model's self-reported confidence, which our own rule treats as unreliable: we confirm on consequence, not on confidence.
5.5 Prompt injection through another member's content
Not mitigated — eliminated. §3: the model sees only the member's own words. There is no untrusted text in the context.
A future change that hands the model the task list to "improve matching" reintroduces the entire class, and would need this section rewritten and our rule for fencing untrusted text applied to it. §9 makes that a review trigger.
5.6 An ephemeral conversation, and whether that is "time-limited content"
Worth stating because it looks like a feature we specifically decided not to build elsewhere. Section 4A(6) of the Rules catches a feature that "enables viewing of material only within a limited period after it has been posted", and our own rule is that adding a retention timer to member-posted content anywhere re-runs the ban self-assessment.
It does not engage here, and the reason is not a technicality. Nothing is posted: there is no other end-user who can view any of it, at any time, ever. The limb is about material other people can see for a while and then cannot. Never persisting a private exchange is not a retention window on shared content; it is the absence of a record, which is the more privacy-preserving of the two and the one the level's own promise implies.
If the conversation is ever persisted and then swept, that is a different design and this section is wrong.
5.7 The phone's model is not measured
The hardware matrix that would measure it is still owed, and the fix for the 2026-09-07 grounding failure is native and has not been compiled. See §8.
6. Structural versus conventional
Structural — the database or the shape of the code proves it:
| Property | What enforces it |
|---|---|
| A sealed title at this level | Enforced in the code that seals a task's title, keyed on groups.privacy_level; the database's own seal_group_task function (error TSK03) refuses a sealed title at any other level |
| The model cannot name a table, column or RPC | A closed union of three intents. There is no field a payload could use to reach one |
| The model never sees group content | The function that calls the model takes a single string argument — the member's own utterance. There is no parameter that could carry a task |
| Nothing is written without a member's tap | The module performs no writes at all; it has no store access and no client |
| A member cannot reach another group's tasks | Unchanged: group_tasks row-level security, and the resolver is handed the store's current group only |
Test-pinned — not proof, but it fails loudly:
| Property | Pinned by |
|---|---|
| The module makes no network call | A source scan asserting it imports no client, in its own suite |
| Extraction's signature stays one argument | An automated check that the function still takes exactly one argument, the same style of check used elsewhere in our tests |
| No match, and ambiguity, refuse rather than guess | Behavioural tests, mutation-checked |
| The copy claims nothing false | The copy suite, alongside the existing on-device constants |
Conventional — remembering, and named because the next reader will assume otherwise:
- Adults only. A check in the interface for whether the account belongs to a child, on the settings row and the Assistant branch. Nothing in the database refuses a child's device from running a local model.
- Nothing is persisted. Component state today because that is how it is written, not because anything refuses a write.
- The exchange is not sent anywhere. True because the module has no client, and a source scan says so — but a future change adding a network call two files away is refused by nothing.
7. Best interests of the child
Children do not get this feature in v1, and the reasoning has to survive being disagreed with.
What they lose: nothing they had. The Assistant tab is already hidden for accounts under 16, so this changes nothing for a child; it declines to change something.
Why not simply ship it to them too. Our own privacy design notes describe a scoped on-device child assistant — a command interface, deliberately never a companion — as a design intent. It has never been built or assessed. Building the adult surface does not prejudge it, and the two are genuinely different products: an adult typing "tick off the bins" is using a shortcut, whereas a child talking to something that answers is a relationship, and the Code's concerns (section 28's automated-decision disclosure, the companion-app risk we have refused elsewhere) attach to the second and not the first.
Where the interests diverge. A child in a Device-only group whose adults use this sees their task list change as a result of something a model heard. That is the same exposure as an adult reassigning a chore by hand, and the child is told what adults can see through the existing s 33 surface; nothing here adds to what is visible about them. No new record about a child is created — this feature creates no record about anybody.
This is not a ban-position argument. Our own rule is explicit that an age-restricted experience carries no meaningful weight in the purpose test, and that three of our documents once said otherwise and were corrected. Withholding this from children is a privacy-Code and product decision. It does nothing for the app's position under the under-16 rule and must not be cited as if it did.
8. Residual risk we accept
- The phone's model has never been measured on this task, and the one measurement that exists is a failure. The 2026-09-07 field report is the on-device brief producing nonsense on an iPhone 16e. Our decision (2026-09-09) is to proceed during family testing: the cause is diagnosed as a code bug — the session was built without the instructions slot — and the fix is already written. The exposure is bounded by construction: a bad extraction reaches a card, never a row, and an unparseable one reaches nothing at all.
- That fix has not yet reached people's phones. Until it is built into an app update and released, an iOS device runs the instruction-less half, so extraction is worse than designed on exactly the handsets family testers hold. Accepted; the failure is a refusal, not a wrong write.
- A confident wrong match that the member confirms without reading. The card is the whole defence and a person in a hurry defeats it. No worse than tapping the wrong checkbox, and the write is reversible — but un-ticking deletes a completion row, so it is not perfectly symmetric.
- "Nothing is sent" is held by a source scan, not by the runtime. A future network call in this path would be caught in review or by that test, and by nothing else.
- The feature is invisible to us. No telemetry, deliberately, so we will learn it is bad only when a tester says so. That is the trade this app makes everywhere.
9. Review trigger
Re-read this document when any of these stops being true:
- The extraction function gains a parameter carrying group content into the model (§3, §5.5).
- The date-parsing step starts accepting anything a model produced rather than words a person typed, or starts defaulting instead of refusing (§5.3a).
- Cancelling stops warning about a series, or starts offering mirrors or past events (§5.3b).
- Anything about the exchange is persisted — a transcript row, a stored proposal card, telemetry, a quality counter (§2, §5.6).
- The intent union grows again — in particular to anything that assigns work to another member, invites somebody to an event, moves an event, deletes a task, or touches the baby tracker. Each of those is refused by name in the code, with its reason recorded; adding one means answering that reason here first.
- The surface is offered to child accounts (§7).
- The feature is offered at a privacy level other than Device-only, where a server assistant already exists and the trade is different.
- The hardware matrix described in §5.7 is run, and this document's §8 stops being a prediction.
10. Checklist against exposure draft s 38(2) (verified 2026-09-08)
| s 38(2) | Requires | Answered in |
|---|---|---|
| (a) | nature, scope, context, flow and purposes | §§1–3 |
| (b) | why collection is strictly necessary | §2 — per item; the honest answer is that nothing is collected |
| (c) | lawful and fair means | §4 |
| (d) | consistency with the best interests of the child | §7 |
| (e) | how the entity complies with the Code and the Act | §4, §6 |
| (f) | risk of harm to, and impact on, children | §5, §7 |
s 38(3): written before the code. s 39: this assessment is listed on our published register of impact assessments.
11. Sources
- Children's Online Privacy Code, exposure draft —
docs/sources/, as cited by the sibling assessments. ss 10, 13, 14(3)(b), 15–17, 20, 28, 33, 38, 39. - Online Safety (Age-Restricted Social Media Platforms) Rules 2025, s 4A(5)–(6) —
docs/sources/. Relevant only to §5.6, and only to explain why it does not engage. docs/ml_experimentation.md→ 2026-09-03 and 2026-09-07 — the measured behaviour of the on-device model in this app.docs/pia/2026-09-ban-self-assessment.md§5.3 — the feedback-feature reasoning §5.6 leans on.docs/privacy_posture.md§5 — the scoped child assistant §7 declines to build yet.
No source was unreachable for this assessment.