Skip to content

v0.7.0: Campaign Trail, Points Market, learner profiles

Choose a tag to compare

@kentlangley kentlangley released this 24 Oct 20:25
· 8 commits to main since this release
0ec55f7

Added

  • Live map update on grind trail: after Explore/Practice / Continue-on-trail, stone colors (and fog when cheap) refresh on the client from the answer map payload + /api/map before Continue is shown. grindTrail.done + client trailDone exclude stones finished this session so Continue never re-offers them.
  • Quest complete high-five: when a quest finishes, a celebratory modal shows the quest medal and Campaign Trail progress (N/12), then three choices — 🧠 Reinforcement (light practice on quest topics / due reviews via /api/reinforce), 🛤️ Grind a Trail (Map zoomed to a quest subject), 🎲 Mini-games (free play). Cannot dismiss into silence; fires once when quest.done flips. Preview: /#quest-celebrate-preview.
  • Continue on this trail (free-play grind): after a My World map mini-session ("Explore now!" / "Practice now"), the end screen offers 🛤️ Continue on this trail (next ready/learning/review stone in the same subject, preferring forward in the same domain) as the primary action, with Back to map secondary. No auto-jump to the map when a next stone exists. Does not force quest mode, skip mastery, or award medals. Engine: nextTrailTopic; miniAfter includes next.
  • Learner profiles (#87): one household, several kids in one app.
    • Per-learner records (#82): a profiles registry; every per-learner key (progress, logs, quest packs, boosts, sandbox, clock offset) is prefixed L:<id>: for added learners, while the primary learner keeps the original keys. migrate-profiles.mjs snapshots every key, adds the registry (idempotent) and verifies the record is untouched; --rollback removes it. /api/state gains profile. No registry = one learner from [vars], exactly as before.
    • Who's playing? (#83): with 2+ profiles the kid app opens on a picker (avatars; a 🔒 PIN pad for profiles with a 4-digit PIN). The pick is remembered on the device (jl cookie, 1 year), so one-learner installs and remembered devices go straight in; "🔄 Switch player" in the footer. PINs are a kid-level guard: salted SHA-256, an HttpOnly token cookie, 1 s delay on a wrong PIN, 401 needPin on the kid APIs. GET /api/profiles, POST /api/profiles/select, POST /api/profiles/forget.
    • Parent view per kid (#84): a card per kid (avatar, grade, points, medals, mastered, day streak, last played) switches the report (/parent?learner=<id>); "👪 Players" adds a learner (name, grade, interests, avatar, optional PIN) and changes avatars, interests and PINs. POST /api/parent/profiles, POST /api/parent/profiles/update; an optional PARENT_PIN secret guards them. The primary learner's name and grade stay in [vars].
    • Per-learner nightly (#85): export-progress.mjs, publish-lessons.mjs, backup-progress.mjs and migrate-medals.mjs take --learner <id> (or LEARNER_ID); without it they act on the primary learner exactly as before (and stay offline with --offline). The export adds profile (id, age, grade, interests) and learners; added learners' exports and backups go to <id>/ sub-folders. LESSONS.md / NIGHTLY.md: loop over learners, one quest per kid at their own level. Wrangler calls time out after 3 minutes instead of hanging.
    • Placement quests (#86): a quest pack with "placement": true finds a new learner's level. Stages carry ladder + band and run from grade level down; ≥ 2/3 right on the first try passes a stage and skips the rest of its ladder; the result (S.placementQuest: level per ladder, beyondMap for subjects passed at the taxonomy's ceiling) goes to the export with taxonomyCeiling. No review gates or pizza starter in a placement quest. The validator checks the placement rules (4-7 stages, ≥ 3 subjects, 2-4 graded per stage, bands descending). The built-in diagnostic window now follows the learner's age (unchanged for ages 10-10.9) and uses a subject's top band above its ceiling. LESSONS.md "Placement quests"; packs/examples/example-placement.json.
      The export's quests.nextStep says PLACEMENT WAITING while a learner's placement quest is unfinished, so the nightly author never replaces it; publish-lessons.mjs prints the learner's real key.
  • Points Market (#74, idea credit: Christopher): points buy fun, choice and peeks, never mastery, medals or permanent fog clear.
    • Wallet (#68): S.wallet spend ledger; balance = earned (S.points, never decremented) − spent, computed server-side. GET /api/market, idempotent POST /api/market/buy {pid,item,meta} (same purchase id → one charge; one-time items → already), wallet on /api/state, purchase log events, a per-PT-day earned snapshot (last 60 days) and a market block in the export (spent7d/earned7d for price tuning). The whole pre-Market earned total is spendable on day one.
    • Skins + Market screen (#69): a 🛒 Market tab (balance, cards, buy → confirm → owned, wear / take off; keyboard and reduced-motion safe). 13 skins in 4 slots (ships 300–900, avatars 300–800, boss skins 600–1,000, map themes 800–1,500), bought once, owned forever, equip free (POST /api/market/equip). Equipped skins draw on the Map ships, a new avatar chip in the header, the boss emoji/frame and the Map sea/land/fog palette. The ⭐ chip now shows the spendable balance; the parent report shows both earned and spendable.
    • Scout flare (#70): 200 ⭐ lights one fogged island for 24 hours (from the Market, or "🎆 Light it up!" on a zoomed Map island): thinned fog, names on the ready/learning stones there, ❓ markers for its unclaimed fog finds. The fog returns at expiry; fogState adds flares only, and mask, clear counts and find eligibility are unchanged.
    • Prove-it ticket (#71): 150 ⭐ on a review that is due now (Market, or the "🎟️ Prove-It tickets" button on a review-gate card): one harder question, one try (a check item's "why" tier, or the generator one level up). Right first try = the normal review pass (Leitner box +1, new due date, normal points) + 20 bonus earned points; a miss = the review stays due (≤ 1 day, no demotion), no refund. Max 2 a Pacific day; POST /api/market/prove {pid, topicId}. Gates are unchanged: the gate on screen keeps its own item.
    • Pick your path (#72): 100 ⭐ picks the next new quest's topic (from 2–3 ready frontier topics offered that Pacific day, one per subject), and another 100 ⭐ picks its story theme (8 themes). One open pick of each kind; Play shows "🧭 Next quest: your pick!". The export lists market.choices / market.openChoices and adds a MARKET CHOICE line to quests.nextStep. A quest pack marks a pick used with "honors": {"path", "theme"} (validated by publish-lessons.mjs); "declined" closes the pick and refunds it with a new ledger row. LESSONS.md "Market choices", NIGHTLY.md and docs/NIGHTLY.md tell the nightly author how.
    • Hidden chests + cheap extras (#73): mystery chests appear in the Market only after nearby fog clears (20 topics world-wide, 15 % of a subject) and hold a cosmetic prize fixed by the purchase id (rare chest-only skins, stickers, titles). Extras: 🤫 Jane whisper hint token (40, also wires the existing Hint Token fog find), 🛡️ streak shield (60; keeps 🔥 on one missed question, never the ⭐ run or any mastery), 🧊 streak freeze (80; covers one missed day of the new 📅 days-played streak). Stock caps 5/3/2. POST /api/whisper; stats.daysPlayed in the export.
  • Campaign Trail + Fog Map (#54): always-on Play trail + taunt, Quest of Quests overlay, literal fog on the Map (direct mastery clears), fog finds (chests/secrets/power-ups, D11 garnish only), and Final Boss unlock persistence with a Finale authoring hook.

Fixed

  • Cosmetics, Jane whisper, fog, and review bursts (Landon feedback, #95): worn ship/theme/sticker show where kids look (map hero ship, app background, header sticker chip); whisper is a clear 💡 control that only spends a token when a real trap exists; map fog is darker with safe draw guards; free-play reviews no longer dump randomly or repeat the same overdue topic back-to-back; empty quest strip is honest when none is live.
  • New subject re-asking the same kid MCQ (#96): "🎨 New subject" no longer re-serves one fixed kid-prompt topic all sitting — skip fixed prompts already in recent when picking focus, and release focus after a fixed quiz item completes.