Repository navigation
Make the running game pedagogy-first: lesson cards, spaced retry, Codex, and sound - #31
Merged
Merged
Conversation
core.autocrlf=true checkouts gave the Node contract tests CRLF sources, and their multiline patterns failed (npm test could not pass on Windows). Pin text to LF on every platform and mark the embedded .rgb/.rgba plates and other assets binary so content sniffing can never rewrite their bytes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Introduce learning.rs, a Tauri- and Bevy-free contract for concept lenses, per-choice rationales, answer evidence, per-lens mastery thresholds, the lesson journal, and a deterministic choice-presentation order that spreads provider-first answers uniformly and moves every choice on each retry. QuizQuestion now carries its lens, rationales, and review flag; CartridgeSpec carries the lesson journal and mastery; the answer recorder receives full AnswerEvidence instead of bare question text. Behavior is unchanged until the question pipeline and engine consume the new fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Committing an answer now opens a lesson card instead of auto-advancing. The four choice frames give way to one composed panel: a miss shows the player's pick in red with its rationale (the misconception it reveals), then the correct answer in green with its rationale; a success shows the answer and why it holds. "-" and "+" markers repeat the verdict without relying on hue. The trial panel footer names the question's lens with its three live mastery runes and shows A:CONTINUE once the existing 45-tick input hold ends; A or Start then continues. The legacy renderer gets the same card over its choice rows. Native layout tests cover worst-case copy (31-character choices, three 34-column rationale rows) for containment, sibling disjointness, and contrast against the panel fill, and write the oracle-lesson-correct / oracle-lesson-missed previews. Choices are drawn in learning::presentation_order, keyed by the normalized question identity and the attempt number in this run (review questions start at attempt 1). run.selected is a display slot mapped to the source choice at commit, so the provider's answer position no longer predicts the answer and every retry moves every choice. A survivable miss inserts a review copy RETRY_GAP (3) questions later, never past the current batch end, and shifts every later batch end, so a batch cannot complete before its misses are retried. Level-up still fires exactly at the shifted end; a ward-breaking miss goes to game over without scheduling a retry. A correct review shows REDEEMED unless an Insight Rune crossing takes precedence. Every commitment records lens evidence into cartridge.mastery and upserts the cartridge lesson journal by question identity, clearing `outstanding` on a later correct attempt. B during an active question now arms a 90-tick in-scene confirmation (B AGAIN:LEAVE); a second B inside the window leaves, any other button or the timeout disarms it, and B is inert on the lesson card. Existing flow tests that relied on the 45-tick auto-advance now press A after the hold, and answer by locating the shuffled answer slot. The Oracle quiz brief and README describe the new flow and resolve both open decisions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The chronicle card credited every GPL-family repository to the Free Software Foundation (the notice at the top of the license text itself) and every Apache-2.0 repository to "[yyyy] [name of copyright owner]" from the license appendix, because the first line starting with "copyright" in LICENSE won and LICENSE was read before NOTICE. Move provenance parsing into a pure provenance module: prefer COPYRIGHT and NOTICE files over license texts (and read COPYING), reject license-steward notices inside GNU license texts, template placeholders, enumerated "(c)" clauses, and prose such as "copyright notice" or "copyright holders". Author credits now come from "shortlog -sn" and merge one person across spellings case-insensitively, ranked by total commits, so a contributor with two addresses no longer takes two of the three slots. The timeline reads root commits and the newest commit instead of formatting every commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The running game now makes learning evidence visible. A new read-only `codex` scene handler shows every concept lens with three mastery runes that wake at exactly 1, 3, and 5 pieces of evidence (first-try successes plus redeemed misses) and an amber count of that lens's lessons awaiting review. Each later page rereads one journal lesson: counter, lens and its runes, the question, the correct answer, and the rationale, with REVIEW PENDING in amber for outstanding lessons. Left/Up/L and Right/Down/R page with wrap; A and Start are deliberately inactive; B emits back. An empty journal says NO LESSONS YET and explains how to fill it. The quiz menu may now emit a new `open-codex` signal. Its second option reads OPEN THE CODEX only when the current menu scene routes that signal, and otherwise keeps RETURN TO TITLE, so manifests without the route behave exactly as before; B still returns to the title. With the route, the menu subtitle summarizes the journal (LESSONS 07 REVIEW 02 / ALL CLEAR). The built-in quiz template and both CODEQUEST.toml copies add quiz-menu -> codex -> quiz-menu, a review-lessons mechanic, and a codex-frame art entry. The new `oracle-codex` visual template composites the mastery page inside the chronicle plate's archive frame and lesson pages over the trial chamber; cartridges without it get a plain NAVY/INK renderer. Native layout tests measure plate interiors, keep worst-case copy contained and disjoint, and check contrast against both panel fills and the brightest plate pixel under directly drawn copy. Rendered-frame tests cross the exact rune breakpoints. The fast validator, schema reference, design brief, and asset README list the new handler, signal, and template. The Oracle preview test now expects 15 distinct frames (13 plus two Codex layouts), and the dogfood template count is 12. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix nine audited shell defects without moving gameplay state into JS: - Enter on a focused shell control activates it instead of becoming START; the FRONT/BACK toggle no longer keeps mouse focus. - Held Space/Enter toggles power once; held cart keys insert once; an insert in flight blocks another; a power-on joins an abandoned cycle's in-flight readiness probe instead of spawning another CLI call. - The real power-on failure (for example CODEX CLI UNAVAILABLE) is shown on the device, on the CHECK BATTERIES guide, on the rear battery tab, and in the battery tray, which now guide installed-but-failed packs. - A failed cartridge load keeps its rack entry unless the folder is no longer a Git repository; startup faults are shown, not swallowed, and an engine error no longer erases the saved battery choice. - Provider checks are scoped to their power generation, and power-off clears a stale CHECKING state. - One visible aria-live device status strip carries messages raised outside the trays; tray message lines stay rendered as live regions, and a failed startup leaves a persistent DEVICE FAULT. - Both trays are labelled modals: the device goes inert, focus moves in, Tab is trapped robustly, focus is restored on close and after rebuilds. - Background branch refresh relabels rack cards in place instead of rebuilding the tray mid-drag. - The browser demo explains RUN IN TAURI when ADD FROM DISK is used. Pure helpers live in src/device-shell.js and src/cartridge-library.js and are pinned by tests/device-shell-robustness-contract.mjs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # src-tauri/src/engine.rs
One flow now pins the contract between the lesson card and the Codex: a miss is journaled as a pending review with the correct answer and its rationale, returns after the retry gap, is redeemed (clearing the pending review and recording redemption in the lens), and the Codex pages through exactly the journal the quiz wrote without changing it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every entry in the Oracle sound ledger was still "Proposed" and the game was
silent. Sound now follows the same rule as rendering: it is a pure function of
observable state. A new audio module describes a constrained four-voice chip
(two duty-selectable pulses, a triangle-like wave, and noise). Each tick the
engine builds an AudioSnapshot of what the player can observe and the
AudioDirector diffs it against the previous tick to start cues, scene-owned
loops, and scripted opening beats. Gameplay code never calls audio, and new or
unknown scenes are silent until the ledger names them.
Notes carry the absolute engine tick they start on. Voices are monophonic and a
zero-volume note is a cut, so a scene's loop ends on the exact tick the scene
exits and a new cue cancels the previous cue instead of stacking. At most one
cue starts per tick, chosen by priority, so one input edge can never produce
two cues. Loops stay at or below chip volume 5 while every cue peaks above it.
Nothing sounds while the device is off or booting.
The compositions are original and cover the whole ledger: dry archival ticks
on the chronicle, an opening whose motif grows from one source pulse to the
four-voice Oracle cadence and tails before the title, restrained title and
menu loops, per-row trait timbres and an Oracle-motif bind cadence, a Datafall
ambience with two, three, and four voices by presentation tier, data, bug,
charge-rune, seal-break, retry, ready, and leave cues, one commit-and-result
cue per answer (rune, flow-stage, and ward variants), a last-ward heartbeat, a
batch telegraph and tier-grown reward cadence inside the 60-tick hold, a
defeat fall and score hold, and an Initiate replay cadence.
The director runs as a Bevy system after advance_game. EngineRuntime copies
its notes into a bounded 512-note queue each frame, and the new engine_audio
Tauri command drains it as { tick, notes }. The shell's new speaker.js module
maps ticks onto the WebAudio clock (60 ms lookahead, re-anchoring beyond
150 ms of drift), plays pulse duty through periodic waves and noise through a
15-bit LFSR buffer, and starts its audio context only on a user gesture. The
device gains a knurled volume wheel on the power-switch edge of both faces,
a three-LED detent meter, V to cycle MUTE, LOW, MID, and HIGH, persistence in
localStorage, and a VOLUME row on the rear label.
Rust tests cover silence while off or booting, one cue per input edge through
scripted engine input, loop cuts on the exit tick, loops yielding to cues,
tier-grown arrangements, the opening arc, cue priority and non-stacking,
determinism, the queue bound, and the engine_audio payload shape.
tests/device-audio-contract.mjs checks the speaker against a recording
WebAudio stand-in and keeps it presentation-only. The design brief and README
now describe the implemented runtime.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shell's CSS already respected prefers-reduced-motion, but the engine kept blinking prompts, bobbing heroes, pulsing sigils, twinkling constellations, and scrolling starfields, which the brief left as the one unfinished accessibility item. The shell now forwards the host preference (at startup and on change) through a new engine_set_reduced_motion command. Decorative motion reads a motion clock that freezes at the resting composition, settling motion jumps to its end state, and blinking prompts stay lit. Scene timing, input edges, Datafall gameplay, and data are unchanged: a test drives both modes and asserts frozen title and atelier frames plus an identical 720-tick scene timeline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move the question domain out of lib.rs into a Tauri-free questions module: the saved-question model, the acceptance policy, prompt construction, provider-response parsing, batch persistence, and learner progress. lib.rs keeps the commands, provider plumbing, cartridge building, and wiring. Generated questions now use payload v2: a concept lens plus a "why" for every choice. Legacy saves with plain-string choices still deserialize and play without rationales. Each generated question must name a lens that learning::Concept::parse accepts and carry location-free rationales that fit the lesson panel; failures drop individually so mixed batches survive. Provider text is normalized to renderable uppercase ASCII, and parsing finds the question array inside prose or fences and salvages finished questions from a truncated reply. The anti-trivia filter now matches phrases and word shapes instead of bare nouns, so questions such as WHY WRITE THE SAVE FILE ATOMICALLY? pass while which-file, where-does, counts, commits, branches, authors, recency, versions, hashes, paths, and real file names stay rejected. A new repo_context module builds a deterministic, budgeted project brief from the README, design notes, a CODEQUEST.toml summary, and doc-comment and signature skeletons of up to 30 components spread across the tree. Excerpts are numbered, never named; path-like words and tracked file and directory names are redacted; vendored, generated, test, binary, huge, and symlinked files are skipped. Prompt v2 adds lens focus, PREDICT transfer questions from level 4, misconception distractors and rationales, varied answer positions, the weakest lens, and previously asked stems, and is bounded to fit a Windows command line. Progress keeps answered_questions as retired questions and adds missed_questions and per-lens mastery. A wrong answer no longer retires a question: misses return as reviews in later runs and launches until redeemed. Cartridge loads now supply the lesson journal and mastery. Saves gain an atomic read-modify-write update under the save lock so batch appends and answer records cannot lose each other, and answers are recorded on a dedicated writer thread instead of the engine loop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Catch-up request: any powered quiz screen past boot keeps a question request in flight while the next question does not exist, so a first request that failed before battery verification is retried behind the opening, and the Oracle and level-up waits share one path. Hero creation (atelier and legacy) now shows the Oracle's real states: ready, writing, retry pending, or contacting. - A new run drops the deck prefix answered earlier in the session and re-queues, at the front and flagged for review, every consumed question whose lesson is still outstanding. Batch ends and levels are rebased. - Batches hold QUESTION_BATCH_SIZE questions: short deliveries top up the open batch (reviews count), saved batches are rebatched on insert, and a level-up only follows a full batch. The prefetch level accounts for the level-ups still ahead and never re-requests a queued level. - CartridgeSpec gains question_batch_levels (empty = unknown), kept aligned through merges, retries, and drains. - Scene-graph robustness: Oracle/quiz entry creates a fresh run when none is live, Oracle/quiz Back clears the run, quiz B works without a run, level-up requests missing questions, and its prompt and A input follow the manifest's can_signal timing instead of a fixed hold. - Question requests carry a sequence number echoed by the reply; a superseded reply is ignored without touching the loading state, and a reply during play appends to an existing pending batch. - Entering the quiz menu focuses BEGIN again. Tests updated: failed_prefetch_waits_before_requesting_ai_again now expects a level-1 top-up for its short 4-question batch, and losing_the_last_heart_at_a_batch_boundary_does_not_level_up asserts the retired deck the next run receives. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # docs/game-design/oracle-quiz.md # package.json # src-tauri/src/lib.rs # src/main.js
…stence # Conflicts: # src-tauri/src/lib.rs
# Conflicts: # src-tauri/src/engine.rs # src-tauri/src/lib.rs
Finish the seams between the parallel streams: - Sound: the Codex is now a known audio scene with page-turn cues, confirm and cancel on entry and exit, the soft unavailable cue for its inactive A/Start, and no ambience under reading. A redeemed review gets its own climbing cadence (ranked below rune awakenings, above flow, matching the banner precedence), and the first B of the two-press leave sounds a distinct warning. Engine tests drive each cue through real input. - Saves: cartridge loads carry each batch's generation level to the engine's prefetch logic and queue lower-level batches first, so a new Initiate run never opens on leftover Oracle-bound questions, while the lesson journal keeps generation order. - The scaffold's temporary dead-code allows are gone: every part of the shared learning contract is now consumed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A live run of the payload-v2 prompt against this repository (Claude Sonnet, levels 1 and 4) produced excellent lens-tagged questions with misconception rationales and PREDICT transfer questions, but only 2 of 12 were accepted: the misconception framing drew choices of 32-45 characters against the display's hard 31-character limit, which would have left the Oracle retrying most of the time. The prompt now asks for choice phrases of 2 to 5 words and at most 28 characters (headroom under the display limit), calibrates with example phrases and their exact lengths, caps questions at 100 characters including any prefix, tells the model to put nuance in the rationale rather than the choice, and asks it to count and rewrite before answering. Re-running the same live evaluation: 11 of 12 accepted (the rejection was a correctly caught location question), answers spread across all four slots, three PREDICT questions at level 4, and level focus respected (5 of 6 purpose/responsibility at level 1, 6 of 6 invariant/tradeoff at level 4). For comparison the pre-release generator accepted 8 of 12 surface-level questions and put 11 of 12 answers in the first slot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… off the UI thread - Provider prompts (generation and the readiness probe) are written to the CLI's stdin instead of argv: `claude -p ...` with no positional prompt and `codex exec ... -`. There is no command-line limit any more, so the prompt cap becomes a byte budget (MAX_PROMPT_BYTES) that never trims a full brief. - command_output_with_timeout takes an optional stdin payload (written from a detached thread, then closed), runs the child in its own process group (Unix) and kills the whole tree on timeout (killpg / taskkill /T /F). Pipe readers report on channels awaited only until the deadline, so a grandchild holding the pipes can no longer outlive it. - Errors keep a short sanitized reason: the io::ErrorKind on spawn failure, the first stderr line or exit code on a failed call. Generation failures are logged instead of silently dropped. - Windows discovery also finds the standalone Codex installer (%LOCALAPPDATA%\Programs\OpenAI\Codex\bin\codex.exe) and npm shims (<name>.cmd on PATH or in %APPDATA%\npm). - engine_set_cartridge and cartridge_branch are async and do their git and save work in spawn_blocking, as pick_cartridge now does too. Loads apply in request order. Every git query goes through a 15 s timeout helper, the provenance queries are bounded by HEAD, the save is read once per load, and an add from disk reuses the picker's build instead of building twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Screenshots come from the real release build under Xvfb with questions generated by Claude for this repository: a lesson card after a deliberate miss (misconception, then answer and rationale) and the Codex page that journals it as a pending review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Oracle quiz brief now carries the skill's pedagogy fields and a pedagogy map, each verified against learning.rs, questions.rs, and the engine: mastery runes at 1/3/5 evidence, spaced retry after up to RETRY_GAP questions, level focus lenses, and PREDICT transfer at level 4+ as prompt-level requirements. The threshold contract gains lens mastery and pending reviews, and the storyboard, mechanics, sound ledger, polish matrix, traceability, and slices now describe the lesson card, the save's retire-or-review semantics, reduced motion, the manifest-gated level-up hold with its 180-tick auto-continue, stdin generation with the anonymized brief, and full six-question batches. Slice 6 is in progress. The game-designer skill describes sound as engine-derived from state (audio entries stay production requirements with no sound schema), points designers at the lesson card, shuffle, spaced retry, lens mastery, Codex route, and reduced motion, and adds evals for both. The TOML reference and Oracle asset README document prompt gating by after_ticks, the concept-quiz lesson card, and the lesson-card and Codex preview tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Generated questions that fail only on display mechanics are now sent back once for a precise fix instead of being discarded. - questions.rs: question_violations reports every reason generated_question_is_acceptable rejects a question (question/choice/why length with measured characters and wrapped lines, missing why, unknown lens, non-ASCII characters, trivia, malformed structure), using the same primitives; a test pins that it is empty exactly when the policy accepts. Trivia and malformed questions are never repairable. - parse_generated_batch keeps the rejected questions beside the accepted ones. GeneratedBatch::repair_request builds a pure repair prompt that lists each violated field with its measurement and limit, repeats the question verbatim in the generation schema plus an id, and asks for the same schema back. merge_repairs accepts at most one repair per sent id, only when it keeps its answer index and passes the full policy, never beyond the count and never repeating an identity. - lib.rs ai_questions: when a reply falls short and a rejection is repairable, one repair call goes through ask_provider (stdin, process-tree timeout) with only what is left of the 120 s budget, skipped under 10 s. Before/after counts are logged. - An ignored live test measures the repair against the real Claude CLI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nwhile The question loader now returns Result<Vec<QuizQuestion>, String>. lib.rs logs the full error and passes the engine a short category: TIMED OUT, CLI UNAVAILABLE, RATE LIMITED, PROVIDER OVERLOADED, LOGIN NEEDED, BATTERIES UNVERIFIED, NOT A GIT REPO, CALL FAILED, REJECTED BATCH, SAVE FAILED, or AI DISABLED. The reply travels in EngineCommand::Questions under the existing request seq, so a superseded or foreign failure is ignored. The engine keeps the 300-tick retry, cleans the reason into one upper-case line of at most 24 characters, treats an empty batch as NO NEW QUESTIONS, and clears the reason on success or when a cartridge is inserted. While the player waits, both Datafall renderers grow their header band by one row (sanctum 15->22 px, legacy 12->21 px). The band still ends above the highest drop spawn. The new line shows: - while a failure waits out its delay: "<REASON> - RETRY IN NS"; - otherwise, one journal lesson as retrieval practice, "REVIEW|RECALL [LENS:] ANSWER", with outstanding misses first and then cleared lessons, newest first, changing every four seconds. The lens is dropped before any of the answer would be cut; - with no journal, "<REASON> - RETRYING" while the retry is in flight. A new lesson is written in over a few ticks. Under reduced motion it appears whole and does not move. Datafall play does not change. Native tests cover seq-mismatched failures, showing and clearing the reason, empty batches, sanitizing, recall order and cycling, the static reduced-motion line, and layout: containment, disjointness from row one, drops, and hero, contrast of at least 4.5:1, no pixels outside the line box, and no change below the band. They also write preview frames 06c-06f. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The game is a canvas, so a screen reader got nothing. The engine now has a third output channel beside the framebuffer and the chip notes: a plain-language transcript of what a sighted player can read and act on. It covers the chronicle credits, each opening beat, the title, the menu with its focus and journal, hero creation, the Datafall status and counts, the trial question with its choices in display order and the focused choice, the lesson card with both rationales, leave confirmation, level-up, game over, the Codex mastery and lesson pages, and the quest screens. The transcript is derived from the renderers' own state and helpers (the display order, the lesson composition, the feedback banner, the journal summary, the Oracle status order), so words and pixels cannot disagree. Blinks, motion, and staged reveals are left out, and the lesson card reads the same before and after its input hold, so nothing is announced twice. EngineRuntime publishes the transcript with a sequence number only when it changes, and engine_transcript(since) returns it only when newer. The shell polls every 150 ms from the animation-frame loop behind its own in-flight guard and writes a visually hidden polite live region only on a new sequence number. The browser demo returns no transcript. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The storyboard, mechanic and traceability rows already describe the failure reason and the recall line; the polish matrix still listed the oracle scene's static and motion elements without it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The Oracle no longer recalls the answer of an outstanding miss whose question is still queued (a retry copy ahead, or the review the next run opens with), so the dwell before a review cannot give it away. - A failure keeps the Oracle line until its retry has been out for a second, so a fast-failing retry never flashes a lesson; each recall starts at the top of the recall order and is written in fresh. - Batch progress counts an open batch as its reviews plus a full batch of new questions, so a miss grows N at once and the top-up never jumps it. - Deliveries top batches up by new questions while reviews ride along, matching rebatch and batch_is_full. - Deliveries skip stems the journal holds as answered correctly and mark stems it holds as missed as reviews, so a parked delivery cannot replay a retired question as a fresh first try or redeem it twice. - The level-up NEXT: footer names the lenses of the batch generated next, not the run's level. - The legacy lesson footer sits on an INK strip so a MAGENTA rune III banner clears 4.5:1. - Quest output and completion carry a quest generation, and readers stop forwarding once the quest is done, so a helper that outlives its shell (Windows) cannot write into a later battle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ool launch A mutation sweep over save.rs, provenance.rs, lib.rs (repository check, failure reasons, timed commands, cartridge building) and the non-Windows paths of external_tools.rs left 23 of 51 compiling mutants alive; 20 change behavior and each is now killed by a new test: - save: a save from a newer schema is refused and left untouched. - provenance: the first of several notices is credited; every prose word and template placeholder is rejected on its own. - cartridges: placeholders for an unborn or detached HEAD, the oldest of several root commits, COPYRIGHT outranking LICENSE, package/Cargo/Make quests, CODEQUEST.md-only custom mode, palette spread, tracked paths. - failure reasons: every rate-limit and login phrase, and signal deaths. - repository check: git is asked for untranslated (LC_ALL=C) messages. - external tools: each launcher honors only its own non-empty override. Two pure helpers were extracted so the tests can reach them without changing behavior: git_repo_check_command and cartridge_color. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jsx_comment_opening tested for `/*` after any `{` or backtick it found, so
an inline code span such as `/* block` opened a JSX comment and hid the
prose after it, up to the next `*/`, across lines. Only `{` (optionally
followed by spaces) now opens one; backticks just skip their code span.
The MDX case in restructured_text_and_asciidoc_comments_stay_hidden gains
a code span beginning with /* that must stay shown.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A mutation sweep over engine.rs's quiz flow (56 mutants) left 14 alive. Eleven were real regressions the suite could not see; each now has a focused test at the end of engine.rs's mod tests: - a miss on a batch-opening question keeps the full retry gap - a missed review is not a redemption - a short batch merged forward keeps the higher level - continuing from a level-up keeps the same run - a broken ward routed back to the Oracle starts a fresh run - retiring a run keeps each remaining batch at its own level - rebatching reads a boundary question from the batch it opens - a reinserted cartridge drops replies to earlier requests - a run that is not live does not set the next batch level - a delivery held through the quiz lands on the level-up - inserting a cartridge drops a held delivery and requests its own The other three are equivalent (or unspecified) in reachable states. No production code changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolution notes: - The lesson card's retry note reads schedule_retry's RetrySlot: At(i) still says BACK IN N from i, and a deferred copy says LATER (RetryNote::Later). The screen transcript now reads the note as drawn. - R1's cartridge-level question_attempts stay the attempt store; a same-launch relearning keeps its saved count (it still returns as a spaced check), and only a retiring success drops it. - A regenerated missed stem arrives as a Review::InSession review; a delivery also skips identities waiting as deferred review copies. - lens_stage is the shared gated rule (mastery, journal, concept); the lens-wake check reads it after both evidence and journal land, and the blinking footer rune leaves cracked runes still. The run ledger grades first tries the way evidence does. - The J3 ledger test, R1/J2 tests, and the property tests now use Review, volume_stage, stage_with, and the relearned list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A mutation sweep of 50 small changes to learning.rs and questions.rs left 13 alive. Each new test pins the behavior one of them broke: - lens parsing tolerates surrounding whitespace - a question on both progress lists stays a review (missed wins) - years at both ends of 1990..=2039 are trivia - a three-segment slashed word is a path - legacy saves with an unknown lens are rejected on load - a repair that moves the answer index is dropped - a full weakest-lens tie goes to the earlier lens - the focus minimum rounds two thirds up - level-0 batches are neither saved nor loaded - typographic dashes and quotes fold to ASCII - each undrawable character is reported once - a question filling exactly the question rows has no diagnostics No production code changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolution notes: - LensRecord keeps J1's always-saved relearned counter; a success after a Codex peek joins same-launch retries as relearning (not evidence, not graded in the recent window). - SavedQuizProgress moves a peeked success to relearned_questions, so it returns once more as a spaced check, keeps its attempt count, and still records J4's missed_picks and clears peeked_questions. - commit_answer grades with J1's demoted Review plus J4's peeked flag and pick; the loader passes picks and peeks to lessons for spaced and retired questions alike. - The peek test now uses a Spaced review, where the peek is what demotes a redemption; property tests randomize peeked in their models. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/architecture.md walks through the runtime, the learning model, and the scene machine; docs/reference/save-format.md documents every cartridge save key and its compatibility rules. Both now describe the merged Codex self-test (sealed pending pages, MarkPeeked and ProgressEvent through the save writer, missed_picks and peeked_questions, and a peeked success as relearning that returns as a spaced check). README links both from its Architecture section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolution notes:
- F1A's retry_sentence replaces the integrator's shorter retry-note line;
it gains the deferred RetryNote::Later ("returns later").
- The single-question missed card now defers its retry, so it says
"returns later"; the ledger transcript test follows J1's gated stages
(an open Invariants miss cracks rune III, so Roles wins the tie).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolution notes: - Deliveries keep F1's journal rules on top of J1: a stem the journal holds as missed becomes a Review::InSession review (never a Spaced one), cleared lessons are skipped, deferred review copies count as queued, and the top-up counts new questions with Review::is_review. - F1's batch_progress and tests use the Review enum and J4's four-argument record_lesson. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolution notes: - The retry rule stays J1's (a miss returns after exactly three other questions, carrying into the next batch); F4's Oracle-line and transcript additions are kept beside it. - The docs contract now checks for "after (exactly) three other questions" and rejects the stale "up to three intervening questions". - The preview-writer lists name the three new writers from J1 and F1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolution notes: - The injectable ask/repaired_questions structure stays; T1's with_timeout_budget now names the budget on both the first call's and the repair call's errors. - ai_questions passes configured_question_timeout(provider) as the budget instead of the removed fixed AI_QUESTION_TIMEOUT, and the save-guard test uses Claude's configured budget. - The design brief and architecture guide describe the provider-aware deadline instead of 120 seconds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The M2 sweep was written against the bool review flag, the three-argument schedule_retry, and record_lesson without a pick. The batch-opening miss test now expects RetrySlot::At with no deferred copy, review checks name Review::InSession or is_review, and the retirement test records the miss's pick. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Gate legacy lens records on their unseen history: evidence an earlier build recorded fills the accuracy window's older slots, so an upgraded veteran's first miss no longer collapses rune III to rune I. - Remember the identities answered in this launch per save, so ejecting and reinserting a cartridge reloads same-launch misses and relearnings as InSession reviews, never as spaced checks that count as evidence. - Seal a relearned lesson whose spaced check is due (CHECK PENDING) and let a reveal there be recorded as a peek, so the check is never open-book. - Keep a retired question retired when a delivered repeat of its stem is relearned, so one question cannot earn redemption evidence twice. - Save the wrong pick's choice text and resolve it by text on reload, so a reordered repeat of a stem shows the choice the player actually picked. - Show CRACKED = LOW ACCURACY (and a matching transcript line) when no cracked lens has a pending review, instead of promising a review. - Align the manifest and reference docs with sealed pages, the spaced-check seal, the two crack legends, and the mastery-page summary. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolution notes: - provenance.rs: both sides appended tests at the end of the module; all five are kept (the integration branch's length-cut and saturation tests, then M3's notice, prose, and placeholder tests). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After F3, a later batch that yields to earlier ticks cancels the slide note before it starts (its stop precedes its start), so it was never sounding and MUTE has nothing to silence there. The mute check now keeps only notes whose stop follows their start and requires at least two of them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolution notes: - engine.rs imports both AtomicBool/Ordering (integration branch) and OnceLock (P1's plate cache). - bench.rs builds its fixtures with Review::Fresh and J4's lesson fields; both ignored benchmarks still run through the merged engine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A question listed as both answered and missed now loads as a Review::Spaced review, and the lens-tie fixture fills the new LensRecord fields from their defaults. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The P1 benchmark builds its journal with the new Lesson::spaced_check. The architecture guide and the save-format reference now describe what the review-3 fixes ship: missed_pick_choices beside missed_picks, peeks on relearned lessons awaiting their spaced check (CHECK PENDING), same-launch reloads as InSession reviews through persist_launch_progress, retired questions staying retired on relearning, gate_accuracy for legacy records, and the CRACKED = LOW ACCURACY legend. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The macOS CI runner took longer than 500 ms (half of QUEST_OUTPUT_GRACE) to kill, reap, and report an aborted quest, so the timing assertion failed before the test reached its real correctness check. The quest's child sleeps 3 s, so any bound well inside that still proves the abort did not wait it out; allow 2 s, report the measured time on failure, and leave the marker check as the proof that the child itself was stopped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
shpwrck
marked this pull request as ready for review
September 25, 2026 19:30
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
The last commit (#30) made the design skill pedagogy-first. This PR makes the running game pedagogy-first, adds sound and a screen-reader transcript, and fixes 104 adversarially verified defects from three independent reviews: 36 in the pre-existing code, 26 + 42 in this branch's own work.
What a player sees now
Measured live on this repository
Screens (real app, live Claude questions)
How it was built and verified
cargo fmt,clippy -D warnings, the full Rust suite (300+ tests, including seeded property tests), Node contracts (including a fake-DOM harness that runs the realmain.js), and the manifest validator.Test plan
-D warnings/ cargo test / npm test / validator: green locallyscripts/eval-questions.sh)Full release notes
Release notes:
claude/codequest-opus-improvements-0069d2vsmainScope: 56 commits (
git log main..HEAD), 46 files, about +23,300 / -2,300 lines.New modules:
learning.rs,questions.rs,repo_context.rs,provenance.rs,audio.rs,engine/transcript.rs,question_eval.rs,src/speaker.js,src/device-shell.js.package.jsonstill reads version 0.2.4. This branch hasnot been tagged or released.
Highlights
choices are replaced by an explanation. On a miss it shows why your pick was
wrong, then why the correct answer holds. The card waits for A or Start
(e417b41).
batch cannot finish until its misses are retried. A miss also carries over to
later runs and launches until you answer it correctly (e417b41, d2729b1).
question and per attempt, and every retry moves every choice (4067e07,
e417b41).
runes that light at 1, 3 and 5 pieces of evidence, and pages through every
lesson you have earned (a3746df).
ledger. A volume wheel (or V) steps through MUTE/LOW/MID/HIGH, and the
setting is remembered (8b9f00f).
every screen goes to a live region. When the system asks for reduced motion,
decorative motion in the framebuffer stops (4cc9b71, 4446aae, 4ca8249).
validation. In a live run, 11 of 12 generated questions were accepted,
against 2 of 12 before calibration (f09c96e).
How the game teaches now
flows, invariants or tradeoffs, and carries a rationale for each choice
(payload v2, d2729b1).
then the answer in green with its rationale.
-and+markers repeat theverdict without relying on colour. The footer shows the lens and its three
live runes. A 45-tick input hold comes first, then the card waits for a fresh
A or Start (e417b41).
RETRY_GAP(3)questions later, never past the end of the current batch. Later batch ends
shift to make room. A correct review shows REDEEMED and is recorded apart from
a first-try success. A miss that breaks the last ward goes to game over
without scheduling a retry (e417b41).
TRIAL 2/7and grows witheach miss. A returning question is labelled
RETRYin amber. The miss cardsays
BACK IN N,UP NEXTorNEXT RUN. A plain success reads CLEAR SIGHT,so REVIEW only means a retry (97dd4bb). An open short batch counts toward six
(310f878).
is the loudest moment in a run: it gets its own banner (
ROLES RUNE I), ablinking footer rune and a LensWake cue that outranks every score cue.
Score thresholds were renamed
INSIGHT I/II/III, so the word RUNE on abanner always means a lens (97dd4bb).
purpose and roles, levels 2-3 on flows and tradeoffs, and levels 4+ on
invariants, with PREDICT transfer questions. The prompt enforces this; the
acceptance filter does not (see Known limitations). Each request names your
weakest lens and your earlier question stems (d2729b1, 451419d).
1ST TRY a/b,REDEEMED NN,REVIEW NNorALL CLEAR, and the lens that rose most, orSEE CODEXwhile reviews are open.and DEEPENS within one. It recaps the batch and names the lenses of the
next batch.
header gains a line that cycles journal lessons every 4 s (outstanding misses
first), or shows why generation failed, with
RETRY IN NS(bcc682e).B AGAIN:LEAVEfor 90 ticks. B does nothing on the lesson card (e417b41).Question generation
Richer, anonymized brief.
repo_context.rsbuilds a deterministic,size-limited brief from:
CODEQUEST.tomlsummaryCOMPONENT nPath-like words and tracked file and directory names are redacted. Vendored,
generated, test, binary, huge and symlinked files are skipped (d2729b1).
Redaction now also catches
engine.rs:42,Cargo.toml's,_engine.rs_,engine.rs::wrap, and marked directories such assrc-tauri. Hidden HTML,reST and AsciiDoc comments, link definitions and TOML front matter are
removed (ec5cc9d).
Anti-trivia filter. The filter now matches phrases and word shapes, not
bare nouns.
WHY WRITE THE SAVE FILE ATOMICALLY?passes. Which-file,where-does, count, commit, branch, author, version, hash and path questions
are still rejected (d2729b1).
More tolerant parsing. The parser finds the question array inside prose
or code fences and recovers finished questions from a truncated reply. Bad
questions are dropped one at a time, so the rest of a mixed batch survives
(d2729b1).
Calibrated limits.
2 of 12 questions, because choices ran 32-45 characters against a
31-character limit.
questions of at most 100 characters.
caught location question. Answers were spread across all four slots, there
were three PREDICT questions at level 4, and the level focus held: 5 of 6
questions were purpose/roles at level 1, and 6 of 6 were invariants/tradeoffs
at level 4.
questions and put 11 of 12 answers in the first slot (f09c96e).
Rationale cap from display geometry.
RATIONALE_MAX_CHARSis 70, derivedfrom the 34-column, 3-row panel with a proof in its doc comment. The old
90-character allowance let through rationales that could not fit (ec5cc9d).
One repair pass. Questions that fail only on display mechanics (length,
missing why, unknown lens, non-ASCII) are sent back once with every violation
and its measurement. The repair uses only what is left of the 120 s budget
and is skipped when less than 10 s remains. Trivia and malformed questions
are never repaired (3bb18f3).
Prompts go to the CLI on stdin, not on the command line.
claude -pandcodex exec -receive the prompt on stdin, so the Windows command-line limitno longer trims the brief. The limit on prompt size is now
MAX_PROMPT_BYTES(1016c5d).
Full batches. Batches always hold six questions: a short delivery tops up
the open batch. Lower-level saved batches play first (50d6856, 390e428).
Sound
AudioSnapshotiscompared with the previous tick to drive four chip voices (two pulses, a
triangle and noise). Gameplay code never calls audio. At most one cue starts
per tick, and scene loops are cut on the tick their scene exits. Nothing
sounds while the device is off or booting (8b9f00f).
the first B of a leave (390e428)
src/speaker.js). Plays notes through WebAudio: pulse duty asperiodic waves and noise from a 15-bit LFSR. The audio context starts only on
a user gesture (8b9f00f).
engine overrun made every later note clamp to poll time.
longer ring past their cut.
tick and the hi-hat no longer sound identical (35d7fea).
screen when muted (README).
Accessibility
Reduced motion. The shell forwards
prefers-reduced-motion, at startupand on change, through
engine_set_reduced_motion. Decorative motionfreezes: blinks stay lit, heroes stop bobbing, starfields stop. A test
asserts an identical 720-tick scene timeline in both modes (4ca8249). New
lesson lines, rune blinks and the recall line are also static under reduced
motion (bcc682e, 97dd4bb). There is no in-game toggle.
Screen-reader transcript. A third engine output channel describes every
screen in plain language:
It is derived from the renderers' own helpers, so the words match the pixels.
It is published only when it changes and polled every 150 ms into a hidden,
polite live region (4cc9b71).
Only what changed is announced. Pressing Down no longer re-reads the whole
question, and each Datafall catch no longer re-reads the screen. A new screen
is read whole, while small in-place changes are read on their own (4446aae).
Keyboard and focus (shell).
START.
focus is restored on close and after rebuilds.
aria-livedevice status strip carries messages from outside thetrays (4068303).
Lesson card colour. Contrast is tested against the panel fill, and the
-/+markers do not rely on hue. The recall line and debrief text meetat least 4.5:1 (e417b41, bcc682e, 2bc7d29).
Reliability and fixes
Question requests and batches
behind the opening instead of leaving the Oracle waiting (50d6856).
or a newer batch; requests carry a sequence number (50d6856).
(50d6856).
(390e428).
padded by reviews closed early (a8b5669).
the same question is never queued twice (a8b5669).
NEEDED, REJECTED BATCH, and so on) instead of retrying silently (bcc682e).
Runs and scene flow
re-queues outstanding misses as reviews (50d6856).
stale run index, level or score cannot carry over (a8b5669, 50d6856).
can_signaltiming, and the quiz menu focuses BEGIN again (50d6856).restarting at attempt 1 (a8b5669).
phrase (310f878).
Git, cartridges and saves
REPOSITORY and forgets the cartridge. Each case reports its own reason. The
check runs under
LC_ALL=Cso the refusal is recognized in any locale(91b80c0, 6be1aca).
Before, it was read as empty and overwritten (91b80c0).
atomic read-modify-write under the save lock, and answers are written on a
background thread (d2729b1).
the UI thread, and every git query has a 15 s timeout (1016c5d).
(
WAIT FOR THE CARTRIDGE). The slot refuses changes while power is on, and asecond eject click is ignored (6be1aca).
repository. Recycling a card clears a saved slot that points at it (4068303,
6be1aca).
Providers and processes
run in their own process group and time out by killing the whole tree
(
killpg, ortaskkill /T /F) (1016c5d).the shell exits (a8b5669, a3dc60e).
banner or CLI version. Only
error:andfatal:lines count as a cause(91b80c0, e263e5d).
.cmdshims are now found(1016c5d).
Device shell
(4068303).
CLI call (4068303).
CODEX CLI UNAVAILABLE) now appears onthe device, the CHECK BATTERIES guide, the rear tab and the tray (4068303).
clears a stale CHECKING state (4068303).
longer undo a power-on (6be1aca).
drag (4068303).
(6be1aca).
Credits and docs
repositories are no longer credited to the
[yyyy] [name of copyright owner]placeholder. A contributor with two addresses no longer takes two author
slots (0ca60fa).
skipped forever (fd76ce6).
Developer experience
#[test]functions went from 103 onmainto 302, counted bygit grep. Eight carry#[ignore]; the live provider tests are among them.npm testruns 12 Node contract files, up from 9. New:device-shell-robustness,device-audioanddevice-transcript.tests/shell-harness.mjsruns the real shell adapter against a fake DOM, amanual clock and a scripted Tauri bridge.
tests/contract-css.mjschecks the reduced-motion CSS.(6be1aca).
verifyjob is unchanged. A newverify-nativejob runscargo clippy -D warningsandcargo testonwindows-latestandmacos-latest, under Git Bash on Windows, with a Swatinem cache. The Windows-and macOS-only code was never linted before (7f26eaa).
question_eval.rsis an ignored test run throughscripts/eval-questions.sh <claude|codex> <levels> [repo]. It runs the app'sreal generation path and prints ACCEPT or REJECT for each candidate with the
violated limits, then answer-position, lens, focus-lens and PREDICT tallies
and the acceptance rate.
CQ_EVAL_ROUNDSandCQ_EVAL_TIMEOUT_SECStune it.It was updated for the repair-era parser (7f26eaa, 200171e).
.gitattributespins text files to LF and marks.rgb/.rgbaand other assets as binary. Before,npm testcould not passon
core.autocrlf=trueWindows checkouts (07e5c1f).lib.rsinto the Tauri-freequestions.rs, and provenance parsing intoprovenance.rs(d2729b1,0ca60fa).
Compatibility
SCHEMA_VERSIONstays 1.plain-string choices without rationales. Those questions play without
lesson rationales.
answered_questionsload as retired questions.missed_questions, per-lens mastery,question_attempts,question_batch_levels) are optional (d2729b1, a8b5669, 50d6856).retire its question. Now it stays queued for review until redeemed
(d2729b1).
refuses to load instead of being silently replaced. An empty file still
starts a fresh save (91b80c0).
codexhandler, theopen-codexsignal and theoracle-codextemplate are optional. Menus without the route keepRETURN TO TITLE, and schema v1 still loads (a3746df). The repository's own
CODEQUEST.tomlanddocs/examples/CODEQUEST.tomlnow use these, andmainhas nocodexhandler. Because unknown handlers refuse the cartridge,builds from
mainwould reject these updated manifests (inferred from thecode, not tested).
claude -pwith no positionalprompt,
codex exec ... -). This depends on each CLI reading stdin in thosemodes (1016c5d).
engine_audio,engine_transcript,engine_set_reduced_motion.engine_set_cartridgeandcartridge_branchare now async. The browser demo returns no transcript and cannot add
cartridges from disk (it shows RUN IN TAURI).
Known limitations
least 4 of 6) or the PREDICT count; both are only asked for in the prompt
(oracle-quiz.md, Open decisions).
Claude Sonnet, this repository). They are not a benchmark across providers or
repositories.
and manifest audio templates are future work.
toggle.
when AI is disabled is also future work.
taskkill /Treaches helper processes only while the direct childis still running (91b80c0).
learner progress does (open decision).
a Developer ID and notarization for public distribution (README).
🤖 Generated with Claude Code