{"_id":"@3halves-labs/score-trivia-core","name":"@3halves-labs/score-trivia-core","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@3halves-labs/score-trivia-core","version":"0.1.0","description":"Shared, zero-dependency contract for SCORE trivia (Ruck & Recall) — question-type registry, fractional-correctness scorers, and capability descriptors","license":"MIT","keywords":["loyalty","trivia","quiz","gamification","scoring","rasch","typescript"],"main":"dist/index.cjs.js","module":"dist/index.esm.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.esm.js","require":"./dist/index.cjs.js"}},"sideEffects":false,"scripts":{"build":"tsup","clean":"rimraf dist","dev":"tsup --watch","typecheck":"tsc --noEmit","prepublishOnly":"npm run clean && npm run build","test":"vitest run","test:watch":"vitest","release":"../scripts/release-sdk.sh score-trivia-core score-trivia-core","release:patch":"../scripts/release-sdk.sh score-trivia-core score-trivia-core patch","release:minor":"../scripts/release-sdk.sh score-trivia-core score-trivia-core minor","release:major":"../scripts/release-sdk.sh score-trivia-core score-trivia-core major"},"devDependencies":{"rimraf":"^5.0.5","tsup":"^8.0.1","typescript":"^5.3.3","vitest":"^2.1.9"},"publishConfig":{"access":"public"},"_id":"@3halves-labs/score-trivia-core@0.1.0","gitHead":"ed0a3175149ac06fb9b1022e84129c4385a0521b","_nodeVersion":"18.20.4","_npmVersion":"10.7.0","dist":{"integrity":"sha512-gqwflTeJOpQRy5Z+leWROy4A822+L2WzpOkZ6pJsKg9jhJabdkYfArUqbAyvh4Uyia+5z4YgL2QOa94ndDCaVQ==","shasum":"f68c0f4b2aa1d918431b91e02a88a3cc6666ae69","tarball":"https://registry.npmjs.org/@3halves-labs/score-trivia-core/-/score-trivia-core-0.1.0.tgz","fileCount":8,"unpackedSize":150517,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIA9SPrG+RGi/TE6aMUEtd0czLmxLUoQ/SIVJLdl6COLpAiEAvL5heDPQaW09MEK1wldKtj0MLUJK1dVgXlIkP/3hG8k="}]},"_npmUser":{"name":"3halves-labs","email":"info@3halves-labs.com"},"directories":{},"maintainers":[{"name":"3halves-labs","email":"info@3halves-labs.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/score-trivia-core_0.1.0_1787108488962_0.4928823763950849"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-19T03:01:28.812Z","0.1.0":"2026-08-19T03:01:29.106Z","modified":"2026-08-19T03:01:29.366Z"},"maintainers":[{"name":"3halves-labs","email":"info@3halves-labs.com"}],"description":"Shared, zero-dependency contract for SCORE trivia (Ruck & Recall) — question-type registry, fractional-correctness scorers, and capability descriptors","keywords":["loyalty","trivia","quiz","gamification","scoring","rasch","typescript"],"license":"MIT","readme":"# @3halves-labs/score-trivia-core\n\nThe zero-dependency shared contract for **SCORE trivia** (Ruck & Recall — URC Infinite Trivia). It defines what a trivia question *is* and how an answer grades to a **fractional correctness in `[0, 1]`**, so the same pure functions run in the generator/verifier, the middleware serving path (the server-side scoring authority), the admin review preview, and any client. Grading can't drift between surfaces.\n\nMirrors [`@3halves-labs/score-interactions-core`](../interactions-core) in shape and build.\n\n> Implemented: **Family A** (`multiple_choice`, `true_false`, `fill_blank`, `multi_select`, `cloze_passage`, `odd_one_out`, `two_truths_one_lie`), **Family B** (`ordering`, `matching`, `categorise`, `higher_lower`, `numeric_estimate`) — with partial credit — plus **Family C** media wrappers and **Family D** modifiers.\n\n## Family C — media wrappers\n\nMedia types are wrappers: the media is the stem, and **scoring delegates to the wrapped Family A/B `base` question** (spec §6.4). The media question carries its own `points`; the base drives correctness.\n\n```ts\ngradeAnswer({\n  id: 'm', type: 'video_spot_error', prompt: 'What did the ref penalise?', points: 10,\n  media: { kind: 'video', url: 'clip.mp4', transcript: '…' },\n  base: { id: 'b', type: 'multiple_choice', prompt: '', options: [\n    { id: 'a', label: 'Offside', correct: true }, { id: 'b', label: 'Knock-on' } ] },\n}, 'a'); // correctness 1, typeWeight 1.2 (media)\n```\n\n`image_hotspot` / `pitch_placement` currently wrap their §13 `multiple_choice` accessibility equivalent; native spatial scoring is a follow-up.\n\n## Family D — modifiers\n\nModifiers wrap any type and adjust scoring/flow (spec §6.5). `applyModifiers` composes them over a graded answer:\n\n```ts\nimport { gradeAnswer, applyModifiers } from '@3halves-labs/score-trivia-core';\n\napplyModifiers(gradeAnswer(q, answer), [\n  { id: 'elimination_ladder', eliminationStep: 1 }, // reward × 2/3\n  { id: 'confidence_wager', wager: 15 },             // wagerDelta = ±stake by calibration\n]);\n```\n\n`chain`, `speed_round`, `prediction` are recognised as flow-only (no per-answer score change).\n\n## Install\n\n```bash\nnpm install @3halves-labs/score-trivia-core\n```\n\n## Scoring one answer\n\n```ts\nimport { gradeAnswer } from '@3halves-labs/score-trivia-core';\n\nconst question = {\n  id: 'q1', type: 'multiple_choice', prompt: 'Who won the first URC title?', points: 10,\n  options: [\n    { id: 'a', label: 'Stormers', correct: true },\n    { id: 'b', label: 'Bulls' },\n  ],\n};\n\ngradeAnswer(question, 'a', { elapsedMs: 0, timeLimitMs: 10_000 });\n// → { correctness: 1, typeWeight: 1, speedFactor: 1.25, weightedScore: 1.25, points: 12.5 }\n```\n\n- **`correctness ∈ [0, 1]`** drives the θ update and badge progress.\n- **`speedFactor ∈ [1, 1.25]`** applies *only* when `correctness ≥ 0.8` and the item is timed — speed can never rescue a wrong answer.\n- **`typeWeight`** keeps guessable formats from being farmed (`true_false` 0.6, `multiple_choice` 1.0, `fill_blank` 1.3).\n\n## Registry\n\n```ts\nimport { listQuestionTypes, getQuestionType, isSupportedType, scoreCorrectness } from '@3halves-labs/score-trivia-core';\n\nisSupportedType('true_false');        // true\ngetQuestionType('fill_blank')?.typeWeight; // 1.3\nscoreCorrectness(question, 'a');      // 1  (raw correctness only)\n```\n\n## Capability negotiation\n\nThe client advertises what it can render; the server serves only the intersection with what it implements (spec §6.1).\n\n```ts\nimport { negotiateTypes, resolveServableType } from '@3halves-labs/score-trivia-core';\n\nnegotiateTypes({ types: ['ordering', 'true_false', 'multiple_choice'] });\n// → ['true_false', 'multiple_choice']   (unsupported dropped, client order kept)\n\nresolveServableType('true_false', { types: ['multiple_choice'] });\n// → 'multiple_choice'   (falls back down the chain)\n```\n\n## Scripts\n\n```bash\nnpm run build       # tsup → CJS + ESM + d.ts\nnpm run typecheck   # tsc --noEmit\nnpm test            # vitest\n```\n\nPublished locally via `npm run release:*` (see `../scripts/release-sdk.sh`).\n","readmeFilename":"README.md","_rev":"1-fdb3ccbb9ee847c6af57c5ca8ea47e2e"}