feat(ktt): Kasimir Teaches TLAs acronym trainer (flag OFF) - #2262
Merged
Merged
Conversation
- Adds KTT_LESSON to the standalone type TaskType enum (db/schema.cds:19) - Adds KTT_LESSON to the inline TaskRecords.taskType enum (db/schema.cds:195) - Creates db/ktt.cds with KttLessons : cuid entity (legacyId, slug, unitId, title, order) - Registers KttLessons in db/persistence.cds for hdbmigrationtable deploy - Adds using from './ktt' to schema.cds for consistency with peer files - Adds test/unit/ktt-model.test.js guarding enum + entity shape
- Add srv/lib/ktt/merge.js: pure mergeProgress(local, remote) helper (sorted union of mastered slugs, numeric max of xp/streak) - Wire syncProgress action handler in ktt-service.js: reconstructs remote state from KTT_LESSON TaskRecords, merges with localJson, back-fills any locally-mastered lessons not yet in HANA via the Task-2 idempotency guard - Add KttLessons to cds.entities destructure in init() - Test: ktt-merge.test.js (pure unit), ktt-complete.test.js (served-mode syncProgress + no-double-insert assertion)
Extends getMyCompletedTutorials in srv/lib/user-progress.js to include
KTT_LESSON TaskRecords alongside TUTORIAL/PUZZLE/PETOBERFEST. Mirrors the
existing PETOBERFEST path: adds KTT_LESSON to the taskType IN list, collects
kttIds, fetches KttLessons metadata via SELECT.from(KttLessons), and maps
records to { kind: 'ktt', slug, title, completionDate, attemptNumber }.
No new module imports — KttLessons comes from cds.entities().
…r sync - Lesson.vue: drives Task 9 engine, story/drill beats, KasimirStage mood binding (teaching/thinking/correct/wrong/celebrate), emits complete(lessonId) - Results.vue: XP display, banter line, continue/back to map - progress.ts: loadLocal/saveLocal (localStorage), mergeProgress (union mastered, max xp/streak) — exact parity with srv/lib/ktt/merge.js - server.ts: isAuthenticated (checks body.authenticated NOT r.ok — Akamai gotcha), completeLesson, syncProgress, fetchBanter; all best-effort, native fetch - App.vue: full screen state machine landing→map→lesson→results; saves progress locally first, then best-effort server sync if authenticated, banter opportunistic - Tests: progress.test.ts (6), Lesson.test.ts (8); all 26 KTT island tests pass - cp-list audit: no srv/lib/ktt/* reachable from content-store.js — no mta.yaml change
…Data banter URL, Fisher-Yates shuffle
1A: Lesson.vue emits complete(lessonId, sessionXp) — real engine XP, not +10 floor.
defineEmits updated to (e:'complete', lessonId:string, xp:number)=>void.
1B: App.vue captures prevXp before merge; lastXp = updated.xp - prevXp (earned delta).
Results now shows the session delta on every lesson, not cumulative.
2: screen.value='results' moved ABOVE auth probe; isAuthenticated+completeLesson+syncProgress
run as fire-and-forget .then() chain — Results screen appears immediately.
3: fetchBanter URL changed to OData v4 function syntax: banter(context='...').
4: drillChoices uses Fisher-Yates shuffle; Lesson.test.ts selects by text not index (shuffle-safe).
Tests: +2 new (two-drill 20 XP; emitted XP is delta not floor) — 28/28 pass (6 files).
Adds /explore/ktt/ route: _index.md content file (type:explore, layout:ktt) and ktt.html layout with SSR unit list, #ktt-mount data attributes, noscript fallback, and island-src.html partial for the content-hashed bundle reference. Data-attribute escaping: double-quoted attribute + Hugo auto-escaping (established pattern matching verb-spine.html); apostrophes in Kasimir's dialog round-trip correctly through ' → ' → JSON.parse().
Extend generate-ktt-lessons.ts to also emit db/data/...-KttLessons.csv (deterministic UUID per legacyId; slug == lesson id). Without this the catalog shipped empty and every KTT completion was dropped from MyCompletions (spec 7.1/7.2). Reconcile ktt-complete to clear the now- seeded table before its fixture; add ktt-seed regression that relies on the seed (no manual insert).
Gate the readonly Lessons projection with a before('READ') 503 reject so
the page fails closed via the runtime DB flag (spec 7.7). Island probes
isKttEnabled() on mount and renders a coming-soon state without mounting
the drill engine or touching localStorage when disabled. Cover both
states in App.test and add READ-gate assertions to ktt-flag-gate.
jung-thomas
force-pushed
the
worktree-ktt-kasimir
branch
from
September 12, 2026 12:05
1cf754d to
3e56e5d
Compare
- server.ts completeLesson/syncProgress now use csrfFetch (@shared/csrf-fetch) to satisfy check-csrf-clients static guard for mutating requests - regenerate db/last-dev/csn.json via cds build --production to include KttLessons (was missing → cds-build-staging check failed) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jwtVerify used the real clock while the fixture signs exp = now(2026-09-11)+24h, so the suite went red for every PR from 2026-09-12 on. Pass currentDate: new Date(base.now) so exp is checked against the frozen fixture time. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…x facepalm z-order, fur tufts + head motion
…rrect sparkle, confetti burst on celebrate
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.
KTT — "Kasimir Teaches TLAs"
A tongue-in-cheek, Duolingo-style trainer at
/explore/ktt/teaching SAP three-letter acronyms, narrated by an animated Professor Kasimir. v1 = 4 units / 12 lessons / 48 acronyms; story-heavy core loop with quiz drills.Built via subagent-driven development from
docs/superpowers/plans/2026-09-11-ktt-kasimir-teaches-tlas.md(spec alongside it). 16 tasks, each spec+quality reviewed; one broad final whole-branch review (opus) + one scoped fix pass.Architecture
hugo-apps/src/ktt/) talking to a thin CAP service (srv/ktt-service.*).TaskRecords/TaskTypecompletion machinery via a newKTT_LESSONtype — no new progress entity.KttLessonsis a lightweight catalog entity so completion rows join to title/slug in MyCompletions.mergeProgressparity client↔server).scripts/generate-ktt-lessons.ts→hugo/data/ktt_lessons.jsonand a committeddb/data/…-KttLessons.csvseed), human-grounded facts.Feature gating
KTT_ENABLEDDB feature flag, default OFF. Toggles without redeploy.LessonsREAD return 503; the island probes on mount and renders a coming-soon state without mounting the drill engine or touching localStorage. Nav entry stays (static build-time link).Testing
user-progressall pass, incl. a seed-relying regression test.cds deploy --to sqlite::memory:clean (KttLessons.csv loads).test/e2e/ktt.spec.ts) runs post-deploy only.Follow-ups (deliberately out of this PR)
srv/lib/ktt/banter.js/srv/ktt-service.js): replace the unverified live-AI shape withOrchestrationClient.chatCompletion()persrv/lib/category-classifier-llm.js. Currently fail-open → users always get scripted fallback lines, never an error. Needs aiOrchestration binding + chat-settings deployment-ID.KasimirStage.vue): move<g id="collar">after the head group before flippingKTT_ENABLEDON (visual polish, needs live-browser iteration).Before flipping the flag ON
Verify on DEV first, then address the collar z-order polish item.
🤖 Generated with Claude Code