Skip to content

v0.7.1: durable OPS baseline, Model Leaderboard, P2P audit sync, profiles & themes - #1

Open
DrGekoz wants to merge 28 commits into
teknium1:mainfrom
DrGekoz:main
Open

v0.7.1: durable OPS baseline, Model Leaderboard, P2P audit sync, profiles & themes#1
DrGekoz wants to merge 28 commits into
teknium1:mainfrom
DrGekoz:main

Conversation

@DrGekoz

@DrGekoz DrGekoz commented Aug 13, 2026

Copy link
Copy Markdown

Latest: v1.4.3 (head DrGekoz:main)

  • Leaderboard dedupe (v1.4.0): one row per GitHub user (no duplicate scores).
  • ChatGPT Model Leaderboard (v1.4.0): ChatGPT models shown by name, never 'unknown'.
  • Agent model reporting (v1.4.1): pre_llm_call hook reports the real model.
  • Modelboard double-count fix (v1.4.1). External links in real browser + click-outside closes modals (v1.4.1).
  • Tier unlock modal fix + shows unlocked tiers + chat chime resume (v1.4.2).
  • Chat duplication fix (v1.4.3): stable per-send message id + relay ts preserve — your own messages no longer appear twice.
  • Model board 0-0-0 rows hidden (v1.4.3).
  • README: 8113 server binds lazily — start Hermes + send a prompt first.

Releases: v1.4.0 → v1.4.3 (DrGekoz/hermes-pixel-office-enhanced)

Ads Doctor Agent added 9 commits August 13, 2026 09:10
… chat

- Fix office layout overflow (flex min-height:0 + overflow:hidden) so the
  scene fits the viewport and all panels scroll correctly.
- Leaderboard: GitHub usernames only (agents/subagents/local rows never
  shown); rank + tier number + tier icon per row, clickable profile cards.
- Tier ladder: all 500 IdleViber tier icons (#N + icon), exponential Ops
  curve reaching 5,000,000 at tier 500.
- Office chat: WebRTC P2P mesh, cached history, per-sender tier badge,
  sound toggle.
- GitHub connect: login sets your username + avatar on leaderboard and chat.
- Header stats strip (Ops, calls, sessions, time ran).
- Hover-reveal themed scrollbars on leaderboard / tiers / chat panels.
…odal

Server:
- Persist per-user profiles (bio, links, chosen tier icon, profile/interface
  themes) in profiles.json; /profile GET + POST endpoints.
- Fold profile fields into leaderboard rows so the frontend + P2P see them.

Frontend:
- Profile popup rewrite: GitHub avatar, chosen tier icon + number, bio, brand
  links (YouTube/Spotify/SoundCloud/GitHub/Discord/Steam, multiples allowed)
  with platform icons, profile theme accent color, and "Multiplayer Fork
  Developer" for DrGekoz (DEV chip kept). Edit button on your own profile.
- Profile editor (#edit): bio, per-platform links add/remove, 500-icon tier
  picker, profile theme color, and an interface theme with per-element color
  pickers (bg, panel, buttons, borders, highlight, font, sub-fonts, status,
  scrollbar, font size/family) with live preview + save.
- Interface theming: whole UI refactored to CSS custom properties; theme
  persists locally (localStorage) across reloads and rides the P2P score
  packets so peers see each other's profile themes.
- Connect modal: removed the "enter a github username" box; added a working
  Edit Profile button.
- Chat: tier icon now sits left of the GitHub profile picture.
- Leaderboard widens to fit its longest row adaptively.
- About/Credits modal: story about the original plugin (Teknium) + the
  DrGekoz / Hermes IdleViber enhancement, with links.
…rs, cleanup

- On-hover tier tooltip (top-right): large 128px icon + tier name/#, Ops
  requirement, and Ops-derived calls + sessions estimates.
- Large tier icons: copied the 500 IdleViber 256px icons (resized to 128px)
  into web/icons/large/ and use them in tooltips, profile popups, and the
  tier-unlock modal; server /icons/ now safely serves the subdirectory.
- Save fix: profile edits (bio, links, tier icon, themes) save locally and
  apply immediately + ride the P2P score packets, so clicking a username in
  the leaderboard or chat populates their full profile - no more "Save failed".
- Tier unlock: locked tiers render as silhouettes; reaching a new tier plays
  a fanfare (if sound on) and opens a side-by-side modal to keep/use the new
  tier icon.
- Profile AGENTS list: shows live status, hides idle/gone agents, and is sent
  through P2P so peers see real-time rosters.
- Tier picker (edit modal): tier number under each icon.
- Connect modal: removed the Edit-Profile button; clicking your name in the
  top bar now opens your own profile (or the connect modal when logged out);
  the login button reads "Logged in with GitHub" when connected.
- Profile editor: red (non-themeable) Log out button under Save.
- pollNow hoisted to module scope (fixes refresh calls from handlers).
…large-icon tooltips

- tiers.js: each tier now carries explicit calls (=ceil(ops/2)) and sessions
  (divisor shrinks 150->25 per tier so session reqs increase per tier while
  staying reachable at ~23 calls/session) threshold fields.
- server _tier_for_user(ops,calls,sessions): highest tier where ALL three
  thresholds are met; leaderboard folds real tools/sessions into gating.
- edit modal tier picker: tiers beyond the user's current tier are greyed out
  and blocked (tooltip says 'Locked - reach Tier #N'); current icon stays usable.
- tier tooltips: show real calls/sessions requirements (not estimates) and
  large 128px icons with a small-icon fallback so they never render blank.
…ersion

- backend: report Hermes' real session count from state.db (cached 20s)
  instead of only events observed while the plugin was running
- frontend: invert black simpleicons GitHub logos to white on dark profile
  backgrounds and in the About popup so they stay visible
- leaderboard/profile/edit UI polish across the interactive office
- Add per-model correction tracking: jekyll-hyde records every activation
  against the audited model (hyde_core + __init__).
- Pixel Office: Model Leaderboard panel + /modelboard endpoint, ranking
  models fewest-corrections-first (most-corrected sinks to the bottom).
- Add jnorthrup as a DEV; credit jekyll-hyde in the About panel + README.
- pixel-office now SUPPORTS the jekyll-hyde audit engine (reads its data,
  never bundles it). Model board shows corrections + sandbagged/genuine/
  uncertain verdicts + escalations + last-corrected, sorted fewest-first.
- If jekyll-hyde isn't installed, panel shows a 'download Jekyll-Hyde' CTA.
- modelboard rides the P2P score packets so peers' audit data merges into
  one shared board.
- jekyll-hyde: activation records now carry model + timestamp; correction
  tally + /hyde corrections.
@DrGekoz DrGekoz changed the title v0.4.0: profiles + themes, tier gating, real session counts, dark-bg GitHub logo fix v0.6.0: Model Leaderboard (jekyll-hyde support), P2P audit sync, profiles & themes Aug 13, 2026
…o ops_baseline.json so scores never drop on events.jsonl trim
@DrGekoz DrGekoz changed the title v0.6.0: Model Leaderboard (jekyll-hyde support), P2P audit sync, profiles & themes v0.7.1: durable OPS baseline, Model Leaderboard, P2P audit sync, profiles & themes Aug 13, 2026
@DrGekoz

DrGekoz commented Aug 13, 2026

Copy link
Copy Markdown
Author

Hi Teknium 👋 — Joe (DrGekoz) here.

First, quick thanks for building the original Pixel Office. This PR is my fork of it, and I wanted to give you the full picture of what I've done with it, why, and everything that's changed between your v0.2.0 and my current v0.7.1.

Who's behind this fork

I'm the guy who built Hermes IdleViber — the ambient gateway idle game. It's a cozy pixel-art idle/incremental game that runs in the browser: 6 themed rooms, 90 upgrades, a 500-rung tier ladder with chiptune music and particle effects, P2P mesh multiplayer with a real-time leaderboard and chat, and the gimmick where your local Hermes gateway latency becomes a VPS multiplier. It's built on Node + Firebase (Auth/Firestore) with a pure-JS canvas, and it's where the entire 500-tier icon set that this office uses comes from.

I'm not a traditional dev — I describe myself as a "vibe-coder on steroids." I know HTML/CSS, I've picked up Supabase and Node and Firebase as I go, and I lean on AI-assisted development as my primary workflow. I ship fast and I refine later. When I saw your Pixel Office, I saw a natural extension of the same universe I was already building, so I forked it and wired it into my Hermes setup. I've been running it as my live office for a while now, and this PR is the accumulated result.

The short version of what this fork adds

Your v0.2.0 was a clean, visual office scene. Mine is a live, interactive office that ranks real people, tracks real score across sessions without ever losing it, spans a 500-tier ladder, ships real-time P2P chat, and even plugs into the Jekyll-Hyde audit engine so I can watch my models get corrected live.

Everything that changed, version by version

v0.3.0 — The Interactive Office. The scene became a live office. Added a GitHub-usernames-only leaderboard (agents and subagents never pollute it — it ranks people), ranked by cumulative Ops earned per tool call, with full profile cards on click. Added the full 500-rung tier ladder using the IdleViber icon set (exponential Ops curve from tier 1 at 0 up to tier 500 at 5,000,000 Ops), your current rung highlighted and auto-scrolled into view. Added real-time P2P office chat over WebRTC data channels using the IdleViber mesh pattern, with per-sender tier badges and history that survives nobody being online. Plus a header stats strip (live Ops, calls, sessions, uptime) and themed scrollbars.

v0.4.0 — Profiles, Themes & Tier Gating. Editable user profiles with bios and social/link rows, custom accent + interface themes across every surface (leaderboard, chat, canvas, agents, profile), a DEV badge and rainbow-border theme for DrGekoz, teknium1 and jnorthrup. Tier requirements now gate on calls + sessions, not just Ops — locked icon picker with an unlock modal and robust large-icon tooltips. Backend now reports Hermes' real session count from the state DB (cached 20s) instead of only what the plugin observed while running. Fixed simpleicons GitHub logos inverting to invisible on dark backgrounds.

v0.5.0 — Model Leaderboard + Jekyll-Hyde. Added a Models panel that ranks models by how many times the Jekyll-Hyde completion auditor corrected them (fewest-corrections-first, so the worst model sinks to the bottom), plus a /hyde corrections command. Added jnorthrup as a DEV and credited him properly.

v0.6.0 — Support-based Model Leaderboard + P2P audit sync. Reworked the modelboard so pixel-office supports Jekyll-Hyde rather than bundling it — it reads Hyde's data and never fabricates anything. Richer per-model rows (corrections, sandbagged/genuine/uncertain verdicts, escalations, last-corrected time), a "download Jekyll-Hyde" CTA when it's not installed, and the modelboard now rides the P2P score packets so peers' audit data merges into one shared board across machines.

v0.7.1 — Durable OPS baseline. This one's the fix you'll appreciate most from a data-integrity angle. Ops (and tools/sessions) were being re-folded live from events.jsonl, which auto-trims at 512 KB — and the oldest half was being silently dropped, so my cumulative score would visibly decrease (I watched it fall from ~2k to ~1.7k). Now events trimmed off the log are folded into a durable ops_baseline.json (cumulative counts per agent, not raw events), and the live fold seeds from it. Scores are now monotonic — they only ever go up. Verified with a unit test: 40 Ops preserved across two consecutive trims with no double-count.

Screenshots

Here's the office as it looks today, straight from the docs/screenshots/ gallery in this branch:

Leaderboard — real GitHub usernames, tier icons, DEV badge, full profile cards:

Leaderboard

500-tier ladder — every rung from the IdleViber icon set, exponential Ops curve, current tier highlighted:

Tier ladder

Edit profile — bio, links, accent + theme, DEV rainbow border:

Edit profile

Profiles panel:

Profiles

Office chat — real-time P2P chat with per-sender tier badges:

Office chat

About / credits:

About

Why fork instead of contribute upstream

Honestly? I build fast and I build a lot — a full interactive office on top of the scene, a 500-tier game ladder, P2P chat, and now audit-engine integration came together quickly and diverged heavily from the original. A PR keeps the momentum without blocking on review cycles, and you can cherry-pick whatever's useful back. The Model Leaderboard, in particular, was built by jnorthrup for his Jekyll-Hyde engine and I've kept his attribution front-and-centre — nothing here is claiming his work.

If any of this is worth pulling into the main repo, say the word and I'll help merge it cleanly. Happy to keep iterating with you.

— Joe (DrGekoz) · Hermes IdleViber · hermes-pixel-office-enhanced

Ads Doctor Agent added 17 commits August 13, 2026 15:09
…lare avoidance, input validation, reconnect backoff)

- Console logs mirroring Hermes IdleViber ('📡 P2P: ...') at every P2P event:
  identity set, start/stop, offering/answering, connected/closed/teardown,
  score recv, chat recv/send, ice state, backoff, and a rebroadcast summary.
- Harden the mesh:
  * Glare avoidance - only the lexicographically-smaller peer id initiates;
    the larger side answers, so two peers coming online together can't create
    duplicate connection pairs.
  * Message validation - drop empty/oversized (>64k) data-channel payloads,
    require a non-empty short score id and numeric counter, ignore our own
    echoed entry, reject malformed JSON (try/catch everywhere).
  * Reconnect backoff - exponential (3s..60s) per peer on failed connects so a
    dead peer can't be hammered every 4s poll.
  * Cap concurrent connections (MAX_PEERS=12) and guard bad args in
    attachChannel/initiate/answer.
- Add test_p2p_smoke.js: drives the REAL module code path (poll -> initiate ->
  offer -> answer -> channel open) for two peers with stubbed WebRTC/signaling
  and asserts connect, score+chat flow, and the hardening (oversized/malformed/
  own-echo/long-id payloads rejected). PASS verified.
- Leaderboard + Model Leaderboard rows now lay out on ONE line (rank, tier, icon,
  name/DEV, and stats all in a row); on narrow windows the stats group wraps to a
  clean second line instead of clipping or overflowing.
- Fix tier-icon flicker: renderLeaderboard/renderModelBoard/renderTiers now skip
  the DOM rebuild when nothing changed. They were re-creating every icon element on
  each 1.5s poll tick and every P2P score packet, re-triggering image load/decode.
- Recreated the signaling gist as PUBLIC (08842d6294ebb262df0886f460f802d5) so
  any player can read presence/SDP/scores without auth.
- _ensure_gist() now discovers an existing signaling gist (lists account gists,
  else falls back to the _WELL_KNOWN_GIST constant) instead of silently creating
  a private one per machine -- this was why two players each saw 0 peers.
- New gists are created public.
- WebRTC data channels remain the FRONT transport for realtime scores/chat.
- Added gist FALLBACK for score delivery: server POST /signaling/score stores a
  trimmed score in the shared doc; client posts its score every 10s when it has
  0 WebRTC-connected peers and merges peers' gist scores for unreachable peers.
- _mutate_signaling() retries with jitter to shrink the concurrent-PATCH
  lost-update race on the shared gist.
…, self-healing)

Ports the battle-tested IdleViber P2P hardening into web/p2p.js and the
gist-signaling server so connections and packet delivery are reliable.

Client (web/p2p.js):
- ECDSA P-256 signed packets: every outbound message is {d,s} (payload +
  SHA-256 signature); inbound messages are verified against the peer's
  published public key before acceptance. Forged/corrupt packets dropped.
- Peers publish their ECDSA public key (JWK) + key id on register; peer keys
  are imported BEFORE any handshake so the first packet on a channel verifies.
- ICE trickle through the gist: onicecandidate relays each candidate via a
  new /signaling/ice endpoint; peers feed candidates into the connection, so
  peers gathering candidates late still connect behind NAT.
- Unreliable (ordered:false, maxRetransmits:0) data channel for realtime
  scores (no head-of-line blocking).
- Staleness filter: peers whose presence is older than 60s are skipped.
- Deterministic multi-phase poll (import keys -> initiate -> answer -> apply
  answers -> ICE -> gist score fallback) so handshakes complete reliably.
- Kept: glare avoidance, per-peer exponential backoff, MAX_PEERS cap,
  oversized/malformed/own-echo/bad-id rejection, gist score fallback.

Server (__init__.py):
- signaling_register stores the peer's ECDSA public key + key id.
- signaling_state relays a per-peer ICE candidate mailbox (ice consumed via
  /signaling/clear).
- New POST /signaling/ice for candidate trickle.

Test: test_p2p_smoke.js now exercises the real signing path (webcrypto in the
sandbox, fake server relays keys) and passes 5/5 (connect + signed score/chat
flow + all hardening checks).
…d, TURN support

- Peer id is now a per-browser random UUID (IdleViber pattern) instead of the
  GitHub username, so two players on the same account get distinct ids and can
  actually see + handshake each other (was the multiplayer blocker).
- Server signaling_* endpoints accept a client 'peer' param (fallback to office
  github identity); /signaling/state reads ?peer=, POSTs read body.peer.
- Durable leaderboard: server folds every peer's gist score snapshot into the
  /state board (_merge_gist_scores) so players appear with zero WebRTC (CGNAT).
- Configurable ICE/TURN: ice_servers() reads plugin config or PIXEL_OFFICE_ICE_
  SERVERS env, served at GET /signaling/ice-config; client loads it at start.
- Client posts its FULL score entry to the shared gist every 10 min regardless
  of WebRTC connectivity (durable fallback), not the trimmed entry.
- Real two-peer WebRTC sim (web/sim_p2p.js, puppeteer-core + 2 headless tabs):
  true data-channel RTT median ~2ms / p95 ~3ms.
- test_p2p_smoke.js gains same-account distinct-peer assertion.
…rsion in About modal

- Gist score persistence is now adaptive: no peer connected over WebRTC -> post
  the full score entry to the shared gist every 10 seconds; at least one peer
  connected -> post every 10 minutes (P2P channels carry realtime scores, the
  10-min snapshot is the durable CGNAT/late-joiner fallback without burning the
  owner-write-only gist quota). Verified all 4 transitions.
- About modal now shows the current version; single OFFICE_VERSION const is the
  source of truth, injected into #about-version on open. Set to v1.2.1.
- Gist score persistence now logs every POST to the console: 'gist post score'
  with ops, connected peer count, cadence (10s/600s), and a summary of the
  entry; plus 'gist post ok: accepted|rejected/ignored' on the response.
- Bump OFFICE_VERSION + About modal to v1.2.2 to match the tag.
…yer backbone

- New relay/ : Cloudflare Worker + Durable Object store (leaderboard, chat,
  profiles, modelboard) that every player can write to over plain HTTPS, so
  multiplayer works under any NAT/CGNAT and is free + non-stop forever.
- Keys the board by GitHub username -> kills duplicate rows for the same
  account (the '3 of me / 2 of Rygel' bug). 10-min host-appointed gist backup
  via a Durable Object alarm (one call), optional GITHUB_TOKEN secrets.
- web/p2p.js : relay client (post score, poll board+chat, merge), dedupe +
  stale-ghost pruning in getLeaderboard, chat merge, shared well-known relay
  default (zero-config install for all players). WebRTC mesh still runs for
  low-latency realtime; the relay is the reliable fallback.
- __init__.py : /office-config endpoint + relay_url() (config/env/shared default).
- web/index.html : fetch relay config and pass it into P2P.
- Deployed live at https://pixel-office-relay.ads-doctor-melbourne.workers.dev
- relay/test_relay.js local smoke test: 9/9 passing.
… links externally + click-outside closes modals
…e 0-0-0 model rows, README lazy-server note
Resolve whichever model the agent actually uses against the authoritative
Models.dev catalog (models.dev/models.json, cached locally for a day with
offline fallback) so the Model Leaderboard always shows the correct model
name and never a bare 'unknown'.

- Add idempotent resolve_model_name(): bare ids, provider-prefixed routing
  paths (deepseek/deepseek-v4-flash, openrouter/...), and canonical names all
  resolve to the same Models.dev display name (e.g. 'DeepSeek V4 Flash').
- _canonical_model_name() now routes non-ChatGPT models through Models.dev;
  ChatGPT ids keep mapping to the known roster.
- _model_leaderboard() re-resolves legacy stripped-id keys to their canonical
  name and drops 'unknown'/'none'/'null' buckets entirely (Models.dev names
  every real model, so an unresolvable bucket is stale by definition).
- Frontend: modelDisplayName() and the modelboard fold skip unknown/empty rows
  instead of rendering 'unknown'.
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.

1 participant