Fix editor state isolation and improve rotation robustness - #32
Merged
Conversation
Scheduler core: - Generation-type filter is now a strict whitelist (undefined/null/'normal'); ST's 'command' (/sendas) and 'extension' message types no longer consume rotation slots, and unknown future types fail closed. - Track the type each GENERATION_STARTED carried: a non-streaming regenerate reaches MESSAGE_RECEIVED coerced to 'normal' (ST deletes the redone message first), so counting now consults the generation's real type. - Mid-rotation 3-strikes recovery actually halts: onGenerationStarted honors tryAdvanceFromFailure's result and stops the rotation instead of leaving a lit bar on a slot whose switches all fail; failure counters reset on every successful switch. - Resume after manual override implements the spec'd divergence branch: resuming on a foreign profile zeroes the counter so the next generation advances and switches instead of counting foreign responses to the slot. - Removed the unused rerollCurrentSlotCounter export. Queue editor and import: - Imported queue JSON could carry an attribute-breakout payload in a countMode-inactive count field (validateQueue only checked the active field; slotRow interpolated all of them raw). Count fields are now validated whenever present and numerically coerced at render. - Slot removal splices by identity (no stale index after rapid clicks) and managed-preset cleanup is deferred to Save, so Cancel is side-effect-free. - Sampler-tuning controls injected after the row's guard pass now get the anti-drag-hijack guard too. - Import reissues slot ids unconditionally and clears tuning.presetName; duplicate clears it as well — two queues can no longer share (and cross-delete) one managed preset. - Removed the dead openQueueEditor popup path. Sampler presets: - savePreset no longer passes skipUpdate: the preset must be in ST's in-memory list or /preset falls into Fuse fuzzy matching (potentially activating an unrelated preset) and cleanup's existence check never sees same-session presets, orphaning them on disk. UI chrome: - --roulette-* tokens are also defined on .popup:has(.roulette-extension): custom properties don't inherit upward, so the popup-chrome override rules computed to unset — the binding-picker dialog rendered transparent (unreadable on light ST themes). - Dot strip folds the rendered window into its fingerprint: queues longer than the variant cap re-window as rotation walks past the edge instead of losing the active capsule. - Ignite flash ends on animationend instead of a wall-clock 700ms, so slow animation speeds no longer cut it short. - /roulette-status posts a visible system message (ST never displays a bare command's pipe value); queue-card start failures surface a toast. - Queue deletion uses a themed Popup confirm; cards say "slots" not the v1.x "chambers"; bar label no longer hashes the 'unassigned' placeholder into a real profile color; bar records the position it actually mounted at. Hygiene: - Dropped the globalThis.__roulette debug global and per-step init logging. - package.json license is the non-deprecated AGPL-3.0-or-later id. - Banner recompressed 2.8MB PNG -> 153KB JPEG (every install clones it). - CLAUDE.md updated to match: whitelist filter, coerced-regenerate guard, microtask flag clearing, full ChatRouletteState shape, resume mechanism, six slash commands, repo tree. - New tests: whitelist fail-closed behavior, count-field validation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XDAj3aSdkvffKR6ZetqcyV
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This release hardens the queue editor's state isolation, fixes several rotation edge cases, and improves the UI's robustness when handling long queues and preset cleanup.
Key Changes
Queue Editor State Isolation
openQueueEditor()popup path;renderEditorInto()is now the sole editor entry pointstructuredClone()of the input queue, ensuring Cancel is completely side-effect-freeupsertQueue()succeeds, so a cancelled edit leaves ST untouchedremovedSlotstracking tobuildEditorElement()return valueRotation Edge Cases
lastGenerationTypenow records the actual type eachGENERATION_STARTEDcarried, andMESSAGE_RECEIVEDconsults it to catch regens that ST coerces to 'normal'isCountableGeneration()to a strict whitelist (onlyundefined/null/'normal'), so unknown future types fail closedresumeRotation()to zero the response counter when the loaded profile diverges from the current slot (per spec §5), preventing stale responses from counting toward the next slotskipCurrentSlot()andonGenerationStarted()now clear failure state on success, and halt rotation after 3 consecutive failures instead of continuing with a broken statererollCurrentSlotCounter()export (unused; counter is now managed correctly at activation and resume)Preset Management
ensureManagedPreset()no longer passesskipUpdate: truetosavePreset(), ensuring the preset is registered in ST's in-memory list so/presetexact-matching and cleanup existence checks work correctlycleanupManagedPresetForSlot()now returns a boolean indicating success, and handles profile-not-found cases by searching all API preset managersImport/Export Safety
presetName: null) so cleanup cannot target unowned presets on the importing installUI Improvements
applyInputDragGuards()helper to prevent form controls from triggering row dragconfirmDialog()utility for themed confirmation dialogs (replaces nativeconfirm())Validation & Robustness
validateQueue()now rejects non-numeric count fields regardless ofcountMode, catching hostile values in inactive fields before they reach the DOMnumOr()helper consistently, preventingnull/undefinedfrom appearing in form values/roulette-statuscommand now emits a system message so users see the output when typed bare in chatDocumentation & Cleanup
index.js.roulette-extensionand.popup:has(.roulette-extension)so popup-hosted surfaces resolve variables correctlycurrentProfileName()export toprofileSwitcher.jsfor profile divergence detectionTesting Notes
https://claude.ai/code/session_01XDAj3aSdkvffKR6ZetqcyV