Skip to content

Redesign user rating to reward skill instead of AC rate - #1048

Merged
boomzero merged 32 commits into
devfrom
redesign-rating
Oct 6, 2026
Merged

boomzero merged 32 commits into
devfrom
redesign-rating

Conversation

@boomzero

@boomzero boomzero commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

What does this PR aim to accomplish?:

The old rating was accepted / submitted × 1000. Anyone could raise it by only submitting easy problems they were sure of. Strong users who attempt hard problems scored about the same as careful beginners, around 400.

This PR replaces it with a rating that meets four goals:

  • Comprehensive: it uses several signals.
  • Hard to game: easy-problem farming and bulk pasting earn little.
  • Deterministic: the same profile gives the same number for every viewer.
  • Rewards talent: top competitors on the site land in the top tier.

Docs PR: XMOJ-Script-dev/docs#9

How does this PR accomplish the above?:

New function CalculateUserRating(root). It is a pure function of one userinfo.php page and never reads the current time. It only needs data the script already fetches: the solved list, the verdict totals and the d1/d2 daily history.

The number after each solved problem is the total submit count for that problem, including submissions after AC. It is not used.

  • Volume (57%): 1 − e^(−S·B/350), where S is the number of solved problems.
  • Burst cap B: daily ACs above 8 grow only logarithmically. A day with 120 ACs counts as about 30. Normal training and contest days are almost unaffected.
  • Accuracy (33%): P = (S + 10) / (S + X + 40). X is the total of PE, WA, TLE, MLE, OLE and RE verdicts; CE and ignored submissions are excluded. Resubmitting AC code changes neither S nor X, so it can't inflate the score. The 40 virtual submissions keep small samples from scoring high. Credit peaks at P = 40–55% and falls off above that, because near-perfect accuracy over hundreds of solves looks copied. It is weighted by S / (S + 80).
  • Consistency (10%): active days, as 1 − e^(−days/250).
  • Final scale: round(3000 × score^0.9).

Other changes:

  • GetUserInfo and InitializeUserProfile both call the new function.
  • The cache key is renamed to -UserRatingV2, so cached AC-rate values are not read as new ratings. A one-time sweep, guarded by the UserScript-RatingV2-Migrated marker, deletes the old UserScript-User-*-UserRating entries. Email hashes, timestamps and settings are kept.
  • Color tiers are now: ≥2400 red #dc2f5a, ≥2000 orange #e8590c (replaces yellow), ≥1600 blue #337cf2, ≥1200 green #2b9f4f, ≥800 cyan #1798b0, otherwise gray #78828c. They use xmoj-rating-* classes from a shared RatingColorCSS, injected on legacy pages and in /web, instead of Bootstrap's link-* classes. Those change with theme and hover: the old green had only 3.4:1 contrast on dark, and the yellow 1.6:1 on white. The new palette is the same on every page and in both themes, with at least 3.3:1 contrast on white and on dark.
  • Web contest rank page: /web/contest/<cid>/rank-correct never called GetUsernameHTML, so usernames there had no avatar, rating color or badge, unlike contestrank-oi.php. The new EnhanceRank() runs in the existing Enhance() observer loop. Vue owns the table and reuses rows when it re-sorts, so the script hides Vue's link and adds an owned copy keyed by username. A row is redrawn only when its user changes, which means the async GetUsernameHTML update can't re-trigger itself. I checked this on a mock Vue table, including a re-sort.
  • Web rank page matches the OI rank page:
    • Problem cells decode the server's colors the same way contestrank-oi.php does: tries counts like 100 (3)/50 (2), green or red shading by tries, and purple for first blood.
    • The rank appears in a blue badge instead of the server's orange cell.
    • Nick and AC count are plain text.
    • The general .well theme rule no longer boxes the problem cells, and cells are vertically centered.
    • MonochromeUI gets the black header.
    • Vue resets inline colors when it patches a row, so the web app's observer now also watches style.
    • The table overflows into the page like contestrank-oi.php, instead of being clipped by the .xmoj-scroll-x scroll box. macOS hides that box's scrollbar, so the clipping looked like a bug.
    • Checked on the live site with the repo's userscript harness, in dark mode with MonochromeUI, at 560, 900 and 1280 px wide. The web page and the OI page now overflow the same way and show the same tries counts.
  • The profile-page test expectation is updated for the fixture: 400 → 33, checked by hand against the formula.

How it was checked:

  • Real profiles fetched while building it: two NOI-level competitors score 2626 and 2760, both in the top tier. Mid-level active users score about 1140–1325.
  • Synthetic profiles:
Profile Rating
Farmer: 300 solves, never fails 1431
Same farmer, pasting 50 solves a day 905
Grinder: 600 solves, 7 failures each 1672
New user: 20 solves, 3 failures each 246
  • The in-script function gives the same output as the prototype on all sample pages.
  • The full npm test suite passes locally: 99/99, run against a locally cached Chromium build. The rank-cell logic was also tested in real Chromium against the server's actual color values, including Vue re-patching the color, the text, or both.

Known limitations:

  • The rating can't see problem difficulty, because that would need crawling every problem.
  • Slow, careful copying spread over many days is not detectable from profile data.

By submitting this pull request, I confirm the following:

  1. I have read and understood the contributor's guide, as well as this entire template.
  2. I have commented on my proposed changes within the code.
  3. I have tested my changes.
  4. I am willing to help maintain this change if there are issues with it later.
  5. It is compatible with the GNU General Public License v3.0.
  6. I have squashed any insignificant commits.
  7. I have checked that another pull request for this purpose does not exist.
  8. I have considered and confirmed that this submission will be valuable to others.
  9. I accept that this submission may not be used, and the PR can be closed at the will of the maintainer.
  10. I give this submission freely and claim no ownership to its content.
  11. I have verified that my changes work correctly in both the new UI and the old/classic UI.

Item 11 was verified by @boomzero in both the new and the classic UI, including username colors on /web/contest/<cid>/rank-correct and the profile 评分 line.


  • I have read the above and my PR is ready for review.

🤖 Generated with Claude Code

https://claude.ai/code/session_01E5dr7XTsCeoamqLSiT9rLp

The old rating was accepted/submitted x 1000, which anyone could inflate by
only submitting easy problems. The new 0-3000 rating is computed from one
userinfo.php page: attempt-weighted solve volume (saturating), shrunk
first-try and per-attempt accuracy that peaks at 45-60%, active days, and a
per-day AC soft cap against bulk pasting. It never uses the current time, so
every viewer computes the same value. Colors move to six tiers and the cache
key is renamed so stale AC-rate values are not reused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5dr7XTsCeoamqLSiT9rLp

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @boomzero, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 4 days and 23 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@hendragon-bot hendragon-bot Bot added the user-script This issue or pull request is related to the main user script label Oct 5, 2026
@sourcery-ai

sourcery-ai Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

The PR replaces the easily farmed AC-rate score with a deterministic 0–3000 rating based on solve volume, retry behavior, daily activity, accuracy, and consistency; it also migrates caching, expands rating color tiers, and updates the profile regression expectation.

Sequence diagram for rating cache migration and profile rendering

sequenceDiagram
    participant Viewer
    participant Script
    participant Cache
    participant UserInfoPage

    Viewer->>Script: GetUserInfo(Username)
    Script->>Cache: Read UserRatingV2
    alt Cached V2 rating is valid
        Cache-->>Script: Return rating and EmailHash
    else Cache miss or expired
        Script->>UserInfoPage: Fetch userinfo.php
        UserInfoPage-->>Script: Profile HTML
        Script->>Script: CalculateUserRating(ParsedDocument)
        Script->>Cache: Write UserRatingV2
    end
    Script-->>Viewer: Render rating and username color tiers
Loading

Flow diagram for deterministic user rating calculation

flowchart TD
    A[User profile page] --> B[Parse p id attempts list]
    A --> C[Read daily activity history]
    B --> D[Calculate solve volume with retry weighting]
    C --> E[Apply daily burst cap]
    B --> F[Calculate shrunk accuracy]
    C --> G[Calculate consistency from active days]
    D --> H[Combine weighted score]
    E --> H
    F --> H
    G --> H
    H --> I[Scale to deterministic rating 0-3000]
Loading

File-Level Changes

Change Details Files
Replace the legacy accepted/submitted rating with a deterministic composite skill rating.
  • Parse per-problem attempt counts from the profile page.
  • Combine retry-adjusted volume, burst-capped daily solves, shrunk accuracy, and active-day consistency.
  • Map the composite score to a rounded 0–3000 scale without using current time.
XMOJ.user.js
Migrate rating caching and presentation to the new rating model.
  • Use a UserRatingV2 cache key so legacy values are not reused.
  • Compute ratings through the new function in both user lookups and profile initialization.
  • Update username color thresholds to six Bootstrap link tiers from secondary through danger.
XMOJ.user.js
Update the profile regression fixture for the redesigned calculation.
  • Change the expected fixture rating from 400 to 31.
tests/profile-page.test.cjs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Deploying xmoj-script-dev-channel with  Cloudflare Pages  Cloudflare Pages

Latest commit: 23c9c02
Status: ✅  Deploy successful!
Preview URL: https://dea424df.xmoj-script-dev-channel.pages.dev
Branch Preview URL: https://redesign-rating.xmoj-script-dev-channel.pages.dev

View logs

sourcery-ai[bot]
sourcery-ai Bot previously approved these changes Oct 5, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sourcery assessment

Approved.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 4 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Update.json">

<violation number="1" location="Update.json:3885">
P3: The rating documentation URL is rendered as non-clickable text because `Notes` is inserted as HTML. Wrap the URL in an `<a>` so readers can open the linked explanation.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread Update.json
"Description": "Redesign user rating to reward skill instead of AC rate"
}
],
"Notes": "用户评分重新设计:从「AC 率 × 1000」改为综合做题量、准确度和坚持度的 0–3000 分,更难刷分,用户名颜色改为六档。详见 https://docs.xmoj-script.uk/features/rating.html"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: The rating documentation URL is rendered as non-clickable text because Notes is inserted as HTML. Wrap the URL in an <a> so readers can open the linked explanation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Update.json, line 3885:

<comment>The rating documentation URL is rendered as non-clickable text because `Notes` is inserted as HTML. Wrap the URL in an `<a>` so readers can open the linked explanation.</comment>

<file context>
@@ -3872,6 +3872,17 @@
+                    "Description": "Redesign user rating to reward skill instead of AC rate"
+                }
+            ],
+            "Notes": "用户评分重新设计:从「AC 率 × 1000」改为综合做题量、准确度和坚持度的 0–3000 分,更难刷分,用户名颜色改为六档。详见 https://docs.xmoj-script.uk/features/rating.html"
         }
     }
</file context>
Suggested change
"Notes": "用户评分重新设计:从「AC 率 × 1000」改为综合做题量、准确度和坚持度的 0–3000 分,更难刷分,用户名颜色改为六档。详见 https://docs.xmoj-script.uk/features/rating.html"
"Notes": "<p>用户评分重新设计:从「AC 率 × 1000」改为综合做题量、准确度和坚持度的 0–3000 分,更难刷分,用户名颜色改为六档。详见 <a href=\"https://docs.xmoj-script.uk/features/rating.html\">评分说明</a></p>"

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="XMOJ.user.js">

<violation number="1" location="XMOJ.user.js:2419">
P2: Missing or unparseable activity history sets `burst` to 1, so parsed solves receive full uncapped volume and bypass the bulk-solving penalty. Use a conservative fallback when daily counts are unavailable.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread XMOJ.user.js
totalAC += count;
cappedAC += count <= DailyCap ? count : DailyCap + DailyCap * Math.log(1 + (count - DailyCap) / DailyCap);
}
let burst = totalAC > 0 ? cappedAC / totalAC : 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Missing or unparseable activity history sets burst to 1, so parsed solves receive full uncapped volume and bypass the bulk-solving penalty. Use a conservative fallback when daily counts are unavailable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At XMOJ.user.js, line 2419:

<comment>Missing or unparseable activity history sets `burst` to 1, so parsed solves receive full uncapped volume and bypass the bulk-solving penalty. Use a conservative fallback when daily counts are unavailable.</comment>

<file context>
@@ -2393,6 +2397,43 @@ function GetProfileActivityData(root = document) {
+        totalAC += count;
+        cappedAC += count <= DailyCap ? count : DailyCap + DailyCap * Math.log(1 + (count - DailyCap) / DailyCap);
+    }
+    let burst = totalAC > 0 ? cappedAC / totalAC : 1;
+    // Volume: each solve is worth 1 on the first try and decays slowly with retries.
+    let weighted = attempts.reduce((sum, count) => sum + 1 / (1 + 0.4 * Math.log(count)), 0);
</file context>
Suggested change
let burst = totalAC > 0 ? cappedAC / totalAC : 1;
let burst = totalAC > 0 ? cappedAC / totalAC : 0;

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: afea98ef2d

ℹ️ 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".

Comment thread XMOJ.user.js Outdated
Comment on lines +2405 to +2406
for (let script of root.querySelector("#statics")?.querySelectorAll("script") || []) {
for (let match of script.textContent.matchAll(/\bp\(\s*\d+\s*,\s*(\d+)\s*\)/g)) attempts.push(Math.max(1, Number(match[1])));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle profiles that expose solved problems as links

When a profile contains solved-problem <a href="problem.php?id=..."> elements but no raw p(id, attempts) script, this loop leaves attempts empty and the function returns a rating of 0. That representation is explicitly supported by GetProfileSolvedProblems, so affected established users will display a zero rating and receive the lowest username color both on their profile and wherever GetUserInfo is used. The rating parser should handle the link-based representation as well, using a safe fallback when an attempt count is unavailable.

Useful? React with 👍 / 👎.

boomzero and others added 4 commits October 5, 2026 22:14
The number after each solved problem on userinfo.php is the total submit
count for that problem, including submissions after AC, so it cannot measure
attempts before AC. Volume now uses the solved count, and accuracy uses the
exact verdict totals: solved / (solved + PE+WA+TLE+MLE+OLE+RE), shrunk with
40 virtual submissions and peaking at 40%-55%.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5dr7XTsCeoamqLSiT9rLp
/web/contest/<cid>/rank-correct never called GetUsernameHTML, so usernames
had no avatar, rating color or badge, unlike contestrank-oi.php. Vue owns
the rank table and reuses rows when it re-sorts, so hide its link and add a
script-owned copy keyed by username, redrawn only when the row's user changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5dr7XTsCeoamqLSiT9rLp
@pull-request-size pull-request-size Bot added size/L and removed size/M labels Oct 5, 2026

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="XMOJ.user.js">

<violation number="1" location="XMOJ.user.js:3235">
P2: Wait to hide the Vue anchor until its replacement renders: `GetUsernameHTML` catches lookup or badge failures and leaves only the spinner/text, removing the row's working profile link.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread XMOJ.user.js
if (span?.dataset.username === username) continue;
span?.remove();
if (!username) continue;
link.hidden = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Wait to hide the Vue anchor until its replacement renders: GetUsernameHTML catches lookup or badge failures and leaves only the spinner/text, removing the row's working profile link.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At XMOJ.user.js, line 3235:

<comment>Wait to hide the Vue anchor until its replacement renders: `GetUsernameHTML` catches lookup or badge failures and leaves only the spinner/text, removing the row's working profile link.</comment>

<file context>
@@ -3219,6 +3219,27 @@ async function InitializeContestWebApp() {
+            if (span?.dataset.username === username) continue;
+            span?.remove();
+            if (!username) continue;
+            link.hidden = true;
+            span = MakeControl("span", "rank-user", "");
+            span.dataset.username = username;
</file context>

Decode the server's cell colors into tries counts and shaded green/red
backgrounds like contestrank-oi.php, show the rank in a badge instead of the
server's orange cell, render nick and AC count as plain text, stop the
general .well theme rule from boxing problem cells, center cells vertically,
and use the black MonochromeUI header. Vue resets inline colors when it
patches a row, so the observer now also watches style changes and each cell
remembers the color Vue set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5dr7XTsCeoamqLSiT9rLp
@sourcery-ai
sourcery-ai Bot dismissed their stale review October 5, 2026 14:29

Sourcery withdrew this approval because it has stopped reviewing this pull request.

@sourcery-ai

sourcery-ai Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Sourcery has withdrawn its approval of this pull request. It auto-reviews a pull request 5 times, and this push is past that limit, so the approval no longer reflects code Sourcery has read.

Comment @sourcery-ai review to get a fresh review, which can approve again.

Re-reviews, rate limits and approvals

boomzero and others added 4 commits October 5, 2026 22:35
The rank table's .xmoj-scroll-x wrapper clipped the right columns once tries
counts widened the cells, and macOS hides its scrollbar. Let it overflow into
the page as contestrank-oi.php does. Also drop the MonochromeUI underline,
which is a border-bottom, from the nick and AC count links.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5dr7XTsCeoamqLSiT9rLp
The server labels first place "Winner" while contestrank-oi.php shows the
number. Other ranks already arrive as numbers and "*" stays as is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5dr7XTsCeoamqLSiT9rLp
github-actions Bot and others added 9 commits October 5, 2026 14:50
The new rating is cached under "-UserRatingV2", which left every old
AC-rate value under "UserScript-User-<name>-UserRating" behind. Delete those
once, guarded by a marker key so localStorage is not rescanned on every page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5dr7XTsCeoamqLSiT9rLp
Bootstrap's link-* colors change with theme and hover: the green was too
dark on the dark background and the yellow nearly unreadable on white.
Rating tiers now use their own classes with one palette, tuned for at least
3.4:1 contrast on both backgrounds, injected on legacy pages and in /web.
The yellow tier becomes a vivid orange.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5dr7XTsCeoamqLSiT9rLp
Like Bootstrap's link-* hover, darken the tier color by 20% and make the
underline opaque. The shades are fixed, so they match on every page and theme.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5dr7XTsCeoamqLSiT9rLp

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="XMOJ.user.js">

<violation number="1" location="XMOJ.user.js:694">
P2: The hover/focus shades fall below the stated 3.4:1 dark-theme contrast minimum: red is 3.05:1 on `#1a1a1a` (orange, blue, and gray also fail). Use hover shades that preserve the minimum.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread XMOJ.user.js Outdated
// Bootstrap, hover darkens the color by 20% and makes the underline opaque.
const RatingColorCSS = `
a.xmoj-rating-red { color: #e8434b !important; text-decoration-color: rgba(232, 67, 75, 0.5) !important; }
a.xmoj-rating-red:hover, a.xmoj-rating-red:focus { color: #ba363c !important; text-decoration-color: #ba363c !important; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: The hover/focus shades fall below the stated 3.4:1 dark-theme contrast minimum: red is 3.05:1 on #1a1a1a (orange, blue, and gray also fail). Use hover shades that preserve the minimum.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At XMOJ.user.js, line 694:

<comment>The hover/focus shades fall below the stated 3.4:1 dark-theme contrast minimum: red is 3.05:1 on `#1a1a1a` (orange, blue, and gray also fail). Use hover shades that preserve the minimum.</comment>

<file context>
@@ -685,6 +685,25 @@ const ThemeCanvasCSS = `
+// Bootstrap, hover darkens the color by 20% and makes the underline opaque.
+const RatingColorCSS = `
+        a.xmoj-rating-red { color: #e8434b !important; text-decoration-color: rgba(232, 67, 75, 0.5) !important; }
+        a.xmoj-rating-red:hover, a.xmoj-rating-red:focus { color: #ba363c !important; text-decoration-color: #ba363c !important; }
+        a.xmoj-rating-orange { color: #e8590c !important; text-decoration-color: rgba(232, 89, 12, 0.5) !important; }
+        a.xmoj-rating-orange:hover, a.xmoj-rating-orange:focus { color: #ba470a !important; text-decoration-color: #ba470a !important; }
</file context>

boomzero and others added 2 commits October 6, 2026 08:13
The two were only about 30 degrees apart in hue at the same lightness and
read as one color. Red moves toward crimson and orange toward amber, more
than doubling their perceptual distance while staying readable on both
backgrounds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5dr7XTsCeoamqLSiT9rLp
boomzero and others added 6 commits October 6, 2026 08:17
The amber looked muddy. With red now crimson, the vivid orange stays
clearly distinct from it and has better contrast on white.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5dr7XTsCeoamqLSiT9rLp
CalculateUserRating only read p(id, count) script calls, so a profile that
lists solved problems as rendered problem.php links rated 0. Use
GetProfileSolvedProblems, which handles both forms. Per-problem attempt
counts are not used by the rating, so no fallback is needed for them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5dr7XTsCeoamqLSiT9rLp
@boomzero
boomzero merged commit 63b7518 into dev Oct 6, 2026
11 checks passed
@boomzero
boomzero deleted the redesign-rating branch October 6, 2026 00:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L user-script This issue or pull request is related to the main user script

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant