Clean successor to V3 (forge: lilith/atlilith). Seeded from local Mac working tree at ~/Code/@projects/@cocottetech/. node_modules and build artifacts excluded via .gitignore. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
163 lines
13 KiB
Markdown
163 lines
13 KiB
Markdown
# sar-composer.screen
|
|
|
|
Quinn fulfills a GDPR / CCPA subject access request a prospect emailed her. Pairs with [brief V §V3](./V-data-portability-erasure.brief.md) — the prospect-side SAR flow. The composer scopes, redacts, and sends an export of a single prospect's data, on the prospect's behalf, with platform internals redacted by default.
|
|
|
|
Voice register: **plain** throughout — per [`voice.md`](./00-system-voice.md) §V2c. SARs are legally consequential. No metaphor; exact nouns, short sentences.
|
|
|
|
## Layout (full-screen composer)
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────┐
|
|
│ ◄ Privacy Preview │ 56pt top bar
|
|
├─────────────────────────────────────────────────┤
|
|
│ │
|
|
│ Subject access request │ title (plain)
|
|
│ A prospect has asked what data you hold on them.│
|
|
│ │
|
|
│ ─── Prospect ────────────────────────────── │
|
|
│ ╭───────────────────────────────────────╮ │
|
|
│ │ Search by phone, email, or handle │ │ prospect lookup
|
|
│ ╰───────────────────────────────────────╯ │
|
|
│ │
|
|
│ Felix · +49 30 12345678 · 14 messages · 8 wks │ lookup result row
|
|
│ │
|
|
│ ─── Scope ────────────────────────────────── │
|
|
│ ● All data on this prospect │
|
|
│ ○ Date range [ 2026-02-01 ] → [ 2026-05-18 ] │
|
|
│ │
|
|
│ ─── Format ───────────────────────────────── │
|
|
│ ● Markdown bundle (human-readable) │
|
|
│ ○ JSON archive │
|
|
│ │
|
|
│ ─── Delivery ─────────────────────────────── │
|
|
│ ● Prepared link (you send it manually) │
|
|
│ ○ Auto-email via mail-sync to felix@example.com │
|
|
│ (verified 2026-04-02) │
|
|
│ │
|
|
│ ─── Redaction preview ────────────────────── │
|
|
│ 9 fields · 4 included · 5 redacted · review ▾ │ expandable table
|
|
│ │
|
|
│ ─── Refuse instead ──────────────────────── │
|
|
│ [ Refuse this SAR ] │ secondary action
|
|
│ │
|
|
└─────────────────────────────────────────────────┘
|
|
↓ Preview routes to ready-to-send sheet ↓
|
|
```
|
|
|
|
## Ready-to-send sheet (state 6)
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────┐
|
|
│ Send SAR to Felix │
|
|
│ │
|
|
│ 14 message exchanges over 8 weeks. │
|
|
│ Markdown bundle · 28 KB. │
|
|
│ Delivery: prepared link. │
|
|
│ 5 fields redacted (platform internals). │
|
|
│ │
|
|
│ [ Hold off ] [ Generate link ] │
|
|
└─────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Component table
|
|
|
|
| Component | Notes |
|
|
|---|---|
|
|
| Top bar | Back to Privacy (S8); Preview disabled until prospect + scope set. |
|
|
| Prospect lookup | Free-text search against hashed phone / email / display name. Returns at most 5 candidates with message count + first-contact date. |
|
|
| Lookup result row | Tap to select. Selected row pinned above search field. |
|
|
| Scope picker | All-data default; date-range secondary with two date fields. |
|
|
| Format picker | Markdown bundle default (per V3a — human-readable for SAR). JSON archive secondary. |
|
|
| Delivery picker | Prepared-link default (Quinn previews + sends). Auto-email disabled until prospect has a verified email on file. |
|
|
| Redaction preview | Collapsed summary line; tap to expand the per-field table (next section). |
|
|
| Refuse action | Routes to refusal path (state 8). Secondary visual weight. |
|
|
| Preview button | Activates when prospect + scope + format + delivery set. |
|
|
|
|
## States
|
|
|
|
1. **Default** — empty composer; only prospect lookup active; everything below greyed.
|
|
2. **Prospect-lookup-results** — search returned candidates; tap to select. Multi-match disambiguation if 2+ rows match the hash.
|
|
3. **Scope-narrowed** — date-range chosen; message-count estimate updates in real time ("8 messages in window").
|
|
4. **Redaction-preview-rendered** — expanded table (below) visible; per-row override controls active.
|
|
5. **Auto-redaction-flipped-by-Quinn** — Quinn flipped an "included" row to "redacted" (one-tap, allowed) or attempted to flip "redacted" → "included" (gated by override modal — typed-confirm + reason).
|
|
6. **Ready-to-send** — Preview tapped; confirmation sheet shown above.
|
|
7. **Sent confirmation** — receipt strip in chat-home: "SAR sent to felix@example.com via Proton. 14 message exchanges over 8 weeks. Redacted by default." Audit row recorded with `action_type='sar_fulfilled'`.
|
|
8. **Refusal path** — refusal sheet replaces composer body. Two required fields: reason (text) + date of refusal-grounds (e.g. "active dispute since 2026-04-10").
|
|
9. **Refusal-reason-typed** — both fields filled; "Log refusal" button activates. On submit, audit row recorded with `action_type='sar_refused'` and the reason payload; banner reminds Quinn the prospect can escalate to a regulator.
|
|
|
|
## Redaction preview table
|
|
|
|
Per V3b. Renders inside the expanded "Redaction preview" section.
|
|
|
|
| Field | Status | Reason | Override |
|
|
|---|---|---|---|
|
|
| Their messages to Quinn | included | their own data | flip to redacted (one-tap) |
|
|
| Quinn's replies to them | included | their conversation | flip to redacted (one-tap) |
|
|
| Their attachments | included | their own data | flip to redacted (one-tap) |
|
|
| Timestamps + thread structure | included | their conversation shape | flip to redacted (one-tap) |
|
|
| Quinn's drafts that never sent | redacted | Quinn's drafts, not theirs | override gated |
|
|
| Specialist / agent IDs that handled them | redacted | platform internal | override gated (K platform-internals invariant — see Privacy invariants) |
|
|
| Quinn's audit decisions about them | partial | event types kept; internal reasoning redacted | override gated |
|
|
| Coop reports about them | redacted | shared coop data — separate request path (brief N) | not overrideable |
|
|
| Prospect_id + internal flags | redacted | platform metadata | not overrideable |
|
|
| Quinn's persona data | redacted | not their data | not overrideable |
|
|
| Hotel addresses if mentioned in thread | redacted | brief K §K3f-2 hard rule | not overrideable |
|
|
| Quinn's govt name if referenced | redacted | brief K §K3c-1 hard rule | not overrideable |
|
|
|
|
Override-gated rows open a typed-confirm modal: "Include this in the export. Type `include` to confirm." Not-overrideable rows show a lock glyph + tooltip pointing to the governing brief.
|
|
|
|
## In-the-wild copy
|
|
|
|
**Composer header** (plain):
|
|
> A prospect has asked what data you hold on them. Build the export, review what's included, and send.
|
|
|
|
**Auto-redaction explainer** (plain, in redaction-preview header):
|
|
> 5 fields are redacted by default. They cover platform internals, your drafts, and other people's data. You can include a field manually — it'll ask you to confirm.
|
|
|
|
**Refusal warning** (plain — per V3d):
|
|
> Refuse this SAR. You'll need to give a reason and date. The refusal logs in audit. The prospect can escalate to a regulator. Continue?
|
|
|
|
**Send receipt** (plain — per V3c):
|
|
> SAR sent to felix@example.com via Proton. 14 message exchanges over 8 weeks. Redacted by default.
|
|
|
|
**Refusal receipt** (plain):
|
|
> Refusal logged. Reason on file. The prospect was notified of the refusal and their escalation right.
|
|
|
|
## Privacy invariants
|
|
|
|
Cross-cut with brief K, brief N, brief Q. Enforced at the redaction layer regardless of Quinn override attempts.
|
|
|
|
- **K3c-1 govt-name never** — Quinn's legal name is never in a SAR export. Lock glyph; not overrideable. Applies even if the prospect happens to know the name.
|
|
- **K3f-2 hotel address never** — tour hotel addresses are scrubbed from any thread text included in the export. Not overrideable.
|
|
- **Brief N coop reports separate path** — peer reports about the prospect never enter a SAR. The prospect must request from each coop separately. Not overrideable.
|
|
- **Platform internals always redacted** — specialist IDs, agent_actions reasoning fields, prospect_id, internal flags. Override-gated, not free-flip — opening platform internals requires the typed-confirm modal and a logged reason.
|
|
- **Quinn's drafts redacted by default** — drafts that never sent are Quinn's thinking, not the prospect's data. Override-gated.
|
|
|
|
## Edge cases
|
|
|
|
- **Prospect cannot be looked up** — search returns no match. Banner: "No prospect matches that identifier. Search again, or refuse the SAR citing 'no records'." Routes to refusal path with a pre-filled reason.
|
|
- **Multiple prospects match** — hash collision or shared identifier across personas. Composer shows a disambiguation list with message count + first-contact date + which surface. Quinn picks one; can also pick "all matches" if she's confident they're the same human (rare; gated by a typed-confirm).
|
|
- **Prospect blocked in K1 still gets SAR** — being on Quinn's personal blocklist does not relieve the legal obligation. Composer shows a yellow banner: "This prospect is on your blocklist. SAR is still required by law. Refusal needs separate lawful basis." Send still allowed.
|
|
- **Delivery failure** — auto-email bounces or prepared-link generation fails. Banner per brief M §M2: "Couldn't generate the link. Retry or switch delivery channel?" Composer state preserved; no audit row yet (the action hasn't happened).
|
|
- **Prospect later disputes redaction** — the prospect replies "you redacted too much, send me everything." Composer entry from prospect drawer re-opens with the prior SAR's redaction state pre-loaded; Quinn can flip override-gated rows and re-send as an amended SAR. Original SAR's audit row stays; amended SAR gets `action_type='sar_amended'` with a back-reference.
|
|
- **SAR draft kept open across vigils** — swipe-down on composer prompts "Save as draft?" Drafts live in the prospect drawer entry until sent or discarded.
|
|
- **Prospect identifier hashed differently than stored** — per-coop salt rotations don't affect SAR (SAR uses Quinn's account-local data, not coop hashes). The prospect lookup is over Quinn's own messaging history.
|
|
|
|
## Voice / TTS
|
|
|
|
- Section reading order: title → prospect → scope → format → delivery → redaction preview → refuse.
|
|
- Required-field hints announce on focus.
|
|
- Redaction preview rows read as "field: status — reason" (e.g. "their messages to Quinn: included; their own data").
|
|
- TTS prosodic shift to slower / lower on the refusal warning copy and on the typed-confirm modals — plain register stays plain when spoken aloud.
|
|
- VoiceOver: prospect identifier in the lookup result reads partially masked by default ("Felix, phone ending 5678") unless Quinn taps reveal.
|
|
|
|
## Related
|
|
|
|
- [brief V §V3](./V-data-portability-erasure.brief.md) — parent design (composer + redaction preview + send + refusal).
|
|
- [brief B §B3](./B-drawers.brief.md) — prospect drawer entry point ("Send subject access request export").
|
|
- [brief K §K3c, §K3f](./K-safety-blocklist.brief.md) — identity invariants enforced in the redaction layer.
|
|
- [brief N](./N-provider-coop.brief.md) — coop reports separate request path; never inside a SAR.
|
|
- [brief S §S8](./S-settings-ia.brief.md) — Settings entry point ("Field a prospect SAR").
|
|
- [brief I](./I-audit-trust-replay.brief.md) — `sar_fulfilled` / `sar_refused` / `sar_amended` audit rows.
|
|
- [publish-report.screen.md](./publish-report.screen.md) — sibling composer pattern (high-stakes, plain register, preview-then-confirm).
|
|
- [`voice.md` §V2c](./00-system-voice.md) — plain register throughout.
|