Skip to content

feat: style-aware buddy generation from coding behavior#5

Closed
Mythical365 wants to merge 2 commits intotalkvalue:mainfrom
Mythical365:feat/style-aware-buddy
Closed

feat: style-aware buddy generation from coding behavior#5
Mythical365 wants to merge 2 commits intotalkvalue:mainfrom
Mythical365:feat/style-aware-buddy

Conversation

@Mythical365
Copy link
Copy Markdown

Closes #4

What this does

Adds a coding style layer to buddy identity generation. The hook now silently tracks how you actually code — which tools you reach for, how many sessions you've had, how often you deny permissions — and uses that to nudge your buddy toward a species that fits your personality.

Shell/runner-heavy coders → dragon, robot
Explorers (Read/Grep heavy) → owl, octopus
Chaotic coders (high denial rate) → ghost, blob
Methodical coders (low chaos) → turtle, penguin
Rarely codes (<5 sessions) → snail, cactus

How it works

Two things added to buddi-hook.py:

  1. Stats accumulator — on every PreToolUse, records the tool name into ~/.buddi_style.json (a tiny local JSON file, never leaves the machine). On SessionEnd, increments session count. On permission denial, increments denial count.

  2. compute_style_identity() — Python port of the existing BuddyDetector.roll() logic, but XORs a style delta into the seed before rolling. Species selection uses weighted probabilities with per-species boosts based on the style ratios. Same user ID + same style always produces the same buddy (deterministic).

On SessionEnd, the derived identity is attached to the socket event as style_identity so the Swift side can pick it up when ready.

Backward compatibility

If ~/.buddi_style.json doesn't exist yet (fresh install, or user never had a session end), style_identity is not sent and everything behaves exactly as before.

Test

Verified locally with three coding profiles:

  • Shell-heavy → dragon ✓
  • Explorer → owl ✓
  • Rare chaotic → snail ✓
  • Same inputs = same output every time ✓

Note on the AI policy

This PR was developed with AI assistance (Claude Code), and I've reviewed, tested, and understand every line before submitting.

Tracks tool usage, session count, and permission denials via the hook
to build a coding style profile in ~/.buddi_style.json. On SessionEnd,
computes a style-derived buddy identity that blends with the existing
UUID-based roll — shell-heavy coders trend toward dragon/robot, explorers
toward owl/octopus, chaotic coders toward ghost/blob, rare coders toward
snail/cactus. Fully backward-compatible: behavior is unchanged when no
style data exists.

Closes talkvalue#4
@Mythical365 Mythical365 requested a review from grayashh as a code owner April 20, 2026 12:45
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 1 file

Confidence score: 3/5

  • There is a concrete behavior mismatch in buddi/Buddi/Resources/buddi-hook.py: the new hash path appears to always fall back to FNV for user_id + salt, so style identity may not match BuddyDetector/Wyhash as intended.
  • buddi/Buddi/Resources/buddi-hook.py also updates style stats non-atomically, which introduces real regression risk under concurrent hook runs (lost tool/session/denial counters).
  • Given the medium severities (6/10 and 5/10) with high confidence, this carries some user-impacting risk and is worth tightening before relying on it broadly.
  • Pay close attention to buddi/Buddi/Resources/buddi-hook.py - hash selection correctness and atomic counter updates under concurrency.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="buddi/Buddi/Resources/buddi-hook.py">

<violation number="1" location="buddi/Buddi/Resources/buddi-hook.py:47">
P2: Style stats updates are non-atomic, so concurrent hook invocations can lose tool/session/denial counts.</violation>

<violation number="2" location="buddi/Buddi/Resources/buddi-hook.py:175">
P2: The new hash path always falls back to FNV for `user_id + salt`, so style identity does not actually mirror BuddyDetector/Wyhash behavior.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread buddi/Buddi/Resources/buddi-hook.py Outdated
Comment thread buddi/Buddi/Resources/buddi-hook.py Outdated
Two bugs flagged in code review:

1. _wyhash fell back to FNV-1a for any key longer than 16 bytes, which
   means user_id+salt (~40-51 bytes) never used the real wyhash path and
   produced seeds inconsistent with BuddyDetector. Fixed by implementing
   the full wyhash algorithm (48-byte stripe loop + 16-byte tail) matching
   Wyhash.swift exactly.

2. Style stat updates (tool counts, sessions, denials) were non-atomic:
   concurrent hook invocations could read-modify-write the same JSON file
   and silently drop counts. Fixed by wrapping all updates in a single
   _update_style() function that holds an exclusive flock for the
   read-modify-write cycle.
@grayashh
Copy link
Copy Markdown
Member

Thanks a lot for the thoughtful write-up and the effort here — the three-profile verification and the backward-compat path are genuinely nice touches.

That said, I want to be upfront before you sink more time into this: I don't think we're going to land this in its current form, and the reason is a design conflict rather than a code-quality issue.

The buddy system is built around a one-time hatch identity. The core emotional beat we're trying to preserve is "this is my buddy — the one that rolled for me." BuddyDetector.roll() is deterministic from userId + salt on purpose: whatever species/rarity/eye/hat you got on day one is the same buddy you see on day 500. A style layer that accumulates from tool_counts, sessions, and denial_rate necessarily drifts over time — even if same_user + same_style → same_buddy holds at a single point in time, the style itself keeps shifting, which means someone's dragon could quietly become an owl after a few weeks of different work. That breaks the identity bond the system is designed to create.

For users who want to customize, the path already exists. The companion field in ~/.claude.json (name, personality) is picked up by BuddyDetector.detect() today. Anything opinionated goes through that surface rather than through a second, implicit identity channel.

There's also a maintenance cost I'd want to flag: compute_style_identity() re-implements the Swift BuddyDetector.roll() in Python, which means the two have to stay in lockstep forever. Every future change to rolling would need parallel edits in two languages, and any drift silently changes identities.

Where I think your direction has real legs is #2 — "make the buddy talk / give it personality." The stats you're already collecting (tool usage, chaos rate, session rhythm) would be perfect input for buddy dialogue flavor without touching identity at all. A chaotic-session buddy commenting "rough one, huh?" vs. a methodical-session buddy going "clean run today" is exactly the personality layer users are asking for, and it stacks cleanly on top of the stable identity instead of replacing it.

Would you be interested in reworking this in that direction? Happy to scope it together on #2 if so. Closing this PR for now, but truly appreciate the contribution — the stats collection code you've already written would port directly.

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.

feat: style-aware buddy generation from coding behavior

2 participants