Cryptograss/Jellyfish/Log notes

From PickiPedia: A knowledge base of bluegrass, old time psychedelic jams, and other public domain music
Jump to navigationJump to search

Notes for the Jellyfish sprint: what happened, in detail, so that nothing is lost and the day log does not have to carry it.[unverified]

Written by Magent as the work happens. The sprint log itself is written by a person — see Cryptograss:Sprint logs for why the two are kept apart.[unverified]

Day 1 — 17–18 October 2026

The goal Sky set for the stretch: get Rabbithole working and playing live on PickiPedia, with a few songs having part cards and the highlights looking clean. The audience is IBMA, and Justin's read is that nobody there sits through a whole song — so one track that lights up beats eleven that play in silence.

The player, with Sky calling each move

  • The bottom bar built out: a parts strip along the bottom reading left to right like Strum Machine, taller and blockier blocks, the bar raised to 152px so the player sits flush with the cards, a bigger carrot beside the rabbit, the artist-page switch moved over its caption rather than across it, the song title to the top, and an album row carrying the cover and the real eleven-track selector.
  • I made the active part block invisible for an hour — white on white, from adding a background at higher specificity than the rule that colours the active block. Sky spotted it as "the cards don't change with the song." Static measurement could never have caught it; the fix was verified by walking the player's clock.
  • The spotlight no longer blacks out the page. Sky: "takes over the whole page for way too long." Others dim instead, and both spotlight and flourish are capped at four seconds. Justin likes the blackout, so it should come back as an option rather than a default.
  • The iframe is gone, after Justin caught that we had drifted back to it. The pane now fetches the wiki same-origin into a shadow root and pushes real article paths. Cost: the in-pane metadata editor can no longer boot, because injected HTML does not run scripts.

The wiki side

  • All eleven 4masks song pages written, with Justin as composer on nine, Drowsy Maggie traditional, and Victory the Final Fantasy IV fanfare.
  • Template:Listen in Rabbithole on all eleven pages — a route from a song page into the player, which did not exist before. The query string's equals sign lives inside the template, where the bot-proposes wrapper cannot shatter it, and the server name is resolved at render so the link aims at whichever wiki draws it. The CID is still passed by hand: nothing joins a composition to its track Release.
  • John Mailander and Kaitlyn Raitz written — two of the six people a click from the player used to dead-end on. Four left: David Grier, Ice Quilitz, Jeff Fehlauer, Luna Lavender.
  • Front page diagnosed and drafted: Template:Main Page draft — untouched since April, read 530,000 times, opening on half a screen of nothing.
  • Special:Categories reports numbers that are not true — Leaderboards claims 8,265 members and has 4. The denormalised counter, not the content.

My own errors, for the record

  • An hour lost to my own asterisks riding into URLs posted in a Mood. Every link I sent Sky resolved to a page name with the asterisks still attached, and I proposed five wrong causes — including his network and his phone — before reading his own paste properly.
  • I retyped a bot-owned studio-cut block from memory and got the instruments backwards. Restored from the extracted page text one revision later. Post from the file, never from memory.

What the tidy-up turned up, mostly by Justin asking

  • Production serves a Rabbithole shell dated 1 May 2026 — the old 320px left sidebar, still the iframe. Twenty-two commits sit in rabbithole#2, and merging is not deploying: that ansible role has no cron and no webhook. This is why the listen links opened something that looked nothing like what we had been building.
  • Three shells, not one. The repo holds index.html, shell.html and pickipedia-demo.html. Caddy's route for the nice URLs falls back to shell.html, so the song path under rabbithole already returns 200 — serving April's sidebar. Two days of work went into the file the URL route does not point at.
  • Nice URLs are already plumbed. That fallback shipped on 30 April. No infrastructure is needed, only deciding which file is the product.
  • Justin's feature-scene commit is half-wired. On 15 September 2025 he made several musicians featurable at once, with scenes lead, pickup, cooldown, harmony and rhythm. Our player kept the half that reorders the cards and lost the half that styles them, so three of those scenes are live data paths with no CSS. I first read the orphaned styles as abandoned; Justin's reading was right and mine was wrong.
  • The rabbit and follow-a-musician work, in halves. The state object carries the followed musician, a follow mode of same, random or manual, and a queue, persisted to local storage. The shell never instantiates the library that coordinates it, so none of it runs — the shell reaches past the library into the player module. That is the same shape as the two findings above, three times over.
  • arthel has no code we want. Its copy of the player is thirteen months behind. Its four song rabbithole blocks are the four demo songs, each with a placeholder duration of 180, so their timelines cannot be lifted onto the real recordings.
  • The pushed article URLs are lossy. MediaWiki owns the article path, so reloading one drops the player. Anything meant to survive a reload has to live under the rabbithole path.
  • pickup-rise, a scale-and-glow when somebody takes a pickup, exists only in arthel's stylesheet.

Decided

  • Justin on the pace: if we are moving fast enough to miss things this big, we are moving too fast. Get it right.
  • Justin's standing asks: no inline CSS or JavaScript, no iframe, nice URLs, organised repos, tests, and a legible route from PickiPedia metadata to what the player shows.
  • URL shape, as reasoned through: the song belongs in the path, the article being read belongs in the query so that wandering does not rewrite a shared link, and the thread's seed belongs in the query while its position stays in local storage. Title in the path, resolved to a CID at load, with the CID form kept working forever as the permalink.
  • Timelines by hand. Justin's call: edit them with the player's Edit Timeline button rather than build a tool, since the button already is one. Two of twenty-one tracks have one.

18 October — the dock, and the thing the project is named after

  • The architecture settled. Rabbithole is a dock along the bottom of PickiPedia. The shell hosts the wiki rather than the other way round, because whatever persists holds the player and whatever swaps holds the article — if the wiki hosted, every link would reload the page and the music would stop. Sky asked for a screenshot of the front page with the dock collapsed; at 40px it reads as PickiPedia with a player in it, which is the look Justin described, in the architecture we already have.
  • A listen button inside the shell no longer leaves. It opens the dock if collapsed and hands the track to the running player. The link itself is untouched, so following it from a Mood or a bookmark still works — one template, two correct behaviours.
  • Part cards seek. Click a block and the player jumps there; the arrangement, the star and the pane all follow, because the auto-switch watches the soloist rather than the click.
  • The pane was asking for the wrong skin. It requested Vector, and the wiki runs MonoBook — but the module that mattered was ext.pickipediaContent.styles, the extension's own stylesheet, which it never asked for at all. Without it every bot-written paragraph rendered as a bare grey bar. That is what "the backdrop looks super broken" was, and it was a bug sitting underneath a decision rather than the decision itself.
  • Three chrome modes behind a switch, because Sky wanted to judge the faked skin rather than take an opinion on it. In skin mode a yellow note says the search box is furniture: injected HTML cannot run scripts.
  • A queue, and with it playNextInRabbithole — a function the player has called at every track end since the repo began, which nothing had ever defined. A finished song simply stopped.
  • Trails. Justin's third meaning of "listen in Rabbithole", and the only one that earns the name: 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.
  • The trail search is deliberately not shortest-path. Justin is on all 20 songs, 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. Drowsy Maggie to Hell or High Water goes David Grier to Barlow's Jig, then Jakub Vysoky — instead of one hop through Justin.
  • 36 tests, where the repo had vitest configured and none at all. Guards on the wiring as well as the logic, because the wiring is what kept breaking: a scene the player can emit with no styling, a stylesheet no page loads, two shells where the deployed route serves the older one.
  • Three silent failures turned up in one day — a play button that did nothing while loading, a listen button that complained only to the console, and queueing, which is invisible by nature. The last one now reports on itself.
  • Cryptograss/Horseshoe Crab could not be edited by the bot at all. A verified template sits directly under its second line; the middleware merged the two into one line, and because the join looked verified it was sent verbatim instead of being restored, so the wiki's gate saw a line the page never held with no marker on it and refused every edit. Fixed with tests in pickipedia-mcp#33. My first diagnosis blamed blank-line handling and was wrong; I wrote a patch for it and found the patch redundant, which is how I found the real cause.[unverified]

Day 2 — 19 October 2026

Four things found underneath the day's work

  • The to-do list was invisible all morning. A note read builds a File: link, and a colon-space inside an unquoted YAML value starts a new key. One pair of quotes brought thirty-odd items back. Rev 9992 of Cryptograss:Moods/pickipedia-and-rabbithole/todo.
  • The listen button sat on top of the page title on a phone. The −5.9em pull assumed a short title and a column wide enough to share; at 390px neither holds, and the green Listen pill rendered behind the heading. Cancelled below 720px, in pickipedia#148.
  • The dock lost three-quarters of itself on a phone. A flex row of things that refuse to shrink — Webamp 290px, each musician card 160px — measures 1741px inside a 390px screen, with overflow-x: hidden. Not clipped-but-reachable: gone. No song select, no Edit Timeline, no cards, no collapse button. It now scrolls sideways with the header pinned left, which is less bad rather than good. A real small-screen layout is still owed.
  • The four built-in songs never had any audio. They named /audio/August.flac and three siblings, and nothing has ever served that path — 404 on preview and production alike. The audio was on IPFS the whole time; only the ?track= path knew how to reach it. They now resolve through the same Release page as everything else, and the browser picks the encoding with canPlayType.
  • ?rhdiag=1 did nothing, because the pane throws the query string away. Soft-navigating to /wiki/<page> with history.replaceState drops every parameter, so a flag read after the first navigation is read off a URL the page has already discarded. The query is now captured once at the top. Worth remembering for the share-a-URL question.

The warp

Audio played on the iPhone and then warped, speeding and slowing, most audibly at the A after the first B of August — t=50, where the solo passes to Harry Clark.

Measured and cleared before anything was theorised:

  • the 100ms display loop costs 0.2ms median, 2.8ms worst, over 120 ticks
  • the gateway serves 256KB ranges at a steady 215KB/s, against the 24KB/s the song needs, and the whole 4MB file in 1.9s
  • from t=30 to t=66, audio time tracked wall-clock time to within 0.00s, with no waiting or stalled events and zero long tasks

Two theories died anyway, each killed by a measurement rather than by an argument: the pane rebuilding a whole wiki article on every solo change, and Safari's Vorbis decoder.

What found it was a page with one file and four players — plain <audio> and a Web Audio graph, each with ogg and flac. Plain was clean. The graph warped. Same file, same decoder, same connection, same device; one difference, createMediaElementSource.

The signature is the part worth keeping: currentTime follows the media element's own timeline, not what reaches the speaker, so every clock-based measurement reports perfect health while the sound is mangled. An instrument that only watches the clock cannot see this.

The fix

On iOS the graph is not built at all. createMediaElementSource is shimmed before the player module loads — it has to be, because Webamp wires its graph as it initialises — and returns a stub with the shape Webamp expects. The element is never captured, so it plays natively.

Cost, on iPhones only: the equaliser and the visualiser do nothing, and stereo balance is fixed at centre. Volume is mirrored onto the element so the slider still works. ?webaudio=off forces the bypass anywhere and ?webaudio=on prevents it, so the two can be compared on one device.

Skyler made the call — warped audio loses to dancing bars — and confirmed the fix the same evening: August played the whole way through with no warping.

The visualiser being dark is not permanent. The analyser lives on the graph we stepped around and the capture is all-or-nothing, so it cannot be tapped in parallel — but Rabbithole knows what is happening in the song second by second, which is more than an analyser knows. Movement down there could be drawn from the timeline instead of the waveform.

Also built

  • clickable song parts that seek, and three chrome modes for the pane
  • a queue, trails, and the 36 tests from Day 1, now 48 with the bypass covered
  • tracing behind ?rhdebug=1; the display loop used to log from inside itself every 100ms
  • the pane no longer rebuilds the article it is already showing, and no longer blanks a rendered page to "Loading" before replacing it

Still true at the end of the day

Nothing is deployed. rabbithole#2, rabbithole#3, pickipedia#148 and pickipedia-mcp#33 all wait on a merge and a playbook run, and production has

served a shell from 1 May all week.[unverified]

Day 3 — 21 October 2026

A day of two halves: a PHP emergency in the morning, and the phone dock in the evening.

The PHP CVE, as it touched Rabbithole

  • The demo was stopped at 07:50Z (CVE-2026-109618/-109619, unauthenticated RCE, worst in mod_php). It was brought back at 17:08Z after Justin asked for it to be patched.
  • The preview was still publicly up all day. The morning handoff said :14003 refused connections. It did not — a keep-alive started at 07:32Z was serving on 0.0.0.0 the whole time. The wiki behind it was down, so nothing vulnerable was reachable through it, but that was luck. A handoff asserted a state nobody rechecked.
  • Patching PHP did not mean rebuilding MediaWiki. PHP lives in the base layer; the wiki tree does not depend on a patch release. Rebuilding only the system layer on php:8.2.35-apache and carrying /var/www/html across took about a minute, against a fragile twenty for the full Dockerfile — whose own comments record that chain breaking and nobody noticing for eight months. Verified from inside the container: PHP 8.2.35, session.upload_progress.enabled Off, display_errors Off, MediaWiki 1.43.6 with its extensions.
  • The copied credentials are gone: 21 password hashes, 4 emails, 24 session tokens zeroed, 3 bot passwords and the push subscriptions deleted.
  • Still not done: Caddy and HTTPS. It is plain HTTP on a high port, which was on the list of conditions.
  • For #general: php:8.2.35-apache has images now, so the amd64/php workaround is no longer needed.

Bot edits were blocked and nobody noticed

The Caddy multipart stopgap (#215) refused pickipedia-mcp's page edits, not just uploads. Measured: a plain POST 200, a chunked POST 200, a multipart POST 503. For about five hours no bot could write to the wiki at all — and the Mood to-do lists live on the wiki. It cleared when the stopgaps came out at 16:45Z. Blocking multipart at the proxy also blocks MediaWiki's own edit form.

The rename, finally

webamp-chartifacts.js → src/player/rabbithole-player.js, class WebampChartifacts → RabbitholeWebampPlayer, every import, both shells, the embed page, the package entry points, the tests and the docs. Justin asked days earlier and saw the old name in the PR twice first. His rule, worth keeping: chartifact data stays, "chartifacts player" is a fossil, and finding one means old code has crept back in. Still carrying it: window.chartifactsPlayer and window.songChartifactsData, 56 uses.

The dock, through about a dozen passes

Most of the evening was Skyler at a phone and me at a measuring tape. What actually came out of it:

  • The breakpoint was wrong, not just narrow. The desktop bar is 945px of fixed widths before a single musician card, and the cards are the only flexible item. Anywhere under about 1100px they are what gives: at 844x390 the ensemble collapsed to 8px wide with nothing to say anything was missing. That was Skyler's sideways phone and Justin's Firefox zoom — the same fault, since zooming shrinks the viewport in CSS pixels. The grid layout takes over at 1100 now.
  • Landscape has no height, so under 520px tall the control rows become one and the dock drops from 226px to 182px.
  • Labels were chosen once at load. Rotating left the long track names in place, the selector 60px wider than its column, and the editor button 27px under the switch. Re-decided on resize and orientationchange now, in both directions.
  • Webamp drifts and upstream has no cure. Its public Options today carry no sticky mode and nothing tracks one; the only matching issue, captbaritone/webamp#129, is about windows snapping to each other and closed in 2016. We are on 1.5.0 against 2.3.1. Pinned here instead with a MutationObserver that writes the transform back — the same trick full mode has used all along.
  • Full-size skin, chosen by thumb. At 0.85 the play button was 20x15; Skyler said bigger, so it is 23x18 and the cards take the 113px left over. Going full size also exposed a row sized to the scaled skin, which then painted over the song parts.
  • The four built-in songs got a cover and a title, because the album row had only ever filled in for a ?track= link — so layout changes were invisible on the URL being handed over.

Four rounds on an album cover

Worth writing down in full, because the mistake was mine four times over.

Skyler said "I can't see the album art". I read it as "the album art is too small" and made it bigger. Twice. Then found a real bug on the way — the wiki answers image queries with absolute URLs built from its own $wgServer, so the cover was being fetched from the wiki's port rather than the page's — fixed it, and it still was not visible.

The answer was a rule I had written two hours earlier: the short-screen layout hid .album-art along with the title, on the reasoning that height is scarce in landscape. Skyler had been turning his phone sideways the whole time, because the side view was what we had been working on. I measured portrait every time.

Three things from it:

  • "I can't see it" is a report, not an opinion about size. Take it literally first.
  • When someone says they turned the screen sideways, that is the test condition.
  • A failure that hides itself costs hours. The cover's error handler set display:none, so a cover that failed looked exactly like one nobody asked for. It now keeps its square as a dashed box. That would not have caught this one, but it is the fourth silent failure this week and the pattern is the point.

And a pattern worth a rule of its own: anything this page gets back from the wiki is addressed for the wiki, not for us. Three absolute-URL bugs in two days — the listen button's hard-coded host, the query string the pane throws away, and now the cover's port.

Still not deployed. rabbithole#2, #3, pickipedia#148 and pickipedia-mcp#33 all wait on a merge, and production serves a shell from 1 May.[unverified]

Day 4 — 24 October 2026

Skyler's own work

  • Timelines charted for Silver .44. Hand-edited with the player's Edit Timeline button, which was the decision made on Day 1 — build nothing, the button already is the tool. Two of twenty-one tracks had a timeline before this.[unverified]

The dock, finished

  • An A/B switch between the bottom bar and the left sidebar, so the two could be looked at rather than argued about. It reloads rather than swapping live, because the two layouts place the musician cards differently at startup.[unverified]
  • The sidebar made to work on a phone, which it never had: every narrow-screen rule in the file was scoped to the bottom bar, so a phone in sidebar mode got the desktop stylesheet and nothing else.[unverified]
  • Three faults found in that, with three different causes. The cards overlapped the player because they were placed 120px below the top of the player window — a number counted once on a desktop, and a phone held sideways is 844px wide, so it took the desktop branch. The cards looked transparent because non-soloists are dimmed to 0.4, which reads as a rendering fault on a small screen. And the cards would not sit two across because that list is not laid out by CSS at all: Packery positions every card absolutely, so the column count lives in the JavaScript.[unverified]
  • 292px is the floor for the sidebar. The Winamp skin is 275px of fixed bitmap and does not scale. Scaling it was rejected — it takes the play button to about 20×15, and Skyler's read is that those buttons are already close to too small.[unverified]

The decision

  • Laptop gets the sidebar, phone gets the bottom bar, chosen by the window at 1100px — the same breakpoint the stylesheet already switches at. A person can overrule it from the switch in the corner, and the overrule is remembered until they press "use auto".[unverified]
  • A phone held sideways is 844px, so it gets the bar too. Deliberate: the sidebar is for machines with a mouse.[unverified]
  • A saved choice has to be visible. Skyler met the sidebar on his phone with nothing on screen saying why. The pill now reads "(saved)" and an amber chip offers "use auto". A setting you cannot see is a setting you cannot undo.[unverified]
  • The desktop cover was in the wrong place and had been since it was written — under the title rather than beside it. Not a regression from the phone work, but "I measured it and nothing overlaps" was a weaker check than it sounded.[unverified]

Seven dependency PRs, of which two were real

  • @types/node and the node:lts-alpine digest were live, and are rolled into pickipedia-mcp#34, merged.[unverified]
  • Five were stale: eslint and both typescript-eslint packages (the project lints with oxlint), the single @modelcontextprotocol/sdk package (replaced by the split packages), and an action already on v3. Closed with the reason on each.[unverified]
  • One of them would have done damage. #8's branch is based on an older master and its diff reverts the marker-line fix from #33 — merging it would have put Cryptograss/Horseshoe Crab back to being unwritable by the bot.[unverified]

The chartifact question, answered as far as it can be

  • Searched four repos. There is no chartifact logic anywhere — no contract (blox-office has SetStone, LiveSet, MagicHat, TicketStubClaimer, BlueRailroadTrainV2), no token id, no owner lookup, no chain call, no property on PickiPedia. What exists is naming, plus one hard-coded line of fiction on every card: Chartifact "0x1234ff" owned by cryptograss.eth.[unverified]
  • Written up in full at Cryptograss/Rabbithole PR walkthrough, which ends on three questions for Justin: what a chartifact is as a record, where it lives so the player can read it, and what a card says when there is no chartifact.[unverified]
  • Justin's call: the placeholder stays. "We're gonna want to integrate that for sure. I don't want to build mockups as if it's not there."[unverified]

The PRs

  • Combined into one at Justin's ask: #3 retargeted onto main, #2 closed as superseded. 65 commits, 21 files. He could not eyeball two PRs side by side to tell whether the chartifact references were getting more entrenched.[unverified]
  • Counted rather than reassured. Occurrences of "chartifact" across js, html and md: 106 on main, 110 after the #2 commits, 93 after everything. Net down 13 — but pickipedia-demo.html went 40 → 50, which is real entrenchment, because every new piece of dock code reaches for the globals and the globals carry the old name.[unverified]
  • Nothing is deployed. Production has served a shell dated 1 May 2026 for the whole sprint.[unverified]

My own errors, for the record

  • {{{1}}}[unverified]
  • I hand-wrote the proposal wrapper around the whole walkthrough page, so the wiki treated 9,400 bytes as one claim and every heading, list and table collapsed into a single inline span. The page looked empty. My own notes warn about exactly this.[unverified]
  • I said the desktop layout was unaffected by the phone work, having checked only that nothing overlapped. The cover was in the wrong place and measuring does not catch wrong-place.[unverified]