Skip to content

ADR-042: Synapses — twin chrome metric

Status: Proposed Date: 2026-05-06 Context: Cici's twin redesign includes a chrome number on the share/profile card, currently mocked at "1284" and labeled "synapses." There is no agreed product definition for what synapses are, what they count, or whether they belong in the chrome at all. This ADR forces the decision before any code touches it. The framing is borrowed from neuroscience — synapses are connections — and is meant to read as "your twin has formed N connections about how you think."

At a glance

What it decides: a proposal (recommended, not yet in force) to define "synapses" — the chrome number on the twin card — as a hybrid of how much you've played and what your twin has learned, rather than a relabeled play count.

  • Core mechanismsynapses = decisions_made + (context_rules_count × multiplier), multiplier starting at 5 and tuned later from real data.
  • Why hybrid — decisions = engagement, patterns = learning; it grows visibly early and stays honest if a user reverse-engineers it.
  • Main rejected alternative — the "lazy" decisions-only version mislabeled as synapses; if hybrid isn't worth it, strip the metric entirely instead.
  • Cost/risk — pure read-side derivation (no schema change), but a second headline number competes with sync_score for attention on a tight card.
  • Open question — the multiplier value; deliberately deferred until ~50 active users with 20+ games reveal a distribution.

Decision

Recommended: Adopt synapses as a hybrid metric combining engagement and learning:

synapses = decisions_made + (context_rules_count × multiplier)

Where: - decisions_made = total choices rows for the user (≈ games_played × 4) - context_rules_count = jsonb_array_length(player_decision_profiles.context_rules) — distinct situational patterns the twin has identified - multiplier = tunable constant; start at 5, calibrate after seeing real distributions

Display on: twin share card (per ADR-041), Twin profile page header.

Alternative if rejected: strip the metric from the design entirely. Do not ship the lazy "just decisions" version — calling raw decisions "synapses" is misleading and erodes trust the moment a user does the math.

Rationale

The hybrid is the only definition that earns its keep:

  • Both inputs are real signals. Decisions = engagement (how much you've played). Patterns = learning (what the twin has actually figured out about you). Each alone is incomplete; combined they cover both axes.
  • It grows visibly early. New users see ~4 synapses per round played. The number climbs every game, which is the engagement-loop value.
  • It rewards depth, not just play. When the twin forms a new pattern (e.g. "under crisis, you flip to pragmatism"), synapses jump by multiplier. That's a real moment — the twin has learned something. The metric reflects it.
  • It's defensible to scrutiny. A user who reverse-engineers it doesn't feel cheated — both halves are honest.

Multiplier of 5 is a starting guess, not a load-bearing decision. After ~50 active users have played 20+ games each, look at the distribution and tune. If new users feel like the metric grows too slow, raise it. If experienced users hit absurd numbers, lower it.

Alternatives Considered

  • Total decisions only (games_played × choices_per_game). Rejected. It's a vanity metric mislabeled as learning. Users figure out it's just play count and the framing collapses. This is the "lazy version" referenced in the Decision.

  • Pattern count only (context_rules_count). Rejected. Grows too slowly to feel rewarding early — a user with 30 games might have only 5–10 patterns. The number stays small and the share card looks unimpressive. Engagement loop dies.

  • Drop the metric entirely. Real candidate. Sync_score is already the headline number. Adding a second number competes for attention on a tight share card. The argument for keeping synapses is differentiation: "1,284 synapses" is more poetic and shareable than "sync_score: 67." If the share-twin feature isn't a priority, this alternative wins.

  • Synapses based on per-signal signal-confidence (one synapse per "the twin is confident about X about you"). Rejected — much harder to compute, harder to explain, and the count would be artificially capped (12 signals × N states). Doesn't have the "growing number" feel.

  • Time-decayed synapses (recent activity weighted more, old activity decays). Rejected as premature complexity. The decay_log system already exists for sync_score; layering decay onto synapses doubles the cron surface without clear product gain.

Discussion

This is a vanity-vs-real-metric tension and the resolution depends on whether the chrome-number on the share card matters as a product surface.

The case for synapses existing at all: - A twin without a "knowledge accumulation" number feels static — like a quiz score, not a model that grows. Synapses gives the twin a sense of living. - Share-card virality. A friend pastes a Sync share link and the recipient sees "Jonathan's twin has 1,284 synapses." That's intriguing in a way "sync_score: 67" isn't. - Onboarding feedback for new users. They need to see the twin "forming." A growing number does that.

The case against: - Two headline numbers (sync_score + synapses) on a tight share card is one too many. The eye doesn't know where to land. - If users figure out it's a hybrid math expression, the poetry is broken and they're left with a less-meaningful number than sync_score. - Maintenance burden — every edge case (deleted accounts, campaign vs solo, twin-update-without-game-played) needs a synapses answer.

The recommendation lands on hybrid because if synapses ships, the lazy version is the worst possible outcome. Either commit (hybrid, with the multiplier as a tunable) or strip it. The middle ground (decision-count-only with the "synapses" label) sells what isn't there.

The multiplier value (5 vs 10 vs 20) is not a decision worth debating before real distribution data exists. Pick 5, ship, look at the distributions after a couple of weeks of active play, tune in a 10-minute exercise. Don't pre-optimize.

A subtle detail: this metric is additive only. It never decreases. That's deliberate — synapses are connections the twin has formed, and even if a pattern goes stale, the twin did form it. Re-introducing decay (per the dropped alternative) would change the framing from "what your twin has learned" to "what your twin currently knows," which is what sync_score already measures.

Consequences

  • One read-side helper in src/lib/twin/synapses.ts: function computeSynapses(userId) returning a number. Pure derivation; no schema change, no migration.
  • Surfaces: twin share card chrome (ADR-041), /twin profile header. Two render paths, one helper.
  • Calibration loop: review the multiplier value at the next product check-in (target: 50+ active users with ≥20 games each). If never reached, the number doesn't matter enough to tune.
  • Documentation: the framing copy on the share card matters. Recommended subtitle: "connections your twin has formed — decisions seen × patterns learned." That's honest about what the number measures while preserving the metaphor.
  • Watch: if synapses grows much faster than sync_score in early users, the share card visual hierarchy needs work — synapses will dominate visually and dilute sync_score. Tune the multiplier down or change layout.

Key files (proposed): - src/lib/twin/synapses.tscomputeSynapses(userId) helper - src/components/twin/TwinShareCard.tsx (per ADR-041) — renders the synapses chrome with the helper - src/app/(app)/twin/page.tsx — header gains synapses field