mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-07-22 21:01:40 +00:00
Adds the Achievements & Feats of Power local engine, fully offline. Core (static/v3/profile.js): the Profile screen becomes tabbed exactly like v3 Settings (.fb-tabbar/.fb-tab/.fb-tabpanel, active tab persisted in localStorage 'v3-profile-tab'). A Profile (main) tab carries the existing cards + a Feats trophy-shelf mount (#v3-profile-feats-slot, earned-only), and an Achievements tab carries a plugin mount (#v3-profile-achievements-mount) + empty-state note. A new `v3:profile-rendered` event fires after every render so the plugin re-injects (mirrors v3:settings-rendered). New bundled plugin (plugins/achievements/): SQLite engine (unlocks/counters/comp_ledger/sync_queue) with pure threshold/criterion math in the testable sibling engine.py (P-V); routes activity/ report-unlock/report-criterion/catalog/earned/feats/remove-me. Feats read activity counters only (batched song:ended POST; notes only when notedetect present — graceful degradation); competency Achievements evaluate from progression events only — the integration law, never crossed. Catalogue is always shown (locked=greyed), grouped by the real progression paths (Global/Guitar/Bass/Drums/Keys, auto-extending) with per-category earned badges. Versioned window.feedBack.achievements registration API with the __feedBackAchievementsPending load-order queue + achievements:ready event. Verified natively (uvicorn) end-to-end + Playwright (tabbar, earned-only Feats shelf, greyed catalogue, registration API, zero console errors); 24 plugin tests pass incl. the integration-law assertion. Opt-in/privacy/data-min gate (PR2) and the hosted wall (PR3) follow. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
69 lines
2.3 KiB
JSON
69 lines
2.3 KiB
JSON
{
|
|
"version": 1,
|
|
"global": [
|
|
{
|
|
"id": "first_steps",
|
|
"title": "First Steps",
|
|
"description": "Reach Mastery Rank 1 — finish onboarding.",
|
|
"category": "global",
|
|
"sourceId": "achievements",
|
|
"criterion": { "type": "mastery_rank", "tiers": [1] },
|
|
"tiers": [1]
|
|
},
|
|
{
|
|
"id": "ascendant",
|
|
"title": "Ascendant",
|
|
"description": "Climb the Mastery Rank ladder.",
|
|
"category": "global",
|
|
"sourceId": "achievements",
|
|
"criterion": { "type": "mastery_rank", "tiers": [10, 25, 50] },
|
|
"tiers": [10, 25, 50],
|
|
"tier_titles": ["Ascendant I", "Ascendant II", "Ascendant III"]
|
|
},
|
|
{
|
|
"id": "steady_hands",
|
|
"title": "Steady Hands",
|
|
"description": "Make a real advance on many separate days.",
|
|
"category": "global",
|
|
"sourceId": "achievements",
|
|
"criterion": { "type": "growth_streak_days", "tiers": [7, 30, 100] },
|
|
"tiers": [7, 30, 100],
|
|
"tier_titles": ["Steady Hands (7)", "Steady Hands (30)", "Steady Hands (100)"]
|
|
},
|
|
{
|
|
"id": "renaissance",
|
|
"title": "Renaissance",
|
|
"description": "Reach Level 10 across several distinct instrument paths.",
|
|
"category": "global",
|
|
"sourceId": "achievements",
|
|
"criterion": { "type": "paths_at_level", "level": 10, "tiers": [2, 3, 5] },
|
|
"tiers": [2, 3, 5],
|
|
"tier_titles": ["Renaissance (2)", "Renaissance (3)", "Renaissance (5)"]
|
|
}
|
|
],
|
|
"per_instrument": [
|
|
{
|
|
"id": "path_rank",
|
|
"title": "{Inst} Mastery",
|
|
"description": "Climb the {Inst} path: Apprentice, Journeyman, Master.",
|
|
"sourceId": "achievements",
|
|
"criterion": { "type": "path_level", "tiers": [10, 25, "max"] },
|
|
"tier_titles": ["{Inst} Apprentice", "{Inst} Journeyman", "{Inst} Master"]
|
|
},
|
|
{
|
|
"id": "personal_best",
|
|
"title": "Personal Best ({Inst})",
|
|
"description": "Beat your own best accuracy on any {Inst} chart.",
|
|
"sourceId": "achievements",
|
|
"criterion": { "type": "personal_best" }
|
|
},
|
|
{
|
|
"id": "challenger",
|
|
"title": "Challenger ({Inst})",
|
|
"description": "Clear a full level-up challenge set on the {Inst} path.",
|
|
"sourceId": "achievements",
|
|
"criterion": { "type": "challenge_set_cleared" }
|
|
}
|
|
]
|
|
}
|