Commit Graph
4 Commits
Author SHA1 Message Date
7c897e9f2b feat(career): gigs backend — propose a setlist, log the completed set (#954)
* feat(career): gigs backend — propose a setlist, log the completed set

Career v3, WS3 (backend half). A gig is career's verb:

- POST /gigs/propose {instrument, genre, size}: setlist from the
  passport's own stubs — qualifying songs (per the genre's badge bar,
  family-aware) shuffled for a free re-roll, topped with the
  highest-accuracy near-bar songs as stakes, and filled from UNPLAYED
  genre songs when the passport is young (the first gig is how stubs
  start). Names the highest venue the current stars can book.
- POST /gigs: logs a COMPLETED set only (abandoned sets never log — no
  fail state). Per-song accuracy = MAX(last_accuracy) from song_stats,
  freshly written by the set's own plays; encore = avg ≥ the data-driven
  bar (passports.json gig.encore_accuracy, 0.75). Appends to the career
  state file (same atomic _save_json pattern).
- Passports view: per-passport gigs (newest first, capped 20) and
  per-instrument gig_count — the profile wall's gig line lights up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(career): gig backfill offsets by qualifying taken, not picks length

CodeRabbit on #954: after the stakes loop appends near-bar songs,
qualifying[len(picks):] overshoots and skips eligible qualifying songs
— a stocked passport could still get a short set.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 01:26:23 +02:00
6cc0312661 feat(career): genre families — sub-genres inherit the family drill (#951)
The enrichment fallback made the passport rack real (hundreds of MB
sub-genres) but only the five exact umbrella keys carried Virtuoso
drills. Genres now resolve to a family by keyword substring (MB's
vocabulary is open — 'metalcore' must hit metal without an alias),
first-match-wins in list order ('blues rock' → blues), and inherit the
family's requirement from the same genres map. Exact entries still win;
per-instrument scoping unchanged; unmatched genres stay songs-only.

Data: families for metal (incl. djent/grindcore/thrash/doom), blues,
jazz (bebop/swing/bossa), funk (disco), rock (punk/grunge/shoegaze).

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 20:05:05 +02:00
4027c31a61 feat(career): curated genre drills — per-instrument, achievably cleared (#943)
Career v2, WS3. Bronze in blues/rock/metal/funk/jazz now also asks for
the genre's signature Virtuoso drill, data-driven in passports.json:
blues_shuffle, rock_power_backbeat, melodic_metal_gallop,
sixteenth_pocket, vl_shells — with career-side display labels the
passport page renders instead of raw node ids.

- virtuoso_nodes becomes {instrument: [node_ids]} so a keys passport
  never demands a guitar drill; a flat list keeps meaning guitar
  (virtuoso's content is guitar-first).
- _node_cleared also accepts keysCleared (a top-tier clean pass in one
  key — virtuoso's FIRST gained-only artifact). The depth rungs
  additionally require a maxed speed tier, too high a bar for Bronze.
- Genres without a curated entry stay songs-only.

Note: pre-release behavior change — v1 passports aren't in any shipped
build, so no earned badge can demote in the wild.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 15:14:39 +02:00
3832a5762b feat(career): passport backend — genre badges computed from stars (#935)
The badge-journey layer on top of career stars (Christian's career-mode
v2 design, composed with the shipped venue system). Badges are computed
on read from song_stats × the library's effective genre — never stored:
Bronze = N genre songs at min_stars (data-driven in passports.json,
default 5 songs at 2★) plus any configured virtuoso drill nodes.

New endpoints under /api/plugins/career/:
- GET  /passports        passport walls per instrument: badges, ticket
                         stubs (qualifying songs), library genres, drills
- POST /passports/commit instrument commitment (idempotent wax seal)
- POST /passports/open   open a genre passport (implies commitment)
- POST /drill-state      intake for the relayed virtuoso.progress
                         snapshot (career's frontend listens on the bus)

Instrument attribution reuses progression.instrument_for_arrangement via
the song_stats arrangement index; the genre column goes through the
host's override-aware effective-genre SQL. Non-graded instruments (bass,
drums) render shown-not-judged — repertoire, never a false badge denial.

Persisted state (commitments, opened passports, drill snapshot) lives
under CONFIG_DIR/career/ and rides the settings export bundle via
settings.server_files; a minimal settings.html documents it.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 11:47:59 +02:00