Skip to content

Fix editor state isolation and improve rotation robustness - #32

Merged
hyperion-001 merged 1 commit into
mainfrom
claude/sillytavern-roulette-release-byri0z
Aug 19, 2026
Merged

Fix editor state isolation and improve rotation robustness#32
hyperion-001 merged 1 commit into
mainfrom
claude/sillytavern-roulette-release-byri0z

Conversation

@hyperion-001

Copy link
Copy Markdown
Contributor

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

  • Removed the legacy openQueueEditor() popup path; renderEditorInto() is now the sole editor entry point
  • Editor now works on a structuredClone() of the input queue, ensuring Cancel is completely side-effect-free
  • Deferred managed preset cleanup to the Save path: removed slots are collected during editing and only deleted after upsertQueue() succeeds, so a cancelled edit leaves ST untouched
  • Added removedSlots tracking to buildEditorElement() return value

Rotation Edge Cases

  • Fixed non-streaming regenerate type coercion: lastGenerationType now records the actual type each GENERATION_STARTED carried, and MESSAGE_RECEIVED consults it to catch regens that ST coerces to 'normal'
  • Tightened isCountableGeneration() to a strict whitelist (only undefined/null/'normal'), so unknown future types fail closed
  • Fixed resumeRotation() 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 slot
  • Added failure recovery: skipCurrentSlot() and onGenerationStarted() now clear failure state on success, and halt rotation after 3 consecutive failures instead of continuing with a broken state
  • Removed rerollCurrentSlotCounter() export (unused; counter is now managed correctly at activation and resume)

Preset Management

  • ensureManagedPreset() no longer passes skipUpdate: true to savePreset(), ensuring the preset is registered in ST's in-memory list so /preset exact-matching and cleanup existence checks work correctly
  • cleanupManagedPresetForSlot() now returns a boolean indicating success, and handles profile-not-found cases by searching all API preset managers

Import/Export Safety

  • Imported queues now always reissue slot IDs to prevent preset name collisions with existing queues
  • Imported slot tuning presets are cleared (presetName: null) so cleanup cannot target unowned presets on the importing install

UI Improvements

  • Added applyInputDragGuards() helper to prevent form controls from triggering row drag
  • Fixed slot removal to splice by identity instead of render-time index, handling prior removals correctly
  • Dot strip now includes window position in its fingerprint key, so long queues that scroll the active slot in/out of view trigger a rebuild
  • Added confirmDialog() utility for themed confirmation dialogs (replaces native confirm())
  • Queue card now pluralizes "slot/slots" correctly
  • Bar position tracking now records the actual position where the bar landed, not the configured preference, so CSS styles the real DOM location

Validation & Robustness

  • validateQueue() now rejects non-numeric count fields regardless of countMode, catching hostile values in inactive fields before they reach the DOM
  • Fixed number input rendering to use numOr() helper consistently, preventing null/undefined from appearing in form values
  • /roulette-status command now emits a system message so users see the output when typed bare in chat

Documentation & Cleanup

  • Updated CLAUDE.md to document the regenerate type coercion trap and strict whitelist behavior
  • Removed debug logging from index.js
  • Cleaned up FUNDING.yml to list only active sponsor link
  • Updated README with clearer install instructions and changed banner from PNG to JPG
  • Fixed CSS custom property inheritance: token definitions now target both .roulette-extension and .popup:has(.roulette-extension) so popup-hosted surfaces resolve variables correctly
  • Added currentProfileName() export to profileSwitcher.js for profile divergence detection

Testing Notes

  • Queue editor Cancel now leaves no side effects (no preset deletions, no state mutations)
  • Regenerates (both streaming and non-streaming) no longer consume rotation slots
  • Long queues (>20 slots) now scroll the

https://claude.ai/code/session_01XDAj3aSdkvffKR6ZetqcyV

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
@hyperion-001
hyperion-001 merged commit e5b3d79 into main Aug 19, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants