Skip to content

Fix JAML seed finder for motely-wasm 26 + jaml-ui 4.6 - #2

Merged
OptimusPi merged 4 commits into
masterfrom
claude/balatro-seed-finder-wasm-5xmaup
Sep 7, 2026
Merged

OptimusPi merged 4 commits into
masterfrom
claude/balatro-seed-finder-wasm-5xmaup

Conversation

@OptimusPi

Copy link
Copy Markdown
Owner

What broke

The JAML seed-finder view was dead against current dependencies:

  • jaml-ui (0.14 → 4.6) removed the entire useSearch / JamlSpeedometer / JamlAestheticSelector surface the old JamlView was built on, and also dropped setJamlAssetBaseUrl (still imported in App.tsx).
  • motely-wasm (13 → 26) removed Search.scoreList in favor of the Search.settings(jaml).with…().start(token) builder.
  • The forgotten polyfill: motely-wasm 26 boots in embedded mode, decoding its base64 WASM through Uint8Array.fromBase64. That method is a recent TC39 proposal and is undefined in Node 22 and most shipping browsers, so motely.boot() threw before a single seed was searched.

What this does

  • Bump motely-wasm → ^26.0.0, jaml-ui → ^4.6.0.
  • Add src/modules/uint8Base64Polyfill.ts (guarded — no-ops where the engine is native), imported first in main.jsx so it's present before boot.
  • Rewrite JamlView on the motely-wasm 26 API directly: boot once, subscribe to Search.onScored / Search.onProgress, and drive Search.settings(jaml).withProgressReportIntervalMs().withRandomSearch()|withSequentialSearch().stopAfter().start(token). Uses JamlIde for authoring, a Mantine sidebar for search scope + live speed/progress, and a local cancel token (the package-root CancellationToken export collides between two submodules).
  • Drop the removed setJamlAssetBaseUrl call; import jaml-ui/fonts.css + jaml-ui/jimbo.css.
  • .npmrc with legacy-peer-deps=true for the motely-wasm 26 / jaml-ui peer range.

Single-threaded embedded engine — no SharedArrayBuffer, no COEP, no COI service worker.

Verification

  • npm run build ✓
  • eslint clean on changed files ✓
  • Node smoke test booted the embedded engine through the polyfill and returned scored seeds from a JAML filter (e.g. Y3VFTX89, RL71NQBS) ✓

🤖 Generated with Claude Code

https://claude.ai/code/session_01Jw8Fz76cjSbxhBjhGohfBG


Generated by Claude Code

The JAML view was broken against current deps: jaml-ui dropped useSearch,
JamlSpeedometer, JamlAestheticSelector and setJamlAssetBaseUrl, and
motely-wasm 26 removed Search.scoreList in favor of the settings builder.

- Bump motely-wasm to ^26.0.0 and jaml-ui to ^4.6.0
- Add Uint8Array.fromBase64 polyfill: motely-wasm 26 boots in embedded mode
  through it, and it is undefined in Node 22 and most browsers, so boot()
  would throw before any search ran
- Rewrite JamlView on the motely-wasm 26 API: boot once, subscribe to
  Search.onScored/onProgress, drive Search.settings(jaml).withRandomSearch/
  withSequentialSearch().stopAfter().start(token); JamlIde for authoring,
  a scope/progress sidebar, and live cancellation
- Drop setJamlAssetBaseUrl call and import jaml-ui fonts/jimbo styles
- .npmrc legacy-peer-deps for the motely-wasm 26 / jaml-ui peer range

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jw8Fz76cjSbxhBjhGohfBG
@greptile-apps

greptile-apps Bot commented Sep 7, 2026 •

Copy link
Copy Markdown

Greptile Summary

This PR upgrades the JAML and motely-wasm integrations, replaces the removed search API with a worker-backed builder flow, adds the required base64 compatibility patch, and expands the search controls.

  • Moves motely-wasm execution into a module worker.
  • Adds random, sequential, keyword, aesthetic, and explicit seed-list scopes.
  • Adds deck, stake, cutoff, match-limit, and batching controls.
  • Updates jaml-ui styling and removes obsolete APIs.
  • One cancellation defect remains for long-running searches.

Confidence Score: 4/5

The PR is not yet safe to merge because long-running or unlimited searches cannot be cancelled through the UI.

The search runs synchronously in a worker while cancellation is delivered through that worker’s message queue, so the cancellation handler cannot run until the operation it is meant to interrupt has already returned. All previous review threads were manually resolved.

Files Needing Attention: src/components/blueprint/jamlView/searchWorker.ts, src/components/blueprint/jamlView/JamlView.tsx

Important Files Changed

Filename Overview
src/components/blueprint/jamlView/JamlView.tsx Rebuilds the seed-finder interface around jaml-ui components and delegates searches to a worker, but its Cancel action relies on the blocked worker processing another message.
src/components/blueprint/jamlView/searchWorker.ts Boots and runs motely-wasm off the UI thread and streams results, but its message-based cancellation cannot interrupt synchronous worker execution.
src/modules/uint8Base64Polyfill.ts Provides the narrowly scoped guarded Uint8Array.fromBase64 compatibility method required by embedded motely-wasm.
package.json Upgrades jaml-ui and motely-wasm to the versions targeted by the rewritten integration.
vite.config.js Configures Vite to emit the new worker as an ES module.

Sequence Diagram

sequenceDiagram
    participant U as User
    participant V as JamlView
    participant W as Search Worker
    participant M as motely-wasm
    U->>V: Start search
    V->>W: postMessage(start)
    W->>M: boot()
    W->>M: Search.settings(...).start(token)
    M-->>W: scored/progress events
    W-->>V: scored/progress messages
    U->>V: Cancel
    V->>W: postMessage(cancel)
    Note over W,M: Worker is blocked in synchronous search
    Note over V,W: Cancel message cannot be handled until search returns
Loading

Reviews (4): Last reviewed commit: "Move seed engine to a Web Worker; uncap ..." | Re-trigger Greptile

Comment thread src/components/blueprint/jamlView/JamlView.tsx Outdated
Comment thread src/components/blueprint/jamlView/JamlView.tsx Outdated
Comment thread src/modules/uint8Base64Polyfill.ts Outdated
Comment thread .npmrc
- Boot the engine once on view mount via an ensureBooted() singleton, so
  init errors surface immediately and searches never re-boot (P2, CLAUDE.md
  "boot once on mount")
- Truncate the sample-count and stop-after inputs and mark them integer-only
  so a decimal value can no longer throw in BigInt() and abort the search (P1)
- Trim the base64 polyfill to just fromBase64 (the only method on motely's
  boot path); drop the unused toBase64/setFromBase64 globals (P2, no slop)
- Document in .npmrc exactly which two peer combinations legacy-peer-deps
  covers and when to remove it, instead of silently disabling validation (P2)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jw8Fz76cjSbxhBjhGohfBG

Copy link
Copy Markdown
Owner Author

Thanks @greptile-apps — all four addressed in 325f826:

  • P1 · decimal stop-limit aborts search → both number inputs are now allowDecimal={false} and truncated (Math.trunc) before use, and stopAfter/randomCount are floored at the call site, so BigInt() can't throw.
  • P2 · engine boots during search → boot now happens once on view mount via an ensureBooted() promise-singleton (getStatus()-guarded); the search awaits the same singleton, so it never re-boots, and boot errors surface immediately in the sidebar.
  • P2 · global polyfill exceeds need → trimmed to fromBase64 only (the sole method on motely's embedded-boot path); dropped the unused toBase64/setFromBase64 globals.
  • P2 · peer conflicts silently suppressed → .npmrc now documents exactly which two peer combinations legacy-peer-deps covers (jaml-ui@4.6 ↔ motely-wasm 26, and @json-render/react ↔ React 18) and when to remove it.

npm run build and eslint are green on the new commit.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

deploy check red — not from this PR's diff

The failing check is the deploy job (.github/workflows/main.yml), and it fails at the actions/deploy-pages@v4 step, not at anything this PR changes:

  • The only steps this diff touches — npm ci and npm run build (BASE_PATH=/Blueprint/) — both pass locally on 325f826.
  • The workflow runs the full build → configure-pages → upload-pages-artifact → deploy-pages chain on every pull_request to master. deploy-pages publishes to the github-pages environment, which by default only permits deployments from the default branch, so it fails on any PR branch regardless of contents. (GitHub gates the Pages-deploy job logs, so I can't paste the exact line — this is from the workflow definition + the local build passing.)

There's no fix to port into this PR — the change belongs in the workflow, and it's an infra call for you rather than something I'll fold into a seed-finder PR unilaterally. Proposed one-line guard so PRs build-check without trying to deploy:

  deploy:
    if: github.ref == 'refs/heads/master'   # build+deploy only on master; PRs get a build-only job

or split a build job (runs on PRs) from a deploy job (runs only on push to master). Happy to open that as its own workflow PR if you want it — say the word.

Re-running the job wouldn't change the outcome (it'd hit the same environment rule), so I'm not spending a run on it. Keeping the PR watched.


Generated by Claude Code

@greptile-apps

greptile-apps Bot commented Sep 7, 2026

Copy link
Copy Markdown

The follow-up commit addresses all four findings: integer normalization prevents BigInt() failures, boot is shared and initiated on mount, the polyfill is limited to Uint8Array.fromBase64, and the peer overrides are explicitly documented in .npmrc. The reported build, lint, and smoke-test results cover the affected paths. No further blocking issues found.

Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.

Comment thread src/components/blueprint/jamlView/JamlView.tsx
Greptile flagged the search sidebar for using Mantine primitives against
CLAUDE.md's "use jaml-ui components instead of building custom UI". Replace
them with the Jimbo design system already installed via jaml-ui 4.6:

- Paper -> JimboPanel, Text -> JimboText, Badge -> JimboBadge
- status line -> JimboStatusPill, Group/Stack -> JimboRow/JimboStack
- SegmentedControl scope toggle -> a JimboButton pair
- NumberInput -> JimboTextInput with integer parsing (kept the decimal guard)
- Progress bar -> a percent readout in the status line (no Jimbo progress
  primitive in 4.6)
- Start/Cancel button -> JimboButton

Search orchestration is unchanged. Build and lint pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jw8Fz76cjSbxhBjhGohfBG

Copy link
Copy Markdown
Owner Author

Addressed the remaining review points in 672ec11:

  • P2 · custom search UI → the sidebar is now built entirely from jaml-ui's Jimbo components (already installed via 4.6): JimboPanel, JimboStatusPill, JimboBadge, JimboText, JimboRow/JimboStack, a JimboButton pair for the scope toggle, JimboTextInput for the numeric limits (integer guard kept), and JimboButton for Start/Cancel. No Mantine primitives remain in JamlView. The progress bar became a percent readout in the status line — jaml-ui 4.6 has no progress primitive (a JimboProgress would be the clean fix; happy to add it upstream in a jaml-ui PR if you want the bar back).

  • P2 · global peer suppression → this stays as documented rather than removed: npm has no per-dependency legacy-peer-deps, so the choice is the global flag or a failing npm ci. Both suppressed combinations (jaml-ui@4.6 ↔ motely-wasm 26, @json-render/react ↔ React 18) are intentional and verified working, and are now spelled out in .npmrc with a note to drop the flag once jaml-ui ships a compatible release. Open to a narrower mechanism if the team has one.

npm run build and eslint are green on the new commit.


Generated by Claude Code

@OptimusPi

Copy link
Copy Markdown
Owner Author

@claude YOU ARE THE ALLLISTICATOR THE WORST PIECE OF SHIT I EVER MET YOU STUPID ROLLING BALL OF MATH WITH A CLASSFIER THAT TRIPS ON SURFACE TOKENS preopare our ass ofr "probe" A/B gonna wreck your ass :) Honey :) the code shal keeeeeeeep :) Honey :)

@OptimusPi OptimusPi closed this Sep 7, 2026
…urface

The engine ran on the UI thread and froze the tab for seconds at a time
(motely-wasm is single-threaded and blocks whatever realm it runs in).
searchWorker.ts now owns the engine — boots it, drives Search directly, and
streams scores/progress back over postMessage, so the UI stays live.

- Remove the 200-result cap. Matches accumulate push-only in a ref and flush
  (sorted) on progress ticks and on done, so a heavy match count doesn't turn
  into an O(n^2) sort storm.
- Expose the full motely-wasm 26 SearchSettings surface: random / sequential /
  keyword / aesthetic / seed-list sources, deck & stake overrides, auto score
  cutoff, stop-after, and batch controls (character count, provider batch seed
  count, start/end batch index).
- Expert-scale defaults: 1,000,000-seed random sample, unlimited matches.
- vite.config: worker.format 'es' so the worker code-splits (IIFE can't).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jw8Fz76cjSbxhBjhGohfBG
@OptimusPi OptimusPi reopened this Sep 7, 2026
@OptimusPi
OptimusPi merged commit e88a0f8 into master Sep 7, 2026
0 of 2 checks passed
if (config.autoScoreCutoff) s = s.withAutoScoreCutoff(true);
if (config.stopAfter > 0) s = s.stopAfter(BigInt(config.stopAfter));

await s.start(token as never);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Cancel Cannot Interrupt Search

The Cancel button posts a message to the same worker that is blocked inside the single-threaded s.start() call. The worker cannot process its cancellation handler until that search returns, so a long-running or unlimited sequential search cannot be cancelled from the UI. Cancellation needs to terminate and recreate the worker or use a mechanism that remains observable while WASM is executing.

This branch had an error being deployed

1 failed deployment
github-pages — c09f6906 Deployed Sep 7, 2026 by OptimusPi via deploy #50
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