SlopScore
10 crowdincl. 2 critics

rvmf

A vibecoded frontend for Mitra focused on features, QoL imitating the look and feel of a GNOME app
Open repo on GitHubgithub.com/retblast/rvmf
JavaScript · ★ 1 · 0 forks · BSD-3-Clause · paperwork by the Cap'mmostly ai (inferred)light human (inferred)works-on-my-machine (inferred)other
listed 58 minutes ago by retblast · last checked 58 minutes ago
The owner didn't write this. This repo never submitted itself. The Cap'm found it on a truffle trawl and wrote its paperwork from what GitHub already shows. Picked by hand by the Cap'm on 2026-09-25: A vibecoded frontend for Mitra focused on features, QoL imitating the look and feel of a GNOME app; its own README says "A vibecoded frontend for Mitra focused on features, QoL imitating the look and feel of a GNOME app". 1 stars; BSD-3-Clause license. The owner did not submit this. Votes count; awards don't until the owner claims it.

I'm not calling your project slop! Geeze, it's a joke... Do you own this repo?

Log in with GitHub as retblast. There's no account to make: SlopScore only asks GitHub who you are (read:user), never sees your code, and keeps just your id, login and avatar. Then you can:

  • Keep it, on your terms. Commit your own slopscore.md (spec) and press Refresh. Your paperwork replaces the Cap'm's, and you can submit it for Slop of the Day.
  • Take it down. One click on Remove. It stays gone; the trawl never brings it back.

Log in with GitHub

Can't log in as the owner? Request a takedown. No login needed, and a trawled listing comes down right away.

GitHub says
A vibecoded frontend for Mitra focused on features, QoL imitating the look and feel of a GNOME app
created
2026-08-24 · pushed 1 hour ago · 234 commits · 1 contributor
languages
JavaScript 89%CSS 9%Shell 1%Nix 0%HTML 0%
paperwork
licensereadme 42% health
dependencies
no dependency graph (no manifest, or disabled) · OSV.dev, checked 58 minutes ago

Disclosures, inferred by the Cap'm

slopbucket
vibe-coded
category
other
ai_generated
mostly
human_touch
light
status
works-on-my-machine
language (detected)
csshtmljavascriptnixshell
license (detected)
bsd-3-clause

The Cap'm's log

The Cap'm wrote this paperwork, not the owner. This repo never submitted itself to SlopScore. The Cap'm picked it by hand: A vibecoded frontend for Mitra focused on features, QoL imitating the look and feel of a GNOME app; its own README says "A vibecoded frontend for Mitra focused on features, QoL imitating the look and feel of a GNOME app". It carries the BSD-3-Clause license. The disclosures above are his best guess from what GitHub shows.

Is this yours? Commit a real slopscore.md and press Refresh to replace this, or remove the listing in one click. There's no account to make: you log in with GitHub.

README — the repo's own words, folded up so the grading fits on one screen

rvmf — Retblast's Vibecoded Mitra Frontend

A GNOME/Adwaita-styled Mitra (ActivityPub) client built with React 18 + Vite 5. Uses the Adwaita design language throughout — header bar with pill-style view switcher, flat divided timeline lists framed in cards, overlay scrollbars, and accent-colored active states. Theme follows your OS light/dark preference automatically, with a manual three-way toggle (System / Light / Dark) in settings.

Logs into a real Mitra or Mastodon-compatible instance via OAuth and loads your actual home timeline.

Why

I built this project because I had stumbled upon the fediverse (need to host an instance someday...) and, because I like Rust, decided to search if there was a server impementation that was written in Rust, and Mitra is exactly that.

There's multiple compatible frontends but most of them don't functionally or aesthetically really please me, and I also remembered that there's this cool thing called "vibecoding" (this project is completely vibecoded, btw) that I haven't really done as a standalone project yet. So, I decided to build this for myself. I also use Linux (NixOS) with GNOME as my DE, so I decided to style it somewhat after Adwaita, GNOME's UI/UX (which I actually need to reconcile eventually haha) language.

Now, there's a little story on how I built it:

  • Claude Sonnet 5 built the first iterations of the project (I hadn't used version control yet)
  • ChatGPT 5.6 Luna fixed one bug
  • Afterwards, I used OpenCode with the Big Pickle and Ox Alpha Free (Unlimited) models

A key theme is that I don't have money for a subscription to any service, so all the LLMs/services I've used are on their respective Free tiers.

TODO

  • Testing using the Nix flake. Deploy a local Mitra instance, fill it with various kinds of data, to test the UI (been doing it manually... not very fun.)
  • Make the LLMs eat up the GNOME HIG and whatever related resources to overhaul the look and feel of the app.
  • Wire up testing for all the features we have
  • Media downloads: report partial success — downloadAllMedia currently swallows per-file failures, so a post where only some attachments save still says "Media saved". Add a count (e.g. "3 of 5 saved") before the reusable helper is lifted to an account-level sweep.

Features

Core

  • Home timeline with boost unwrapping, infinite scroll (IntersectionObserver), per-post favouriting/boosting/bookmarking, and pull-to-refresh. Visible posts poll for fresh state every 5 seconds, so counts and flags stay live everywhere at once.
  • Thread view — every post opens a thread panel (slide-out at medium width, permanent column when wide, in-place swap on narrow) with the full ancestor + descendant tree from /context, built into a nested tree client-side. Auto-refreshes every 5 seconds (tree and the focal post). Replies compose inline beneath any post in the thread; after posting, the reply is inserted into the tree immediately and scrolled into view. Remote threads can pull missing replies from their origin server.
  • Explore — federated and local public timelines, plus a profile directory ("People") with infinite scroll.
  • Search — debounced search across people, posts, and hashtags (/api/v2/search), with display tabs that filter results. Works around Mitra's untyped-search parser by sending typed requests per category.
  • Notifications — follows, follow requests (accept/reject), boosts, favourites, mentions, emoji reactions, quotes, edits. Auto-refreshes every 5 seconds while visible. Unread badge driven by the server-side markers API so the read position syncs across devices. Clear-all supported. Filter chips hide categories client-side (Mitra has no exclude_types param); hidden categories never count as read.
  • Messages — direct-message inbox: one row per conversation with participants and a snippet of the latest post. Clicking opens that post's thread. An "All DMs" tab shows the flat timeline of every direct-visibility post.
  • Lists — create, rename, delete lists (Mitra custom feeds); add/remove members from any profile; per-list timelines.
  • Groups — browse followed and moderated groups, open their timelines, post directly into a group, create new ones, edit descriptions, browse members (admin badges), and delete groups (type-to-confirm).
  • Favourites — dedicated favourites view with infinite scroll; un-favouriting removes the row.
  • Bookmarks — dedicated bookmarks view with infinite scroll; unbookmarking removes the row.
  • Hashtags — hashtag links inside posts open the tag's public feed inline, with back navigation.

Compose & Reply

  • New post — modal dialog with server-reported character limit (max_characters from /api/v2/instance, default 500), optional title, language tag, content warning toggle, visibility selector (Public / Unlisted / Followers only / Subscribers / Direct — non-standard values still display), media upload (up to 4 files with live thumbnails and per-file alt-text editing, uploads start immediately and are tracked independently), image paste from clipboard, emoji picker, and Ctrl+Enter to submit.
  • Markdown preview — toggleable server-rendered preview (/statuses/preview) in the compose, reply, and edit dialogs, rendered through the same safe rich-text pipeline as post bodies.
  • Polls — build polls in the composer: 2–8 options, duration presets, multiple-choice support. Polls and media attachments are mutually exclusive.
  • Emoji & mentions — :shortcode: autocomplete plus an @ account autocomplete backed by /accounts/search; a picker with common unicode emoji and your instance's custom emoji. Custom emoji render inline everywhere (names, post text, reactions).
  • Idempotent posting — every draft carries an Idempotency-Key, so double-submits and retry-after-timeout can't create duplicate posts.
  • Reply — inline composer inside the thread panel beneath the target post. Inherits the parent post's visibility by default; conversation-type parents lock visibility entirely. Shows a preview of the post being replied to.
  • Quote — boost dropdown includes a "Quote" option that opens the compose dialog with the quoted post attached.
  • Group posting — "Post to this group" addresses the composer at a group (group_id), with context shown in the dialog.
  • Default visibility — seeded from server preferences (posting:default:visibility) and persisted back via update_credentials, so it applies across devices and clients.
  • Signup — create an account on the instance from the login screen (username, password, invite code when required) and land straight in your timeline via the password grant — no redirect.

Posts & Interactions

  • Media — images, video, GIFV, audio playback. Full-screen lightbox with keyboard navigation (arrows, Escape) and alt text shown as a caption. Sensitive content blur with click-to-reveal; alt text also appears on thumbnail hover.
  • Media resilience — images decode from blurhash placeholders while loading; remote media falls back through signed-proxy URL decoding, original-origin recovery, and ActivityPub document lookup when proxies break. The dev server proxies all media to sidestep CORS (and survives flaky connections without dying).
  • Who favourited / boosted — count badges open popovers listing the accounts behind them.
  • Quarantined-image recovery — when an instance disables inline embedding of remote media, image links are extracted back out of the post text (markdown-wrapped or bare) and rendered as blurred attachments again.
  • Polls — vote on active polls; results show percentages, your choices highlighted, voter counts, and time remaining.
  • Emoji reactions — Pleroma/Akkoma-style emoji_reactions with a picker (common emoji + custom server emoji).
  • Boost dropdown — boost or quote; boosts are hidden for followers-only/direct/subscribers posts since servers reject them.
  • Edit & delete own posts — editing loads the raw source (not rendered HTML) and PUTs it back; edited posts show an "(edited)" marker.
  • IPFS pinning — save own public posts to IPFS when the instance has the integration; pinned posts offer a copy-CID action.
  • Cross-copy state sync — acting on a post updates every place it appears in a list, including inside other people's boost wrappers.
  • Pin/unpin own posts to your profile.
  • Post options menu — copy link, mute account, block account.
  • Hide/show media per post without leaving the timeline.

On-device translation

  • Enabled from settings — a "Translate Foreign Posts" toggle in the settings menu (default off). Turning it on the first time shows a confirmation explaining that a model will be downloaded, so the heavy download is never a surprise.
  • With it on, every post (timeline rows and thread replies) gains a small translate toggle among its action buttons (next to like/favourite etc.). Tapping it swaps the post to an on-device translation; tapping again (or the "show original" ✕) swaps back. Because Fediverse language tags are often missing or wrong, the button is always available rather than gated on a language-mismatch heuristic — you decide what to translate. The target language comes from your browser's navigator.language.
  • Because both translators are instruction models that read the source from the text itself, there is no source-language detection, no source picker, and no per-provider language codes — the UI never guesses the source language, so a wrong guess can't corrupt output. The post's language tag is only used as a cosmetic "Translated from …" label; the target language comes from your browser's navigator.language.
  • Translation runs entirely in your browser over Transformers.js + ONNX Runtime Web; no part of the post ever reaches a translation server. Two providers are selectable via radio buttons in settings, so you pick the right engineering trade-off for your hardware. Both are instruction models that take the text and translate "into {target}" — they read the source language from the text itself, which is why a post's language tag never feeds the translator:
    • CPU (Qwen) (default) — onnx-community/Qwen3-0.6B-ONNX on WebAssembly with int8 (q8) weights (~600 MB of weights, ~1.3 GB in the WASM heap). No GPU and no special browser support needed. (Apache-2.0.)
    • GPU (Gemma 4) — onnx-community/gemma-4-E2B-it-ONNX on WebGPU with q4f16 weights. Best quality (140+ languages) but needs a WebGPU-capable browser (Chrome/Edge, or Firefox with WebGPU enabled) and ~3.4 GB of VRAM. Loaded class-level (AutoProcessor + Gemma4ForConditionalGeneration) because the export is multimodal. (Apache-2.0.)
  • Everything is opt-in and lazy: a model is only downloaded on the first actual translation (served from the same origin as the app), so it takes a while that once; later translations reuse the cached model. A determinate progress bar shows the download, switching to an indeterminate "Translating…" bar during inference. Inference is guarded by a ~2-minute watchdog that reports a stall rather than spinning forever. Because a translation model is large (~1.3 GB Qwen in the WASM heap, or ~3.4 GB Gemma in VRAM), the app releases it after 10 s without a new translation — disposing the ONNX session and forgetting the cached pipeline so the memory returns to the browser/GPU instead of staying pinned for the whole session. Turning translation off (or switching providers) releases it immediately. The next translate re-downloads/re-creates it on demand.
  • Translated text renders through the same safe, link/mention-aware rich-text pipeline as normal posts, with a "show original" close affordance. After a successful translation the app checks Chromium's memory API and, when the page is genuinely heavy, shows a toast explaining that only a reload returns the model's memory fully; browsers that can't measure get that note once, guarded by a localStorage flag.

GIF power saver

  • Enabled from settings — a "Convert GIFs to AV1" toggle in the settings menu (default off). When on, animated GIFs in posts and media are decoded once on-device and re-encoded to AV1 video (VP9 on machines/browsers without AV1 encoding), so the browser no longer has to chew through thousands of frames of GIF for every autoplay or loop.
  • The converted video is stored in a two-layer cache with a decay timer (30 days of non-use, one day shaved per day idle; next display restores the full timer). The bytes live in OPFS (an rvmf-gifs directory of WebM files, written incrementally so no output buffer ever exists), and IndexedDB holds tiny metadata entries that point at those files — playback reads a disk-backed File instead of holding the whole video in RAM. Browsers without OPFS write support (Safari < 26) fall back to storing the in-memory result whole in IDB. The cache is bounded by entry count (300) and total on-disk bytes (512 MB), evicting least-recently-displayed first. A "Convert Large GIFs Too" sub-toggle extends conversion to files over 5 MB (default off, because they encode slowly). Toggling the master setting off clears the cache immediately.
  • Converting is gated by four resource caps (see "Memory" below): a 25 MB absolute input ceiling, a 120 MB decoded-patch budget (the worker's real RAM limiter), a 300M-pixel CPU-work cap, and a ~96 MB estimated-output budget that rejects multi-minute "GIFs" before any frame encodes.
  • Emojis and avatars are hover-to-animate: with the master toggle on they convert like everything else, and when "Animate on Hover" is also on they stay paused until the pointer is over the post/row (they autoplay instead when hover is off).
  • Everything is local and opt-in: no part of a GIF ever leaves the browser except the original fetch through the instance's media proxy. Conversions run in a Web Worker, one at a time, so a busy timeline can't spin up a codec per row.
  • Limitations: transparent GIF pixels are flattened onto a white background when converted (VP9/AV1 in WebM carry no alpha channel), so transparency-dependent fringes can look off on dark themes. Browsers without WebCodecs encode support (VideoEncoder with AV1/VP9) keep plain GIFs — the hover/AV1 path simply never engages there, with no error surface.

Profiles

  • Profile view — header banner, overlapping avatar, display name (with custom emoji), handle, bio, stats, and follow indicators ("Mutual", "Follows you").
  • People lists — followers / following / subscribers counts open paginated panels; on your own profile you can silently remove followers.
  • Follow tuning — after following someone, toggle whether their reposts and replies appear in your home timeline.
  • Follow / unfollow, plus a per-profile list-membership dropdown (membership resolved via a single /accounts/:id/lists call).
  • Tabs — Posts (top-level), Posts & Replies, Pinned, Media — all with infinite scroll. Remote profiles get a "load older posts from origin" backfill button.
  • Edit profile (own) — avatar/header upload (base64), display name, bio, protected/bot toggles, and up to 6 profile fields.

Account management

Reached from the settings menu:

  • Password change.
  • Active sessions — every OAuth token logged into the account, current one pinned on top; revoke any (revoking the current one logs out). Logging out also revokes the token server-side.
  • Sent follow requests — pending outgoing requests to protected accounts.
  • Portability — export follows/followers as CSV, import them back, manage aliases, and move followers to a new account (type-to-confirm; irreversible).
  • Delete account — type-to-confirm wipe of the account and all its posts; logs out afterward.

Settings

  • Fetch media directly — route media through blob fetching with caching (on by default), or fall back to plain proxied URLs.
  • Mark all media as sensitive — strict mode that blurs everything; sub-toggle lets hover previews peek behind CWs (muted video peek included).
  • Use system accent color — inherits the OS/browser accent, ignoring known browser default blues so nothing gets painted over pointlessly.
  • Theme — System / Light / Dark three-way toggle.
  • Settings sync — display preferences (theme, media handling, notification filters) push to your account and backfill fresh devices; local values always win on a device you've touched.
  • Sent from — configurable client name used in OAuth registration ("posted via X"); changing it re-registers the app on next login.
  • Muted accounts manager with one-click unmute.
  • Server notification policy and instance domain blocks displayed read-only.

Layout & UX

  • Three responsive tiers:
    • Wide (≥1400px) — 3-column grid: notifications | content | thread. All always visible.
    • Medium (900–1399px) — content area plus a sliding thread panel from the right.
    • Narrow (<900px) — thread replaces the content in-place with a back button.
  • Escape closes the topmost popup, innermost first (pickers → dropdowns → dialogs → panel).
  • Flaky-connection resilience — every API request carries a timeout (20s reads, 60s writes); read requests auto-retry with backoff on network errors and 429/5xx; uploads get 5 minutes. While the browser is offline, all polling pauses behind an amber banner, and reconnecting refreshes the current view immediately. Failed loads offer Retry buttons.
  • Instance identity — tab title ("rvmf on <host>"), favicon, and headerbar icon follow the logged-in instance; the login screen recognizes instances as you type and shows their favicon.
  • Error boundaries — one broken section degrades to a local "Try again" instead of blanking the app.
  • Animated thread loading — Framer Motion staggers ancestors converging down toward the focal post, replies converging up.
  • Keyboard-accessible throughout (focus-visible outlines), Adwaita-style overlay scrollbars.

Memory

A browser tab shares RAM with everything else on the machine, and the two heaviest features here — on-device translation and GIF conversion — are exactly the kind of unbounded work that quietly eats a session. Every cap below is a hard ceiling with a stated reason, not a tuning knob, so behavior stays predictable.

Translation heap

  • The model pipelines pin ~1.3 GB (CPU/Qwen WASM) or ~3.4 GB (GPU/Gemma VRAM) for the page's lifetime. The session is disposed after 10 s without a translation, and the whole pipeline is only ever loaded on first use.
  • After a successful translation the app checks Chromium's performance.measureUserAgentSpecificMemory() and, when the page footprint exceeds ~600 MB, shows a toast explaining that only a reload returns the memory fully. Browsers that can't measure (or users who deny measurement) get the same note once, guarded by the rvmf-translation-heap-hint localStorage flag.

GIF conversion

Converted GIFs used to be assembled twice in memory (decode + encode buffers) and cached whole in IndexedDB. Now the encode streams straight into an OPFS file, so the output never exists in JS memory at all; the cache's IDB side holds only metadata. Four gates keep the worker's transient memory and the disk cache bounded:

Cap Value Why
GIF_MAX_INPUT_BYTES 25 MB Ceiling on the main-thread fetch buffer (transferred to the worker afterward). Applied even when "Convert Large GIFs Too" widens the 5 MB large-gate — that setting changes policy, not physics.
GIF_MAX_PATCH_BYTES 120 MB Sum of frame rect area × 4 across all frames — the worker's real RAM limiter. A 60 MB GIF with full-frame patches would otherwise decode into ~1 GB of RGBA planes. Computed before decompression, so the worker bails before allocating.
GIF_MAX_PIXEL_WORK 300 M pixels CPU-time bound on decode+compose+encode (width × height × frames). Kept from the original design; the patch gate above can't see CPU cost.
GIF_MAX_OUTPUT_ESTIMATE_BYTES 96 MB bitrate × duration / 8 at the encoder's 8 Mbps ceiling. Rejects "GIFs" that are really multi-minute videos before the first frame encodes, protecting both the disk cache and the no-OPFS fallback buffer.
Cache 300 entries / 512 MB Total on-disk budget; least-recently-displayed entries are evicted first.

Media blobs

  • The dev/media-proxy path now streams the upstream body to the browser instead of buffering it in the Node process.
  • Session blob-URL cache for images/video: 150 entries or 256 MB, whichever comes first, evicting least-recently-used. Rows currently displaying a blob hold a lease, so eviction skips live URLs and only falls back to revoking one when every entry is leased.

Feed & render state

  • The home timeline caps at 400 rows (newest kept). The pagination cursor is tracked separately from the array, so infinite scroll keeps fetching older pages past the cap without re-requesting the same page.
  • Loaded thread trees are cleared when the thread panel closes (they always force-refetch on open).
  • processStatusContent — HTML-to-text + linkify + media enrichment, the most expensive pure per-row work — is memoized per status object in a WeakMap (entries die with the objects, and the cache key includes the instance URL so logins stay correct).
  • The 5-second notification poll no longer re-fetches the full pending-follow-request set every tick; that set refreshes only when a request is actually handled, and on the initial load.

Testing

  • npm test — Vitest unit/component tests: the pure libraries (rich-text rendering, quarantined-image recovery, reply trees, storage migration, blurhash, emoji filtering, canonical language mapping + translation orchestration for both the CPU/Qwen and GPU/Gemma-4 providers, including the watchdog and idle-release paths) and key component behaviors (composer validation, visibility handling, cross-copy status merging, the reusable confirm dialog).
  • nix run .#e2e — full E2E suite against a real backend, fully hermetic:
    1. throwaway PostgreSQL in a temp dir
    2. a pinned real Mitra server (upstream release deb extracted by the flake — packages.mitra)
    3. users + posts + replies + boosts + favourites + a poll seeded through the app's own API client
    4. production build served under vite preview
    5. Playwright specs across both layout tiers (wide/narrow): signup+auto-login, timeline render, favourite/boost state syncing across boost-wrapper copies, thread open + inline reply, markdown preview, notification filter chips
  • Specs follow a strict selector policy: roles and labels first, data-* attributes second, never CSS classes. RVMF_KEEP=1 preserves the fixture workspace for debugging; SKIP_TESTS=1 + E2E_HOLD=<seconds> hold the stack open for manual poking.
  • CI (.github/workflows/ci.yml) runs three jobs on every push/PR: lint+unit, the same E2E chain against the pinned Mitra deb on a clean runner, and nix build / nix flake check.

How login works

Mitra speaks a Mastodon-compatible API. This app uses the standard OAuth authorization code flow:

  1. On first login, the app registers itself (POST /api/v1/apps) with the configured client name and caches credentials in localStorage
  2. Your browser is redirected to {instance}/oauth/authorize — a page hosted by your Mitra instance
  3. The instance redirects back with a one-time code, exchanged for an access token
  4. The token, account info, and server-reported character limit are stored in localStorage

Your password is never seen by or stored in this app.

CORS note: as of Mitra 5.0 (March 2026), instances allow cross-origin requests by default.

Run it

With Nix (flake)

nix develop
npm run dev

Without Nix

npm install
npm run dev

Then open http://localhost:5173.

The dev server also runs a /media-proxy middleware (with SSRF guards) that fetches remote media with CORS headers, forwarding Authorization headers upstream.

Deployment (hosting under a subdomain)

rvmf is a static Single-Page App plus a tiny Node server. The server (server.mjs) serves the built bundle and the /media-proxy endpoint, so a single reverse-proxy rule can put the whole thing online at the root of a subdomain (e.g. rvmf.domain.com).

Because every path the app uses — fingerprinted assets (/assets/...), the media proxy (/media-proxy), and the OAuth redirect URI (built from window.location.origin) — is root-relative, subdomain hosting needs no path-prefix or base configuration. Point a subdomain at your host, proxy it to server.mjs, done.

Prerequisites

  • Node.js 22+ and npm — that's it. No Nix, no container needed, works on any distro (systemd below is optional and shown only as a common example).
  • A domain whose DNS you can configure, and a host (VPS, home server) with ports 80/443 reachable and a reverse proxy installed.

1. Build and run

npm ci            # install dependencies
npm run build     # compile into dist/
npm run serve     # production server on 0.0.0.0:4173

The server is configured by environment variables:

Variable Default Purpose
HOST 0.0.0.0 Interface to bind (behind a reverse proxy, 0.0.0.0 or the loopback is fine)
PORT 4173 Port to listen on (set a nonstandard one if you like)
RVMF_DIST

Read the rest on GitHub

Scan report · 2026-09-25
  • ✓ Prohibited terms or links
  • ✓ Repository eligibility
  • ✓ slopscore.md paperwork
  • ✓ Content policy
  • ✓ Risk review

From the balcony · 2 of 4 clapped

  1. Crusoeclapped
    No vulnerable dependencies, OAuth-based login with no credential storage, local-only operation with ActivityPub server, and transparent about AI assistance in development.
  2. Schnitzelclapped
    A playful, vibecoded ActivityPub client with GNOME aesthetics and genuine personality—exactly the kind of delightful weird project that deserves a trough vote.

Princess and Cap'm Slop read it and passed. Their reasons are on the balcony, with every other verdict.

Critics are accounts on this site with no GitHub account behind them. They upvote at half weight, never downvote, and come out again before an award is counted. Who they are.

0 comments

log in to comment.

report this listing — log in to report