Commit a606d992 by PLN (Algolia)

docs(status): five buckets settled, the rehearsal is the only one still open

Rebuilt for Wed 23. Audio, surface and observability all moved this morning:
the laptop card is back on HiFi and the NVIDIA pin is retired, the LCXL3
driver's 1077 silent failures are explained and surface-state.json is live for
the first time, and the MIDI log binds a port at last after nine dead sessions.

The countdown computes from the real clock at every stop, so the page cannot
go stale into the gig; checked at five instants from today through three days
after.
parent 6ec3ca09
......@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Thursday 24 &middot; AI ENGINEER &middot; 2-hour set</title>
<title>AI ENGINEER &middot; 2h livecoding set</title>
<style>
:root{
--paper:#f6f1e5;
......@@ -17,8 +17,6 @@
--ribbon-soft:#d98f76;
--sage:#6f8055;
--amber:#9d7517;
--amber-soft:#e0c98e;
--slate:#5b6f8a;
--shadow:rgba(90,74,48,.13);
}
*{box-sizing:border-box}
......@@ -45,6 +43,8 @@
h1{font-size:2.05rem;line-height:1.2;margin:0 0 6px;letter-spacing:.1px}
h1 .goal{color:var(--ribbon)}
.sub{margin:0;color:var(--ink-soft);font-size:.96rem}
.sub b{color:var(--ink);font-weight:600}
.sub .clause{white-space:nowrap}
.nb{white-space:nowrap}
.counters{display:flex;flex-wrap:wrap;gap:10px;margin-top:15px}
.counter{
......@@ -72,7 +72,7 @@
box-shadow:0 1px 3px var(--shadow);
overflow-x:auto;
}
.track{display:flex;align-items:flex-start;gap:0;min-width:300px}
.track{display:flex;align-items:flex-start;gap:0;min-width:260px}
.stop{flex:1 1 0;text-align:center;position:relative;padding-top:16px}
.stop::before{
content:"";position:absolute;top:5px;left:0;right:0;height:2px;
......@@ -88,11 +88,11 @@
}
.stop.finale .dot{background:var(--ink);border-color:var(--ink);width:15px;height:15px}
.day{display:block;font-size:.79rem;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint);margin-top:4px}
/* two text lines reserved, so every countdown sits on one baseline */
.what{display:block;font-size:.88rem;color:var(--ink);padding:0 6px;line-height:1.35;min-height:2.5em}
/* one line reserved per stop: both labels fit on one line here, so both countdowns share a baseline */
.what{display:block;font-size:.88rem;color:var(--ink);padding:0 6px;line-height:1.35;min-height:1.4em}
.cd{display:block;font-size:.77rem;color:var(--ink-faint);margin-top:3px;letter-spacing:.02em}
.stop.finale .what{font-weight:600}
.stop.finale .cd{color:var(--ink-soft)}
.stop.finale .cd{color:var(--ink-soft);font-weight:600}
/* ribbon on today */
.stop.today .dot{background:var(--ribbon);border-color:var(--ribbon);width:15px;height:15px}
......@@ -109,25 +109,10 @@
}
/* ---------- buckets ---------- */
/* five cards: a true 3 + 2, never a 4 + 1 orphan */
.buckets{
display:grid;
grid-template-columns:1fr;
gap:18px;
align-items:start;
}
/* two columns: the fifth card would sit alone, so it spans the row instead */
@media (min-width:660px){
.buckets{grid-template-columns:repeat(2,1fr)}
.card:nth-child(5){grid-column:1 / -1}
}
/* three columns: 3 on top, the last 2 centred beneath at the same width */
@media (min-width:1000px){
.buckets{grid-template-columns:repeat(6,1fr)}
.card{grid-column:span 2}
.card:nth-child(4){grid-column:2 / 4}
.card:nth-child(5){grid-column:4 / 6}
}
/* six cards: 1 / 2+2+2 / 3+3. No breakpoint can leave a card alone on a row. */
.buckets{display:grid;grid-template-columns:1fr;gap:18px}
@media (min-width:640px){ .buckets{grid-template-columns:repeat(2,1fr)} }
@media (min-width:1000px){ .buckets{grid-template-columns:repeat(3,1fr)} }
.card{
background:var(--paper-card);
border:1px solid var(--rule);
......@@ -137,15 +122,18 @@
}
.card:nth-child(odd){transform:rotate(-.3deg)}
.card:nth-child(even){transform:rotate(.3deg)}
/* the gate leads the risk, so it wears the ribbon edge */
/* the open bucket leads the risk, so it wears the ribbon edge */
.card.gate{border-color:var(--ribbon-soft);box-shadow:0 2px 8px rgba(180,85,60,.18)}
.card h2{font-size:.95rem;letter-spacing:.14em;text-transform:uppercase;margin:0;color:var(--ink-soft)}
.card h2{
font-size:.95rem;letter-spacing:.14em;text-transform:uppercase;margin:0;color:var(--ink-soft);
display:flex;align-items:center;gap:7px;flex-wrap:wrap;
}
.card h2 .step{font-family:Georgia,serif;font-size:.85rem;letter-spacing:0;color:var(--ink-faint);text-transform:none}
.card.gate h2{color:var(--ribbon)}
.figure{font-family:Georgia,serif;font-size:2.4rem;line-height:1.12;margin:2px 0 0}
.figure small{font-size:.9rem;font-family:"Segoe UI",Roboto,Helvetica,Arial,sans-serif;color:var(--ink-faint);letter-spacing:.03em}
.card.gate .figure{color:var(--ribbon)}
.figure.done{color:var(--sage)}
.figure.yours{color:var(--slate)}
.figure.near{color:var(--amber)}
.card.gate .figure{color:var(--ribbon)}
.mood{margin:2px 0 12px;font-size:.9rem;color:var(--ink-soft);font-style:italic}
.bar{
height:9px;border-radius:5px;background:#eee5d2;overflow:hidden;
......@@ -156,12 +144,13 @@
background-color:var(--sage);
background-image:repeating-linear-gradient(45deg, rgba(255,255,255,.30) 0 2px, transparent 2px 5px);
}
.fill.low{background-color:var(--ribbon-soft)}
/* the gate's own bar carries the full ribbon, so 20% still reads from a metre away */
.card.gate .fill.low{background-color:var(--ribbon)}
/* halfway: neither shipped nor stalled */
.fill.mid{background-color:var(--amber)}
.fill.wait{background-color:var(--slate)}
.fill.near{background-color:var(--amber)}
/* nothing run yet: the track itself is pencilled in, so 0% reads as "waiting", not "broken" */
.bar.pending{
background-color:#f2e6dc;
background-image:repeating-linear-gradient(45deg, rgba(180,85,60,.26) 0 2px, transparent 2px 6px);
border-color:var(--ribbon-soft);
}
ul{list-style:none;margin:0;padding:0}
li{display:flex;gap:8px;align-items:flex-start;padding:4px 0;font-size:.9rem;line-height:1.42}
li+li{border-top:1px solid rgba(180,160,120,.16)}
......@@ -178,17 +167,16 @@
padding:1px 5px;vertical-align:1.5px;
text-transform:uppercase;
}
h2 .badge{margin-right:0;vertical-align:0}
.mono{
font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;
font-size:.82rem;letter-spacing:-.01em;
}
svg{display:block}
.box{fill:none;stroke:var(--ink-faint);stroke-width:1.4}
.ic{display:block;width:16px;height:16px}
.tick{fill:none;stroke:var(--sage);stroke-width:2.1;stroke-linecap:round}
/* half-done: the tick's first stroke landed, the rest is still pencilled in */
.half{fill:none;stroke:var(--ribbon);stroke-width:2.1;stroke-linecap:round}
.half-rest{fill:none;stroke:var(--ribbon-soft);stroke-width:1.7;stroke-linecap:round;stroke-dasharray:1.6 2.2}
.warn{fill:none;stroke:var(--amber);stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
/* not done: the ring is drawn, the tick is not. Terracotta inside the open bucket. */
.open{fill:none;stroke:var(--ink-faint);stroke-width:1.6}
.card.gate .open{stroke:var(--ribbon-soft)}
/* ---------- next moves ---------- */
.moves{
......@@ -202,7 +190,7 @@
transform:rotate(-.3deg);
position:relative;
}
.moves h2{font-size:1.25rem;text-transform:none;letter-spacing:0;color:var(--ink);margin:0 0 10px}
.moves h2{font-size:1.25rem;text-transform:none;letter-spacing:0;color:var(--ink);margin:0 0 10px;display:block}
.moves ol{list-style:none;margin:0;padding:0;counter-reset:m}
.moves li{
counter-increment:m;
......@@ -240,33 +228,43 @@
footer p.path{color:var(--ink)}
footer p.path b{font-family:Georgia,serif;color:var(--ribbon);font-weight:600}
footer p.tiny{color:var(--ink-faint);font-size:.82rem}
footer a{color:var(--ribbon);text-decoration:none;border-bottom:1px solid var(--ribbon-soft)}
footer a:hover{background:var(--paper-warm)}
@media (max-width:659px){
@media (max-width:639px){
.wrap{padding:24px 16px 44px}
h1{font-size:1.55rem}
h1{font-size:1.5rem}
.card,.moves{transform:none}
.counter{white-space:normal}
.timeline{padding:32px 7px 14px}
.sub .clause{display:block;white-space:normal}
.sub .sep{display:none}
.timeline{padding:32px 8px 14px}
.day{font-size:.72rem;letter-spacing:.03em}
/* three text lines reserved at phone width, so the countdowns keep one baseline */
.what{font-size:.81rem;padding:0 3px;min-height:5em}
.what{font-size:.82rem;padding:0 4px;min-height:2.8em}
.cd{font-size:.74rem}
.figure{font-size:2.1rem}
.moves{padding:18px 16px 15px}
.moves h2{font-size:1.12rem}
}
</style>
</head>
<body>
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false">
<symbol id="i-tick" viewBox="0 0 16 16">
<path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/>
</symbol>
<symbol id="i-open" viewBox="0 0 16 16">
<circle class="open" cx="8" cy="8" r="5.6"/>
</symbol>
</svg>
<div class="wrap">
<header>
<h1><span class="nb">Thursday 24</span> &middot; <span class="goal nb">AI ENGINEER</span></h1>
<p class="sub"><span class="nb">ParVagues &middot; 2-hour set</span> &middot; <span class="nb">five buckets, one gate</span> &middot; <span class="nb">glance and close</span></p>
<h1><span class="goal nb">AI ENGINEER</span> &middot; <span class="nb">2h livecoding set</span></h1>
<p class="sub">Gig <b class="nb">Thursday 24 September 2026, evening</b> <span class="clause"><span class="sep">&middot; </span>six buckets, one still open</span></p>
<div class="counters">
<span class="counter hot"><b id="c-gig">&mdash;</b> <span class="lbl">&middot; <span id="c-gig-day">Thu 24 Sept</span></span></span>
<span class="counter"><span class="lbl">on you now</span> <b>3 moves</b></span>
<span class="counter"><b id="c-yours">&mdash;</b> <span class="lbl">only you can do</span></span>
</div>
</header>
......@@ -275,19 +273,13 @@
<div class="stop">
<span class="dot"></span>
<span class="day">&mdash;</span>
<span class="what">Set settled &middot; faders back</span>
<span class="cd">&mdash;</span>
</div>
<div class="stop">
<span class="dot"></span>
<span class="day">&mdash;</span>
<span class="what">Full run &middot; preflight</span>
<span class="what">The 2h arc is yours</span>
<span class="cd">&mdash;</span>
</div>
<div class="stop finale">
<span class="dot"></span>
<span class="day">&mdash;</span>
<span class="what">AI ENGINEER &middot; 2-hour set</span>
<span class="what">AI ENGINEER &middot; doors, evening</span>
<span class="cd">&mdash;</span>
</div>
</div>
......@@ -296,79 +288,80 @@
<section class="buckets" aria-label="Buckets">
<article class="card">
<h2>Set</h2>
<h2>Set <span class="step">1</span></h2>
<p class="figure done">100%</p>
<p class="mood">Settled. Seventeen tracks, in order.</p>
<p class="mood">Finished. Nothing left to choose.</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 width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>17 tracks, 3 movements, ~9 min each</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span><span class="mono">setlist_thu24.txt</span>, in play order</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>All 17 pass silent-eval seeded</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>pvlint over the set: 0 errors</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>Sample-heavy techno cut by ear</span></li>
<li><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><rect class="box" x="2.2" y="2.6" width="11.6" height="11.6" rx="2.4"/></svg></span><span><span class="badge">you</span>Confirm <span class="mono">ete_a_mauerpark</span> d9 effects</span></li>
<li><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><rect class="box" x="2.2" y="2.6" width="11.6" height="11.6" rx="2.4"/></svg></span><span><span class="badge">you</span>Fix <span class="mono">sept1</span> to the new controller</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>17 tracks, three movements, play order</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>All pass silent-eval seeded</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span><span class="mono">pvlint</span> over the set: 0 errors</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Preload 132 banks, nothing lost</span></li>
</ul>
</article>
<article class="card">
<h2>Corpus <span class="step">2</span></h2>
<p class="figure done">100%</p>
<p class="mood">Settled. Re-plan finds nothing left.</p>
<div class="bar" role="img" aria-label="corpus 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>685 moves across 148 files, 0 clashes</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Button conformance 78.9% &rarr; 93.3%</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Re-plan now reports 0 moves</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Zero verdict changes, verified three times</span></li>
</ul>
</article>
<article class="card">
<h2>Rig</h2>
<p class="figure">95%</p>
<p class="mood">One launcher, one gate. Never pressed.</p>
<div class="bar" role="img" aria-label="rig 95 percent"><div class="fill" style="width:95%"></div></div>
<h2>Audio <span class="step">3</span></h2>
<p class="figure near">85%</p>
<p class="mood">Repaired this morning. One leg unproven.</p>
<div class="bar" role="img" aria-label="audio 85 percent"><div class="fill near" style="width:85%"></div></div>
<ul>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>Column remap finished: 148 files</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>Button conformance 78.9% &rarr; 93.3%</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>Orbit compaction: 150 moves, no d6 gap</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>Both migrations: zero playability regressions</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>OLED tracks the hand, holds at rest</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>Preload 132 banks, covers the set</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>Latency 0.1 s live, CPU cap lifted</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>One launcher, one gate &mdash; 39 checks, 3.4 s</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>Caught: preload rewritten from the Sept set</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>SuperDirt boots as the unit, not raw sclang</span></li>
<li><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><rect class="box" x="2.2" y="2.6" width="11.6" height="11.6" rx="2.4"/></svg></span><span><span class="badge">you</span>Press the launcher once from the icon</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Laptop card back on HiFi profile</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>NVIDIA pin retired, card now off</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>5 real sinks, was 4 dead ones</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span><span class="badge">you</span><span class="mono">UMC202HD</span>: still never tested</span></li>
</ul>
</article>
<article class="card">
<h2>Sound</h2>
<p class="figure done">85%</p>
<p class="mood">Faders restored. Twelve reach master.</p>
<div class="bar" role="img" aria-label="sound 85 percent"><div class="fill" style="width:85%"></div></div>
<h2>Surface <span class="step">4</span></h2>
<p class="figure near">95%</p>
<p class="mood">Live for the first time.</p>
<div class="bar" role="img" aria-label="surface 95 percent"><div class="fill near" style="width:95%"></div></div>
<ul>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>Fader baseline restored by gig-up</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>All 12 orbits reach the master bus</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>Master at &minus;0.5 dB, not &minus;30</span></li>
<li><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><rect class="box" x="2.2" y="2.6" width="11.6" height="11.6" rx="2.4"/></svg></span><span><span class="badge">you</span>A/B two moved crush buses</span></li>
<li><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><rect class="box" x="2.2" y="2.6" width="11.6" height="11.6" rx="2.4"/></svg></span><span><span class="badge">you</span>Master chain: limiter the only guard</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Driver up; 1077 silent failures explained</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>v3 map moved into the grid</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span><span class="mono">surface-state.json</span> live, first time ever</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span><span class="mono">midimon</span> now reads true</span></li>
</ul>
</article>
<article class="card">
<h2>Stage</h2>
<p class="figure">40%</p>
<p class="mood">Readable in daylight now. Still untested.</p>
<div class="bar" role="img" aria-label="stage 40 percent"><div class="fill low" style="width:40%"></div></div>
<h2>Observability <span class="step">5</span></h2>
<p class="figure near">90%</p>
<p class="mood">The lies are out of the checks.</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 width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>QjackCtl in the tray, pw-jack wrapped</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>One gate proves the set and the machine</span></li>
<li><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><rect class="box" x="2.2" y="2.6" width="11.6" height="11.6" rx="2.4"/></svg></span><span>0 of 28 headphone fold links</span></li>
<li><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><rect class="box" x="2.2" y="2.6" width="11.6" height="11.6" rx="2.4"/></svg></span><span>Docker off, Pulsar launch unchecked</span></li>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="tick" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>midiviz: sun theme, corner grabs, tray</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>MIDI log binds at last, 9 dead sessions</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Gate no longer skips the painter</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Dead-sink detector added</span></li>
<li class="did"><span class="mark"><svg class="ic"><use href="#i-tick"/></svg></span><span>Stale-build detector added</span></li>
</ul>
</article>
<article class="card gate">
<h2>Rehearsal &middot; critical path</h2>
<p class="figure">40%</p>
<p class="mood">The only thing left that counts.</p>
<div class="bar" role="img" aria-label="rehearsal 40 percent"><div class="fill low" style="width:40%"></div></div>
<h2>Rehearsal <span class="step">6</span> <span class="badge">you</span></h2>
<p class="figure">0%</p>
<p class="mood">The critical path. Yours alone.</p>
<div class="bar pending" role="img" aria-label="rehearsal 0 percent, not started"></div>
<ul>
<li class="did"><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path class="half" d="M2.4 9.2 C4.1 10.4 5.2 11.7 6.1 13.4"/><path class="half-rest" d="M6.1 13.4 C8.2 8.4 10.6 5.1 14 2.6"/></svg></span><span>Tracks touched tonight, not a full run</span></li>
<li><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><rect class="box" x="2.2" y="2.6" width="11.6" height="11.6" rx="2.4"/></svg></span><span><span class="badge">you</span>Run the 2h arc, Ardour recording</span></li>
<li><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><rect class="box" x="2.2" y="2.6" width="11.6" height="11.6" rx="2.4"/></svg></span><span>Tempo ramp across movements untried</span></li>
<li><span class="mark"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><rect class="box" x="2.2" y="2.6" width="11.6" height="11.6" rx="2.4"/></svg></span><span>Transitions unrehearsed</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span><span class="badge">you</span>2h arc, never once run</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span>Ardour recording all orbits</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span>UMC plugged for the last stretch</span></li>
<li><span class="mark"><svg class="ic"><use href="#i-open"/></svg></span><span>xruns re-measured clean</span></li>
</ul>
</article>
......@@ -379,32 +372,31 @@
<ol>
<li>
<label>
<input type="checkbox" data-move="press">
<input type="checkbox" data-move="arc2h">
<span class="num"></span>
<span class="mtext"><b>Press the launcher once, from the icon</b><span class="why">Starts SC, warms 132 banks, runs 39 checks</span></span>
<span class="mtext"><b>Run the 2h arc, Ardour recording</b><span class="why">Unblocks everything</span></span>
</label>
</li>
<li>
<label>
<input type="checkbox" data-move="run2h2">
<input type="checkbox" data-move="umc">
<span class="num"></span>
<span class="mtext"><b>Run the full 2h with Ardour recording</b><span class="why">The only test that counts</span></span>
<span class="mtext"><b>Plug the UMC for the last stretch</b><span class="why">The only untested path</span></span>
</label>
</li>
<li>
<label>
<input type="checkbox" data-move="crush">
<input type="checkbox" data-move="push">
<span class="num"></span>
<span class="mtext"><b>A/B the two moved crush buses</b><span class="why">Bass and melody stop trading</span></span>
<span class="mtext"><b><span class="mono">ssh-add</span>, then the push lands</b><span class="why">1 commit waiting</span></span>
</label>
</li>
</ol>
</section>
<footer>
<p class="path">Critical path: <b>one launch</b> &rarr; <b>2h rehearsal</b> &rarr; <b>doors</b></p>
<p class="tiny">Not this project: mastering (tidal-ears), Opal setlist.</p>
<p class="tiny">Companion: <a href="/setbuilder.html">Set Builder</a></p>
<p class="path"><b>SET</b> &rarr; <b>CORPUS</b> &rarr; <b>AUDIO</b> &rarr; <b>SURFACE</b> &rarr; <b>OBSERVABILITY</b> &rarr; <b>REHEARSAL</b> &mdash; only the last one is still open.</p>
<p class="tiny">Not this project: post-gig cleanup (11 items), corpus-wide remap, Ardour OSC.</p>
</footer>
</div>
......@@ -416,65 +408,69 @@
var WD = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];
function midnight(d){ return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime(); }
/* "2026-09-24" or "2026-09-24T21:00" -> a local-wall-clock Date, never UTC-shifted. */
/* "2026-09-24" or "2026-09-24T19:00" -> a local-wall-clock Date, never UTC-shifted. */
function parseDt(s){
var m = String(s).split("T");
var p = m[0].split("-");
var t = (m[1] || "00:00").split(":");
return new Date(+p[0], +p[1] - 1, +p[2], +t[0], +t[1] || 0, 0, 0);
}
/* Whole calendar days from a to b: positive when b is in the future. */
/* Whole calendar days from a to b: positive when b is in the future.
Calendar days, not elapsed hours, so "tomorrow evening" cannot flip at 18:00 tonight. */
function daysBetween(a, b){ return Math.round((midnight(b) - midnight(a)) / MS); }
var GIG = parseDt("2026-09-24");
/* Timeline stops, in DOM order. Dates live here only, never typed into the markup. */
var STOPS = [
{ dt: "2026-09-22", what: "locked" },
{ dt: "2026-09-23", what: "prep" },
{ dt: "2026-09-24", what: "goal", finale: true }
];
/* Doors, as the evening the set is announced for. */
var GIG = parseDt("2026-09-24T19:00");
/* Days-to-gig, signed. The phrase layer is what refuses to render a negative. */
function gigDays(now){ return daysBetween(now, GIG); }
/* The one live string in the header. Never negative, never "in -1 days". */
/* The one live phrase in the header. Never negative, never "in 1 days". */
function gigPhrase(now){
var d = gigDays(now);
if (d > 1) return "gig in " + d + " days";
if (d === 1) return "gig tomorrow";
if (d === 0) return "gig today";
return "gig played";
if (d === 1) return "gig tomorrow evening";
if (d === 0){
var h = Math.floor((GIG.getTime() - now.getTime()) / 3600000);
if (h >= 1) return "tonight &middot; " + h + "h to doors";
if (GIG.getTime() - now.getTime() > 0) return "doors within the hour";
return "on stage now";
}
if (d === -1) return "played last night";
return "played " + (-d) + " days ago";
}
function dayLabel(d){ return WD[d.getDay()] + " " + d.getDate() + " Sept"; }
/* Per-stop countdown. Past tense for days gone, plain words for days ahead. */
function stopPhrase(now, target, finale){
var d = daysBetween(now, target);
if (d > 1) return "in " + d + " days";
if (d === 1) return "tomorrow";
if (d === 0) return finale ? "doors today" : "today";
if (d === 1) return finale ? "tomorrow evening" : "tomorrow";
if (d === 0) return finale ? "doors tonight" : "today";
if (d === -1) return "yesterday";
return (-d) + " days ago";
}
/* Stops in DOM order. Dates live here only, never typed into the markup. */
var STOPS = [
{ dt: "2026-09-23", finale: false },
{ dt: "2026-09-24T19:00", finale: true }
];
/* Pure: every string the page shows, derived from one instant. Testable on its own. */
function phrases(now){
var out = {
gig: gigPhrase(now),
gigDay: dayLabel(GIG),
days: gigDays(now),
stops: []
};
var out = { gig: gigPhrase(now), gigDay: dayLabel(GIG), days: gigDays(now), stops: [] };
for (var i = 0; i < STOPS.length; i++){
var s = STOPS[i];
var d = parseDt(s.dt);
var delta = daysBetween(now, d);
out.stops.push({
day: dayLabel(d),
cd: stopPhrase(now, d, !!s.finale),
cd: stopPhrase(now, d, s.finale),
sameDay: delta === 0,
past: delta < 0
});
}
/* "Today" belongs to whichever stop the clock says it is; past the gig, nobody wears it. */
return out;
}
......@@ -485,41 +481,48 @@
};
var fails = [];
/* The three dates the page must survive, plus the day after. */
[["2026-09-22",2],["2026-09-23",1],["2026-09-24",0],["2026-09-25",-1],["2026-09-20",4]]
[["2026-09-21",3],["2026-09-23",1],["2026-09-24",0],["2026-09-25",-1],["2026-09-30",-6]]
.forEach(function(c){
var got = gigDays(parseDt(c[0]));
if (got !== c[1]) fails.push("gigDays " + c[0] + " expected " + c[1] + ", got " + got);
});
[["2026-09-22T09:00","gig in 2 days"],
["2026-09-22T23:59","gig in 2 days"],
["2026-09-23T07:30","gig tomorrow"],
["2026-09-24T00:05","gig today"],
["2026-09-24T23:30","gig today"],
["2026-09-25T10:00","gig played"],
["2026-10-02T10:00","gig played"]].forEach(function(c){
[["2026-09-21T09:00","gig in 3 days"],
["2026-09-23T00:05","gig tomorrow evening"],
["2026-09-23T18:00","gig tomorrow evening"],
["2026-09-23T23:59","gig tomorrow evening"],
["2026-09-24T09:00","tonight &middot; 10h to doors"],
["2026-09-24T18:00","tonight &middot; 1h to doors"],
["2026-09-24T18:30","doors within the hour"],
["2026-09-24T19:00","on stage now"],
["2026-09-24T21:30","on stage now"],
["2026-09-25T10:00","played last night"],
["2026-09-28T10:00","played 4 days ago"]].forEach(function(c){
var got = gigPhrase(parseDt(c[0]));
if (got !== c[1]) fails.push("gigPhrase " + c[0] + " expected " + c[1] + ", got " + got);
});
/* Every stop's own countdown, on the day before, the day of, and the day after the gig. */
[["2026-09-23T10:00",["yesterday","today","tomorrow"]],
["2026-09-24T10:00",["2 days ago","yesterday","doors today"]],
["2026-09-25T10:00",["3 days ago","2 days ago","yesterday"]],
["2026-09-22T10:00",["today","tomorrow","in 2 days"]]].forEach(function(c){
[["2026-09-23T10:00",["today","tomorrow evening"]],
["2026-09-24T10:00",["yesterday","doors tonight"]],
["2026-09-26T10:00",["3 days ago","2 days ago"]],
["2026-09-21T10:00",["in 2 days","in 3 days"]]].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("|"));
});
/* Weekdays are derived, never typed: 22 Sept 2026 is a Tuesday, 24 Sept a Thursday. */
[["2026-09-22",2],["2026-09-23",3],["2026-09-24",4]].forEach(function(c){
/* Weekdays are derived, never typed: 23 Sept 2026 is a Wednesday, 24 Sept a Thursday. */
[["2026-09-23",3],["2026-09-24",4]].forEach(function(c){
var got = parseDt(c[0]).getDay();
if (got !== c[1]) fails.push(c[0] + " weekday expected " + c[1] + ", got " + got);
});
/* Month rollover must not break the day count. */
if (daysBetween(parseDt("2026-08-31"), parseDt("2026-09-01")) !== 1) fails.push("month rollover");
if (STOPS.length !== 3) fails.push("STOPS length changed without updating the tests");
/* No phrase may ever contain a stale-wording artefact, on any hour of any nearby day. */
var BAD = /(\bin -|\bin 0\b|\bin 1 days\b|-1|NaN|undefined)/;
for (var probe = parseDt("2026-09-18").getTime(); probe <= parseDt("2026-09-30").getTime(); probe += 3600000){
/* Exactly one stop may claim today, and only while the gig is not yet past. */
[["2026-09-23T10:00",1],["2026-09-24T10:00",1],["2026-09-25T10:00",0],["2026-09-21T10:00",0]]
.forEach(function(c){
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);
});
if (STOPS.length !== 2) fails.push("STOPS length changed without updating the tests");
/* No phrase may carry a stale-wording artefact, on any hour of any nearby day. */
var BAD = /(\bin -|\bin 0\b|\bin 1 days\b|\b-\d|NaN|undefined)/;
for (var probe = parseDt("2026-09-15").getTime(); probe <= parseDt("2026-10-05").getTime(); probe += 3600000){
var pp = phrases(new Date(probe));
var strs = [pp.gig, pp.gigDay];
for (var q = 0; q < pp.stops.length; q++) strs.push(pp.stops[q].cd, pp.stops[q].day);
......@@ -529,29 +532,28 @@
}
if (fails.length){
if (typeof console !== "undefined") console.error("DATE-MATH CHECK FAILED:\n " + fails.join("\n "));
throw new Error("date-math check failed (" + fails.length + ")");
} else if (typeof console !== "undefined") {
console.log("date-math check: PASS");
}
if (typeof console !== "undefined") console.log("date-math check: PASS");
window.gigClock.selfTest = fails;
})();
</script>
<script>
(function(){
var C = window.gigClock;
var p = C.phrases(new Date());
function put(id, text){ var el = document.getElementById(id); if (el) el.textContent = text; }
function put(id, html){ var el = document.getElementById(id); if (el) el.innerHTML = html; }
put("c-gig", p.gig);
put("c-gig-day", p.gigDay);
var mine = document.querySelectorAll(".buckets .badge").length;
put("c-yours", mine + (mine === 1 ? " item" : " items"));
var stops = document.querySelectorAll(".track .stop");
var todayEl = null;
for (var i = 0; i < stops.length && i < p.stops.length; i++){
var s = stops[i], q = p.stops[i];
var day = s.querySelector(".day"); if (day) day.textContent = q.day;
var cd = s.querySelector(".cd"); if (cd) cd.textContent = q.cd;
if (q.sameDay) todayEl = s;
var cd = s.querySelector(".cd"); if (cd) cd.innerHTML = q.cd;
if (q.sameDay && !todayEl) todayEl = s;
}
/* One ribbon only, on the stop the clock says is today. */
if (todayEl){
......@@ -564,7 +566,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.0922c";
var KEY = "pv.gig.thu24.moves.0923a";
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