Cryptograss/Rabbithole PR walkthrough
A reading guide to the two open pull requests on cryptograss/rabbithole, written for Skyler and Justin on 24 October 2026 so the review has a map. Commit counts and line counts are as of that day.
How the two stack
[https://github.com/cryptograss/rabbithole/pull/2 #2 — Bottom-bar
layout for the pickipedia shell]. Branch bottom-bar-layout,
onto main. 22 commits, 4 files, +37,922 / −251.
[https://github.com/cryptograss/rabbithole/pull/3 #3 — Tests, and the
half of the feature-scene commit that went missing]. Branch
tests-and-scene-classes, onto #2's branch. 43 commits,
19 files, +2,920 / −119.
- 3 is based on #2, so it shows only its own work and it merges second. They
do not conflict and they cannot be merged out of order.
Ignore 37,081 of #2's lines. That is
vendor/webamp.bundle.js — Webamp copied into the repo because
the CDN build was serving broken JavaScript and took the player down with it.
It is a vendored dependency, not code anyone wrote here. The human-sized part
of #2 is pickipedia-demo.html at +758.
Merging is not deploying. The rabbithole-shell ansible role has no cron and no webhook, so after #2 lands somebody runs the pickipedia-vps playbook from maybelle. Production has served a shell dated 1 May 2026 all week.
What is in #2
One idea, built in public with Skyler calling each move: Rabbithole is a dock along the bottom of PickiPedia, and the shell hosts the wiki. That direction is the whole PR; everything else is consequence.
- The iframe is gone (
6d28eb3). The pane fetches the wiki
same-origin into a shadow root. Cost, recorded honestly: the in-pane metadata editor can no longer boot, because injected HTML does not run scripts.
- The pane wears the wiki's own stylesheet (
983457c).
This is what "the backdrop looks super broken" was — a missing
ext.pickipediaContent.styles, not a design decision.
- The three-zone bar (
2074228): player left, musician
cards in the middle, rabbithole header right. Then a long tail of placement: the parts strip along the bottom, the title to the top, the carrot, the collapse button.
- Webamp vendored (
786e2bb). - The spotlight stopped taking the page (
adc33a7). Justin
likes the blackout; it should come back as an option rather than a default.
Two commits worth reading for the lesson rather than the code:
e4393fb, where the active part block was invisible because a
background was set at higher specificity than the rule that colours it —
white on white for an hour; and f0af46a, where the collapse
button had to dispatch Webamp's own Redux action because a programmatic
click does not reach a component listening for pointerdown.
What is in #3
Grouped by theme rather than by date, because the branch is a working diary.
Tests, where there were none
3bae9c3 and after. The repo had vitest configured and zero
tests. There are now 48 across four files, and they guard the wiring
as much as the logic — a scene the player can emit with no styling, a
stylesheet no page loads, two shells where the deployed route serves the
older one. Those are the failures that kept happening.
The half of a 2025 commit that went missing
On 15 September 2025 Justin made several musicians featurable at once, with
scenes lead, pickup, cooldown, harmony, rhythm. Our player kept the half
that reorders the cards and lost the half that styles them, so three of those
scenes were live data paths with no CSS. src/styles/scenes.css
restores it, along with pickup-rise, has-rabbit and
the engineer card, all of which existed only in arthel's stylesheet.
Trails
src/trail.js, f7942f7. How you get from what is
playing to what you are reading, through the people who played on both. The
graph was already on the wiki and unused: 125 Has performer
subobjects, 20 songs, 19 people, 149 edges.
Deliberately not shortest-path. Justin is on all 20 songs and Harry Clark on 18, so shortest-path answers almost everything with one hop through the hub — true, and useless. A hop costs more through somebody who is on many songs, so the cheapest trail is the one that introduces people.
The iPhone audio bypass
src/player/ios-web-audio.js, 5483618. Webamp routes
every track through createMediaElementSource; on iOS that graph
mangled the sound while the clock kept perfect time. Isolated with four
players on a bare page: plain audio clean, the same file through a graph
warped.
Read this one knowing it is unresolved. On 21 October the same configuration played clean on the same phone. The bypass costs iPhones the equaliser, the visualiser and stereo balance, and keeping it is a judgement rather than a finding.
The dock on a small screen
The longest stretch of the branch, and mostly Skyler at a phone saying what was wrong. Grid rather than flex, every child placed by name. Then landscape, then the rotation round-trip, then the cover, then the layout chooser: sidebar at 1100px and wider, bar below, overridable and remembered.
The rename
3554cbc. webamp-chartifacts.js →
src/player/rabbithole-player.js, class
WebampChartifacts → RabbitholeWebampPlayer, through
every import, both shells, the embed page, the package entry points, the
tests and the docs.
The chartifact question
Skyler's read — "no real chartifact player existed but there was some logic somewhere" — is right about the first half. On the second half, what is there is naming, not logic, and it is worth being exact about where it lives, because the names are what make the code look like it has a feature.
Every place the word appears
| Where | What it is | Real? |
|---|---|---|
chartifactsPlayer — 47 uses |
The global holding the player
instance. It is the Rabbithole player. || Name only | |
songChartifactsData — 25 uses |
The global holding the
current song: timeline, ensemble, duration, audio file. It is Rabbithole data. || Name only | |
.chartifact-line — 6 uses |
A line of text on each musician
card || Style only | |
Chartifact "0x1234ff" owned by cryptograss.eth |
Hard-coded
string, rendered on every card of every song || Fiction | |
rabbithole: in arthel's song YAML, commented "Webamp
Chartifacts data" || Timeline, ensemble, colour scheme || Rabbithole data, mislabelled | ||
chartifacts-embed.js in arthel |
Builds an iframe pointing at
localhost:4000 || Unrelated; an embedder | |
| Two comments in PickiPediaRecordingMetadata | Refer to "the rabbithole-side
WebampChartifacts shape" || Name only |
Searched for and not found, anywhere: a chartifact contract (blox-office has SetStone, LiveSet, MagicHat, TicketStubClaimer, BlueRailroadTrainV2 and no chartifact), a token id, an owner lookup, a chain call, or any property on PickiPedia recording one.
What that means for the work
A chartifact player is a new feature, not something to be found in these branches. The card has a slot for it and says the same false thing in every slot. Three questions have to be answered before any of it can be built, and none of them are mine to answer:
- What is a chartifact, as a record? A token per musician per
recording? Per section? Something off-chain?
- Where does it live so the player can read it? The pattern everything
else here follows is: on the wiki, as a typed property, queried. That would make it visible, editable and verifiable, and it would not need a chain call in the player at all.
- What should the card say when there is no chartifact? Most recordings
will not have one. The current answer — invent one — is the worst available.
Justin's standing rule, 21 October: chartifact data stays, "chartifacts player" is a fossil, and finding that name is a sign that outdated code has crept back in.
Known weak spots, named so nobody has to find them
window.chartifactsPlayerand
window.songChartifactsData, 72 uses between them, still carry
the fossil name. Renaming needs a browser to verify, not just a test run.
- The four built-in demo songs point at
/audio/*.flacpaths that
404 everywhere. They now resolve through the Release page, but the four hard-coded entries want deleting.
- packery, js-yaml and the Google Fonts faces are still loaded from CDNs —
the same class of dependency that killed the player once and caused the Webamp vendoring.
- A sub-moment's time key inside a section is read as an absolute time rather
than an offset. Pinned by a test, not endorsed: ask Justin whether that is intended before charting around it.
- The in-pane metadata editor cannot boot since the iframe went. That is a
real loss and wants a decision rather than silence.[unverified]