Skip to content

feat(ingestor): per-minute timeline snapshots (powerspike foundation)#568

Merged
ilyanfraimbault merged 2 commits into
developfrom
feat/567-per-minute-snapshots
Jun 19, 2026
Merged

feat(ingestor): per-minute timeline snapshots (powerspike foundation)#568
ilyanfraimbault merged 2 commits into
developfrom
feat/567-per-minute-snapshots

Conversation

@ilyanfraimbault

Copy link
Copy Markdown
Owner

First step of the reworked powerspike feature.

Why

The current snapshots are only at 5 fixed marks (5/10/15/20/30 min) — too coarse to measure an item's impact around its completion time. To draw a power curve over time and align spikes to events (items completed, levels 6/11/16), we need per-minute resolution.

Change

  • TimelineSnapshotBuilder: sample every minute (1..30) instead of 5 fixed marks. Riot frames are ~1/min, so each mark resolves to its own frame.
  • No schema change: IntervalMinute is already int; the unique index (MatchId, ParticipantId, IntervalMinute) still holds. No migration / no compiled-model regen.
  • ChampionTimelineLeadsQueryService: pinned to the canonical marks (5/10/15/20/30) so the existing leads curve renders identically across old (5-mark) and new (per-minute) games, and every interval keeps the full population behind the sample floor.

Forward-only

Denser sampling applies to newly-ingested games only (no backfill on the dev API key) — the powerspike read will fill in as games accumulate.

Verification

  • dotnet build --configuration Release: 0 warnings
  • 370 unit tests pass (builder tests unchanged — their sparse frames still only match 5/10)

Next (separate PR)

The powerspike read model + spike-detection (power-curve composite: combat + gold lead, opponent-relative; events annotated; spike magnitude = slope change after each event).

Closes #567

Capture a snapshot at every minute mark (1..30) instead of the 5 fixed marks
(5/10/15/20/30), so reads can align state to arbitrary event times (item
completed, level 6/11/16) and draw a power curve over time — the foundation
for the reworked powerspike read.

No schema change: IntervalMinute is already an int and the unique index
(MatchId, ParticipantId, IntervalMinute) still holds. Denser sampling is
forward-only (no backfill on the dev API key); games ingested earlier keep
their 5 marks.

Pin ChampionTimelineLeadsQueryService to the canonical marks (5/10/15/20/30)
so the leads curve renders identically across old (5-mark) and new (per-minute)
games and every interval keeps the full game population behind the sample floor.

Refs #567
Comment thread backend/tests/TrueMain.UnitTests/TimelineSnapshotBuilderTests.cs

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

Ce que fait ce PR
Passe l'echantillonnage des snapshots de 5 marques fixes (5/10/15/20/30 min) a 1 snapshot par minute (1..30), sans changement de schema. Le service de leads existant est epingle aux 5 marques canoniques pour maintenir une grille coherente entre les jeux anciens et nouveaux.

Points verifies

  • Enumerable.Range(1, MaxIntervalMinute) genere bien 1..30 (aucun off-by-one).
  • Filtre s2 : le second SelectMany joint sur s2.IntervalMinute == self.s1.IntervalMinute, ce qui contraint implicitement s2 aux memes intervalles que s1 — pas de bug de produit cartesien.
  • EF Core traduit correctement int[].Contains() en IN (5, 10, 15, 20, 30).
  • SelectFrame V-shape : l'optimisation early-break reste valide a 30 iterations/minute.
  • Duplication de [5, 10, 15, 20, 30] : intentionnelle et documentee — le commentaire explique explicitement pourquoi le service de leads ne derive pas de IntervalMinutes.

Aucun point bloquant identifie.

Suggestions (non bloquantes) :
Voir commentaire inline sur TimelineSnapshotBuilderTests.cs : un cas de test avec frames denses permettrait de prouver directement le nouveau comportement (minutes 1-N emises) plutot que de se reposer uniquement sur la regression des frames epars.

The existing tests only proved the regression case (sparse frames still yield
the 5/10 marks). Add a dense-timeline case (a frame on every minute 1..10) that
asserts a snapshot is emitted for each covered minute — direct coverage of the
per-minute change.

Refs #567
Comment thread backend/tests/TrueMain.UnitTests/TimelineSnapshotBuilderTests.cs

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changeset propre et bien ciblé. Aucun point bloquant.

Ce qui a été vérifié :

  1. TimelineSnapshotBuilder : Enumerable.Range(1, 30) produit [1..30] ; MaxIntervalMinute en private const est approprié.
  2. ChampionTimelineLeadsQueryService : LeadIntervalMinutes.Contains() se traduit en IN (5,10,15,20,30) par EF Core. Le filtre explicite sur s2 est inutile car le join s2.IntervalMinute == self.s1.IntervalMinute (ligne 99) contraint implicitement l'opposant aux mêmes marques canoniques — logique correcte.
  3. Tests : Build_EmitsRowForEveryMinuteCoveredByFrames valide le comportement clé ; la rétrocompat via frames sparse est couverte par le test existant.
  4. Pas de changement de schéma : l'index unique (MatchId, ParticipantId, IntervalMinute) reste cohérent.

Suggestions (non bloquantes) : voir commentaire inline — ajouter un second participant au nouveau test confirmerait l'absence d'interférence entre participants sur la sélection de frame.

@ilyanfraimbault ilyanfraimbault merged commit ad0103d into develop Jun 19, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(ingestor): per-minute timeline snapshots (powerspike foundation)

1 participant