fix(statusline): expire the achievement banner instead of latching it - #170
Conversation
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 19 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
This PR fixes a statusline bug where the achievement (“trophy”) banner could latch indefinitely across all sessions because it was read from shared status.json without any expiration. The change introduces a timestamp (achievementAt) and makes the statusline only render the banner while it’s fresh (using the same TTL as reactions), with tests covering the expected expiry behavior and a package version bump.
Changes:
- Add
achievementAttoStatusStateand stamp it on writes (0when no achievement is present). - Update
statusline/buddy-status.shto expire the achievement banner usingreactionTTL(and treat legacystatus.jsonwithout the field as fresh). - Add new test cases for achievement banner expiry behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| statusline/buddy-status.test.ts | Adds tests validating banner expiry and legacy behavior. |
| statusline/buddy-status.sh | Expires trophy banner using achievementAt + reactionTTL, avoiding indefinite latching. |
| server/state.ts | Adds achievementAt to persisted status state and stamps it during writeStatusState. |
| package.json | Bumps package version to reflect the fix release. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The trophy banner rendered unconditionally from status.json with no TTL, while reactions expire via _sweep_expired_reactions. status.json is shared across sessions (reactions are per-SID), so a single unlock pinned every session's bubble to that achievement until some path happened to call writeStatusState with no achievement — observed as a "Diplomat" banner stuck for ~5h after conflict_resolver unlocked. writeStatusState now stamps achievementAt, and the statusline expires the banner on the same reactionTTL clock as a reaction. An explicit 0 never renders; a legacy status.json with no field is treated as fresh and self-heals on the next server write. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H2GXsqpMvUhxBSNabSqZdk
Review catch (Copilot, Devin): the freshness gate was skipped wholesale when reactionTTL=0, so an explicit achievementAt of 0 still rendered — contradicting the field's documented "nothing pending" meaning and re-enabling cross-session latching for TTL=0 users. Validity and age are now separate gates. A zeroed or malformed achievementAt never renders regardless of TTL; reactionTTL=0 disables expiry only, so a validly stamped achievement still persists as it does for reactions. Both cases are covered by tests. The zeroed-under-TTL=0 case was confirmed failing against the previous commit's script before the fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H2GXsqpMvUhxBSNabSqZdk
308c855 to
ee7ad8c
Compare
Review round 1 addressed —
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ee7ad8c7ba
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if [ "$REACTION_TTL" -gt 0 ] 2>/dev/null; then | ||
| ACH_AGE=$(( ($(date +%s) * 1000 - ACHIEVEMENT_AT) / 1000 )) | ||
| [ "$ACH_AGE" -ge "$REACTION_TTL" ] && ACH_FRESH=0 | ||
| fi |
There was a problem hiding this comment.
Honor the fake clock when expiring achievements
When snapshot tests set BUDDY_FAKE_NOW, this age calculation still calls the real system clock, even though the script documents that variable as its wall-clock override. For example, an achievement stamped at the real current time remains visible when BUDDY_FAKE_NOW is advanced beyond the TTL, making expiry snapshots nondeterministic and preventing tests from exercising a fixed timestamp. Calculate against the existing NOW value instead.
Useful? React with 👍 / 👎.
Ships the achievement-banner expiry fix from #170. The trophy banner rendered from the shared status.json with no TTL while reactions expire on a sweep, so a single unlock pinned every session's bubble until some path happened to call writeStatusState with no achievement — observed as a "Diplomat" banner stuck for ~5h after conflict_resolver unlocked. writeStatusState now stamps achievementAt and the statusline expires the banner on the same reactionTTL clock as a reaction. Validity and age are separate gates: a zeroed or malformed stamp never renders regardless of TTL, and reactionTTL=0 disables expiry only. Claude-Session: https://claude.ai/code/session_01H2GXsqpMvUhxBSNabSqZdk Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
The bug
The trophy banner rendered unconditionally from
status.jsonwith no TTL, while reactions expire via_sweep_expired_reactions.status.jsonis shared across all sessions (reactions are per-SID), so a single unlock pinned every session's bubble to that achievement until some path happened to callwriteStatusStatewith no achievement.Observed live:
conflict_resolver("Diplomat") unlocked at1785186841582and the banner was still pinned ~5h later.The fix
server/state.ts—StatusStategainsachievementAt, stamped on write,0when there is no achievement.statusline/buddy-status.sh— the banner expires on the samereactionTTLclock as a reaction. The block moved below the config load since it needsREACTION_TTL.Compatibility: an explicit
0never renders; a legacystatus.jsonwith noachievementAtat all is treated as fresh and self-heals on the next server write, so existing snapshot fixtures are unaffected.Tests
4 new cases in
statusline/buddy-status.test.ts: fresh shows, older-than-TTL drops, zeroed drops, legacy-missing-field shows.bun test→ 469 pass, 0 fail.🤖 Created with the help of AI (Claude Opus 5).
Note
Expire the achievement banner in the statusline after
REACTION_TTLsecondsREACTION_TTLseconds by comparing the current time against a newachievementAtepoch timestamp stored instatus.json.achievementAtasDate.now()when an achievement is present, or0when absent.achievementAtand suppresses the banner if its age (in seconds) meets or exceedsREACTION_TTL; settingREACTION_TTL=0disables expiry for validly stamped achievements.status.jsonfiles withoutachievementAttreat the achievement as fresh to preserve backward compatibility.achievementAtnow suppresses the banner entirely, regardless ofREACTION_TTL.Macroscope summarized ee7ad8c.