Skip to content

feat(prime): surface teammate names and sync'd context throughout sessions#508

Merged
rsnodgrass merged 1 commit intomainfrom
ryan/team-delight-ux
Apr 13, 2026
Merged

feat(prime): surface teammate names and sync'd context throughout sessions#508
rsnodgrass merged 1 commit intomainfrom
ryan/team-delight-ux

Conversation

@rsnodgrass
Copy link
Copy Markdown
Contributor

@rsnodgrass rsnodgrass commented Apr 13, 2026

Summary

Weaves teammate identity throughout agent sessions so AI coworkers credit specific people by name alongside SageOx as the enabler — transforming generic "Based on SageOx guidance" into "SageOx surfaced Ryan's discussion about API design."

  • Attribution guidance — Prime and murmur framing now instruct agents to credit both teammates and SageOx (e.g., "SageOx noticed Ryan is working on...")
  • Whisper from= attribute — Murmur whisper entries now carry from="Ryan" derived from the sender's principal ID, giving agents a name to use
  • Discussion participantsox agent team-ctx extracts unique speakers from VTT transcripts and displays them alongside discussion titles
  • Cross-session credit — Prime instructions nudge agents to find and attribute prior sessions/discussions to teammates by name
  • PrincipalID plumbing — Threads human principal identity through heartbeat → daemon instance store → IPC response for future teammate features

Mermaid: Attribution Flow

flowchart LR
    A[Heartbeat] -->|PrincipalID| B[Daemon Instance Store]
    B --> C[InstanceInfo.PrincipalID]
    D[Murmur] -->|principal_id| E[Whisper Entry]
    E -->|FirstNameFromSlug| F["from='Ryan' in XML"]
    G[VTT transcript] -->|UniqueSpeakers| H["Discussion (Ryan, Sarah)"]
    I[Prime guidance] -->|attribution phrases| J["'SageOx surfaced Ryan's discussion...'"]
Loading

Test plan

  • make lint — clean
  • make test — 12,940 tests pass
  • ox agent prime — attribution section includes teammate+SageOx phrases
  • Murmur whisper XML includes from= attribute when PrincipalID present
  • ox agent team-ctx shows speaker names next to discussion titles

Co-Authored-By: SageOx ox@sageox.ai

Summary by CodeRabbit

  • New Features
    • Whisper entries now display speaker attribution, showing who contributed each message.
    • Recent discussions output now includes participant names and visual types.
    • Enhanced guidance for crediting teammates by name when attributing insights and approaches.
    • Agent instances now include principal identity information.

…ions

Thread teammate identity through whispers, discussions, and attribution
guidance so agents credit specific people alongside SageOx as the enabler.

Co-Authored-By: SageOx <ox@sageox.ai>
SageOx-Session: https://sageox.ai/repo/repo_019c5812-01e9-7b7d-b5b1-321c471c9777/sessions/2026-04-13T15-04-ryan-OxcXPu/view
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 13, 2026

📝 Walkthrough

Walkthrough

This PR adds principal identity attribution throughout the agent system. It extends heartbeat payloads to capture PrincipalID from user credentials, updates whisper XML rendering to include teammate attribution via derived first names, adds participant tracking from discussion transcripts, expands prime XML instructions for crediting teammates, and provides identity utility functions.

Changes

Cohort / File(s) Summary
Heartbeat & Principal Identity Tracking
cmd/ox/heartbeat.go, internal/daemon/heartbeat.go, internal/daemon/ipc.go, internal/daemon/daemon.go
Added PrincipalID field to heartbeat payloads and InstanceInfo struct; HeartbeatHandler now maintains per-agent principal metadata map, exposes it via GetAgentPrincipalID(), and cleans up stale entries; heartbeat assignment derives PrincipalID from token user info (preferring Name, falling back to Email).
Whisper Attribution & Agent Output
cmd/ox/agent.go, internal/identity/person_info.go
Whisper XML now includes from="..." attribution when PrincipalID is present; new FirstNameFromSlug() helper derives display-friendly first names from principal slugs; updated murmur framing text to reference SageOx delivery and crediting.
Prime XML & Attribution Guidance
cmd/ox/agent_prime_xml.go, internal/prime/attribution.go
Expanded instructions for crediting teammates by name when attributing insights; added new "Teammate Attribution" block with example phrasing and directives to use ox session list/ox query for finding related work; emphasized crediting both teammate and SageOx.
Discussion Participants & Team Context
cmd/ox/agent_team_ctx.go, cmd/ox/distill_discussions.go, internal/vtt/parse.go
DiscussionIndexEntry now includes Participants []string field extracted from transcripts; discussion output formatting now includes participant/visual-type suffixes; UniqueSpeakers() helper iterates VTT cues to collect distinct speaker names in order.

Sequence Diagram

sequenceDiagram
    participant Agent as Agent<br/>(cmd/ox/agent.go)
    participant Heartbeat as Heartbeat<br/>(internal/daemon/heartbeat.go)
    participant Identity as Identity<br/>(internal/identity/person_info.go)
    participant Whisper as Whisper<br/>Renderer

    Agent->>Heartbeat: Emit heartbeat with token user info
    Heartbeat->>Heartbeat: Extract & store PrincipalID from token<br/>(Name or Email)
    Agent->>Heartbeat: Query GetAgentPrincipalID()
    Heartbeat-->>Agent: Return PrincipalID
    Agent->>Identity: Derive first name via<br/>FirstNameFromSlug(PrincipalID)
    Identity-->>Agent: Return capitalized first name
    Agent->>Whisper: Format whisper entry with<br/>from="FirstName"
    Whisper-->>Agent: Return XML with attribution
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 With whispers now signed, teammates can see
Who nudged an idea 'cross the team's whole tree,
First names pop out from slugs, clear and bright,
Attribution flows through heartbeats of night—
SageOx says "Credit them!" and oh, what delight! 🌟

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately captures the main change: surfacing teammate names and context throughout sessions, which is the central theme unifying all file changes across attribution guidance, whisper entries, discussion participants, and principal ID propagation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ryan/team-delight-ux

Comment @coderabbitai help to get the list of available commands and usage tips.

@rsnodgrass rsnodgrass marked this pull request as ready for review April 13, 2026 23:02
@rsnodgrass rsnodgrass changed the title feat(prime): surface teammate names and credit SageOx throughout sessions feat(prime): surface teammate names and sync'd context throughout sessions Apr 13, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/ox/heartbeat.go (1)

193-201: ⚠️ Potential issue | 🟡 Minor

HeartbeatWithCreds does not set PrincipalID, unlike Heartbeat.

The Heartbeat function sets payload.PrincipalID from token user info (lines 101-106), but HeartbeatWithCreds omits this. This inconsistency means sessions using HeartbeatWithCreds won't have teammate attribution.

🔧 Proposed fix to add PrincipalID in HeartbeatWithCreds
 		if token, err := auth.GetTokenForEndpoint(projectEndpoint); err == nil && token != nil {
 			hbCreds.AuthToken = token.AccessToken
 			hbCreds.UserEmail = token.UserInfo.Email
 			hbCreds.UserID = token.UserInfo.UserID
+			// derive principal ID for teammate attribution
+			if token.UserInfo.Name != "" {
+				payload.PrincipalID = token.UserInfo.Name
+			} else if token.UserInfo.Email != "" {
+				payload.PrincipalID = token.UserInfo.Email
+			}
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/ox/heartbeat.go` around lines 193 - 201, HeartbeatWithCreds currently
populates hbCreds.AuthToken, hbCreds.UserEmail and hbCreds.UserID but does not
set payload.PrincipalID, causing missing teammate attribution; update
HeartbeatWithCreds so that after retrieving the token from
auth.GetTokenForEndpoint(projectEndpoint) you assign payload.PrincipalID =
token.UserInfo.UserID (same field Heartbeat uses), ensuring payload.PrincipalID
is set alongside hbCreds.AuthToken, hbCreds.UserEmail and hbCreds.UserID.
🧹 Nitpick comments (2)
cmd/ox/heartbeat.go (1)

101-106: Email fallback produces incorrect first names for teammate attribution.

When token.UserInfo.Name is empty and the fallback to Email is used, FirstNameFromSlug (used downstream in cmd/ox/agent.go for whisper display) will not handle the email format correctly.

splitIdentifier only splits on ., -, _ — not @. For an email like "ryan@example.com", it splits to ["ryan@example", "com"], producing "Ryan@example" as the display name instead of "Ryan".

Extract the local part before storing as PrincipalID when using the email fallback. The codebase already has this pattern in resolvePrincipal() (internal/daemon/file_change_source.go):

♻️ Proposed fix to handle email format
 			// derive principal ID for teammate attribution
 			if token.UserInfo.Name != "" {
 				payload.PrincipalID = token.UserInfo.Name
 			} else if token.UserInfo.Email != "" {
-				payload.PrincipalID = token.UserInfo.Email
+				// extract local part for consistent slug-like format
+				if idx := strings.IndexByte(token.UserInfo.Email, '@'); idx > 0 {
+					payload.PrincipalID = token.UserInfo.Email[:idx]
+				} else {
+					payload.PrincipalID = token.UserInfo.Email
+				}
 			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/ox/heartbeat.go` around lines 101 - 106, The fallback that sets
payload.PrincipalID to token.UserInfo.Email can produce incorrect display names
because FirstNameFromSlug/splitIdentifier doesn't strip the email domain; update
the chain in heartbeat.go so that when token.UserInfo.Name is empty you extract
the local part (substring before the '@') from token.UserInfo.Email and assign
that to payload.PrincipalID instead of the full email; mirror the existing
pattern used by resolvePrincipal() in internal/daemon/file_change_source.go and
ensure you still preserve the Email fallback only when a non-empty local part
exists.
cmd/ox/agent_prime_xml.go (1)

113-117: Consider centralizing the teammate-attribution copy.

The same policy text now lives here and in internal/prime/attribution.go Lines 33-40. A shared helper/constant would keep the XML and plain-text renderers from drifting.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/ox/agent_prime_xml.go` around lines 113 - 117, Extract the duplicated
teammate-attribution text into a single shared symbol (e.g., a package-level
constant TeammateAttributionCopy or accessor GetTeammateAttribution()) in the
internal/prime package, then replace the inline sb.WriteString(...) block in
agent_prime_xml.go and the duplicated block in internal/prime/attribution.go to
reference that single symbol; ensure the symbol name is exported or accessible
from both files and update imports/usages so both XML and plain-text renderers
read from the same source.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@cmd/ox/heartbeat.go`:
- Around line 193-201: HeartbeatWithCreds currently populates hbCreds.AuthToken,
hbCreds.UserEmail and hbCreds.UserID but does not set payload.PrincipalID,
causing missing teammate attribution; update HeartbeatWithCreds so that after
retrieving the token from auth.GetTokenForEndpoint(projectEndpoint) you assign
payload.PrincipalID = token.UserInfo.UserID (same field Heartbeat uses),
ensuring payload.PrincipalID is set alongside hbCreds.AuthToken,
hbCreds.UserEmail and hbCreds.UserID.

---

Nitpick comments:
In `@cmd/ox/agent_prime_xml.go`:
- Around line 113-117: Extract the duplicated teammate-attribution text into a
single shared symbol (e.g., a package-level constant TeammateAttributionCopy or
accessor GetTeammateAttribution()) in the internal/prime package, then replace
the inline sb.WriteString(...) block in agent_prime_xml.go and the duplicated
block in internal/prime/attribution.go to reference that single symbol; ensure
the symbol name is exported or accessible from both files and update
imports/usages so both XML and plain-text renderers read from the same source.

In `@cmd/ox/heartbeat.go`:
- Around line 101-106: The fallback that sets payload.PrincipalID to
token.UserInfo.Email can produce incorrect display names because
FirstNameFromSlug/splitIdentifier doesn't strip the email domain; update the
chain in heartbeat.go so that when token.UserInfo.Name is empty you extract the
local part (substring before the '@') from token.UserInfo.Email and assign that
to payload.PrincipalID instead of the full email; mirror the existing pattern
used by resolvePrincipal() in internal/daemon/file_change_source.go and ensure
you still preserve the Email fallback only when a non-empty local part exists.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 81af0b30-1d5d-46e4-8f67-9b576a5ec101

📥 Commits

Reviewing files that changed from the base of the PR and between 750c4eb and 87e9a2e.

📒 Files selected for processing (11)
  • cmd/ox/agent.go
  • cmd/ox/agent_prime_xml.go
  • cmd/ox/agent_team_ctx.go
  • cmd/ox/distill_discussions.go
  • cmd/ox/heartbeat.go
  • internal/daemon/daemon.go
  • internal/daemon/heartbeat.go
  • internal/daemon/ipc.go
  • internal/identity/person_info.go
  • internal/prime/attribution.go
  • internal/vtt/parse.go

@rsnodgrass rsnodgrass merged commit 73e3637 into main Apr 13, 2026
3 checks passed
@rsnodgrass rsnodgrass deleted the ryan/team-delight-ux branch April 13, 2026 23:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant