# description
Source-of-truth JSON Schema for client-emitted user events, client-session context, and typed user preferences. Generates contracts/observe/user/events.generated.ts (zod) via scripts/codegen/forward/workspace/zod/user.mjs; Go structs at services/backend/api/internal/handler/telemetry/events.go must keep field tags in sync.

# $comment
$defs library schema — top-level shape is intentionally absent (no `type`/`oneOf` at root); strictness lives on each `$defs` member individually via their own `additionalProperties: false`.

# $defs.BlogEventName.description
Closed blog analytics namespace. The backend accepts these names only from the blogs surface.

# $defs.BlogPageInstanceID.description
Ephemeral in-memory page instance ID; never persisted in a cookie or local storage.

# $defs.VoiceCadencePref.description
How readily Billy volunteers on a live call. The backend stamps this into the LiveKit room metadata as `interjection_cadence`; the AI worker's personalized open-turn gate (ADR-293 Seam 3) reads it — quiet raises the bar (Billy waits to be asked), chatty lowers it (Billy speaks up sooner). balanced is neutral.

# $defs.VoiceConsentPrefs.description
GDPR-shaped consent block. Each toggle is opt-in by default (false). Backend voice handlers MUST consult these before persisting recordings, accepting clone enrollments, or performing passive voice-print capture.

# $defs.VoiceConsentPrefs.properties.recording.description
Persist call audio + transcripts for memory recall.

# $defs.VoiceConsentPrefs.properties.cloning.description
Allow F5-TTS clone enrollment from a reference sample.

# $defs.VoiceConsentPrefs.properties.passiveEnrollment.description
Allow background diarizer to auto-enroll speaker prints from session audio.

# $defs.VoicePrefs.description
Mirrors voxpb.v1.SynthesizeRequest defaults plus a small consent block. Backend / voice bridge resolve the voice pipeline from these prefs before every Synthesize / Transcribe call. `threadOverrides` lets a single thread pin a different voice without touching the global default (e.g. clone for the user's own thread, Bella for a shared meeting).

# $defs.VoicePrefs.properties.style.description
Orpheus emotion tag (e.g. 'neutral', 'excited'). Ignored by Kokoro / F5-TTS.

# $defs.VoicePrefs.properties.sttLanguage.description
BCP-47 hint for VoxService.Transcribe; empty = auto-detect.

# $defs.VoicePrefs.properties.threadOverrides.description
Per-thread voiceId override keyed by thread id.

# $defs.TileKind.description
What a placed element IS. Three are currencies the Store installs — an App is a place you enter, a Glance is a reading you take, an Action is a control you pull. The fourth, a Folder, is the one kind the user authors rather than installs: a container the user makes by dropping one tile onto another, holding tiles on its own small lattice.

# $defs.TileSize.description
Footprint on the reference 8-column lattice: 1x1 icon/action/folder, 2x2 small glance, 4x2 wide glance, 4x4 instrument.

# $defs.Tile.description
One placed element, on the field or inside a folder. Field coordinates are stored against the 8-column reference lattice and projected down for narrower windows. When `folder` is set the tile is a folder member and its page/col/row address the folder's own lattice instead, so one flat array holds both planes and neither can orphan the other.

# $defs.Tile.properties.ref.description
App plugin key, Glance widget type, Action id, or a minted folder id.

# $defs.Tile.properties.name.description
User-authored label. Folders only — every other kind reads its name from the catalog.

# $defs.Tile.properties.folder.description
Id of the folder tile holding this one. Absent for anything sitting on the field. A folder may not carry it: containers do not nest.

# $defs.DashboardPrefs.description
Launchpad home layout. v3 stores free-placement `tiles` on a paged lattice; `widgets` is the retired v2 ordered-list shape, retained only so the client can migrate a stored v2 arrangement forward without losing a widget.

# $defs.MachinePrefs.description
How far Billy may reach on the person's own machine, over and above the per-folder grants the Billy Bridge holds on disk (ADR-100). A grant says a folder MAY be reached; this section says how widely a reach may range once the grants exist. Opt-in by default (false), durable until the person turns it back off in Settings -> Computer or tells Billy to.

# $defs.MachinePrefs.properties.searchAllFolders.description
Let a read-only search span every folder already granted to the bridge, instead of only the folder this conversation opened. Off by default, in which case an unopened conversation searches Billy's own home plane alone. Grants nothing new: a folder still has to be granted to be searched, and writes still require an explicitly opened folder.

# $defs.BrowserPrefs.description
Whether Billy may drive a browser on the person's machine and ACT there as them, and on which sites (ADR-402). Deliberately not part of `machine`: a folder grant is about files sitting still, where this is about acting on the web wearing their session — the one capability in the family that can spend money. Off by default; the switch and the list are separate so turning it off for a while does not cost them the approvals they already made.

# $defs.BrowserPrefs.properties.enabled.description
May Billy open his own browser on this machine and act in it. Off by default. One switch so revoking is one action rather than an audit of every site.

# $defs.BrowserPrefs.properties.origins.items.description
One approved origin — scheme + host + non-default port, no path.

# $defs.BrowserPrefs.properties.origins.description
Where Billy may act, exactly. Reading the open web is unbounded because it carries no identity; acting is bounded by this list, matched per-origin against the page's LIVE url on the device (no subdomain wildcards — `*.example.com` reads like a convenience and spends like an authorization for whatever else that domain hosts).