🌱 lifestrument v1.0.1 — post-ship review fixes (fable)

Three finds Opus's pre-ship review missed, all verified in-browser:
the heart-connect button was unclickable (pointer-events inherited
none from #readout), orb taps rang the 2.5s Moment bell per tap (now
a short pluck via the newly exposed Synth.pluck), and Game Day's
asleep copy leaked --sports jargon to civilians. Plus the Grimoire
now names the gentle door, and the brief gains §14: review verdict
and the v1.1 punch list (hub Cache-Control for .html, manual section,
knob double-tap, per-vibe info sheets, PWA manifest, wake lock).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-12 14:52:04 +10:00
parent 09abd33901
commit 232475e0ce
3 changed files with 49 additions and 4 deletions

View File

@ -246,3 +246,45 @@ Natal/birthday integration (needs the chart caster), a shareable "postcard"
image of your session, preset remixing/saving, Lifestrument-specific accounts or image of your session, preset remixing/saving, Lifestrument-specific accounts or
theming per user, Apple Watch workarounds, camera-based pulse detection, any theming per user, Apple Watch workarounds, camera-based pulse detection, any
server-side anything. server-side anything.
---
## 14. v1.1 notes — post-ship review (Fable, 2026-07-12)
**Verdict on the v1 build**: faithful to the brief and good. One deliberate,
sensible deviation — the default Vibe is Daylight (instant sound from world
data) rather than Heartbeat (silent until a tap/strap); keep it. The
understand→build→adversarial-review process caught 21 real issues pre-ship;
this review caught three more post-ship, all fixed:
- `#heartbtn` was unclickable — it lives inside `#readout`
(`pointer-events: none`, inherited). Fixed with `pointer-events: auto` on
`.on`. Lesson for future UI inside `#readout`: opt back in explicitly.
- Orb taps rang the 2.5s Moment bell per tap (4 taps to set tempo = 4
overlapping bells). `Synth.pluck(midi, vel)` is now exposed; taps pluck.
- Game Day's asleep copy leaked `--sports` CLI jargon; now says "ask the
keeper to open it". House rule: no flags, keys, or code words in
Lifestrument copy — civilians only.
- The Grimoire (index.html, after the zen bullet) now mentions Lifestrument as
"the gentle door". Keep that in sync if the URL or pitch changes.
**Follow-ups worth doing (v1.1, small):**
1. **Hub `Cache-Control`** — the hub serves HTML with no cache headers, so
browsers heuristically cache; after every deploy some users run a stale
page until a hard refresh (bit us during this very review). Add
`Cache-Control: no-cache` for `.html` responses in hub.py's HTTP handler.
Tiny, safe, helps godstrument too. (The v1 "zero server changes" rule was
for the build, not forever.)
2. **Manual** — GODSTRUMENT_MANUAL_SOURCE.md needs a Lifestrument section; a
background task may already be adding Earth Echo — check before editing to
avoid a collision.
3. **Knob double-tap** resets to 50%.
4. **Per-vibe info sheet** — long-press a vibe card → one honest paragraph on
what it listens to (the data-honesty ethos, one tap deeper).
5. **PWA manifest + icon** so phones can install it (needs a small manifest
file next to life.html; the single-file rule can bend for a manifest).
6. **Screen Wake Lock** while playing (optional; audio survives screen-off,
but the orb doesn't).
**v2 list in §13 stands.** If sports monetizes, remember `sport.goal`
(see the sports-source memory) would make Game Day genuinely great.

View File

@ -1026,6 +1026,7 @@
<li><b>Press <kbd>P</kbd> — performance mode.</b> Everything that is not the world vanishes: the wordmark, the header, the counts, the godtime readout, the ticker, every button and chip — <i>pure visuals</i>, with only a rotating line of dispatches from the world (<i>bitcoin at $62,902 · the moon is 81% lit · mercury is retrograde · M4.2 earthquake</i>) rising for the audience. Put the console on the projector, hit <kbd>P</kbd>, and conduct. (Want even the labels and dispatches gone? <kbd>Z</kbd> is zen — nothing but the sky.)</li> <li><b>Press <kbd>P</kbd> — performance mode.</b> Everything that is not the world vanishes: the wordmark, the header, the counts, the godtime readout, the ticker, every button and chip — <i>pure visuals</i>, with only a rotating line of dispatches from the world (<i>bitcoin at $62,902 · the moon is 81% lit · mercury is retrograde · M4.2 earthquake</i>) rising for the audience. Put the console on the projector, hit <kbd>P</kbd>, and conduct. (Want even the labels and dispatches gone? <kbd>Z</kbd> is zen — nothing but the sky.)</li>
<li><b>Press <kbd>W</kbd> — the sky wheel.</b> The zodiac rises behind the whole instrument, huge and faint, drawn the way a natal chart is drawn: a <b>sign band</b> with the twelve glyphs in their element colours and degree ticks running the rim, the <b>living planets</b> just inside it exactly where they stand tonight, the sky's <b>aspects strung as chords</b> across the wheel — red for the hard angles, green for the soft, gold for the meetings — and <b>the sign the Sun is walking right now lit in its own colour</b> (Cancer in early July), so one glance says where in the year you are. Cast a chart (✨ your stars) and the layer deepens: your <b>whole-sign houses</b> divide the wheel, the ASC and MC stand in gold, your natal planets sit as gold ghosts on the inner ring, and transit chords strike them as the sky moves. The same wheel the zodiac skin draws, now living under the main view. <kbd>W</kbd> again to put it away; it remembers.</li> <li><b>Press <kbd>W</kbd> — the sky wheel.</b> The zodiac rises behind the whole instrument, huge and faint, drawn the way a natal chart is drawn: a <b>sign band</b> with the twelve glyphs in their element colours and degree ticks running the rim, the <b>living planets</b> just inside it exactly where they stand tonight, the sky's <b>aspects strung as chords</b> across the wheel — red for the hard angles, green for the soft, gold for the meetings — and <b>the sign the Sun is walking right now lit in its own colour</b> (Cancer in early July), so one glance says where in the year you are. Cast a chart (✨ your stars) and the layer deepens: your <b>whole-sign houses</b> divide the wheel, the ASC and MC stand in gold, your natal planets sit as gold ghosts on the inner ring, and transit chords strike them as the sky moves. The same wheel the zodiac skin draws, now living under the main view. <kbd>W</kbd> again to put it away; it remembers.</li>
<li><b>Press <kbd>Z</kbd> — zen.</b> Every word vanishes: labels, readouts, panels, the header, the ticker, the dial — all of it — and only the visuals remain, full screen. The spheres, the cables, the shockwaves, the sky wheel if it's up, still moving to the world; nothing left to read, everything left to watch. <kbd>Z</kbd> or <kbd>esc</kbd> brings the words back.</li> <li><b>Press <kbd>Z</kbd> — zen.</b> Every word vanishes: labels, readouts, panels, the header, the ticker, the dial — all of it — and only the visuals remain, full screen. The spheres, the cables, the shockwaves, the sky wheel if it's up, still moving to the world; nothing left to read, everything left to watch. <kbd>Z</kbd> or <kbd>esc</kbd> brings the words back.</li>
<li><b>🌱 Lifestrument — the gentle door.</b> The same hub, a second instrument: <a href="/life.html" style="color:#9fd0ff">godstrument.pro/life.html</a> plays only the sources about <i>living</i> — your pulse, the weather, the sun going down, a baby born somewhere — as eight one-tap <b>Vibes</b> with two knobs and no cables at all. Made for a phone in one hand and a friend who doesn't want a manual. This console is where you build; that one is where you feel. (Right-click the sky → 🌱 lifestrument.)</li>
<li><b>The keyboard is yours — <kbd>F1</kbd><kbd>F10</kbd> and beyond.</b> A default set of function keys is ready to fly: <kbd>F1</kbd> the grimoire, <kbd>F2</kbd> play/stop the synth, <kbd>F3</kbd> the skin view, <kbd>F4</kbd> next skin, <kbd>F5</kbd> cycle mood, <kbd>F6</kbd> tracks, <kbd>F7</kbd> performance mode, <kbd>F8</kbd> lock the godtime, <kbd>F9</kbd> the earth echo, <kbd>F10</kbd> the stage. But every key is remappable: right-click the sky → <b>⌨ keys</b> to bind any key to any action, or <b>map a key</b> from a voice's own right-click menu. Want <kbd>a</kbd> and <kbd>s</kbd> to raise and lower a drone's level like a fader? Right-click that voice → <i>map a key → raise its level</i>, press <kbd>a</kbd>; again for <i>lower</i>, press <kbd>s</kbd> — now they nudge it live, hold to sweep. Your whole flow, under your fingers.</li> <li><b>The keyboard is yours — <kbd>F1</kbd><kbd>F10</kbd> and beyond.</b> A default set of function keys is ready to fly: <kbd>F1</kbd> the grimoire, <kbd>F2</kbd> play/stop the synth, <kbd>F3</kbd> the skin view, <kbd>F4</kbd> next skin, <kbd>F5</kbd> cycle mood, <kbd>F6</kbd> tracks, <kbd>F7</kbd> performance mode, <kbd>F8</kbd> lock the godtime, <kbd>F9</kbd> the earth echo, <kbd>F10</kbd> the stage. But every key is remappable: right-click the sky → <b>⌨ keys</b> to bind any key to any action, or <b>map a key</b> from a voice's own right-click menu. Want <kbd>a</kbd> and <kbd>s</kbd> to raise and lower a drone's level like a fader? Right-click that voice → <i>map a key → raise its level</i>, press <kbd>a</kbd>; again for <i>lower</i>, press <kbd>s</kbd> — now they nudge it live, hold to sweep. Your whole flow, under your fingers.</li>
<li><b>Press <kbd>C</kbd> — chakra view.</b> The sources leave their column and take up their stations on the subtle body: seven wheels on a spine of light, crown to root, each feed orbiting the chakra it belongs to — the sky's machinery at the crown, the omens at the third eye, the species talking at the throat, weather and air at the heart, fire and money at the solar plexus, birth and water at the sacral, quakes and debt and the mortal weight at the root. Each wheel glows with the live average of its members and spins its traditional petals, counter-rotating wheel to wheel. <b>Drag empty space to spin the whole body 360°</b>, drag up/down to tilt, flick and let momentum carry it. Everything still works in there: click a node to mute it, drag it onto a voice to patch, shift-click for its tab. <kbd>C</kbd> again and the sources glide home.</li> <li><b>Press <kbd>C</kbd> — chakra view.</b> The sources leave their column and take up their stations on the subtle body: seven wheels on a spine of light, crown to root, each feed orbiting the chakra it belongs to — the sky's machinery at the crown, the omens at the third eye, the species talking at the throat, weather and air at the heart, fire and money at the solar plexus, birth and water at the sacral, quakes and debt and the mortal weight at the root. Each wheel glows with the live average of its members and spins its traditional petals, counter-rotating wheel to wheel. <b>Drag empty space to spin the whole body 360°</b>, drag up/down to tilt, flick and let momentum carry it. Everything still works in there: click a node to mute it, drag it onto a voice to patch, shift-click for its tab. <kbd>C</kbd> again and the sources glide home.</li>
<li><b>Right-click anything — the everything menu.</b> Every node carries its full settings under the right button: mute, <b>solo</b>, a <b>level</b> fader (02×), a <b>trim</b> offset, every cable it carries with its amount slider and an unpatch ×, and a nested <i>patch to →</i> list for laying new cables without dragging. Right-click the empty sky for the view menu. And the modifiers, for the fast hands: <b>click</b> mutes · <b>⇧-click</b> opens the node's tab · <b>⇧-drag</b> rides the level up and down · <b>⌥-click</b> solos a source · <b>⌘-click</b> multi-selects for grouping · <b>⌘⇧-click</b> resets a node to neutral. People play differently; the instrument shouldn't care which hand you reach with.</li> <li><b>Right-click anything — the everything menu.</b> Every node carries its full settings under the right button: mute, <b>solo</b>, a <b>level</b> fader (02×), a <b>trim</b> offset, every cable it carries with its amount slider and an unpatch ×, and a nested <i>patch to →</i> list for laying new cables without dragging. Right-click the empty sky for the view menu. And the modifiers, for the fast hands: <b>click</b> mutes · <b>⇧-click</b> opens the node's tab · <b>⇧-drag</b> rides the level up and down · <b>⌥-click</b> solos a source · <b>⌘-click</b> multi-selects for grouping · <b>⌘⇧-click</b> resets a node to neutral. People play differently; the instrument shouldn't care which hand you reach with.</li>

View File

@ -66,7 +66,7 @@
background: rgba(var(--accent),0.14); border: 1px solid rgba(var(--accent),0.5); background: rgba(var(--accent),0.14); border: 1px solid rgba(var(--accent),0.5);
padding: 9px 16px; border-radius: 999px; cursor: pointer; min-height: 40px; font-family: inherit; padding: 9px 16px; border-radius: 999px; cursor: pointer; min-height: 40px; font-family: inherit;
} }
#heartbtn.on { display: inline-flex; align-items: center; } #heartbtn.on { display: inline-flex; align-items: center; pointer-events: auto; } /* #readout is pointer-events:none; the button must opt back in */
/* vibe strip */ /* vibe strip */
#vibes { #vibes {
@ -412,7 +412,7 @@
wires: [ { src: "sport.inplay", param: "density", amt: 0.9, curve: "scurve" }, wires: [ { src: "sport.inplay", param: "density", amt: 0.9, curve: "scurve" },
{ src: "sport.events_today", param: "droneLevel", amt: 0.4, curve: "lin" } ], { src: "sport.events_today", param: "droneLevel", amt: 0.4, curve: "lin" } ],
fact: () => { const ip = sources.get("sport.inplay"); return ip && ip.hist.length fact: () => { const ip = sources.get("sport.inplay"); return ip && ip.hist.length
? Math.round(ip.raw) + " matches in play right now" : "the stadium sleeps — ask the keeper for --sports" } }, ? Math.round(ip.raw) + " matches in play right now" : "the stadium sleeps — ask the keeper to open it" } },
{ id: "room", emoji: "🎤", name: "The Room", promise: "your space sings back", { id: "room", emoji: "🎤", name: "The Room", promise: "your space sings back",
tint: [200, 150, 240], tempo: 84, wantsVoice: true, tint: [200, 150, 240], tempo: 84, wantsVoice: true,
@ -664,6 +664,7 @@
return true; return true;
}, },
chime, ctx: () => ctx, chime, ctx: () => ctx,
pluck(midi, vel) { if (ctx) pluckAt(ctx.currentTime, midi, vel != null ? vel : 0.12); },
rms() { rms() {
if (!N) return 0; if (!N) return 0;
const buf = new Uint8Array(N.analyser.fftSize); N.analyser.getByteTimeDomainData(buf); const buf = new Uint8Array(N.analyser.fftSize); N.analyser.getByteTimeDomainData(buf);
@ -785,9 +786,10 @@
burst(8); burst(8);
} }
function pluckTap() { function pluckTap() {
const ac = Synth.ctx(); if (!ac) return; // a short pluck, not the 2.5s Moment bell — setting your tempo with four
// taps shouldn't ring four overlapping bells
const steps = SCALES[params.scale] || SCALES.pentatonic; const steps = SCALES[params.scale] || SCALES.pentatonic;
Synth.chime(params.root + 12 + steps[(Math.random() * steps.length) | 0]); Synth.pluck(params.root + 12 + steps[(Math.random() * steps.length) | 0], 0.14);
} }
// =========================================================================== // ===========================================================================