Telemetry policy
The published, opt-in, metadata-only metric schema — what is measured, from which columns, and what can never enter telemetry.
This page publishes the telemetry schema (v0.2, 2026-07-14) as maintained
in the repository as TELEMETRY.md. It states what is measured, from which
stored columns, and what can never enter telemetry.
Position
- Opt-in, org-level, default OFF. An org owner enables telemetry; until then nothing is derived. Opting out stops derivation and deletes the org's telemetry rows — they are aggregates, disposable by construction.
- Metadata-only. Telemetry derives from envelope and typed columns only — never payloads. No goal text, no task titles, no decision text, no note bodies, no file paths, no slugs, no emails.
- Decision-linked only. Every stream below names the decision it informs. A metric that informs no decision is not collected.
- Server-derived, zero new egress. Every telemetry user is a sync user, so the inputs are already in the columns the service stores operationally. Telemetry is a derivation over that data, not a new collection path — the opt-in gates the analytics use, not the sync. There is no client-side telemetry ping for local-only users.
- Pseudonymous store. Telemetry rows carry opaque ids and per-org-salted hashes of user ids — the telemetry store holds no personal data and survives user erasure.
The five streams
1. Write-back compliance
- Inputs: session lifecycle events (
session_started/session_ended) per initiative stream. - Definition: sessions that ended with a write-back (summary + next action recorded) ÷ sessions started, per org per day.
- Decision informed: is the protocol being followed without ceremony? Falling compliance means the write-back is too expensive — fix the product, not the user.
2. Staleness
- Inputs: the engine's own freshness fold, read from materialized initiative state.
- Definition: distribution (p50/p95) of events-since-last-write-back across active initiatives; count of stale active phases.
- Decision informed: can a teammate trust the record? Staleness is the leading indicator of trust collapse.
3. Handoff / resume events
- Inputs: session boundaries per initiative, ordered by event id; first post-resume activity event.
- Definition: a handoff is session N+1 starting on an initiative after session N ended. Recorded per handoff: resume gap (seconds), same-tool vs cross-tool, and time-to-productive-work — seconds from session start to its first task status change or decision.
- Decision informed: is the cross-session wedge real at scale?
- Honesty note: re-explanation words and clarifying-question counts would require reading conversation content — that violates metadata-only, so they stay manual (spot-checks with pilot consent). Time-to-productive-work is the automatable proxy.
4. Cross-tool ratio
- Inputs: distinct
sourcevalues per initiative per window, agent tools only —cliandhookare surfaces, not tools, and never count. - Definition: initiatives touched by ≥ 2 distinct tools ÷ active initiatives (28-day window); secondary: cross-tool handoffs ÷ all handoffs.
- Decision informed: the core product gate. A zero cross-tool ratio means Sofar is a single-tool accessory, not a protocol.
5. Multi-human touch
- Inputs: distinct pushing users (salted hash) per initiative per window — attribution is mechanical, never inferred.
- Definition: initiatives with ≥ 2 distinct humans pushing events (28-day window), per org.
- Decision informed: which orgs crossed from solo to team use.
Storage shape
One typed row per org per UTC day — pure aggregates, no JSON blobs:
telemetry_daily
org_id ulid FK · day date
sessions int · writebacks int -- stream 1
staleness_p50 int NULL · staleness_p95 int NULL
stale_active_phases int · active_initiatives int -- stream 2
handoffs int · cross_tool_handoffs int
resume_gap_p50_s int NULL · ttpw_p50_s int NULL -- stream 3
multi_tool_initiatives int -- stream 4
multi_human_initiatives int -- stream 5
PK (org_id, day)Percentile columns are NULL when the day had nothing to measure — never fake zeros. Derivation runs daily at 00:10 UTC over opted-in orgs, idempotent per day. Deleting an org's rows is a single delete — nothing else references them.
Derivation semantics
- Day bucketing is the UTC calendar day of the event's own timestamp (work time), never the server arrival time (transport time).
- "Active initiative" has one definition shared by streams 2, 4, and 5: at least one event in the trailing 28-day window ending with the derived day.
- Handoff pairing: each session start pairs with the most recent session end before it in event order; a close without write-back still hands off, but only a proper write-back counts as compliance in stream 1. Clock-skewed gaps clamp to zero.
- Staleness snapshots the current materialized state's freshness fold at derivation time — the engine's own functions, never a re-implementation.
Surface
Owner-only; non-owner and unknown org both answer 404.
GET /v1/orgs/:slug/telemetry→{opted_in, opted_in_at, days}— the derived rows (≤ 90 days, newest first).PUT /v1/orgs/:slug/telemetrybody{enabled: bool}. Repeat enables keep the original consent timestamp; disable deletes the org's rows in the same request.
What never enters telemetry
Payload content of any event type; task, phase, and initiative titles; goals; slugs; decision and note text; file paths; raw user ids or emails; repo names; conversation content. If a future metric needs any of these, it is not telemetry — it is a product feature requiring its own consent surface.