Port the group-quiz demo and JATOS packaging scripts out of jsPsych#3694 - #56
Merged
Conversation
… jsPsych#3694 Rescues three artifacts from the jspsych/jsPsych `multiplayer` branch before it is stripped to core-only (jsPsych.multiplayer + tests + reference docs) upstream. examples/group-quiz/ — a live, Kahoot-style quiz: one participant hosts, others play. The host drives phases via a monotonic `step` counter rather than an exact `phase === X` barrier, which deadlocks under JATOS snapshot skipping (a lagging client whose snapshot jumps a phase is left with a permanently unsatisfiable condition). Rewired to this repo's example conventions: unpkg for core/stock plugins, `../../packages/*/dist/index.browser.min.js` for the multiplayer packages, and the same honest "illustrative, not runnable until #3694 ships" header caveat as ultimatum-game-jatos.html. Verified against this repo's sync plugin, which has drifted ahead of the version the demo was written against. Kept hand-rolled rather than composed from this repo's scoreboard/countdown/choice plugins: the host view is not a jsPsych timeline (so it cannot run plugins at all), and the quiz is host-authoritative where those plugins coordinate peer-to-peer. Reasoning recorded in docs/group-quiz-design.md. scripts/ — the repo's first packaging tooling. Assembles an importable .jzip (flat assets + a .jas with groupStudy: true, then zip), so ultimatum-game-jatos.html can finally be uploaded to JATOS at all. Asset resolution rewired for this repo's layout: only the multiplayer packages live in packages/, so jsPsych core and the stock plugins resolve from node_modules (added as devDependencies) instead. build-jatos-ultimatum.js points at this repo's ultimatum-game-jatos.html, not the fork's superseded version, and leaves the batch uncapped so the demo's documented spectator-overflow path stays reachable. Path rewrites now match on the quoted attribute value and fail loudly if one finds no match — a silently-missed rewrite would only surface inside JATOS. Both archives carry a printed caveat: the bundled core is a published release, so it lacks jsPsych.multiplayer and the study fails at connect() until #3694 ships. Not ported, per an audit of both repos: adapter-multiplayer-jatos and plugin-multiplayer-sync (this repo's copies are strictly ahead — spot-checked at 265 vs 145 and 197 vs 148 source lines, with studyResultId keying, connect re-entry guard + timeout, onClose handling, subscriber isolation, and sync's wait_error/on_load fixes present only here), and the fork's ultimatum examples (superseded by examples/ultimatum-game-jatos.html). Verified: 556 tests pass; both scripts produce valid .jzip archives with correct groupStudy metadata and no unrewritten asset references. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Pin jspsych 8.2.3 as an explicit root devDependency: the packaging scripts hard-require node_modules/jspsych/dist/index.browser.js and css/jspsych.css, which previously resolved only via hoisting of sub-package devDeps. Exact pin (no caret) since the scripts embed it. - Reword the nodeModulesAsset error message so it accurately describes where jsPsych core and the stock plugins come from and how to fix a missing install. - Add a CI smoke-test step running both build:jatos:* scripts after the package build (ubuntu-latest ships the required zip binary). - Add an Attribution subsection to the group-quiz section of examples/README.md, crediting the jsPsych#3694 demo (MIT), matching the ultimatum section's format. - Document two packaging gotchas in examples/README.md: the zip CLI requirement on macOS/Linux (PowerShell on Windows), and that each build mints fresh JATOS UUIDs so re-imports create a new study. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…abort Port of jspsych/jsPsych#3694 commit 5512c5b5 by Josh de Leeuw, which landed on the core PR just before plugin-multiplayer-sync was moved out to this repo. Routes the minimum_wait hold through pluginAPI.setTimeout so the pending timeout is cancelled when the experiment aborts, instead of a raw setTimeout that leaks past teardown. The single holdMinimumWait helper covers both the success and timeout paths. Adds pluginAPI.setTimeout to the test double. Co-Authored-By: Josh de Leeuw <josh.deleeuw@gmail.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Rescues three artifacts from the jspsych/jsPsych#3694 branch before it is stripped to core-only (
jsPsych.multiplayer+ tests + reference docs) at a maintainer's request. Anything not living here is lost when that happens.Important
Stacked on #53 (
migrate-multiplayer-namespace), which is the base of this PR — so the diff below is just my one commit. It has to be stacked:main's plugins still reach the API viajsPsych.pluginAPI, and current #3694 has no pluginAPI fallback (createJointPluginAPIObjectcomposes only keyboard/timeout/media/simulation;JsPsych.tsaddsmultiplayeras a separate property). Based onmain, the group-quiz demo would die at its first sync barrier. GitHub will retarget this tomainonce #53 merges.What this adds
1.
examples/group-quiz/— a live, Kahoot-style quiz. Everyone opens one URL; one person clicks Host (the presenter screen), everyone else clicks Player. It's the repo's demo of the asymmetric pattern — one authoritative driver plus many followers — versusultimatum-game-jatos.html, where every client runs the same timeline and coordination is by deterministic consensus. The host half is deliberately not a jsPsych timeline: it's vanilla JS driving the adapter directly, because a presenter screen reacts continuously (subscribe) rather than advancing through trials.The load-bearing idea is the monotonic step counter. The host advances by overwriting a
phasefield, and JATOS doesn't guarantee a client observes every intermediate snapshot — so the obvious barrier deadlocks:A lagging player whose snapshot jumps straight from
questiontoleaderboardis left with a permanently unsatisfiable condition and hangs forever. Every host push therefore carries astepthat only ever increases, and players wait onhostStepValue(group) >= phaseStep(…)— a>=test against a monotonic value can never be missed. Generalized: on a snapshot-based transport, barrier predicates must be monotone.2.
scripts/— the repo's first packaging tooling. These assemble an importable.jzip(flat assets + a.jaswithgroupStudy: true, then zip). There was none before, soultimatum-game-jatos.htmlcouldn't be uploaded to JATOS at all. Probably the highest-value part of this PR.3.
docs/group-quiz-design.md— rewritten from the fork'sgroup-quiz-plan.md, which was a three-track work-assignment plan for a team that has since built the thing. Kept the durable content (protocol, phase table, scoring, the host-only answer-key rationale) and replaced the task checklists with the step-counter explanation, the composition rationale, and known limitations.What I rewired
packages/jspsych/dist/…). Here only the multiplayer packages live inpackages/, so core and the stock plugins now resolve fromnode_modules(added as devDependencies). A JATOS study must be self-contained, so the examples' CDN<script src>are resolved to bundled copies and rewritten to flat filenames.https://unpkg.com/jspsychis a prefix of…/jspsych/css/jspsych.css, so unanchored replacement corrupts one depending on iteration order. And a silently-missed rewrite ships a.jzipwhoseindex.htmlstill points at a CDN — failing only at run time inside JATOS, long after whoever broke it moved on.build-jatos-ultimatum.jstargets this repo's example and leaves the batch uncapped. The fork capped it at 2 members, but this repo's version routes extra arrivals to aspectatorscreen — a 2-member cap makes that documented path unreachable.Composition decision
The demo hand-rolls its leaderboard, timer, and answer buttons rather than composing this repo's scoreboard/countdown/choice plugins, and I kept it that way. The host view isn't a jsPsych timeline, so half the game can't run plugins at all;
countdownresolves min-across-slots consensus while this clock is host-authoritative;choicebarriers until everyone picks, but the quiz needs a private answer with a host-timed reveal and speed scoring. Closest call wasscoreboard'sbuildLeaderboard, but it expects{[dataKey]: {score, label}}while this protocol is flat and the host page loads no plugins — reshaping the wire contract to avoid a nine-line sort. Full reasoning in the design doc.Not ported, deliberately
adapter-multiplayer-jatosandplugin-multiplayer-sync— this repo's copies are strictly ahead (265 vs 145 and 197 vs 148 source lines;studyResultIdkeying, connect re-entry guard + timeout,onClosehandling, subscriber isolation, and sync'swait_error/on_loadfixes exist only here). The fork's ultimatum examples are superseded byexamples/ultimatum-game-jatos.html.One remaining gap I found and have not actioned: the fork's
docs/developers/adapter-development.md(162 lines — per-method gotchas, a worked example, and a new-adapter checklist including "adaptersubscribe()must be future-only, no replay — the API handles it"). We have the interface shape in the reference docs but none of that. Happy to port it separately if we think it won't survive the strip.Verification
npm run buildclean.groupStudy: true, correctdirName/htmlFilePath, and no unrewritten asset references in the packaged HTML.dist/group-quiz-jatos/plugin-multiplayer-sync.jscontainsmultiplayer ?? instance.pluginAPI.c5c4499(verified as the live PR head viagh api).Not verified: a live JATOS run. Like the other JATOS examples here, this is illustrative — until #3694 ships, the bundled core lacks
jsPsych.multiplayer, so the archive imports cleanly but fails atconnect(). Both scripts print that caveat, and the examples carry it in their headers.🤖 Generated with Claude Code