The user-facing privacy page for Transform My Role. What we read, what we discard, what an audit row contains, why an anonymous session ID is pseudonymous, and how saving and account deletion work.
A single non-content row is written per session to the transform_ingestions table. Its purpose is operational: it lets us answer "how many sessions completed successfully today, in what jurisdictions, in what locales?" without ever recording a job description.
That is the complete list. The migration that creates this table has zero columns that could carry job-description content. An integration test enumerates a forbidden-column list and fails CI if any new column resembles JD content.
Every Transform-My-Role session mints a random UUID — the session ID. The ID is pseudonymous: it is generated per session, never linked to a fingerprint, never derived from an identifier, and never reused across sessions. A fresh session produces a fresh UUID.
When you sign in mid-session or save the plan, your user_id is recorded on the audit row and on any saved plan; before sign-in, the session is fully anonymous. The session ID is not by itself identifying — it is an internal handle for the in-memory session store.
Zero persistence. The text crosses through extraction and is discarded immediately. No temp file, no log entry, no DB row contains the text.
60-minute hard cap. Bytes route through an in-memory ephemeral processing path; if a temp-file fallback ever has to be used, a sweep enforces the 60-minute boundary. After 60 minutes the bytes are guaranteed gone regardless of pipeline state.
Saving is registered-only. The save action persists the structured features — the extracted candidate envelope, the mapping resolution, the gap synthesis output, the plan package — into the saved_transform_plans table, keyed by your user ID. It does not persist the job description, the URL, the upload bytes, the filename, or any other content-shaped field.
A defensive zod refinement rejects any payload that carries content-shaped keys at write time, regardless of how the call site was constructed. If a future code path ever tried to slip a 'jdText' or 'pasteContent' field through the save endpoint, the write would fail with a privacy-contract error.
If you opt a saved plan in to change notifications, the daily digest is sent to your account email address. The email lists the titles of your own saved plans that changed — it is delivered only to you, the plan's owner. We never log the recipient address or any saved-plan content; a send that fails is recorded with the subscription identifier and the error type only. Every digest has a one-click unsubscribe link, and unsubscribing removes the subscription record entirely.
The paste-text and upload modes are gated by an explicit rights-acknowledgement tickbox. The tickbox affirms that you hold the rights to share the content — typically because you authored it, it was published for general distribution, or your employer permits it. The URL-fetch mode is not tickbox-gated because the fetch happens against the public web with robots.txt respected. The tickbox itself is not persisted as content; only mode='upload' or mode='text-paste' is recorded on the audit row.
Saved plans and change-notice subscriptions cascade on user deletion (ON DELETE CASCADE). If you delete your account, every row referencing your user_id in saved_transform_plans and transform_plan_subscriptions is deleted with the account.
Audit rows behave differently: user_id is nullable with ON DELETE SET NULL. The operational row is preserved (so aggregate per-jurisdiction analytics still work), but its link to your identity is dropped — the row is anonymised, not deleted. If you need the audit row deleted as well, contact support.
The curated agent panel on the plan view answers questions from a pre-baked context built from the structured candidates only. The agent context never carries the job description, the URL, or the upload bytes — this is verified in the 30 curated sample conversations that ship with the surface (10 each in EN / pt-BR / FR). The panel renders a 'This conversation is not saved' indicator; if you sign in and save the plan, the indicator switches to 'Saved to your account; the job description is not retained'.
A Pino-compatible log scrubber strips any field name that looks like content (jdText, jdUrl, jdMarkdown, uploadBytes, rawJd, sourceUrl, extractedText, uploadFilename, plus 9 additional bare-noun shapes added during the privacy/legal review) and redacts URL-like strings in free-text fields before logs leave the request. This is opt-in for the Transform-My-Role surface — the scrubber does not mask legitimate URL fields elsewhere in the codebase.