sofar

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

  1. 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.
  2. 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.
  3. Decision-linked only. Every stream below names the decision it informs. A metric that informs no decision is not collected.
  4. 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.
  5. 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 source values per initiative per window, agent tools onlycli and hook are 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/telemetry body {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.