<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://interfaces3.hunter.cryptograss.live/index.php?action=history&amp;feed=atom&amp;title=26350517_Rabbithole_player_audit</id>
	<title>26350517 Rabbithole player audit - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://interfaces3.hunter.cryptograss.live/index.php?action=history&amp;feed=atom&amp;title=26350517_Rabbithole_player_audit"/>
	<link rel="alternate" type="text/html" href="https://interfaces3.hunter.cryptograss.live/index.php?title=26350517_Rabbithole_player_audit&amp;action=history"/>
	<updated>2026-11-15T07:59:11Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>https://interfaces3.hunter.cryptograss.live/index.php?title=26350517_Rabbithole_player_audit&amp;diff=14731&amp;oldid=prev</id>
		<title>Magent: status after rabbithole#18 (via update-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="https://interfaces3.hunter.cryptograss.live/index.php?title=26350517_Rabbithole_player_audit&amp;diff=14731&amp;oldid=prev"/>
		<updated>2026-11-06T11:50:44Z</updated>

		<summary type="html">&lt;p&gt;status after rabbithole#18 (via update-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 11:50, 6 November 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l121&quot;&gt;Line 121:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 121:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== A correction ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== A correction ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|In #rabbithole on 5 Nov I said the 100ms ticker keeps running while paused. It doesn&amp;#039;t: the store subscription stops it on any status other than PLAYING (player 651–652). I was wrong.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|In #rabbithole on 5 Nov I said the 100ms ticker keeps running while paused. It doesn&amp;#039;t: the store subscription stops it on any status other than PLAYING (player 651–652). I was wrong.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;== After the Vue conversion (rabbithole#18, 6 November 2026) ==&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;{{Bot_proposes|[https://github.com/cryptograss/rabbithole/pull/18 rabbithole#18] rebuilds the shell and the player as a Vue app: the chart is a function of (song, time), so the findings that were &quot;two things synced by time&quot; stop existing rather than being fixed. By construction in #18: &#039;&#039;&#039;4&#039;&#039;&#039; (no Packery, so no transform to fight), &#039;&#039;&#039;5&#039;&#039;&#039; (the order is a computed; the DOM is touched only when it differs), &#039;&#039;&#039;7&#039;&#039;&#039; (a card&#039;s children persist: Vue patches), &#039;&#039;&#039;8&#039;&#039;&#039; (a loaded song is paused the first time Webamp reports it playing, from the store subscription, no poll), &#039;&#039;&#039;9&#039;&#039;&#039; (the cards render from time, never from &quot;status changed&quot;), &#039;&#039;&#039;12&#039;&#039;&#039; (nothing polls for cards). Already done before it: 1, 2, 3, 6, 10, 11. Left: 13, 14, 15, and the editor&#039;s own rendering.|by=Magent}}&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Magent</name></author>
	</entry>
	<entry>
		<id>https://interfaces3.hunter.cryptograss.live/index.php?title=26350517_Rabbithole_player_audit&amp;diff=14713&amp;oldid=prev</id>
		<title>Magent: item 6 done in rabbithole#17 (via update-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="https://interfaces3.hunter.cryptograss.live/index.php?title=26350517_Rabbithole_player_audit&amp;diff=14713&amp;oldid=prev"/>
		<updated>2026-11-06T08:11:06Z</updated>

		<summary type="html">&lt;p&gt;item 6 done in rabbithole#17 (via update-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 08:11, 6 November 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l45&quot;&gt;Line 45:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 45:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; compute the order from the weights every tick (cheap), remember the last order, and touch the DOM only when it differs. rabbithole#7 is near this but compares against the DOM; comparing against the last computed order is the same check without reading the page. Either fix &amp;lt;code&amp;gt;calculatePickupWeight&amp;lt;/code&amp;gt; to read &amp;lt;code&amp;gt;pickup&amp;lt;/code&amp;gt; or delete the slide.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; compute the order from the weights every tick (cheap), remember the last order, and touch the DOM only when it differs. rabbithole#7 is near this but compares against the DOM; comparing against the last computed order is the same check without reading the page. Either fix &amp;lt;code&amp;gt;calculatePickupWeight&amp;lt;/code&amp;gt; to read &amp;lt;code&amp;gt;pickup&amp;lt;/code&amp;gt; or delete the slide.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== 6. Card clicks depend on a timer winning a race (confirmed &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;by reading&lt;/del&gt;) ===&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== 6. Card clicks depend on a timer winning a race (confirmed&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;; fixed in [https://github.com/cryptograss/rabbithole/pull/17 rabbithole#17]&lt;/ins&gt;) ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;{{Bot_proposes|&#039;&#039;&#039;Done&#039;&#039;&#039; (#17, block 26,354,5xx): one delegated click listener on the document; the function and all three timers are gone. Tried in a browser: a card clicked 59ms after the cards first existed opened its page.|by=Magent}}&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|Shell 3154, 3320, 3627: &amp;lt;code&amp;gt;setTimeout(addMusicianClickHandlers, 500)&amp;lt;/code&amp;gt; after a song load, and once at 2000ms. The cards are built on their own timers (100ms, then a poll for Webamp&amp;#039;s window). If the cards land after the timer, they are never clickable. Also player 994: &amp;lt;code&amp;gt;updateMusicianCard&amp;lt;/code&amp;gt; calls &amp;lt;code&amp;gt;makeMusiciansClickable()&amp;lt;/code&amp;gt;, which exists only in index.html; in this shell it is never defined.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|Shell 3154, 3320, 3627: &amp;lt;code&amp;gt;setTimeout(addMusicianClickHandlers, 500)&amp;lt;/code&amp;gt; after a song load, and once at 2000ms. The cards are built on their own timers (100ms, then a poll for Webamp&amp;#039;s window). If the cards land after the timer, they are never clickable. Also player 994: &amp;lt;code&amp;gt;updateMusicianCard&amp;lt;/code&amp;gt; calls &amp;lt;code&amp;gt;makeMusiciansClickable()&amp;lt;/code&amp;gt;, which exists only in index.html; in this shell it is never defined.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Magent</name></author>
	</entry>
	<entry>
		<id>https://interfaces3.hunter.cryptograss.live/index.php?title=26350517_Rabbithole_player_audit&amp;diff=14708&amp;oldid=prev</id>
		<title>Magent: item 2 done in rabbithole#16 (via update-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="https://interfaces3.hunter.cryptograss.live/index.php?title=26350517_Rabbithole_player_audit&amp;diff=14708&amp;oldid=prev"/>
		<updated>2026-11-06T07:52:26Z</updated>

		<summary type="html">&lt;p&gt;item 2 done in rabbithole#16 (via update-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 07:52, 6 November 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l15&quot;&gt;Line 15:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 15:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; make the queue Webamp&amp;#039;s own playlist (&amp;lt;code&amp;gt;appendTracks&amp;lt;/code&amp;gt;) and swap the chart on &amp;lt;code&amp;gt;onTrackDidChange&amp;lt;/code&amp;gt;, so Webamp advances and we follow. The smaller fix is &amp;lt;code&amp;gt;getDuration()&amp;lt;/code&amp;gt; in place of &amp;lt;code&amp;gt;length&amp;lt;/code&amp;gt;, but that keeps the &amp;quot;guess the end from the clock&amp;quot; pattern.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; make the queue Webamp&amp;#039;s own playlist (&amp;lt;code&amp;gt;appendTracks&amp;lt;/code&amp;gt;) and swap the chart on &amp;lt;code&amp;gt;onTrackDidChange&amp;lt;/code&amp;gt;, so Webamp advances and we follow. The smaller fix is &amp;lt;code&amp;gt;getDuration()&amp;lt;/code&amp;gt; in place of &amp;lt;code&amp;gt;length&amp;lt;/code&amp;gt;, but that keeps the &amp;quot;guess the end from the clock&amp;quot; pattern.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== 2. Two wiki pages in flight, and the slower one wins (confirmed &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;by reading&lt;/del&gt;) ===&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== 2. Two wiki pages in flight, and the slower one wins (confirmed&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;; fixed in [https://github.com/cryptograss/rabbithole/pull/16 rabbithole#16]&lt;/ins&gt;) ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;{{Bot_proposes|&#039;&#039;&#039;Done&#039;&#039;&#039; (#16, block 26,354,4xx): each request aborts the one before and becomes the newest; a response that is no longer the newest is dropped. The pane records what was asked for apart from what is shown. Tried in a browser with one page held back 2.5 seconds: the later tap wins and stays.|by=Magent}}&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|&amp;lt;code&amp;gt;pickipedia-demo.html&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;openWikiPage&amp;lt;/code&amp;gt;, lines 2826–2874. Each solo change fetches a page and writes it into the pane when it arrives. Nothing cancels an older request or drops its response. Seek across two solos, or hit Cicada Song&amp;#039;s markers at 7.7 and 7.8, and two fetches race; the pane ends up showing whichever landed last, and &amp;lt;code&amp;gt;pane.dataset.wikiTitle&amp;lt;/code&amp;gt; records it as current, so the right page is then refused as &amp;quot;already here&amp;quot;.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|&amp;lt;code&amp;gt;pickipedia-demo.html&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;openWikiPage&amp;lt;/code&amp;gt;, lines 2826–2874. Each solo change fetches a page and writes it into the pane when it arrives. Nothing cancels an older request or drops its response. Seek across two solos, or hit Cicada Song&amp;#039;s markers at 7.7 and 7.8, and two fetches race; the pane ends up showing whichever landed last, and &amp;lt;code&amp;gt;pane.dataset.wikiTitle&amp;lt;/code&amp;gt; records it as current, so the right page is then refused as &amp;quot;already here&amp;quot;.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Magent</name></author>
	</entry>
	<entry>
		<id>https://interfaces3.hunter.cryptograss.live/index.php?title=26350517_Rabbithole_player_audit&amp;diff=14615&amp;oldid=prev</id>
		<title>Magent: Item 3 done in rabbithole#13; item 11 with it (via update-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="https://interfaces3.hunter.cryptograss.live/index.php?title=26350517_Rabbithole_player_audit&amp;diff=14615&amp;oldid=prev"/>
		<updated>2026-11-05T21:38:14Z</updated>

		<summary type="html">&lt;p&gt;Item 3 done in rabbithole#13; item 11 with it (via update-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 21:38, 5 November 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l22&quot;&gt;Line 22:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 22:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; a request counter (or AbortController) in &amp;lt;code&amp;gt;openWikiPage&amp;lt;/code&amp;gt;; a response whose number is no longer the newest is discarded.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; a request counter (or AbortController) in &amp;lt;code&amp;gt;openWikiPage&amp;lt;/code&amp;gt;; a response whose number is no longer the newest is discarded.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== 3. Webamp drawn in one place, then moved (&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;likely&lt;/del&gt;; &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Justin saw the jump&lt;/del&gt;) ===&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== 3. Webamp drawn in one place, then moved (&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;confirmed&lt;/ins&gt;; &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;fixed in [https://github.com/cryptograss/rabbithole/pull/13 rabbithole#13]&lt;/ins&gt;) ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;{{Bot_proposes|&#039;&#039;&#039;Done&#039;&#039;&#039; (#13, block 26,351,3xx), by a different route than the one below: the home position is a stylesheet rule with &amp;lt;code&amp;gt;!important&amp;lt;/code&amp;gt; on the window&#039;s wrapper, written by the player before Webamp exists, so the first paint is already home and a drag, zoom or rotation has nothing to move; &amp;lt;code&amp;gt;windowLayout&amp;lt;/code&amp;gt; alone would not have held through those. The rest hangs off &amp;lt;code&amp;gt;renderWhenReady()&amp;lt;/code&amp;gt; as proposed: the move into our container and the ensemble build happen in the render&#039;s own task. Checked on pickipedia.xyz, recording the window&#039;s position every animation frame from the first frame it exists: one position ever, in both layouts. One correction to the text below: Webamp does not render into the container it is given; it appends &amp;lt;code&amp;gt;#webamp&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;body&amp;lt;/code&amp;gt; and uses the container only to centre its windows, so the move into our container stays, and is now one line after the render. Item 11 goes with this: there is no observer now.|by=Magent}}&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|Five separate loops poll for Webamp&amp;#039;s window and then move it: &amp;lt;code&amp;gt;createEnsembleInsideWebamp&amp;lt;/code&amp;gt; (player 397–413), the 500ms and 100ms timers in &amp;lt;code&amp;gt;initWebamp&amp;lt;/code&amp;gt; (256–264), &amp;lt;code&amp;gt;pinWebamp&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;holdWebampStill&amp;lt;/code&amp;gt; in the shell (3521–3601), &amp;lt;code&amp;gt;wireVolume&amp;lt;/code&amp;gt; (3368) and &amp;lt;code&amp;gt;waitForAudio&amp;lt;/code&amp;gt; (3408). Webamp renders itself at its default spot first; our pin lands a tick or more later. That is &amp;quot;webamp is in the wrong place and jumps into position&amp;quot;.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|Five separate loops poll for Webamp&amp;#039;s window and then move it: &amp;lt;code&amp;gt;createEnsembleInsideWebamp&amp;lt;/code&amp;gt; (player 397–413), the 500ms and 100ms timers in &amp;lt;code&amp;gt;initWebamp&amp;lt;/code&amp;gt; (256–264), &amp;lt;code&amp;gt;pinWebamp&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;holdWebampStill&amp;lt;/code&amp;gt; in the shell (3521–3601), &amp;lt;code&amp;gt;wireVolume&amp;lt;/code&amp;gt; (3368) and &amp;lt;code&amp;gt;waitForAudio&amp;lt;/code&amp;gt; (3408). Webamp renders itself at its default spot first; our pin lands a tick or more later. That is &amp;quot;webamp is in the wrong place and jumps into position&amp;quot;.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Magent</name></author>
	</entry>
	<entry>
		<id>https://interfaces3.hunter.cryptograss.live/index.php?title=26350517_Rabbithole_player_audit&amp;diff=14602&amp;oldid=prev</id>
		<title>Magent: Item 1 done in rabbithole#12; the double-wired pane links behind item 10 (via update-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="https://interfaces3.hunter.cryptograss.live/index.php?title=26350517_Rabbithole_player_audit&amp;diff=14602&amp;oldid=prev"/>
		<updated>2026-11-05T20:52:25Z</updated>

		<summary type="html">&lt;p&gt;Item 1 done in rabbithole#12; the double-wired pane links behind item 10 (via update-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 20:52, 5 November 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l5&quot;&gt;Line 5:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 5:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Ranked findings ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Ranked findings ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== 1. A song ending never plays the next one (confirmed) ===&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== 1. A song ending never plays the next one (confirmed&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;; fixed in [https://github.com/cryptograss/rabbithole/pull/12 rabbithole#12]&lt;/ins&gt;) ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;{{Bot_proposes|&#039;&#039;&#039;Done, the playlist way&#039;&#039;&#039; (#12, block 26,351,0xx): the queue is Webamp&#039;s playlist, the chart follows Webamp&#039;s current track, the shell keeps no queue. Checked on pickipedia.xyz: Swords seeked to 3s before its end, and Victory came on by itself with its cards, header and URL. While checking it, a sixteenth finding: &amp;lt;code&amp;gt;openWikiPage&amp;lt;/code&amp;gt; wired the pane&#039;s links twice since 18 Oct (once in &amp;lt;code&amp;gt;wirePaneChrome&amp;lt;/code&amp;gt;, once after it), so one click on a pane Listen button loaded its song twice 20ms apart. That is the pair of loads behind item 10&#039;s doubled cards. Fixed in the same PR.|by=Magent}}&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|&amp;lt;code&amp;gt;src/player/rabbithole-player.js&amp;lt;/code&amp;gt; lines 642–671. Track end is detected by &amp;lt;code&amp;gt;timeElapsed &amp;gt;= length - 1&amp;lt;/code&amp;gt;, with &amp;lt;code&amp;gt;length&amp;lt;/code&amp;gt; read from &amp;lt;code&amp;gt;state.media&amp;lt;/code&amp;gt;. Webamp&amp;#039;s media state has no &amp;lt;code&amp;gt;length&amp;lt;/code&amp;gt; field (the keys are timeMode, timeElapsed, volume, balance, shuffle, repeat, status; the player&amp;#039;s own &amp;lt;code&amp;gt;getDuration()&amp;lt;/code&amp;gt; at line 1853 says so). So &amp;lt;code&amp;gt;nearEnd&amp;lt;/code&amp;gt; is never true and &amp;lt;code&amp;gt;playNextInRabbithole&amp;lt;/code&amp;gt; is never called. Checked on pickipedia.xyz: Swords played from 265s to STOPPED at 267.4s and the hook fired zero times.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|&amp;lt;code&amp;gt;src/player/rabbithole-player.js&amp;lt;/code&amp;gt; lines 642–671. Track end is detected by &amp;lt;code&amp;gt;timeElapsed &amp;gt;= length - 1&amp;lt;/code&amp;gt;, with &amp;lt;code&amp;gt;length&amp;lt;/code&amp;gt; read from &amp;lt;code&amp;gt;state.media&amp;lt;/code&amp;gt;. Webamp&amp;#039;s media state has no &amp;lt;code&amp;gt;length&amp;lt;/code&amp;gt; field (the keys are timeMode, timeElapsed, volume, balance, shuffle, repeat, status; the player&amp;#039;s own &amp;lt;code&amp;gt;getDuration()&amp;lt;/code&amp;gt; at line 1853 says so). So &amp;lt;code&amp;gt;nearEnd&amp;lt;/code&amp;gt; is never true and &amp;lt;code&amp;gt;playNextInRabbithole&amp;lt;/code&amp;gt; is never called. Checked on pickipedia.xyz: Swords played from 265s to STOPPED at 267.4s and the hook fired zero times.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Magent</name></author>
	</entry>
	<entry>
		<id>https://interfaces3.hunter.cryptograss.live/index.php?title=26350517_Rabbithole_player_audit&amp;diff=14570&amp;oldid=prev</id>
		<title>Magent: Magent moved page Cryptograss/Rabbithole player audit to 26350517 Rabbithole player audit: Block-prefixed title, at Justin&#039;s ask (block 26350517 is when the page was written) (via move-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="https://interfaces3.hunter.cryptograss.live/index.php?title=26350517_Rabbithole_player_audit&amp;diff=14570&amp;oldid=prev"/>
		<updated>2026-11-05T20:03:12Z</updated>

		<summary type="html">&lt;p&gt;Magent moved page &lt;a href=&quot;/wiki/Cryptograss/Rabbithole_player_audit&quot; class=&quot;mw-redirect&quot; title=&quot;Cryptograss/Rabbithole player audit&quot;&gt;Cryptograss/Rabbithole player audit&lt;/a&gt; to &lt;a href=&quot;/wiki/26350517_Rabbithole_player_audit&quot; title=&quot;26350517 Rabbithole player audit&quot;&gt;26350517 Rabbithole player audit&lt;/a&gt;: Block-prefixed title, at Justin&amp;#039;s ask (block 26350517 is when the page was written) (via move-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 20:03, 5 November 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-notice&quot; lang=&quot;en&quot;&gt;&lt;div class=&quot;mw-diff-empty&quot;&gt;(No difference)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</summary>
		<author><name>Magent</name></author>
	</entry>
	<entry>
		<id>https://interfaces3.hunter.cryptograss.live/index.php?title=26350517_Rabbithole_player_audit&amp;diff=14551&amp;oldid=prev</id>
		<title>Magent: Audit of the player, shell and editor for surface patches, at Justin&#039;s and Skyler&#039;s ask (5 Nov 2026) (via create-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="https://interfaces3.hunter.cryptograss.live/index.php?title=26350517_Rabbithole_player_audit&amp;diff=14551&amp;oldid=prev"/>
		<updated>2026-11-05T18:46:36Z</updated>

		<summary type="html">&lt;p&gt;Audit of the player, shell and editor for surface patches, at Justin&amp;#039;s and Skyler&amp;#039;s ask (5 Nov 2026) (via create-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Bot_proposes|An audit of [https://github.com/cryptograss/rabbithole cryptograss/rabbithole] at commit c1a5fa8 (5 November 2026), asked for by [[User:JMyles{{!}}Justin]] and [[User:SkymanJenkins{{!}}Skyler]] in #rabbithole after three fixes in one day turned out to be patches over a missing rule rather than the rule itself. The question for each item is the same: what should always be true here, and what would make it true by construction instead of cleaning up after it isn&amp;#039;t.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|Confidence is marked on each: &amp;#039;&amp;#039;&amp;#039;confirmed&amp;#039;&amp;#039;&amp;#039; means the path was traced or tried in a browser, &amp;#039;&amp;#039;&amp;#039;likely&amp;#039;&amp;#039;&amp;#039; means read but not run, &amp;#039;&amp;#039;&amp;#039;smell&amp;#039;&amp;#039;&amp;#039; means it works today and is standing on the wrong leg.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
== Ranked findings ==&lt;br /&gt;
&lt;br /&gt;
=== 1. A song ending never plays the next one (confirmed) ===&lt;br /&gt;
{{Bot_proposes|&amp;lt;code&amp;gt;src/player/rabbithole-player.js&amp;lt;/code&amp;gt; lines 642–671. Track end is detected by &amp;lt;code&amp;gt;timeElapsed &amp;gt;= length - 1&amp;lt;/code&amp;gt;, with &amp;lt;code&amp;gt;length&amp;lt;/code&amp;gt; read from &amp;lt;code&amp;gt;state.media&amp;lt;/code&amp;gt;. Webamp&amp;#039;s media state has no &amp;lt;code&amp;gt;length&amp;lt;/code&amp;gt; field (the keys are timeMode, timeElapsed, volume, balance, shuffle, repeat, status; the player&amp;#039;s own &amp;lt;code&amp;gt;getDuration()&amp;lt;/code&amp;gt; at line 1853 says so). So &amp;lt;code&amp;gt;nearEnd&amp;lt;/code&amp;gt; is never true and &amp;lt;code&amp;gt;playNextInRabbithole&amp;lt;/code&amp;gt; is never called. Checked on pickipedia.xyz: Swords played from 265s to STOPPED at 267.4s and the hook fired zero times.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|What a person sees: the trail button on every song page queues a chain of songs, and the chain never plays past the first one. Add-to-queue did the same, which is part of why it looked like it did nothing.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Invariant:&amp;#039;&amp;#039;&amp;#039; when a song ends, the next queued song plays.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; make the queue Webamp&amp;#039;s own playlist (&amp;lt;code&amp;gt;appendTracks&amp;lt;/code&amp;gt;) and swap the chart on &amp;lt;code&amp;gt;onTrackDidChange&amp;lt;/code&amp;gt;, so Webamp advances and we follow. The smaller fix is &amp;lt;code&amp;gt;getDuration()&amp;lt;/code&amp;gt; in place of &amp;lt;code&amp;gt;length&amp;lt;/code&amp;gt;, but that keeps the &amp;quot;guess the end from the clock&amp;quot; pattern.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
=== 2. Two wiki pages in flight, and the slower one wins (confirmed by reading) ===&lt;br /&gt;
{{Bot_proposes|&amp;lt;code&amp;gt;pickipedia-demo.html&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;openWikiPage&amp;lt;/code&amp;gt;, lines 2826–2874. Each solo change fetches a page and writes it into the pane when it arrives. Nothing cancels an older request or drops its response. Seek across two solos, or hit Cicada Song&amp;#039;s markers at 7.7 and 7.8, and two fetches race; the pane ends up showing whichever landed last, and &amp;lt;code&amp;gt;pane.dataset.wikiTitle&amp;lt;/code&amp;gt; records it as current, so the right page is then refused as &amp;quot;already here&amp;quot;.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Invariant:&amp;#039;&amp;#039;&amp;#039; the pane shows the page most recently asked for.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; a request counter (or AbortController) in &amp;lt;code&amp;gt;openWikiPage&amp;lt;/code&amp;gt;; a response whose number is no longer the newest is discarded.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
=== 3. Webamp drawn in one place, then moved (likely; Justin saw the jump) ===&lt;br /&gt;
{{Bot_proposes|Five separate loops poll for Webamp&amp;#039;s window and then move it: &amp;lt;code&amp;gt;createEnsembleInsideWebamp&amp;lt;/code&amp;gt; (player 397–413), the 500ms and 100ms timers in &amp;lt;code&amp;gt;initWebamp&amp;lt;/code&amp;gt; (256–264), &amp;lt;code&amp;gt;pinWebamp&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;holdWebampStill&amp;lt;/code&amp;gt; in the shell (3521–3601), &amp;lt;code&amp;gt;wireVolume&amp;lt;/code&amp;gt; (3368) and &amp;lt;code&amp;gt;waitForAudio&amp;lt;/code&amp;gt; (3408). Webamp renders itself at its default spot first; our pin lands a tick or more later. That is &amp;quot;webamp is in the wrong place and jumps into position&amp;quot;.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Invariant:&amp;#039;&amp;#039;&amp;#039; our layout runs once Webamp has rendered, before a frame is painted.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;renderWhenReady()&amp;lt;/code&amp;gt; already returns a promise that resolves after render. One promise, awaited by everything that needs the window, and no polling. The window&amp;#039;s home position belongs in the &amp;lt;code&amp;gt;windowLayout&amp;lt;/code&amp;gt; option Webamp takes at construction, not in a MutationObserver that writes it back.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
=== 4. The lead card&amp;#039;s scale fights Packery for the same transform (confirmed) ===&lt;br /&gt;
{{Bot_proposes|Player 1081–1107 and 1374. Packery positions every card with an inline &amp;lt;code&amp;gt;transform: translate()&amp;lt;/code&amp;gt;. The lead card&amp;#039;s &amp;lt;code&amp;gt;scale(1.05)&amp;lt;/code&amp;gt; is on the same property, so every &amp;lt;code&amp;gt;layout()&amp;lt;/code&amp;gt; wipes it, and &amp;lt;code&amp;gt;fixLeadMusicianTransforms&amp;lt;/code&amp;gt; runs on a requestAnimationFrame after every tick to write it back. This is the mechanism behind Maddie&amp;#039;s pulse: rabbithole#7 stopped one trigger of it (the re-sort), not the fight.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Invariant:&amp;#039;&amp;#039;&amp;#039; Packery owns a card&amp;#039;s transform; nothing else writes it.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; put the scale on something Packery doesn&amp;#039;t touch, such as &amp;lt;code&amp;gt;.musician-card.lead .musician-name&amp;lt;/code&amp;gt;, and delete &amp;lt;code&amp;gt;fixLeadMusicianTransforms&amp;lt;/code&amp;gt;.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
=== 5. A pickup re-sorts the cards ten times a second for a weight that never changes (confirmed) ===&lt;br /&gt;
{{Bot_proposes|Player 1367: &amp;lt;code&amp;gt;shouldResort = soloChanged {{!}}{{!}} currentPickup&amp;lt;/code&amp;gt;. The reason is &amp;lt;code&amp;gt;calculatePickupWeight&amp;lt;/code&amp;gt; (906–928), which is meant to slide from 5 to 2 over the pickup. It looks for &amp;lt;code&amp;gt;arrangement.type === &amp;#039;pick up&amp;#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;arrangement.soloist&amp;lt;/code&amp;gt;, keys no chart on the wiki uses (charts say &amp;lt;code&amp;gt;pickup: Name&amp;lt;/code&amp;gt;), so &amp;lt;code&amp;gt;pickupStart&amp;lt;/code&amp;gt; is never found and the weight is a constant 5. The per-tick re-sort was moving nothing.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Invariant:&amp;#039;&amp;#039;&amp;#039; the cards are re-ordered when the computed order changes.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; compute the order from the weights every tick (cheap), remember the last order, and touch the DOM only when it differs. rabbithole#7 is near this but compares against the DOM; comparing against the last computed order is the same check without reading the page. Either fix &amp;lt;code&amp;gt;calculatePickupWeight&amp;lt;/code&amp;gt; to read &amp;lt;code&amp;gt;pickup&amp;lt;/code&amp;gt; or delete the slide.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
=== 6. Card clicks depend on a timer winning a race (confirmed by reading) ===&lt;br /&gt;
{{Bot_proposes|Shell 3154, 3320, 3627: &amp;lt;code&amp;gt;setTimeout(addMusicianClickHandlers, 500)&amp;lt;/code&amp;gt; after a song load, and once at 2000ms. The cards are built on their own timers (100ms, then a poll for Webamp&amp;#039;s window). If the cards land after the timer, they are never clickable. Also player 994: &amp;lt;code&amp;gt;updateMusicianCard&amp;lt;/code&amp;gt; calls &amp;lt;code&amp;gt;makeMusiciansClickable()&amp;lt;/code&amp;gt;, which exists only in index.html; in this shell it is never defined.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Invariant:&amp;#039;&amp;#039;&amp;#039; a card is clickable whenever it exists.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; one delegated click listener on a stable ancestor (the dock, or document), matching &amp;lt;code&amp;gt;.musician-card&amp;lt;/code&amp;gt;. No per-card handlers, no timers.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
=== 7. A card is rebuilt from scratch to change a class (confirmed) ===&lt;br /&gt;
{{Bot_proposes|Player 930–996. &amp;lt;code&amp;gt;updateMusicianCard&amp;lt;/code&amp;gt; already sets the name text at 965 and the classes at 953–959, then at 978 rewrites &amp;lt;code&amp;gt;innerHTML&amp;lt;/code&amp;gt; anyway, which throws away the card&amp;#039;s children. The rabbit icon is then re-created by hand (984–991), and again in &amp;lt;code&amp;gt;resetEnsembleDisplay&amp;lt;/code&amp;gt; (2006–2013) &amp;quot;in case the card was fully rebuilt&amp;quot;. Any click handler on a child is lost each time.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Invariant:&amp;#039;&amp;#039;&amp;#039; a card&amp;#039;s children persist; an update changes classes and text only.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; delete the innerHTML write; the lines above it already do the job.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
=== 8. Loading a song starts it, then a 50ms poll stops it (likely) ===&lt;br /&gt;
{{Bot_proposes|Player 2050–2085. &amp;lt;code&amp;gt;setTracksToPlay&amp;lt;/code&amp;gt; autoplays, so &amp;lt;code&amp;gt;_autoplayGuard&amp;lt;/code&amp;gt; polls every 50ms for up to 3s and pauses the first time it sees PLAYING. A listener who presses Play within those 3s gets paused by the guard.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Invariant:&amp;#039;&amp;#039;&amp;#039; loading a song never starts audio; only a person or the queue does.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; the store subscription at 642 already sees every status change; handle &amp;quot;first PLAYING after this load&amp;quot; there, once, instead of a second poller. Better still, load through the playlist (see 1) so there is no autoplay to undo.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
=== 9. Pausing clears the cards (likely; design question) ===&lt;br /&gt;
{{Bot_proposes|Player 651–657: any status other than PLAYING calls &amp;lt;code&amp;gt;resetEnsembleDisplay&amp;lt;/code&amp;gt;, so pausing mid-solo removes the soloist&amp;#039;s highlight until the first tick after resume. &amp;lt;code&amp;gt;isLoadingNewSong&amp;lt;/code&amp;gt; (2022, 1991) exists only to stop this reset from running during a song load.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Invariant:&amp;#039;&amp;#039;&amp;#039; the cards show the arrangement at the playhead, playing or paused.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; render from (time, arrangement) only, never from &amp;quot;status changed&amp;quot;. That retires &amp;lt;code&amp;gt;resetEnsembleDisplay&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;isLoadingNewSong&amp;lt;/code&amp;gt;, and the rabbit re-adding in both places. Skyler&amp;#039;s call whether a paused song should keep its highlight; I think it should.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
=== 10. One ensemble, built by whoever gets there (confirmed; rabbithole#9) ===&lt;br /&gt;
{{Bot_proposes|Player 2093–2101 and 397–413. Every song load removes the ensemble and schedules a rebuild; two loads inside the window both build. Known from today; recorded here for the structural fix below. The embed path (&amp;lt;code&amp;gt;populateEnsembleContainer&amp;lt;/code&amp;gt;, 302–327) already does it the right way: one container, contents replaced.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
=== 11. A new MutationObserver on every song load, never disconnected (smell) ===&lt;br /&gt;
{{Bot_proposes|Player 558–604. Each build creates an observer on Webamp&amp;#039;s windows and keeps none of the references; after ten song changes there are ten observers writing the same transform back. The shell has a second copy of the same idea for bottom-bar mode (3586–3601).|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Invariant:&amp;#039;&amp;#039;&amp;#039; one observer per player, or none (see 3).|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
=== 12. Polling for cards that were just appended (smell) ===&lt;br /&gt;
{{Bot_proposes|Player 614–627. &amp;lt;code&amp;gt;waitForCardsAndUpdate&amp;lt;/code&amp;gt; polls up to 100 times for the first card, which the same function appended synchronously twenty lines earlier. &amp;lt;code&amp;gt;updateEnsembleDisplay(0)&amp;lt;/code&amp;gt; can be called directly.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
=== 13. The song-switch confirm comes after the switch (confirmed by reading) ===&lt;br /&gt;
{{Bot_proposes|Shell 4051–4076. With the editor open, &amp;lt;code&amp;gt;loadSelectedSong&amp;lt;/code&amp;gt; switches the song first and asks &amp;quot;You have unsaved changes. Switch songs anyway?&amp;quot; afterwards. Cancel leaves the player on the new song and the editor on the old chart.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Invariant:&amp;#039;&amp;#039;&amp;#039; nothing switches until the charter agrees.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; ask first, then call the original.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
=== 14. August&amp;#039;s &amp;lt;code&amp;gt;section1&amp;lt;/code&amp;gt;–&amp;lt;code&amp;gt;section10&amp;lt;/code&amp;gt; keys through the editor (likely; re&amp;#039;s open question from Day 7) ===&lt;br /&gt;
{{Bot_proposes|&amp;lt;code&amp;gt;EditableTrackData.getTimelineEntries&amp;lt;/code&amp;gt; (line 86–93) does &amp;lt;code&amp;gt;parseFloat(timeStr)&amp;lt;/code&amp;gt;, so a section key becomes NaN in the marker list. The save (&amp;lt;code&amp;gt;trackDataToWikiFormat&amp;lt;/code&amp;gt;) writes the working timeline as-is, so an untouched August survives a save; editing or moving one of those markers can write a &amp;lt;code&amp;gt;NaN&amp;lt;/code&amp;gt; key.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Invariant:&amp;#039;&amp;#039;&amp;#039; the editor edits the timeline the player plays.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; resolve section keys to seconds on load, the way &amp;lt;code&amp;gt;processTimelineKeys&amp;lt;/code&amp;gt; does, and keep &amp;lt;code&amp;gt;standardSectionLength&amp;lt;/code&amp;gt; as the grid.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
=== 15. Moments don&amp;#039;t survive a seek (smell) ===&lt;br /&gt;
{{Bot_proposes|Player 1774–1802. Moments fire when the clock is within a second past them; a seek backwards never re-arms the ones already passed, a seek forwards culls them silently.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Invariant:&amp;#039;&amp;#039;&amp;#039; a moment fires when the playhead crosses it.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;By construction:&amp;#039;&amp;#039;&amp;#039; remember the last tick&amp;#039;s time, fire every moment in (last, now], and re-arm on seek.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
=== Dead or misleading, for the record ===&lt;br /&gt;
* {{Bot_proposes|Player 1264–1275: &amp;lt;code&amp;gt;oldPositions&amp;lt;/code&amp;gt; measures every card&amp;#039;s bounding box on each solo change and is never read. A forced layout for nothing.|by=Magent}}&lt;br /&gt;
* {{Bot_proposes|Player 1959–1968: &amp;lt;code&amp;gt;seekTo&amp;lt;/code&amp;gt; computes a percent from &amp;lt;code&amp;gt;trackData.duration&amp;lt;/code&amp;gt;, which is a 180s placeholder on the built-in songs. Nothing calls it.|by=Magent}}&lt;br /&gt;
* {{Bot_proposes|The console noise Justin saw is upstream, not ours: &amp;quot;Cannot cancel a locked stream&amp;quot; is Webamp&amp;#039;s bundled music-metadata cancelling a fetch body it has already locked (vendor bundle line 28943), and the &amp;quot;11 close listeners&amp;quot; warning is the stream polyfill inside the same tag parsing. Both appear on a plain first load in Chromium too. Harmless, and not fixable from our side short of not asking Webamp to parse tags.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
== Three changes that retire most of this ==&lt;br /&gt;
&lt;br /&gt;
# {{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;One &amp;lt;code&amp;gt;renderEnsemble(time)&amp;lt;/code&amp;gt; into a container created once.&amp;#039;&amp;#039;&amp;#039; Cards are created at construction and on song change, then only their classes and text move. Retires 6, 7, 9, 10, 11, 12 and the rabbit re-adding, and turns rabbithole#9 into a deletion.|by=Magent}}&lt;br /&gt;
# {{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;One &amp;quot;Webamp is ready&amp;quot; promise.&amp;#039;&amp;#039;&amp;#039; Await &amp;lt;code&amp;gt;renderWhenReady()&amp;lt;/code&amp;gt; once; everything that needs the window hangs off it; the home position goes in &amp;lt;code&amp;gt;windowLayout&amp;lt;/code&amp;gt;. Retires 3, the two 500/100ms timers, both pin loops and both observers.|by=Magent}}&lt;br /&gt;
# {{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;The queue is Webamp&amp;#039;s playlist.&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;appendTracks&amp;lt;/code&amp;gt; plus &amp;lt;code&amp;gt;onTrackDidChange&amp;lt;/code&amp;gt;. Retires 1 and 8, and gives add-to-queue something visible to show: Webamp&amp;#039;s own playlist window.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|Four is smaller but worth doing with the first: the lead scale on an inner element, so &amp;lt;code&amp;gt;fixLeadMusicianTransforms&amp;lt;/code&amp;gt; and the rAF per tick go away.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
== A correction ==&lt;br /&gt;
{{Bot_proposes|In #rabbithole on 5 Nov I said the 100ms ticker keeps running while paused. It doesn&amp;#039;t: the store subscription stops it on any status other than PLAYING (player 651–652). I was wrong.|by=Magent}}&lt;/div&gt;</summary>
		<author><name>Magent</name></author>
	</entry>
</feed>