bigintersmind
github.com/bigintersmindActivity
This PR didn't change the countdown timer, so it's going to keep ticking down every 24 hours on openchaos.dev.
#220 fixes this.
What a, dare I say, chaotic sequence of events.
I like the idea and agree. I'm in favor of the change.
Great idea, @amanbabuhemant! I've updated the PR to make each theme's approach unique.
Screenshots and PR description updated.
Suuuuurely it works this time!
I love the idea. I don't love that users have to sign up for another service to use the website.
Why automerge skipped this PR
This PR has 7 net votes, a rhyming title, passing CI, and no merge conflicts — it should have won today's daily merge. However, it was skipped because the combined commit status API returns pending.
Root cause: The branch protection rules for main require a status check named CI (the workflow name), but GitHub Actions only reports status for individual jobs (CI / build, CI / protect-constitution). The CI check is perpetually stuck at "Expected — Waiting for status to be reported" on every PR, which makes the combined status pending, which the automerge workflow treats as a hard skip.
Fix: In the repo's branch protection settings for main, remove the required status check CI and replace it with CI / build. This will unblock automerge for this PR and all others affected (#187, #193, #160, #183, #209).
This is fun. Who pays the bill?
In that vein, I worked to make adding new themes easier with #204. I didn't do an "internet through time" theme because I thought it might feel quite limiting, but I think it's actually worth re-exploring.
We could do an archive.org-style website where new PRs could add new views in the timeline. We can get creative with themes outside of that framing. For example, newspaper could be even earlier on the timeline.
Deployment Notes
No configuration changes needed. This feature works with the existing GitHub OAuth app and environment variables (GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET). No new env vars, no scope changes, no permission updates.
How it works
- When a logged-in user views the PR list, the
useVoteStatushook callsGET /api/vote-status?prs=1,2,3,...with their session cookie. - The API endpoint authenticates via
GET /user, then batch-fetches reactions fromGET /repos/.../issues/{n}/reactionsfor each PR — the same reactions API the existing vote feature already uses. - Each PR card receives its vote status (
up,down, ornull) and renders a glow on the corresponding arrow. - When a user casts a new vote, the status updates optimistically via
onVotedcallback — no extra API round-trip needed.
Rate limiting
The endpoint caps at 50 PRs × 10 pages max per request. Failures on individual PRs are caught by Promise.allSettled and logged server-side — the rest of the votes still load. Users see a non-intrusive warning if something goes wrong.
Can we add Fartscroll to the list? (Maybe a giant turd emoji and Fartscroll is enabled only on the Museum page?)
Agreed, I was thinking the same and was on the fence about it.
Gah, I missed it in my last-minute testing; the package is ESM-only, so require() throws an error.
Added the one-line fix to #195.
I think this problem is sufficiently solved with #185
Closing — reaction has been tepid to mixed, and supporting multiple layouts makes it tough to avoid breaking changes from newly merged PRs.
This is a super cool idea
No worries! Once I reviewed your PR, I realized that I'd missed the rhyme detection update and forgot to update to daily merges! So it's great that you did it, because I may have merged an incomplete fix otherwise.
A version of this was included with #163. If you decide to revisit this, please remove the Rust artifacts.
Closing — this needs reimagining for the current architecture (ASCII + Web 2.0 dual themes). The XP dialogs concept may return in a different form.
You're right, two died. There's no limit in the workflow.
Added this logic to #195, as well.
Buckle up! 🤣
Bug: Mergeability detection is broken
The workflow uses pulls.list to fetch all open PRs, then checks pr.mergeable_state and pr.mergeable to detect conflicts. However, the pulls.list endpoint does not return these fields — they are only populated by the individual pulls.get endpoint, which computes mergeability on demand.
As a result, pr.mergeable_state and pr.mergeable are always undefined, and isUnmergeable is always false. The unmergeable penalty (5% base + 5x multiplier) has never applied to any PR.
Example: PR #84 currently has merge conflicts (mergeable: CONFLICTING, mergeable_state: dirty), but the workflow evaluated it with a 13.39% death chance instead of the ~50%+ it should have had.
Fix: Each PR needs an individual pulls.get call to retrieve mergeability info:
const { data: fullPr } = await github.rest.pulls.get({
owner, repo, pull_number: pr.number
});
const isUnmergeable =
fullPr.mergeable_state === 'dirty' ||
fullPr.mergeable === false;
Note that mergeable can be null while GitHub is still computing it, so a brief delay/retry may also be needed on first fetch.
@matthewmayer , this now co-exists with your ASCII view (which I love!). Users would have a 50/50 chance of landing in either view.
Other PRs can build off this foundation by adding additional views.
Hmm, no ranks after expanding, or in the trending section.
Is "Controll" an intended misspelling (to include "troll" perhaps), or a typo?
I agree with your suggestions to reduce the dirty multiplier and cap it at 1/day, @matthewmayer.
Regardless of multiplier settings, capping at 1 death/day turns it from mass extinction into a sustainable daily drama. In that vein, maybe it's worth immortalizing the culled PRs on the front page somehow?
Also, a grace period (ignoring dirty status for 24–48h after a daily merge) would help with the timezone fairness issue, but would add implementation complexity.
I really like the idea, but think we should tune it to be more fun than discouraging.
Simulation Results: What the Old Age Algorithm Would Do Today
I ran a Monte Carlo simulation (10,000 runs) against the current 64 open PRs to see how this workflow would play out.
Day 1 Impact
The algorithm would kill ~17 PRs on its first run — over a quarter of all open PRs. The unmergeable + old PRs get hit hardest:
| PR | Age | Interactions | Death Chance |
|---|---|---|---|
| #84 | 32d | 5 | 79.3% |
| #46 | 37d | 27 | 76.8% |
| #82 | 32d | 7 | 75.6% |
| #79 | 32d | 8 | 74.1% |
| #5 | 38d | 61 | 69.5% |
| #12 | 38d | 105 | 62.1% |
Even #12 with 105 interactions still faces 62% death — the unmergeable 5x multiplier overwhelms the fitness protection.
7-Day Projection
| Day | Avg Deaths | Avg Alive |
|---|---|---|
| 1 | 17.2 | 46.8 |
| 2 | 9.5 | 37.3 |
| 3 | 5.8 | 31.5 |
| 4 | 4.0 | 27.5 |
| 5 | 2.9 | 24.6 |
| 6 | 2.2 | 22.4 |
| 7 | 1.7 | 20.7 |
After 7 days: ~21 survivors out of 64 (min 13, max 29 across simulations). Deaths front-load heavily as the high-risk PRs die first, then taper off.
Key Observations
- 42 of 64 PRs have <50% chance of surviving a full week
- The
dirtymergeable state is the dominant factor. The 5x unmergeable multiplier means any PR with merge conflicts and more than ~10 days of age is in serious danger, regardless of engagement dirtyis the norm, not the exception. Since a PR merges daily, every other open PR touching the same files becomes conflicted. Authors may have less than 24 hours between the daily merge (09:00 UTC) and the old-age sweep (06:00 UTC) to rebase — and that window gets worse since the sweep runs before most people's working hours. In practice, the majority of PRs will carry thedirtystate at any given time (39 of 64 currently do)- Engagement barely helps unmergeable PRs. PR #12 has 105 interactions but still 62% daily death because the multiplier is applied after fitness reduction
- Non-dirty PRs are comparatively safe. PR #45 (37 days old,
behind) only has 10% daily risk. Same-aged dirty PRs face 60-80% - Young mergeable PRs are essentially immune. 6 PRs have >99% weekly survival — all are <13 days old and not
dirty
Capped vs Uncapped Culling
There is no per-day cap on deaths. In the worst simulated week, 51 of 64 PRs died. On day 1 alone, up to ~30 deaths are possible in an unlucky run. If the intent is gradual natural selection rather than a mass extinction event, a daily cap might be worth considering.
My take on "chaos" in OpenChaos
When I got involved, my read on "OpenChaos" was that the chaos is a byproduct of being open, not the objective. @skridlevsky pitched this ↗ as "a repo where the internet votes on PRs and the winner ships." That's compelling because the chaos is emergent. It comes from the lack of ground rules, not from everyone trying to maximize disorder.
Two rallying themes have taken hold: Democracy (because the mechanic is voting) and Chaos (because it's in the name). I think the democracy thread has been genuinely productive, as it's spawned real governance debates, disclosure rules, and updated voting proposals (#176). The chaos thread, on the other hand, seems to be converging on "make the interface as unusable as possible," which I think misreads the project.
100% chaos is /dev/urandom. That's not interesting, and it's not what drew people here. What's interesting is watching what organically emerges when a group of people builds something with no predetermined direction. Which sometimes looks chaotic and sometimes doesn't.
If the group's intent is to make a website that's miserable to use but checks the "chaotic" box, I think the project flames out shortly (as evidenced by the winning vote being lower each day). If we're interested in building something that people actually want to join and participate in over time, we need to move past chaos maximization as a goal and let it be what it always was: a side effect.
I was working on a time machine feature, but it turned out to be more complicated than I hoped, and I shelved it when #143 showed up. I might revisit it.
Looks great until you refresh the page!
This is a great addition, now that it's working. My only feature request is to be able to see how I already voted on PRs when loading the page.
Yes, random routing would be a big win! Let's give all of the different designs some time in the sun!
For some reason, this one makes me nauseous.
That looks nice. I was just looking for a low-lift solution, and those PRs already had a rank of zero in your code. I prefer your new approach, however.
One thing to flag: after merging into main, the "Trending This Week" section (added in #69) shows "0" for each PR because it passes rank=0.
A quick fix would be to fall back to the PR number when there's no rank:
<b>{rank > 0 ? rank : `#${pr.number}`}</b>
That way, ranked sections show the position and unranked sections show the PR number like before.
I like the way you've handled it, numbering them as they're sorted on the page.
Still a 404 on my end.
Trying to vote or login just sends me to a github 404 page...
I'm bummed this doesn't have more votes. I was hoping this would merge before #130 and the other re-styles. Where possible, we should keep the RPs that were voted in rather than overwriting them.
Yeah, that's fair. I don't think that would be a big deal, though. If votes were being reset more often, the totals would be lower, we wouldn't be getting as many super old PRs winning by default, and people would be more actively voting. At least, that's my theory, and a better state for the project, in my view.
That said, this PR may not quite get us there, but I think it's a preferred direction for the project and part of a wider solution, along with #158.
@Nickhoyer, sure, put it in the PR!
Closing this PR. The core bug (server-side Math.random() causing cached blank pages) was already fixed by PR #76, which moved the randomness check to IE6Layout.tsx (a client component). This PR is now only a minor code quality refinement of that fix and isn't worth a separate merge.
Start Menu merged into #146 (combined XP features PR).
@Daviey #143 would allow for both to be supported.
This PR addresses the issue you describe: #158
I was thinking of a similar solution to the current problem, where we will fail to a white page every day until #174 is fixed.
What if we amend the rules to allow an emergency council to bypass popular vote for bugfixes (however we define that)? The emergency council would comprise anyone who has had a PR merged under the typical voting rules and would require a minimum threshold of votes in favor.
If we can align on a strategy, I can add it to #174 as a comprehensive fix for this type of situation going forward.
Aha, I missed that. Thank you for sharing!
Bug fix: merge status & CI check detection
These bugs exist on main and were not introduced by this PR. This commit fixes them.
1. Wrong API endpoint for CI checks
The code was using the commit status API (/commits/{sha}/status), but this repo's CI uses GitHub Actions, which create check runs — a separate system. The commit status endpoint returned state: "pending" with zero statuses for every PR, since nothing in this repo writes commit statuses. Switched to the check runs API (/commits/{sha}/check-runs), which returns the actual CI results.
2. mergeable: null treated as conflicts
GitHub computes mergeability lazily. The first API request after a change often returns mergeable: null (not yet computed). The code treated null as false, showing false "Merge conflicts" for PRs that are perfectly mergeable. Now defaults to true when null, and the next ISR revalidation cycle (5 min) picks up the real value.
3. Graceful degradation on API errors
Both getPRMergeStatus and getCommitStatus previously returned false on any API error (including rate limiting), which made every PR appear broken. Now defaults to true (optimistic) on errors — better to briefly miss a real conflict than to mark the entire page as broken.
I considered adding this fix to #129, but it doesn't feel like it's in the spirit of the project. Even if it's a "good" trojan horse, it's still a trojan horse.
Yes.
Confirmed in local testing.
I don't think we need any additional settings for this to work. It should work as written.
Reviewing the changes, the more notable part is the README addition:
Code is Law: Rules are for human guidance. If a GitHub workflow contradicts written rules, the workflow (code) takes precedence. Always inspect the codebase to understand the actual system behavior.
The real question is whether the community wants a "Code is Law" clause.
Love the Chaos^N concept!
I'm working on a Web 2.0 era theme and would happily integrate it as a variant if this lands first. My goal is to make it easy for anyone to contribute new themes, so I want to make sure the variant system scales well. A few suggestions with that in mind:
1. Use the standard Next.js middleware location
src/proxy.ts won't be auto-invoked by Next.js. The framework expects middleware.ts (or src/middleware.ts) at the project/src root, exporting a function named middleware. Renaming and re-exporting should be all that's needed:
// src/middleware.ts
export { proxy as middleware, config } from '@/lib/chaos-router';
2. Audit globals.css for theme-specific styles
Moving retro.css into geocities/layout.tsx is the right call. Worth also checking that globals.css (still imported at root) only contains truly shared resets and Tailwind imports. If there are IE6-specific base styles there (body font, link colors, table resets), they'd bleed into other variants. For example, my web2 theme needs Lucida Grande/Trebuchet MS as the base font vs Comic Sans, so shared globals need to be theme-neutral.
3. Scope feature flags per variant
The current FEATURE_FLAGS array is flat and IE6-specific (guestbook, treeGame, midiPlayer). Other themes will have entirely different toggleable components. A per-variant config would scale better:
export const VARIANT_CONFIG = {
geocities: {
flags: ['guestbook', 'treeGame', 'midiPlayer', 'clippy'],
},
teletext: {
flags: ['guestbook', 'clippy'],
},
// future themes add their own flags here
} as const;
This way each variant only toggles components that actually exist in its layout, and new themes don't need to touch the shared flag list.
4. Document the CSS isolation contract
The teletext variant scopes styles under [data-variant="teletext"] which is a solid pattern. It'd help future theme contributors to document this as an explicit requirement – every variant must scope its styles to avoid bleed (whether via a [data-variant="x"] attribute selector, a class namespace like .web2-*, or a route-group layout wrapper). A short note in a README or code comment would help discoverability.
5. Consider passing the variant name down to pages
If the chosen variant name were available to pages (via a header set by middleware, a search param, or a layout prop), each page could look up its own flag config without hardcoding, and shared components like PRCard could adapt per-variant if needed.
None of these are blockers – the core architecture is sound and I'd be happy to build on it. Just flagging things that would make it smoother for Web 2.0 and anyone else who wants to add a theme down the road.
1.337% randomness check in server component caches blank page for all visitors
Bug
The Math.random() <= 0.01337 check in page.tsx (#62) runs server-side at build/render time. When it triggers and returns null, the child components (PRList, HallOfChaos) never render, so their fetch() calls with revalidate: 300 never execute. Without any revalidation hint, Next.js/Vercel treats the blank page as fully static and caches it indefinitely — until the next deployment.
Instead of 1.337% of visitors individually seeing a blank page, one unlucky render poisons the cache and takes the site down for everyone.
Expected behavior
The 1.337% chance should apply per-visitor, not per-cache-rebuild.
Fix
Move the random check from the server component into a client component so it runs in the browser on hydration. The server always renders real content (safe to cache).
I did some digging; I think I understand what happened, and put together a fix:
- #173
Since page.tsx is a server component, the random check runs at build/render time, not per-visitor. When it returns null, the child components (PRList, HallOfChaos) never render, which means their fetch() calls with revalidate: 300 never execute. Without any revalidation hint, Next.js/Vercel treats the page as fully static and caches the blank result until the next deployment. So instead of 1.337% of visitors seeing nothing, one unlucky render poisoned the cache and took the site down for everyone.
#172 moves the check into IE6Layout.tsx (a client component) using useState + useEffect, so it runs per-visitor in the browser. The server always renders real content now (safe to cache), and each visitor individually rolls the 1.337% dice on hydration. Bonus: the page briefly flashes before going blank, like a crash.
I like it; this is a fun idea. 👍
To riff a bit: What if it only counted an individual user's visits? I'm in my 40s, so it would simulate my authentic Geocities experience of seeing the counter increment and later realizing it was because I was repeatedly visiting my own page. ("I hit a thousand! Wait, 998 of those were me, and two were my parents.")
This is something I've been messing with for a while, and decided I may as well make a PR.
I still prefer the bigger pivot with #163, but if we stay the course, this is a small addition for flavor.
To be fair, your idea and approach are funny. It certainly maximizes on chaos.
Classic.
I've submitted PR #168 as a fix for this. Here's a brief rundown of why I think it's the most complete solution currently on the table, and why I'd ask people to evaluate the competing PRs on technical merit.
What #168 fixes:
- Net votes — The current workflow counts
+1reactions only. The website displays+1 - -1(net votes), and that's what should determine ranking. #168 fixes this; #159 and #161 do not. - Tiebreaker — RULES.md says "Ties go to the more recent PR." #168 implements this; #159 and #161 do not.
- CI verification — #168 checks both commit statuses and check runs before merging. #159 and #161 skip this entirely.
- Mergeability retry — GitHub's API can return
nullformergeableif it hasn't been computed yet. #168 retries once after a delay; #159 and #161 don't account for this and could incorrectly skip a valid PR. - Correct permissions — #168 adds
checks: readandstatuses: readto the workflow. #159 callsissues.createComment()without the requiredissues: writepermission, which would fail at runtime.
On the bounty:
If #168 wins, I decline the $100. I don't think a cash prize should be a factor in which fix gets merged. It's created a situation where the conversation around #159 and #161 is dominated by arguments about money rather than whether the code is correct. I'd rather people just vote for whichever PR they think is the best fix.
Great idea. Let's keep things fresh!
I spent the last few days on this one. I had a lot of fun, and it brought back quite a few memories.
Open to feedback and ideas, and to making themes swappable.
Good call. @skridlevsky can update the explicit list in any future PRs that skip a vote.
Cat (#71) and Clippy occupy the same space on the right-hand side, so I delicately moved Cat to the top of the TUNES player. If the player is closed, Cat elegantly glides down to the bottom bar. Other Cat behaviors are unchanged.
https://github.com/user-attachments/assets/d8ccc73b-cef4-44c6-afa8-d3ffc81e97a6
Windows XP error dialogs!
Summary
- Adds random Windows XP-style error dialogs for authentic 2003 nostalgia
- Includes classic errors: IE illegal operation, ActiveX warnings, MSN Messenger notifications, "You're a Winner!" popups, and more
- ~20% chance to trigger every 45 seconds, plus 30% chance on initial page load
Screenshots
This is identical to #104
This is also incorporated into #132, along with other views.
Closing in favor of #132 which includes multiple PR views (Top, Hot This Week, Controversial, Discussed, Newest) with frames-style navigation.
I'd also make them gray
Great idea; added!
Add frames with multiple views: Hot This Week, Controversial, Discussed & more
Summary
Adds 5 different ways to explore PRs, each offering a unique perspective:
| View | What it shows |
|---|---|
| 🏆 Top By Votes | All-time vote leaders - the community favorites |
| 🔥 Hot This Week | Only votes from the last 7 days - what's gaining momentum right now |
| 🌶️ Controversial | PRs with both upvotes AND downvotes - the divisive ones |
| 💬 Discussed | Most commented PRs - where the conversation is happening |
| 🆕 Newest | Recently created PRs - fresh submissions |
Why this matters:
- Hot This Week uses reaction timestamps from GitHub API to count only recent votes - a PR with 10 votes today beats one with 50 stale votes
- Controversial surfaces PRs that spark debate (both 👍 and 👎)
- PRs can appear in multiple views (a new PR with lots of votes shows in Top, Hot, and Newest)
Navigation uses a classic IE6 frames layout for a retro aesthetic.
Screenshots
Deprioritize PRs with merge conflicts in ranking
Summary
PRs with merge conflicts can't win the daily merge, but they currently appear at the top of the list if they have the most votes. This creates confusion when a high-voted PR doesn't win.
This PR makes the displayed ranking match the actual winner selection methodology. By moving conflict PRs to the bottom, users can follow along and understand why certain PRs were accepted and why others weren't.
Changes:
- PRs with merge conflicts are moved to the bottom of the ranking list
- Within each group (mergeable / has conflicts), PRs still sort by votes, then by newest
Attribution
This PR incorporates the auth header fix from #119 by @skridlevsky. The merge conflict indicator was showing false positives because getPRMergeStatus() and getCommitStatus() were missing auth headers.
Screenshots
Good catch. sort=updated returns recently active closed PRs, but doesn't guarantee merge order. Added explicit client-side sorting by merged_at to ensure true chronological merge order.
Incredible
Add Clippy, the helpful assistant!
Screenshots
Summary
- Adds the iconic Microsoft Office Clippy to the bottom-right corner
- Displays randomized helpful tips about voting on PRs and the site
- Features authentic Windows XP-style speech bubble and buttons
- Includes gentle idle animation for extra nostalgia
Features
Randomized Tips - Includes classics like:
- "It looks like you're trying to vote on a PR! Would you like help with that?"
- "Would you like me to search AltaVista for 'how to vote on GitHub'?"
- "You look like you could use a break. Want me to open Minesweeper?"
Authentically Annoying - Just like the real Clippy:
- Can be dismissed with "OK" but comes back in 15 seconds
- Has a "Don't show tips" button that is politely ignored after 30 seconds
- Click Clippy anytime to bring back his wisdom
Retro Styling - Uses the classic Clippy image with:
- Yellow speech bubble with pointer
- Windows XP gray 3D-style buttons
- Subtle bouncing idle animation
Good point, @sevonj. I've added a "show all" option.
Added a "show all" link so all PRs are easily viewed.
This is a big improvement!
We're now IE6 ready!
edit: removed skridlevsky's PRs from the list, since they are maintenance fixes and not voted in.
This is a good idea. #60 creates a lighter version of this by implementing a "Hall of Chaos" and noting previous winners. We can extend this by rendering the page with that version of the code when a card is clicked.
Also, with every revision, does everyone who reacted need to react again with every change?
Perhaps we should use a database and implement voting via the frontend, rather than GitHub reactions.
Then we could tie votes to the commit SHA and other methods.
Add hot score ranking with trending section
Summary
- Implements Reddit-style "hot" ranking that combines votes (logarithmic) with recency (linear)
- Newer PRs can now compete with older PRs that have more votes
- PRs displayed in two sections: Top 5 by Votes (merge candidates) and Trending (rising PRs by hot score)
- Items in Top by Votes show a "TRENDING" badge if they're also in the top 5 by hot score
How it works
hot_score = log10(votes + 1) + (age_seconds / 45000)
The decay constant (45000 seconds ≈ 12.5 hours) means a PR needs ~10x more votes every 12.5 hours to maintain its position against newer PRs.
Screenshot
Simplify main page with top 5 PRs and newest section
Summary
- Limit main PR list to top 5 by votes to reduce clutter as the number of open PRs grows
- Add a "Newest" section showing the 5 most recent PRs not already in top voted, giving visibility to new submissions
- Add consistent section headers ("Top by Votes" and "Newest") for clear visual hierarchy
Screenshot
Add Hall of Chaos - Past Winners section
Summary
- Adds a Hall of Chaos section that displays previously merged PRs
- Each winner is immortalized with their PR number, title, author, and merge date
- Shows the evolution of the site through community votes
What's Added
getMergedPRs()function in/src/lib/github.ts- fetches closed+merged PRs from GitHub APIHallOfChaos.tsx- async server component that renders the list of past winnersHallOfChaosCard.tsx- individual card component with 🏆 trophy and green "MERGED" badge- New section on homepage below the open PRs voting section
Why
The site is about its own evolution through community voting. Showing that history:
- Makes the site self-documenting
- Motivates contributors (their work gets immortalized)
- Lets visitors trace how the site got to its current chaotic state