From 9ab1b0f5aeb28ee93419051e37cfed881f748a9b Mon Sep 17 00:00:00 2001 From: type-two Date: Sat, 11 Jul 2026 10:21:03 +1000 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=B9=20health-sweep=20tidy-up:=205=20lo?= =?UTF-8?q?w-severity=20loose=20ends=20(0=20broken=20found)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A 13-agent adversarial whole-app sweep (client controls / runtime / server-auth / new features / feed-config-honesty, each finding reproduced-or-refuted) found ZERO broken functions — verdict healthy. Fixed the reachable low-severity edges it did confirm: - viz/index.html:1804 — arranger 'grooves' tab threw TypeError (nSel.isNote) when dests is empty in the cold-connect window (post-sign-in, pre-WS-hello). Guard: dests.get(arrSelKey) || {isNote:false} (the 'clips' tab already tolerates empty). - viz/index.html — picking a skin from the sky menu / F4 next-skin / F4 prev while in ZERO mode force-enabled an invisible, un-toggleable chakraMode (every clear path is zeroMode-guarded). Added the same !zeroMode guard to all three sites (byte-identical outside zero — no regression). - hub.py:117 — GODSTRUMENT_READONLY parse treated any non-empty non-'0' value as true (so 'false'/'no'/'off' enabled read-only). Now an explicit truthy set {1,true,yes,on}; prod uses '1' → stays read-only (verified before deploy). - viz/index.html:2996 — removed a dead else branch in zero 'save as vibe' (its 'vibes land in the next update' placeholder was unreachable; saveVibe is a real sibling method) → call this.saveVibe(gk) directly. - transform.py:40 — collapsed a no-op ternary (both branches float(v)). - viz/manual.html — regenerated (was stale vs build_manual.py; picked up the admin-panel CSS added in 764e28b; grimoire prose was already in sync). Left as documented cosmetic: sanitizeVibe caps emoji at 2 code points (can split a ZWJ/flag glyph on a shared vibe — display only, no XSS). Verified: node --check, all .py compile, auth --selftest green, app boots into zero clean, console clean. Co-Authored-By: Claude Opus 4.8 --- hub.py | 2 +- transform.py | 2 +- viz/index.html | 10 +++++----- viz/manual.html | 19 +++++++++++++++++++ 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/hub.py b/hub.py index b110ca1..f6502d0 100644 --- a/hub.py +++ b/hub.py @@ -115,7 +115,7 @@ class Hub: # (the ws control channel has no auth — safe to expose publicly this way). # Set via config "readonly": true or env GODSTRUMENT_READONLY=1. self.readonly = bool(cfg.get("readonly")) or \ - os.environ.get("GODSTRUMENT_READONLY", "") not in ("", "0") + os.environ.get("GODSTRUMENT_READONLY", "").strip().lower() in ("1", "true", "yes", "on") self.signals: dict[str, Signal] = {} self._raw_lock = threading.Lock() diff --git a/transform.py b/transform.py index 572759e..92ada12 100644 --- a/transform.py +++ b/transform.py @@ -37,7 +37,7 @@ class Tweak: if spec: for k, v in spec.items(): if k in self.p: - self.p[k] = float(v) if not isinstance(v, bool) else float(v) + self.p[k] = float(v) self._held: dict[str, float] = {} # freeze holds, keyed by signal name self._lag: dict[str, float] = {} # smoothing state, keyed by signal name self.orbit_lfo = 1.0 # external planetary-orbit gain (hub-set) diff --git a/viz/index.html b/viz/index.html index fd97594..3291371 100644 --- a/viz/index.html +++ b/viz/index.html @@ -1800,7 +1800,7 @@ pick.appendChild(lb); } const hint2 = document.createElement("span"); hint2.className = "albl"; - const nSel = dests.get(arrSelKey); + const nSel = dests.get(arrSelKey) || { isNote: false }; // dests can be empty in the cold-connect window (pre-hello) hint2.textContent = nSel.isNote ? "piano roll · " + NOTE_NAMES[gScale.root] + " " + gScale.name.replace("_", " ") + " · click to place · stack 2+ in a column → plays one at random" : "16-step gate · lit steps let the world's signal through"; @@ -2993,7 +2993,7 @@ unp.onclick = () => { for (const src of node.members) this._dropSource(src); this.refreshSockets(); this.persist(); m.remove(); }; m.appendChild(unp); const vibe = document.createElement("div"); vibe.textContent = "💾 save as vibe"; vibe.style.cssText = "color:#9fd0ff;cursor:pointer;margin:5px 0"; - vibe.onclick = () => { m.remove(); if (this.saveVibe) this.saveVibe(gk); else eventTicker.unshift({ text: "💾 vibes land in the next update — build a feeling, save it, hand it to a friend", tAdded: now(), src: "sys" }); }; + vibe.onclick = () => { m.remove(); this.saveVibe(gk); }; m.appendChild(vibe); document.body.appendChild(m); const off = (ev) => { if (!m.contains(ev.target)) { m.remove(); document.removeEventListener("mousedown", off, true); } }; @@ -6580,7 +6580,7 @@ for (const sk of Object.keys(SKINS)) { const it2 = document.createElement("div"); it2.className = "it"; it2.textContent = (SKINS[sk].key === activeSkin.key ? "✓ " : "") + SKINS[sk].label; - it2.onclick = () => { setSkin(sk); if (!chakraMode) { chakraMode = true; clearTrails(); } closeCtx(); }; + it2.onclick = () => { setSkin(sk); if (!chakraMode && !zeroMode) { chakraMode = true; clearTrails(); } closeCtx(); }; skinSub.appendChild(it2); } ctxItem(performMode ? "leave performance mode" : "performance mode", @@ -7170,8 +7170,8 @@ grimoire: { label: "open the grimoire (help)", run: () => { const g = document.getElementById("grimoire"); if (g) g.classList.add("open"); } }, play: { label: "play / stop the synth", run: () => { if (typeof Synth !== "undefined") Synth.toggle(); } }, chakraView:{ label: "chakra / skin view", run: () => { if (zeroMode) return; chakraMode = !chakraMode; if (!chakraMode) { layoutDirty = true; exitMind(); } else clearTrails(); } }, - skinNext: { label: "next skin", run: () => { setSkin(nextSkinKey()); if (!chakraMode) { chakraMode = true; clearTrails(); } } }, - skinPrev: { label: "previous skin", run: () => { setSkin(prevSkinKey()); if (!chakraMode) { chakraMode = true; clearTrails(); } } }, + skinNext: { label: "next skin", run: () => { setSkin(nextSkinKey()); if (!chakraMode && !zeroMode) { chakraMode = true; clearTrails(); } } }, + skinPrev: { label: "previous skin", run: () => { setSkin(prevSkinKey()); if (!chakraMode && !zeroMode) { chakraMode = true; clearTrails(); } } }, moodCycle: { label: "cycle mood (first light / drift / storm)", run: () => cycleMood() }, tracks: { label: "tracks / arranger", run: () => { arr.open ? closeArranger() : openArranger(); } }, perform: { label: "performance mode", run: () => window.dispatchEvent(new KeyboardEvent("keydown", { key: "p" })) }, diff --git a/viz/manual.html b/viz/manual.html index 42f397a..39fa180 100644 --- a/viz/manual.html +++ b/viz/manual.html @@ -351,6 +351,25 @@ padding: 4px 9px; user-select: text; -webkit-user-select: text; transition: background 0.15s; } #account .codechip:hover { background: rgba(124,255,178,0.18); } #account .codechip.copied { color: #fff; border-color: #7cffb2; background: rgba(124,255,178,0.3); } + /* admin — user management */ + #account.wide .box { width: 520px; } + #account .userlist { margin-top: 6px; } + #account .urow { padding: 8px 0; border-bottom: 1px solid rgba(120,150,200,0.12); } + #account .urow .uhead { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; } + #account .urow .uname { color: #eaf1ff; font-size: 13px; font-weight: 600; } + #account .urow .umeta { color: rgba(160,180,210,0.6); font-size: 10.5px; } + #account .urow .ubadge { font-size: 9.5px; letter-spacing: .5px; text-transform: uppercase; padding: 1px 6px; border-radius: 20px; + background: rgba(255,214,92,0.14); color: #ffd65c; border: 1px solid rgba(255,214,92,0.35); } + #account .urow .uact { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; } + #account .urow .ubtn { background: rgba(255,255,255,0.05); color: #b9c8e2; border: 1px solid rgba(120,150,200,0.22); + border-radius: 7px; padding: 4px 9px; cursor: pointer; font-size: 10.5px; } + #account .urow .ubtn:hover { border-color: rgba(150,180,255,0.55); color: #eaf1ff; } + #account .urow .ubtn.danger:hover { border-color: rgba(255,120,120,0.6); color: #ffb3b3; } + #account .urow .uedit { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; } + #account .urow .uedit input { flex: 1 1 130px; min-width: 0; padding: 5px 8px; border-radius: 7px; font: inherit; font-size: 11px; + background: rgba(8,12,24,0.7); color: #eaf1ff; border: 1px solid rgba(120,150,200,0.25); } + #account .urow .umsg { font-size: 10.5px; margin-top: 4px; min-height: 12px; } + #account .urow .umsg .tmp { font-family: monospace; color: #7cffb2; user-select: text; } /* ---- gated landing page (shown until you sign in) ---- */ #landing {