Skip to content

feat: implement AI Guilt-O-Meter (Phase 3 PRD #2)#106

Merged
nitrocode merged 5 commits into
mainfrom
copilot/implement-least-effort-prd
Apr 27, 2026
Merged

feat: implement AI Guilt-O-Meter (Phase 3 PRD #2)#106
nitrocode merged 5 commits into
mainfrom
copilot/implement-least-effort-prd

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 27, 2026

Summary

Implements the AI Guilt-O-Meter — the next unimplemented low-effort/high-impact Phase 3 PRD. A <progress> bar fills 0→100% over 5 minutes of page time, cycling through 6 increasingly panicked guilt labels. At 100% the visitor earns the certified_hypocrite badge. A share button appears at ≥20% fill.

Changes

  • Add GUILT_LABELS constant and getGuiltLabel(pct) pure function to death-clock-core.js; export both
  • Add certified_hypocrite badge to BADGE_DEFS in src/js/14-badges.js
  • Create src/js/22-guilt-meter.js: initGuiltMeter() (share button wiring) + updateGuiltMeter() (called on 1s interval)
  • Rename 22-boot.js23-boot.js to preserve strict numeric file ordering; wire initGuiltMeter() + updateGuiltMeter() into boot
  • Update scripts/build-js.js PARTS array for new file order
  • Add <section id="guilt-meter-section"> to index.html with <progress> element, full ARIA attributes, and share button
  • Add guilt meter CSS to styles/features.css — includes WebKit/Firefox progress pseudo-elements and prefers-reduced-motion overrides
  • Update src/js/00-state.js destructuring to include GUILT_LABELS, getGuiltLabel
  • 14 new unit tests for GUILT_LABELS and getGuiltLabel in tests/death-clock.test.js
  • Add learning entry to docs/LEARNINGS.md

Agent Checklist

  • npm run test:ci passes (all unit tests green, coverage not decreased)
  • npm run build && npm run test:e2e passes (all E2E tests green)
  • No generated files committed (script.js, styles.css, *-data.js)
  • All dynamic innerHTML values pass through escHtml()
  • No DOM references introduced in death-clock-core.js
  • No new runtime npm packages added
  • GitHub Actions uses: pins use a full commit SHA + inline semver comment
  • PR title follows Conventional Commits (feat:, fix:, docs:, chore:, etc.)
  • project-stats.yaml updated if this session merges one or more PRs
  • Relevant entry added to docs/LEARNINGS.md (new pattern or lesson learned)

Summary by CodeRabbit

  • New Features

    • Added "AI Guilt-O-Meter": progress bar with live aria label, periodic updates, share flow (unlocked ~20%), and badge awarding at thresholds (including "certified_hypocrite" at 100%).
  • Styles

    • New guilt-meter styles with cross‑browser progress styling and reduced‑motion handling.
  • Tests

    • Unit tests validating guilt-label data and getGuiltLabel behavior.
  • Documentation

    • Added learnings/changelog entries for the new guilt meter and related feature notes.

Copilot AI and others added 2 commits April 27, 2026 04:02
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ec3b390a-ee79-4854-8278-e4637eb84ffb

📥 Commits

Reviewing files that changed from the base of the PR and between 3b71f44 and d4131a4.

📒 Files selected for processing (1)
  • src/js/22-guilt-meter.js
✅ Files skipped from review due to trivial changes (1)
  • src/js/22-guilt-meter.js

📝 Walkthrough

Walkthrough

I pity the fool — Adds an "AI Guilt-O-Meter": new GUILT_LABELS and getGuiltLabel in core, a client guilt-meter module with UI and styles, badge integration, build ordering adjustments, docs entry, and unit tests for label behavior.

Changes

Cohort / File(s) Summary
Core Logic & Tests
death-clock-core.js, tests/death-clock.test.js
Introduce GUILT_LABELS and getGuiltLabel(pct) exports; tests validate label structure, ascending min thresholds, clamping behavior, and require min === 100 entry ("Certified Hypocrite").
Guilt Meter Module & Init
src/js/22-guilt-meter.js, src/js/23-boot.js, src/js/00-state.js
Add per-second guilt-meter: computes 0–100% over 5 minutes, updates progress/ARIA/label, shows share button ≥20%, awards certified_hypocrite at 100% once; boot wiring and state imports updated.
UI & Styles
index.html, styles/features.css
Add progress bar UI, aria-live label, hidden "Share My Guilt" button and cross-browser progress styling (including reduced-motion handling).
Badges, Build, Docs
src/js/14-badges.js, scripts/build-js.js, docs/LEARNINGS.md
Add certified_hypocrite badge definition; update build ordering to include 22-guilt-meter.js and rename boot to 23-boot.js; document feature and lessons learned.

Sequence Diagram(s)

sequenceDiagram
  participant Page as Page (UI)
  participant Core as DeathClockCore
  participant State as State Module
  participant Badges as Badges System
  participant Share as Share Popup

  Page->>State: init (imports GUILT_LABELS, getGuiltLabel)
  Page->>Page: render guilt meter elements
  Page->>Page: setInterval every 1s -> updateGuiltMeter()

  loop every 1s
    Page->>State: compute elapsed pct
    State->>Core: getGuiltLabel(pct)
    Core-->>State: label (icon,text)
    State->>Page: update progress value, aria, label text
    alt pct >= 20
      Page->>Page: show share button
    end
    alt pct == 100 and not awarded
      State->>Badges: award('certified_hypocrite')
    end
  end

  Page->>Share: on share click -> openSharePopup(constructed text)
  Share-->>Page: popup opened
  Page->>Badges: award('spreading_doom')
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • nitrocode

I pity the fool who don't review this carefully!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main feature: implementing the AI Guilt-O-Meter as Phase 3 PRD item #2, directly aligned with all code changes.
Description check ✅ Passed The description fully covers the Summary section, provides a comprehensive checked-off Changes list matching all file modifications, and includes a complete Agent Checklist with all items marked complete.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 copilot/implement-least-effort-prd

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

@nitrocode nitrocode marked this pull request as ready for review April 27, 2026 04:05
@nitrocode nitrocode self-requested a review as a code owner April 27, 2026 04:05
github-actions Bot added a commit that referenced this pull request Apr 27, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 27, 2026

👁️ PR Preview

🚀 Open Preview

Deployed from commit d4131a4 · Updates on every push to this PR
(Preview is removed automatically when the PR is closed.)

Screenshots

Desktop Mobile
Desktop Mobile

github-actions Bot added a commit that referenced this pull request Apr 27, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 27, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (d2d12fa) to head (d4131a4).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##              main      #106   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            1         1           
  Lines          256       263    +7     
  Branches       112       113    +1     
=========================================
+ Hits           256       263    +7     
Flag Coverage Δ
unit 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
death-clock-core.js 100.00% <100.00%> (ø)

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d2d12fa...d4131a4. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/js/22-guilt-meter.js (1)

32-34: Don’t bail out the whole meter when share button is missing — keep rendering alive, sucker.

At Line 33, returning early blocks initial meter render (updateGuiltMeter) if only the button is absent. Make the click wiring conditional, not the whole init.

♻️ Proposed refactor
   function initGuiltMeter() {
     const shareBtn = document.getElementById('guiltShareBtn');
-    if (!shareBtn) return;
-
-    shareBtn.addEventListener('click', () => {
-      const elapsed = Date.now() - pageLoadTime;
-      const mins    = Math.floor(elapsed / 60000) || 1;
-      const labelEl = document.getElementById('guiltMeterLabel');
-      const labelText = labelEl ? labelEl.textContent : '';
-      const shareText =
-        '\uD83D\uDE2C I\u2019ve been watching AI consume tokens for ' +
-        mins + ' minute' + (mins !== 1 ? 's' : '') +
-        ' and done absolutely nothing about it.' +
-        ' My guilt level: ' + labelText +
-        '. Are you as bad as me?\n\u2192 ' + SITE_URL +
-        ' `#TokenDeathClock` `#CertifiedHypocrite`';
-      openSharePopup(shareText);
-      awardBadge('spreading_doom');
-    });
+    if (shareBtn) {
+      shareBtn.addEventListener('click', () => {
+        const elapsed = Date.now() - pageLoadTime;
+        const mins    = Math.floor(elapsed / 60000) || 1;
+        const labelEl = document.getElementById('guiltMeterLabel');
+        const labelText = labelEl ? labelEl.textContent : '';
+        const shareText =
+          '\uD83D\uDE2C I\u2019ve been watching AI consume tokens for ' +
+          mins + ' minute' + (mins !== 1 ? 's' : '') +
+          ' and done absolutely nothing about it.' +
+          ' My guilt level: ' + labelText +
+          '. Are you as bad as me?\n\u2192 ' + SITE_URL +
+          ' `#TokenDeathClock` `#CertifiedHypocrite`';
+        openSharePopup(shareText);
+        awardBadge('spreading_doom');
+      });
+    }
 
     // Render initial state
     updateGuiltMeter();
   }

Also applies to: 51-53

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

In `@src/js/22-guilt-meter.js` around lines 32 - 34, The current early return when
document.getElementById('guiltShareBtn') is falsy prevents the rest of the meter
initialization (including updateGuiltMeter) from running; instead, remove the
return and make the click wiring conditional: find the shareBtn via
document.getElementById('guiltShareBtn') and if (shareBtn) attach the event
listener and any share-specific logic, but always continue to run the remainder
of the init so updateGuiltMeter still executes. Apply the same change to the
similar block around lines referenced (the other getElementById check at 51-53)
so missing elements only skip their event wiring and do not abort the entire
setup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/LEARNINGS.md`:
- Line 146: Update the heading "#### PR — feat: implement AI Guilt-O-Meter
(Phase 3 PRD `#2`)" to include the concrete PR ID `#106` (e.g., append " (`#106`)")
so it matches surrounding log format and aids traceability; while here also
verify the entry follows the repo's learning pattern by confirming it has a
category header, concise description, a link/reference to the PR, and that any
Categorised Learnings tables are updated accordingly (search for the heading
text to locate the entry).

In `@src/js/22-guilt-meter.js`:
- Around line 13-15: Negative elapsed values from system clock jumps must be
clamped to zero before deriving progress and minutes: replace the direct
subtraction used to compute elapsed in the guilt meter (where elapsed is set
from Date.now() - pageLoadTime) with a non-negative clamp (e.g., elapsed =
Math.max(0, Date.now() - pageLoadTime)) and use that clamped elapsed when
computing pct (with GUILT_DURATION_MS) and mins; update any other places that
compute elapsed (the second occurrence around mins) to use the same clamped
value so getGuiltLabel(pct) and the minutes display never receive negative
inputs.

---

Nitpick comments:
In `@src/js/22-guilt-meter.js`:
- Around line 32-34: The current early return when
document.getElementById('guiltShareBtn') is falsy prevents the rest of the meter
initialization (including updateGuiltMeter) from running; instead, remove the
return and make the click wiring conditional: find the shareBtn via
document.getElementById('guiltShareBtn') and if (shareBtn) attach the event
listener and any share-specific logic, but always continue to run the remainder
of the init so updateGuiltMeter still executes. Apply the same change to the
similar block around lines referenced (the other getElementById check at 51-53)
so missing elements only skip their event wiring and do not abort the entire
setup.
🪄 Autofix (Beta)

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 386c2143-4b38-4b72-9809-ae632685c159

📥 Commits

Reviewing files that changed from the base of the PR and between a794491 and 0bcc0d2.

📒 Files selected for processing (10)
  • death-clock-core.js
  • docs/LEARNINGS.md
  • index.html
  • scripts/build-js.js
  • src/js/00-state.js
  • src/js/14-badges.js
  • src/js/22-guilt-meter.js
  • src/js/23-boot.js
  • styles/features.css
  • tests/death-clock.test.js

Comment thread docs/LEARNINGS.md Outdated
Comment thread src/js/22-guilt-meter.js Outdated
@nitrocode
Copy link
Copy Markdown
Owner

@coderabbitai apply

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 27, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@nitrocode, let me check what review suggestions are pending on this PR, fool! I pity the fool who applies without checkin' first! 💪


🏁 Script executed:

#!/bin/bash
# Get review comments from coderabbitai on PR `#106`
gh pr view 106 --repo nitrocode/token-deathclock --json reviews,comments

echo "---REVIEW COMMENTS---"
gh api repos/nitrocode/token-deathclock/pulls/106/comments --jq '.[] | {id: .id, user: .user.login, body: .body, path: .path, line: .line, diff_hunk: .diff_hunk}' 2>/dev/null

echo "---ISSUE COMMENTS---"
gh api repos/nitrocode/token-deathclock/issues/106/comments --jq '.[] | {id: .id, user: .user.login, body: .body}' 2>/dev/null

Length of output: 55701


@nitrocode, I pity the fool who doesn't fix these issues! Here are the 3 changes I'll apply to fix all my review comments, sucka! 💪

  • Fix docs/LEARNINGS.md heading to include PR #106
  • Clamp elapsed to non-negative in src/js/22-guilt-meter.js (both occurrences)
  • Make share-button wiring conditional instead of early-returning in src/js/22-guilt-meter.js

🏁 Script executed:

#!/bin/bash
# Read the current content of docs/LEARNINGS.md around line 146
sed -n '140,160p' docs/LEARNINGS.md

Length of output: 2327


🏁 Script executed:

#!/bin/bash
# Read the current content of src/js/22-guilt-meter.js
cat src/js/22-guilt-meter.js

Length of output: 1847


@nitrocode, listen up fool! Here are all 3 fixes. I pity the fool who ships unclamped clocks and bailing inits! 💪

  • Fix 1: Add PR #106 to the heading in docs/LEARNINGS.md
  • Fix 2: Clamp elapsed in updateGuiltMeter() to avoid negative pct from clock jumps
  • Fix 3: Wrap share-button click wiring in if (shareBtn) and always call updateGuiltMeter() — also clamp elapsed in the click handler

🧠 Learnings used
Learnt from: CR
Repo: nitrocode/token-deathclock PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-27T00:20:01.920Z
Learning: Applies to tests/death-clock.test.js : Maintain unit test coverage at or above 80% for lines and functions, 70% for branches — do not let coverage decrease on any PR

Learnt from: CR
Repo: nitrocode/token-deathclock PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-27T00:20:01.920Z
Learning: Applies to tests/death-clock.test.js : Add unit tests in `tests/death-clock.test.js` for every new or modified pure function in `death-clock-core.js`

Learnt from: CR
Repo: nitrocode/token-deathclock PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-27T00:20:01.920Z
Learning: Applies to death-clock-core.js : When changing `MILESTONES` array in `death-clock-core.js`, keep it sorted in ascending `tokens` order and include all required fields: `id`, `name`, `icon`, `tokens`, `shortDesc`, `description`, `consequence`, `followingEvent`, `color`, `darkColor`

Learnt from: CR
Repo: nitrocode/token-deathclock PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-27T00:20:01.920Z
Learning: Applies to tests/e2e/death-clock.spec.js : Write E2E tests in `tests/e2e/death-clock.spec.js` for DOM behaviour and visual flows when practical

  • 📌 Commit the changes to this branch

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 27, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 2 file(s) based on 2 unresolved review comments.

Files modified:

  • docs/LEARNINGS.md
  • src/js/22-guilt-meter.js

Commit: 488ca7c03d0924d96bc67ef183b6c50598d38ea1

The changes have been pushed to the copilot/implement-least-effort-prd branch.

Time taken: 2m 2s

Fixed 2 file(s) based on 2 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
github-actions Bot added a commit that referenced this pull request Apr 27, 2026
github-actions Bot added a commit that referenced this pull request Apr 27, 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/js/22-guilt-meter.js`:
- Around line 31-33: In initGuiltMeter(), stop bailing out when
document.getElementById('guiltShareBtn') is missing; instead remove the early
"if (!shareBtn) return" and conditionally attach the click handler only when
shareBtn exists (e.g., if (shareBtn) { shareBtn.addEventListener(...) }), then
always call updateGuiltMeter() so the meter renders/updates regardless of the
button. Apply the same change to the other occurrence noted around lines 51-52
(replace early return with conditional handler attachment and always call
updateGuiltMeter()).
🪄 Autofix (Beta)

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 871312bb-e878-48e3-aaa4-f4b512aa0c86

📥 Commits

Reviewing files that changed from the base of the PR and between 0bcc0d2 and 488ca7c.

📒 Files selected for processing (2)
  • docs/LEARNINGS.md
  • src/js/22-guilt-meter.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/LEARNINGS.md

Comment thread src/js/22-guilt-meter.js Outdated
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 27, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Cannot run autofix: This PR has merge conflicts.

Please resolve the conflicts with the base branch and try again.

Alternatively, use @coderabbitai resolve merge conflict to automatically resolve the conflicts.

Signed-off-by: RB <7775707+nitrocode@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 27, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 1 file(s) based on 1 unresolved review comment.

Files modified:

  • src/js/22-guilt-meter.js

Commit: d4131a4f797e9b7b8eceaee850f3398f3f49e853

The changes have been pushed to the copilot/implement-least-effort-prd branch.

Time taken: 2m 9s

@nitrocode nitrocode enabled auto-merge (squash) April 27, 2026 12:59
github-actions Bot added a commit that referenced this pull request Apr 27, 2026
github-actions Bot added a commit that referenced this pull request Apr 27, 2026
Fixed 1 file(s) based on 1 unresolved review comment.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
github-actions Bot added a commit that referenced this pull request Apr 27, 2026
github-actions Bot added a commit that referenced this pull request Apr 27, 2026
@nitrocode nitrocode merged commit 07b4e8b into main Apr 27, 2026
10 checks passed
@nitrocode nitrocode deleted the copilot/implement-least-effort-prd branch April 27, 2026 13:02
github-actions Bot added a commit that referenced this pull request Apr 27, 2026
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.

2 participants