Skip to main content
← Back to list
01Issue
FeatureShippedSwamp CLIPublic
Assigneesstack72

Relationships

#1679 Serve telemetry attribution for collectives

Opened by stack72 · 8/17/2026· Shipped 8/17/2026

Problem

When a swamp serve daemon runs with a collective API key (SWAMP_API_KEY=[REDACTED-SECRET-1]), every cli_invocation telemetry event it produces is unattributed. The events exist in the pipeline but nobody gets XP for them.

Root cause

The /ingest handler resolves API keys via verifyApiKey(), which only checks the apikey collection (personal keys). Collective keys live in collective_apikey and are resolved by verifyCollectiveApiKey() — but the telemetry service never calls it. So swamp_org_* keys silently fail to resolve, and the event is stored without a username.

Proposed solution

Step 1: Resolve collective API keys at ingest (change)

In services/telemetry/lib/server.ts, the API key resolution currently only calls verifyApiKey(). It needs to also try verifyCollectiveApiKey() when the personal lookup returns null.

When a collective key resolves, override distinct_id to collective:<collectiveId> at ingest time. This naturally separates collective events from individual ones in downstream MVs without requiring schema changes to cli_daily.

Stamp the event with:

  • username@<collectiveSlug>
  • distinct_idcollective:<collectiveId>
  • collective_token: true in properties
  • member_user_ids and member_usernames — resolved from the member collection at ingest time (membership snapshot frozen at ingest, matching the extension publish pattern)

verifyCollectiveApiKey is already in shared/auth/api_key.ts — just needs to be imported into the telemetry service's main.ts and wired as a fallback.

Step 2: New activity-distribution projector (new)

Analogous to publish-distribution.sql. Reads directly from swamp.events (not cli_daily — the MV lacks collective_token and member arrays).

  • Reads cli_invocation events where collective_token = true
  • Runs activity through the existing diminishing-returns curve first, THEN applies 1.5x multiplier (post-curve, so the ceiling lifts for teams — rewarding collaboration)
  • Distributes across members via arrayJoin over member_user_ids
  • Each member gets their own score_grants row with distinct_id = member_id, origin_distinct_id = collective:<id>

No changes needed to the individual activity projector — because distinct_id is overridden to collective:<id> at ingest, these events flow into cli_daily under a synthetic id that no identity_map entry will ever resolve to a real username. The individual projector naturally ignores them.

Step 3: CLI-side audit field (nice-to-have, separate repo)

In the swamp CLI (src/serve/telemetry.ts, src/serve/handlers/*.ts), include initiatedBy in event properties to record which user triggered the serve API call. Audit-only — not used for scoring. The telemetry service doesn't need this to attribute correctly.

Risks to address during implementation

  1. Double-scoring window at deployscore_grants is append-only. If the distribution projector goes live while events are still being scored individually, scores inflate permanently. Deploy atomically, or gate the projector on a date cutover.

  2. Volume — Serve daemons can generate far more events than human CLI users. Each event carrying a full member array means repeated data. Monitor score_grants row growth after rollout.

  3. cli_daily carries the synthetic collective:<id> distinct_id — these rows are harmless (the activity projector won't resolve them to a username) but they do exist. If cli_daily is ever used for something that should exclude collective activity, this will need attention.

Files to change (swamp-club)

  • services/telemetry/main.ts — import and wire verifyCollectiveApiKey as fallback resolver
  • services/telemetry/lib/server.tshandleIngest: try collective key when personal fails; stamp username, collective_token, members; override distinct_id
  • services/telemetry/lib/activity-distribution.sql — new projector for collective cli_invocation scoring (post-curve 1.5x, arrayJoin fan-out)

Existing pattern to follow

routes/api/v1/extensions/confirm.ts (emit side) + services/telemetry/lib/publish-distribution.sql (projector side) — this is the proven collective scoring pattern. The new work mirrors it for activity events.

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 7 MOREREVIEW+ 4 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

8/17/2026, 2:45:32 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack728/17/2026, 12:51:50 AM

Sign in to post a ripple.