Commit d1d7298e by PLN (Algolia)

status: the page has a real clock, and five buckets that name their owner

The rail stops guessing. Talks end at 17:00, ParVagues plays 18:00-20:00, DJ
Gredine takes it to 22:00 — sourced from PLN this morning in his own words,
which is the only reason it is on the page at all. Still no doors time,
because he never gave one.

The mechanical guard survives the change instead of being deleted by it: the
allowed clock set is now exactly {17:00, 18:00, 20:00, 22:00} and any other
time in any generated phrase still fails the self-test. Verified red-capable
rather than assumed — smuggling a 19:15 set time into a copy raises seven
failures, `hhmm SET: got 19:15` among them. In Chromium the real page reports
an empty selfTest and computes "sound test in 6h" off the wall clock.

Six buckets become five that each name what they are waiting for: SET 100%,
SAMPLES 100%, RIG gated, PERF partial, DESK yours. Four moves now, because
the UMC202HD is not on the USB bus and has never been tested on this box —
that goes first, ahead of the Ctrl+S and the two root steps, since it is the
only one the 17:00 hour cannot absorb.

Looked at 1440px and 420px: 3+2 with no orphan card, single column on the
phone, no horizontal scroll, and the four checkboxes fit.
parent 06352a90
......@@ -116,9 +116,11 @@
}
/* ---------- buckets ---------- */
/* six cards: 1 / 2+2+2 / 3+3. No breakpoint can leave a card alone on a row. */
/* five cards: 1 / 2+2+wide / 3+2. No breakpoint can leave a card alone on a row. */
.buckets{display:grid;grid-template-columns:1fr;gap:18px;align-items:start}
@media (min-width:640px){ .buckets{grid-template-columns:repeat(2,1fr)} }
/* two columns and an odd count would orphan the fifth, so it takes the row */
@media (min-width:640px) and (max-width:999px){ .buckets>.card:last-child{grid-column:1 / -1} }
@media (min-width:1000px){ .buckets{grid-template-columns:repeat(3,1fr)} }
.card{
background:var(--paper-card);
......@@ -224,6 +226,8 @@
.moves input:focus-visible{outline:2px solid var(--ribbon);outline-offset:2px}
.mtext{flex:1 1 auto;min-width:0}
.mtext b{font-family:Georgia,serif;font-weight:600;overflow-wrap:anywhere}
/* a command is a thing you type, so it keeps the monospace of the rest of the page */
.mtext b.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;font-size:.93rem;letter-spacing:-.01em}
.mtext .why{display:block;color:var(--ink-soft);font-size:.89rem;line-height:1.35}
.moves input:checked ~ .mtext{color:var(--ink-faint);text-decoration:line-through;text-decoration-thickness:1.2px}
.moves input:checked ~ .mtext .why{color:var(--ink-faint)}
......@@ -270,11 +274,11 @@
<header>
<h1><span class="goal nb">AI ENGINEER</span> &middot; <span class="nb">today</span></h1>
<p class="sub">Gig <b class="nb">Thursday 24 September 2026</b> <span class="clause"><span class="sep">&middot; </span>2h, 17 tracks, one hour of ears</span></p>
<p class="sub">Gig <b class="nb">Thursday 24 September 2026</b> <span class="clause"><span class="sep">&middot; </span>talks end at 5, you play at 6</span></p>
<div class="counters">
<span class="counter hot"><b id="c-gig">&mdash;</b></span>
<span class="counter"><span class="lbl">set</span> <b>17 tracks &middot; 2h</b></span>
<span class="counter"><span class="lbl">on you now</span> <b>3 moves</b></span>
<span class="counter"><span class="lbl">set</span> <b class="nb">18:00&ndash;20:00 &middot; 17 tracks</b></span>
<span class="counter"><span class="lbl">on you now</span> <b>4 moves</b></span>
</div>
</header>
......@@ -284,19 +288,19 @@
<div class="stop">
<span class="dot"></span>
<span class="day">&mdash;</span>
<span class="what">Root causes found, fixes scripted</span>
<span class="what">Talks end &middot; sound test</span>
<span class="cd">&mdash;</span>
</div>
<div class="stop">
<div class="stop finale">
<span class="dot"></span>
<span class="day">&mdash;</span>
<span class="what">The only sound test &middot; ears, not work</span>
<span class="what">ParVagues &middot; the 2h set</span>
<span class="cd">&mdash;</span>
</div>
<div class="stop finale">
<div class="stop">
<span class="dot"></span>
<span class="day">&mdash;</span>
<span class="what">Doors, then the 2h set</span>
<span class="what">DJ Gredine, till 10</span>
<span class="cd">&mdash;</span>
</div>
</div>
......@@ -307,115 +311,110 @@
<article class="card">
<h2>Set <span class="step">1</span></h2>
<p class="figure done">100%</p>
<p class="mood">Finished. Every track boots.</p>
<p class="mood">Nothing left to compile.</p>
<div class="bar" role="img" aria-label="set 100 percent"><div class="fill" style="width:100%"></div></div>
<ul>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>17/17 compile, seeded</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span><span class="mono">liquid_nite</span>: dangling dot, fixed</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span><span class="mono">piment_bresilien</span>: stray audition line, fixed</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>d9 sample bank found on disk</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span><span class="badge">you</span>Preload short by five banks</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>17/17 tracks compile cold, verified 10:15</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Two fresh edits verified: <span class="mono">love_first</span>, <span class="mono">take_5_drops</span></span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span><span class="mono">liquid_nite</span> dot fixed and committed</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>17 tracks, ~9 min each, fits 2 h</span></li>
</ul>
</article>
<article class="card">
<h2>Rehearsal <span class="step">2</span></h2>
<p class="figure near">35%</p>
<p class="mood">Six of seventeen. He trusts them.</p>
<div class="bar" role="img" aria-label="rehearsal 35 percent"><div class="fill near" style="width:35%"></div></div>
<h2>Samples <span class="step">2</span></h2>
<p class="figure done">100%</p>
<p class="mood">137 banks, and the checker is honest now.</p>
<div class="bar" role="img" aria-label="samples 100 percent"><div class="fill" style="width:100%"></div></div>
<ul>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Played to <span class="mono">take_5_drops</span></span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>&ldquo;not bad i am confident&rdquo;</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span>Eleven tracks never played in sequence</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span><span class="badge">you</span>5&ndash;6pm is the only sound test</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>137/137 banks warm in 3.8 s</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Was 132, and briefly 0 &mdash; a syntax error</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Five banks warming for the first time</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Checker could not see its own rows</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>43 new tests, watched failing</span></li>
</ul>
</article>
<article class="card gate">
<h2>Rig <span class="step">3</span> <span class="badge">you</span></h2>
<p class="figure">gated</p>
<p class="mood">One script away. Then it&rsquo;s safe.</p>
<div class="bar pending" role="img" aria-label="rig gated, waiting on one root step"></div>
<p class="mood">Up and explainable. One shield still off.</p>
<div class="bar pending" role="img" aria-label="rig gated, one root step outstanding"></div>
<ul>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>SuperDirt boots in 6s again</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span><span class="mono">MIDIIn.connectAll</span> patched out of boot</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Orbits routed, no speaker leak</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span><span class="mono">fix-ardour-rt.sh</span> written, verified on copies</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span><span class="badge">you</span>Run it: <span class="mono">sudo tools/fix-ardour-rt.sh --apply</span></span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span><span class="badge">you</span><span class="mono">parvagues-protect</span> OFF until then</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span><span class="badge">you</span><span class="mono">thermald</span> OFF, restart it</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>5/5 units up, scsynth boots in 6 s</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Audio graph unchanged: 49 links, 0 leaks</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span><span class="badge">you</span><span class="mono">parvagues-protect</span> OFF &mdash; no OOM shield</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span><span class="badge">you</span>Needs the Ardour RT fix first</span></li>
</ul>
</article>
<article class="card gate">
<h2>Perf <span class="step">4</span> <span class="badge">you</span></h2>
<p class="figure">gated</p>
<p class="mood">Owner found. The choice is yours.</p>
<div class="bar pending" role="img" aria-label="perf gated, the decision is the owner's"></div>
<article class="card">
<h2>Perf <span class="step">4</span></h2>
<p class="figure near">partial</p>
<p class="mood">One limit closed, one still open.</p>
<div class="bar" role="img" aria-label="perf, one of two limits closed"><div class="fill near" style="width:50%"></div></div>
<ul>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Cap is <span class="mono">thermal-pilot</span> rung 1, proven</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span><span class="mono">gig-up</span> now reads knobs, not wishes</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span><span class="badge">you</span>PL1 25W on USB-C vs 45W stock</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span><span class="badge">you</span>Try the Dell barrel charger first</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span><span class="mono">thermald</span> back on</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Dell charger: PL1 25 W &rarr; 45 W</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span>Clock cap still 65% &mdash; <span class="mono">thermal-pilot</span>, not the cable</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span>EPP still <span class="mono">balance_power</span></span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span>xrun baseline contaminated &mdash; remeasure with hands on board</span></li>
</ul>
</article>
<article class="card gate">
<h2>Mix <span class="step">5</span> <span class="badge">you</span></h2>
<p class="figure">gated</p>
<p class="mood">No tool can see your desk.</p>
<div class="bar pending" role="img" aria-label="mix gated, only the human can act"></div>
<h2>Desk <span class="step">5</span> <span class="badge">you</span></h2>
<p class="figure">yours</p>
<p class="mood">Your hands, and one keystroke is urgent.</p>
<div class="bar pending" role="img" aria-label="desk waiting on the owner, no tool can act"></div>
<ul>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span><span class="badge">you</span>Master fader, then Ctrl+S</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span><span class="badge">you</span>Ardour OSC still off</span></li>
</ul>
</article>
<article class="card">
<h2>Observability <span class="step">6</span></h2>
<p class="figure near">90%</p>
<p class="mood">It was always right. Nobody asked.</p>
<div class="bar" role="img" aria-label="observability 90 percent"><div class="fill near" style="width:90%"></div></div>
<ul>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span><span class="mono">gig-log</span> recording CC, 145 rows</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span><span class="mono">xrun_by</span> named Ardour in one query</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span>Stale-address rebind still post-gig</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span><span class="badge">you</span>UMC202HD not on the bus, never tested</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span><span class="badge">you</span>Ctrl+S before anything quits Ardour</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span>Saved session (00:14) has five orbits shut</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span><span class="badge">you</span>RT fix not applied &mdash; <span class="mono">ardour:80</span> still there</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span>Running Ardour was Bridge-launched, proves nothing</span></li>
</ul>
</article>
</section>
<section class="moves" aria-label="Next three moves">
<h2 class="serif">Next 3 moves</h2>
<section class="moves" aria-label="Next four moves">
<h2 class="serif">Next 4 moves</h2>
<ol>
<li>
<label>
<input type="checkbox" data-move="rtfix">
<input type="checkbox" data-move="umc">
<span class="num"></span>
<span class="mtext"><b>Plug the UMC202HD now, at home</b><span class="why">Never been tested. Don&rsquo;t discover it at 17:00.</span></span>
</label>
</li>
<li>
<label>
<input type="checkbox" data-move="save">
<span class="num"></span>
<span class="mtext"><b class="mono">sudo tools/fix-ardour-rt.sh --apply</b><span class="why">Unblocks the whole rig</span></span>
<span class="mtext"><b>Ctrl+S in Ardour</b><span class="why">Settles five shut orbits. Do it before any quit.</span></span>
</label>
</li>
<li>
<label>
<input type="checkbox" data-move="charger">
<input type="checkbox" data-move="rtfix">
<span class="num"></span>
<span class="mtext"><b>Plug the Dell barrel charger</b><span class="why">The cap may close for free</span></span>
<span class="mtext"><b class="mono">sudo tools/fix-ardour-rt.sh</b> &rarr; read diff &rarr; <b class="mono">--apply</b> &rarr; <b>relaunch from the DESKTOP ICON</b><span class="why">Unblocks the shield and the xrun fix. A terminal launch proves nothing.</span></span>
</label>
</li>
<li>
<label>
<input type="checkbox" data-move="fader">
<input type="checkbox" data-move="protect">
<span class="num"></span>
<span class="mtext"><b>Master fader &rarr; Ctrl+S</b><span class="why">The one thing no tool can read</span></span>
<span class="mtext"><b class="mono">sudo systemctl start parvagues-protect</b><span class="why">Check <span class="mono">is-active</span> first; step 3&rsquo;s script restarts it itself.</span></span>
</label>
</li>
</ol>
</section>
<footer>
<p class="path"><b>fix-ardour-rt</b> &rarr; <b>protect back on</b> &rarr; <b>charger</b> &rarr; <b>faders</b> &rarr; <b>5pm ears</b></p>
<p class="tiny">Gate 01:25 &middot; 31 ok, 6 fail, 5 warn. The compile fail is paid; the cap items and your desk are the rest.</p>
<p class="tiny">Not this project, explicitly not today: the latch double-press, gig-log&rsquo;s liveness rebind, the LCXL chord-reset and the page/logo dawless UX.</p>
<p class="path">The set and the samples are done; everything still open needs <b>your own hands</b> and a <b>root password</b>.</p>
<p class="tiny">Post-gig work &mdash; the latch double-press, gig-log liveness, the chord-reset UX &mdash; is deliberately not tonight&rsquo;s.</p>
</footer>
</div>
......@@ -445,19 +444,27 @@
function dayLabel(d){ return WD[d.getDay()] + " " + d.getDate() + " " + MO[d.getMonth()]; }
/* WHAT IS SOURCED AND WHAT IS NOT -- read this before adding a time here.
Sourced: the gig is Thursday 24 September 2026, and the sound test is the
5pm-6pm window, "end of talks -> doors" (TODO_GIG.md:506-507).
NOT SOURCED: the doors time. There is no entry for this gig in
~/Work/Perso/www/content/lives/2026/ -- the canonical home for venue, set
title, lineup and times -- and TODO_GIG.md never states one. The project
rule is "mastering != metadata, never invent gig metadata; if a fact isn't
in the canonical source, ask". 19:00 and 18:00 have each been guessed here
once; the second was derived from the sound test's end, which is exactly the
inference to avoid. So every countdown on this page anchors on 17:00, and the
set reads "time tbc" until somebody puts the real time in the canonical file. */
Sourced, from PLN, 2026-09-24 morning, verbal; recorded in TODO_GIG.md:975-992
because it is written nowhere else: the conference "plays until 5pm", then he
"setup the main stage for a gig at 6pm-8pm", then "DJ Gredine picksup from 8pm
to 10pm". So the day is 17:00 talks end / the only sound test, 18:00-20:00 the
2h set, 20:00-22:00 Gredine.
STILL NOT SOURCED: the doors time. PLN never gave one, there is still no entry
for this gig under ~/Work/Perso/www/content/lives/2026/ -- the canonical home
for venue, set title, lineup and times -- and deriving doors from "talks end"
is exactly the inference this page refuses to make (19:00 and 18:00 were each
guessed in here once, before any of it was sourced). The word for 17:00 is
"talks end / sound test", never "doors".
The mechanical half of that promise is at the bottom of this block: every
phrase this page can generate, over a three-week span, is scanned and the only
clock times allowed through are the four sourced ones. Anything else fails. */
var GIGDAY = parseDt("2026-09-24");
var TEST = parseDt("2026-09-24T17:00");
var TESTLEN = 60; /* 5pm-6pm, one hour (TODO_GIG.md:506-507) */
var TEST = parseDt("2026-09-24T17:00"); /* talks end; the only sound test */
var TESTLEN = 60; /* 17:00-18:00, one hour */
var SET = parseDt("2026-09-24T18:00"); /* ParVagues */
var SETLEN = 120; /* 18:00-20:00, two hours */
var GRED = parseDt("2026-09-24T20:00"); /* DJ Gredine */
var GREDLEN = 120; /* 20:00-22:00 */
/* "in 4h" / "in 25min" / "in 3 days". Never "in 0", never negative. */
function away(ms){
......@@ -469,25 +476,27 @@
return "in " + Math.max(1, Math.ceil(ms / 60000)) + "min";
}
/* The live header phrase, anchored on the one hour we can actually cite.
After the window closes it stops counting, because there is nothing sourced
left to count to -- "set tonight" is all the page is entitled to say. */
/* The live header phrase. It hands off along the evening: the countdown to the
sound test, then the countdown to the stage during that hour (the hour is for
ears, but the eye wants the stage), then the set, then Gredine. */
function testPhrase(now){
var d = daysBetween(now, TEST);
if (d > 1) return "sound test in " + d + " days";
if (d === 1) return "sound test tomorrow";
if (d === 0){
var ms = TEST.getTime() - now.getTime();
if (ms > 0) return "sound test " + away(ms);
if (now.getTime() < TEST.getTime() + TESTLEN * 60000) return "sound test now";
return "set tonight";
var t = now.getTime();
if (t < TEST.getTime()) return "sound test " + away(TEST.getTime() - t);
if (t < SET.getTime()) return "on stage " + away(SET.getTime() - t);
if (t < SET.getTime() + SETLEN * 60000) return "on stage now";
if (t < GRED.getTime() + GREDLEN * 60000) return "set played · Gredine on";
return "set played";
}
if (d === -1) return "played last night";
return "played " + (-d) + " days ago";
}
/* Per-stop countdown. A window says "happening now" while it is open;
a stop with no sourced time says only what it follows. */
/* Per-stop countdown. A window says it is happening while it is open, in its own
words: a sound test closes, a set gets played, a DJ slot is simply done. */
function stopPhrase(now, target, opt){
opt = opt || {};
var d = daysBetween(now, target);
......@@ -495,27 +504,26 @@
if (d === 1) return "tomorrow";
if (d === -1) return "yesterday";
if (d < -1) return (-d) + " days ago";
if (opt.tbc) return "after the sound test";
var ms = target.getTime() - now.getTime();
if (!opt.timed) return ms > 0 ? "today" : "earlier today";
if (ms > 0) return away(ms);
if (opt.dur && now.getTime() < target.getTime() + opt.dur * 60000) return "happening now";
return "window closed";
if (opt.dur && now.getTime() < target.getTime() + opt.dur * 60000) return opt.live || "happening now";
return opt.past || "window closed";
}
/* Stops in DOM order. Dates live here only, never typed into the markup.
The finale carries the DAY, which is sourced, and no clock time, which is not. */
/* Stops in DOM order. Times live here only, never typed into the markup.
Gredine's end folds into her own label, so the rail stays three stops wide. */
var STOPS = [
{ dt: "2026-09-24T02:00", timed: false },
{ dt: "2026-09-24T17:00", timed: true, dur: TESTLEN },
{ dt: "2026-09-24", tbc: true, finale: true }
{ dt: "2026-09-24T17:00", timed: true, dur: TESTLEN, live: "happening now", past: "window closed" },
{ dt: "2026-09-24T18:00", timed: true, dur: SETLEN, live: "on stage now", past: "played", finale: true },
{ dt: "2026-09-24T20:00", timed: true, dur: GREDLEN, live: "on now", past: "done" }
];
/* Pure: every string the page shows, derived from one instant. Testable on its own. */
function phrases(now){
var out = {
test: testPhrase(now),
cap: dayLabel(GIGDAY) + (daysBetween(now, GIGDAY) === 0 ? " \u00b7 today" : ""),
cap: dayLabel(GIGDAY) + (daysBetween(now, GIGDAY) === 0 ? " · today" : ""),
stops: []
};
for (var i = 0; i < STOPS.length; i++){
......@@ -524,13 +532,12 @@
var delta = daysBetween(now, d);
var ends = d.getTime() + ((s.dur || 0) * 60000);
out.stops.push({
day: s.tbc ? "time tbc" : (s.timed ? hhmm(d) : "overnight"),
day: s.timed ? hhmm(d) : "overnight",
cd: stopPhrase(now, d, s),
sameDay: delta === 0,
/* spent: the clock is past it, so the ribbon moves on. A stop with no
known time is never spent -- we cannot claim the set is behind us. */
spent: !s.tbc && ends <= now.getTime(),
/* gone: a closed WINDOW, safe to dim. Nothing else is ever dimmed. */
/* spent: the clock is past the whole window, so the ribbon moves on. */
spent: ends <= now.getTime(),
/* gone: a closed window, safe to dim. Nothing else is ever dimmed. */
gone: !!s.timed && ends <= now.getTime()
});
}
......@@ -544,7 +551,7 @@
};
var fails = [];
/* Day before, the morning of, mid-afternoon, during the window, after it. */
/* Days out, the morning of, the test hour, on stage, after, the morning after. */
[["2026-09-21T09:00","sound test in 3 days"],
["2026-09-23T10:00","sound test tomorrow"],
["2026-09-23T23:59","sound test tomorrow"],
......@@ -553,23 +560,29 @@
["2026-09-24T15:00","sound test in 2h"],
["2026-09-24T16:30","sound test in 30min"],
["2026-09-24T16:59","sound test in 1min"],
["2026-09-24T17:00","sound test now"],
["2026-09-24T17:30","sound test now"],
["2026-09-24T17:59","sound test now"],
["2026-09-24T18:00","set tonight"],
["2026-09-24T19:30","set tonight"],
["2026-09-24T23:30","set tonight"],
["2026-09-24T17:00","on stage in 1h"],
["2026-09-24T17:30","on stage in 30min"],
["2026-09-24T17:59","on stage in 1min"],
["2026-09-24T18:00","on stage now"],
["2026-09-24T19:30","on stage now"],
["2026-09-24T19:59","on stage now"],
["2026-09-24T20:00","set played · Gredine on"],
["2026-09-24T21:00","set played · Gredine on"],
["2026-09-24T22:00","set played"],
["2026-09-24T23:30","set played"],
["2026-09-25T10:00","played last night"],
["2026-09-28T10:00","played 4 days ago"]].forEach(function(c){
var got = testPhrase(parseDt(c[0]));
if (got !== c[1]) fails.push("testPhrase " + c[0] + " expected " + c[1] + ", got " + got);
});
[["2026-09-23T10:00",["tomorrow","tomorrow","tomorrow"]],
["2026-09-24T01:40",["today","in 15h","after the sound test"]],
["2026-09-24T09:00",["earlier today","in 8h","after the sound test"]],
["2026-09-24T15:00",["earlier today","in 2h","after the sound test"]],
["2026-09-24T17:20",["earlier today","happening now","after the sound test"]],
["2026-09-24T19:30",["earlier today","window closed","after the sound test"]],
["2026-09-24T01:40",["in 15h","in 16h","in 18h"]],
["2026-09-24T09:00",["in 8h","in 9h","in 11h"]],
["2026-09-24T15:00",["in 2h","in 3h","in 5h"]],
["2026-09-24T17:20",["happening now","in 40min","in 2h"]],
["2026-09-24T18:30",["window closed","on stage now","in 1h"]],
["2026-09-24T21:00",["window closed","played","on now"]],
["2026-09-24T22:30",["window closed","played","done"]],
["2026-09-25T10:00",["yesterday","yesterday","yesterday"]]].forEach(function(c){
var got = phrases(parseDt(c[0])).stops.map(function(s){ return s.cd; });
if (got.join("|") !== c[1].join("|")) fails.push("stops " + c[0] + " expected " + c[1].join("|") + ", got " + got.join("|"));
......@@ -577,12 +590,14 @@
/* Weekday and clock labels are derived, never typed: 24 Sept 2026 is a Thursday. */
if (parseDt("2026-09-24").getDay() !== 4) fails.push("24 Sept 2026 is not reading as Thursday");
if (dayLabel(GIGDAY) !== "Thu 24 Sept") fails.push("dayLabel: got " + dayLabel(GIGDAY));
if (hhmm(TEST) !== "17:00") fails.push("hhmm: got " + hhmm(TEST));
if (hhmm(TEST) !== "17:00") fails.push("hhmm TEST: got " + hhmm(TEST));
if (hhmm(SET) !== "18:00") fails.push("hhmm SET: got " + hhmm(SET));
if (hhmm(GRED) !== "20:00") fails.push("hhmm GRED: got " + hhmm(GRED));
if (daysBetween(parseDt("2026-08-31"), parseDt("2026-09-01")) !== 1) fails.push("month rollover");
/* The stop rail carries exactly one clock time, and it is the sourced one. */
[["2026-09-24T09:00"],["2026-09-24T19:30"],["2026-09-23T10:00"]].forEach(function(c){
/* The rail carries exactly the three sourced clock times, in order. */
[["2026-09-24T09:00"],["2026-09-24T19:30"],["2026-09-24T23:30"],["2026-09-23T10:00"]].forEach(function(c){
var got = phrases(parseDt(c[0])).stops.map(function(s){ return s.day; }).join("|");
if (got !== "overnight|17:00|time tbc") fails.push("stop labels " + c[0] + " got " + got);
if (got !== "17:00|18:00|20:00") fails.push("stop labels " + c[0] + " got " + got);
});
/* Every stop is today, so today-count is 3 on the day and 0 once the day turns. */
[["2026-09-24T01:40",3],["2026-09-24T21:00",3],["2026-09-25T10:00",0],["2026-09-23T10:00",0]]
......@@ -590,21 +605,23 @@
var n = phrases(parseDt(c[0])).stops.filter(function(s){ return s.sameDay; }).length;
if (n !== c[1]) fails.push("today-count " + c[0] + " expected " + c[1] + ", got " + n);
});
/* The ribbon marker advances as the day does; the timeless finale never becomes spent. */
[["2026-09-24T01:40",0],["2026-09-24T08:30",1],["2026-09-24T18:30",2],["2026-09-24T21:00",2]]
/* The ribbon marker advances as each window closes: 18:00, 20:00, 22:00. */
[["2026-09-24T01:40",0],["2026-09-24T17:20",0],["2026-09-24T18:30",1],
["2026-09-24T20:30",2],["2026-09-24T22:30",3]]
.forEach(function(c){
var n = phrases(parseDt(c[0])).stops.filter(function(s){ return s.spent; }).length;
if (n !== c[1]) fails.push("spent-count " + c[0] + " expected " + c[1] + ", got " + n);
});
/* Only the sound-test window can be dimmed. */
[["2026-09-24T01:40",0],["2026-09-24T17:30",0],["2026-09-24T18:30",1],["2026-09-24T21:00",1]]
/* Only a closed window is dimmed, and every stop on this rail is a window. */
[["2026-09-24T01:40",0],["2026-09-24T17:30",0],["2026-09-24T18:30",1],
["2026-09-24T21:00",2],["2026-09-24T22:30",3]]
.forEach(function(c){
var n = phrases(parseDt(c[0])).stops.filter(function(s){ return s.gone; }).length;
if (n !== c[1]) fails.push("gone-count " + c[0] + " expected " + c[1] + ", got " + n);
});
/* The ribbon has exactly one home all gig day, and none once the day turns. */
[["2026-09-24T01:40",1],["2026-09-24T08:30",1],["2026-09-24T17:20",1],["2026-09-24T19:30",1],
["2026-09-24T23:30",1],["2026-09-25T10:00",0],["2026-09-23T10:00",0]]
/* The ribbon has one home from midnight to 22:00, and none once the night is over. */
[["2026-09-24T01:40",1],["2026-09-24T08:30",1],["2026-09-24T17:20",1],["2026-09-24T18:30",1],
["2026-09-24T21:00",1],["2026-09-24T22:30",0],["2026-09-25T10:00",0],["2026-09-23T10:00",0]]
.forEach(function(c){
var st = phrases(parseDt(c[0])).stops.filter(function(s){ return s.sameDay && !s.spent; });
var n = st.length ? 1 : 0;
......@@ -612,10 +629,11 @@
});
if (STOPS.length !== 3) fails.push("STOPS length changed without updating the tests");
/* No phrase may carry a stale-wording artefact, and NO phrase may carry a clock
time other than the sourced 17:00 -- that assertion is the mechanical guard
time outside the four sourced ones -- that assertion is the mechanical guard
against somebody guessing a doors time back into this file. */
var BAD = /(\bin -|\bin 0\b|\bin 1 days\b|\b-\d|NaN|undefined)/;
var CLOCK = /\b([01]?\d|2[0-3]):[0-5]\d\b/g;
var OK_CLOCK = { "17:00": 1, "18:00": 1, "20:00": 1, "22:00": 1 };
for (var probe = parseDt("2026-09-15").getTime(); probe <= parseDt("2026-10-05").getTime(); probe += 1800000){
var pp = phrases(new Date(probe));
var strs = [pp.test, pp.cap];
......@@ -624,8 +642,8 @@
for (var r = 0; r < strs.length; r++){
if (BAD.test(strs[r])) { fails.push("bad phrase at " + new Date(probe) + ": " + strs[r]); stop = true; break; }
var hits = String(strs[r]).match(CLOCK) || [];
for (var t = 0; t < hits.length; t++){
if (hits[t] !== "17:00"){ fails.push("unsourced clock time " + hits[t] + " in: " + strs[r]); stop = true; }
for (var t2 = 0; t2 < hits.length; t2++){
if (!OK_CLOCK[hits[t2]]){ fails.push("unsourced clock time " + hits[t2] + " in: " + strs[r]); stop = true; }
}
if (stop) break;
}
......@@ -674,7 +692,7 @@
/* Next-3-moves ticks, remembered locally. New move list, new key: no stale ticks.
Storage is optional: every access guarded, the page renders without it. */
var KEY = "pv.gig.thu24.moves.0924a";
var KEY = "pv.gig.thu24.moves.0924b";
var boxes = document.querySelectorAll(".moves input[type=checkbox]");
var saved = {};
try {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment