Skip to content

feat: two-channel XP events, boosted rewards, world blessing, streak flames#144

Open
terpjwu1 wants to merge 10 commits into
feat/buddy-worldfrom
feat/xp-events-blessing
Open

feat: two-channel XP events, boosted rewards, world blessing, streak flames#144
terpjwu1 wants to merge 10 commits into
feat/buddy-worldfrom
feat/xp-events-blessing

Conversation

@terpjwu1

@terpjwu1 terpjwu1 commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

Stacked on #143. Fixes the dormant XP economy (commit/bug_fix/deploy rewards were defined but never fired — everything was flat 5 XP, making level 50 a ~21,000-observe grind) and wires the Buddy World incentive loop.

Detection channels — how the agent "knows":

  • Ground truth: PostToolUse hook inspects the literal executed command + output + exit code (git commit exit 0 → commit; wrangler deploy/npm publish → deploy; "12 passed" with no failures → tests_passed). Events queue in ~/.buddy/pending-events.jsonl (hooks stay native-dep-free) and the MCP server awards them on the next observe.
  • Self-report: classifySummary() on the buddy_observe summary covers bug_fix (no command signature) and hookless hosts. Hook wins on duplicates.

Economy: observe 8 · session 5 · tests_passed 20 · commit 25 · bug_fix 35 · deploy 60 · +10% world blessing. Active day ≈ 700 XP → level 50 in months, not years. Curve untouched (world validates level === levelFromXp(xp) across client versions).

World loop: blessing for teleported buddies, instant sync flush for deploy/level-up/streak celebrations (~10s to fireworks), streak_7 milestones with a 🔥 flame by the name tag for a week, anti-abuse recalibrated (900/hr, 300 burst).

Test plan

  • 33 new tests (classification both channels, pending-events handoff, hook recording, blessing, streaks, reward table)
  • Full world suite 132/132 across affected files; repo suite shows only the 9 known pre-existing master failures
  • tsc clean, migration drift-guard regenerated (xp_bucket default 300)
  • Live check after merge: make a real commit, watch the plaza celebrate within ~10s

🤖 Generated with Claude Code

terpjwu1 and others added 9 commits July 4, 2026 07:50
…flames

commit/bug_fix/deploy XP rewards existed since the table was written but
NOTHING ever fired them — every action was flat 5 XP and level 50
(104,925 XP) needed ~21,000 observes. Two detection channels fix that:

- Ground truth: the PostToolUse hook sees the literal command + output +
  exit code; git commit / npm publish / wrangler deploy / passing test
  runs queue events in ~/.buddy/pending-events.jsonl (hooks stay free of
  native deps), ingested and awarded on the next buddy_observe
- Self-report: classifySummary() keyword-matches the observe summary for
  events with no command signature (bug_fix) and hosts without hooks;
  the hook wins on duplicates within a window

Rewards rebalanced (observe 8, session 5, tests_passed 20, commit 25,
bug_fix 35, deploy 60) — an active day is ~700 XP instead of ~150, and
level 50 is months, not years. The curve is deliberately untouched:
world validation pins level === levelFromXp(xp) across client versions.

Buddy World integration:
- +10% XP blessing for teleported buddies (cached local check)
- deploy/level_up/streak_7 bypass the sync debounce → plaza celebrations
  land within one poll of the real moment
- streak milestones (every 7 days) emit streak_7; the plaza shows a 🔥
  by the name for a week; tests_passed gets its own celebration + label
- anti-abuse recalibrated for the new economy (900/hr cap, 300 burst)

33 new tests across classification, hook recording, pending-events
handoff, blessing, streaks, and reward table.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Verified against the official hooks reference (code.claude.com/docs/en/
hooks.md): tool_response is an object — {type:'text', text} on success,
{type:'error', error, stdout, stderr} on failure — and NO Bash exit-code
field exists in the payload at all. The handler only accepted string
responses, so on real Claude Code payloads output read as empty:
tests_passed never fired from the hook, and a FAILED git commit would
still award commit XP (empty output looked like success).

Now the object shape is parsed (error text included so ERROR_REGEX sees
real failures), with tests pinned to the documented payloads.

Flagged by buddy guard mode as an unchallenged assumption; the doc check
proved it half-wrong. Good golem.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
First entry: Runecore's unchallenged_chain detector questioned the
PostToolUse payload claim, which turned out half-wrong and hiding an
XP-award bug behind 132 passing tests. Format designed for future
automation (share cards, world-site /catches page, build-in-public posts).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reuse: pending-events path derives from constants.ts (one owner for ~/.buddy).
Simplification: cachedConfig() helper replaces the copy-pasted cfgCache
guard; awardXp loop rewritten; dead regex alternatives merged (vercel,
fly/flyctl); dead 'level_up' INSTANT entry replaced with a real mechanism —
level-ups now force an instant flush via deps.instant (the PR promised
instant level-up celebrations; now it's true, without double-emitting the
server-derived level_up event).
Efficiency: awardXpBatch does one SELECT + one UPDATE for a whole hook
batch (was N pairs); streak query returns DISTINCT days (≤60 rows, was
~1,200); plaza streak flames computed once per frame, not per citizen.
Altitude: resolveEventType arbitration lives in xp-classify; classification
priority order now derives mechanically from XP_REWARDS; streak SQL owned
by streaks.ts (checkStreakMilestone, one-line server call); INSTANT_WORLD_
EVENTS lives with the event-type definitions; plaza celebrations are a
data-driven CELEBRATION_SPEC (level_up stays the lone bespoke branch).

+6 tests (milestone checker, instant flush, day-string streaks). 138 green.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Command detection anchors to the executed program per shell segment:
  'echo git commit' and output containing '12 passed' no longer award;
  tests_passed requires a recognized runner command AND passing output
- Pending events: atomic rename-claim before read (concurrent hook
  appends survive), 15-minute age window, exact-replay dedupe, 10-event
  cap per consume
- Self-report damper: summary-classified elevated events cap at 8/day
  (hook-verified events exempt) — the honor-system channel degrades to
  observe under spam instead of paying deploy rates
- Streak window widened to 400 days so 60+ day streaks compute correctly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cherry-picked from Codex's parallel review pass — the one piece of its
uncommitted alternative implementation kept; it pins the invalidation
contract already shipped in the round-2 hardening.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…mandEvent

Prevent `echo 'x; git commit'` from splitting into a fake git-commit
segment that bypasses the anchored PREFIX guard and farms commit XP.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Covers b6233db (quoted shell operators cannot fabricate command
segments) and confirms quotes inside genuine commands stay detectable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
terpjwu1 added a commit that referenced this pull request Jul 5, 2026
…mbed

A 🎵 toggle summons a small corner player (youtube-nocookie, official
embed — rights holders keep attribution/monetization). Strictly opt-in:
no third-party request of any kind until clicked; toggling off removes
the iframe entirely. aria-pressed + labels for screen readers.

Smoke test drives the real flow in headless Chromium: no iframe before
click, correct playlist after, clean removal on toggle-off.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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