Skip to content

feat: add detectAbilitySpike detector using activePlayer.abilities#41

Closed
Copilot wants to merge 12 commits into
mainfrom
copilot/add-detect-ability-spike-detector
Closed

feat: add detectAbilitySpike detector using activePlayer.abilities#41
Copilot wants to merge 12 commits into
mainfrom
copilot/add-detect-ability-spike-detector

Conversation

Copilot AI commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

What changed

New detectAbilitySpike detector that tracks activePlayer.abilities.{Q,W,E,R}.abilityLevel between ticks and fires on R power spikes:

  • context.ts — Added lastAbilityLevels: { Q, W, E, R } | null to ContextState. Initializes as null to distinguish "never observed" from "all abilities at zero"
  • detectors.tsdetectAbilitySpike fires ult_unlock (priority 78) on R 0→1, ult_rank_up (priority 74) on subsequent R rank-ups. Includes a first-snapshot guard: when lastAbilityLevels is null, it seeds current levels into state without firing, preventing bogus prompts on mid-game attach. Registered in ALL_DETECTORS ahead of detectLevelSpike so the more specific signal wins
  • prompts.json — Two new trading templates with {ability} / {ability_level} placeholders, filtered via existing canResolve logic
  • context.test.ts — 5 tests: first-snapshot seeding without firing, ult unlock, ult rank-up, no false positive on unchanged R, state tracking

Kept separate from detectLevelSpike — ability unlocks are a distinct signal with different prompt templates.

Why

detectLevelSpike fires generically on levels 2/3/6/9/11/16 without knowing which ability was actually ranked. The API exposes per-ability levels that change between ticks, enabling targeted prompts like "You just unlocked R — look for an all-in."

How to test

  • npx vitest run — 33/33 pass
  • npm run build — clean

Checklist

  • Builds clean
  • Tests pass
  • Tested manually

Copilot AI and others added 2 commits April 10, 2026 11:17
Track ability levels (Q/W/E/R) between ticks in ContextState.
Fire trading signals on R unlock (0→1, priority 78) and R rank-up
(priority 74). Add ability-spike prompt templates to trading category.

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/bd73da1a-80d0-4657-9fc1-6cfa24d07aa7

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>
Copilot AI changed the title [WIP] Add detect ability spike detector for active player abilities feat: add detectAbilitySpike detector using activePlayer.abilities Apr 10, 2026
Copilot AI requested a review from Ilia01 April 10, 2026 11:20
@Ilia01

Ilia01 commented Apr 10, 2026

Copy link
Copy Markdown
Owner

@copilot This needs a first-snapshot guard.

Right now lastAbilityLevels starts at zero, so if Beacon attaches mid-game to a champ that already has R unlocked or ranked, the first observed tick looks like a fresh ult spike and fires a bogus prompt.

I only want this detector to react to changes between observed ticks, not initial state seeding. Seed the stored ability levels on first observation and only fire once we've actually seen a transition.

Use null sentinel for lastAbilityLevels initial state. On first
observation, seed the levels without firing a spike. This prevents
bogus prompts when Beacon attaches mid-game to a champion that
already has R unlocked.

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/9de8993b-2547-4cef-879e-e07d94763238

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>

Copilot AI commented Apr 10, 2026

Copy link
Copy Markdown
Contributor Author

@copilot This needs a first-snapshot guard.

Right now lastAbilityLevels starts at zero, so if Beacon attaches mid-game to a champ that already has R unlocked or ranked, the first observed tick look...

Added in d0b4479. lastAbilityLevels now initializes as null instead of zeroes. On first observation, the detector seeds the current levels into state and returns null — no spike fires. Only subsequent ticks with an actual R level increase trigger ult_unlock or ult_rank_up. Added a dedicated test for mid-game attach.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a new stateful detector that uses activePlayer.abilities deltas to detect ultimate unlock/rank-up spikes and surface more targeted trading prompts than the existing level-based detector.

Changes:

  • Extend ContextState with lastAbilityLevels to persist per-ability levels across ticks.
  • Add detectAbilitySpike to emit ult_unlock / ult_rank_up trading signals based on R level changes, and register it in ALL_DETECTORS.
  • Add new trading prompt templates plus unit tests covering seeding, unlock, rank-up, and no-false-positive behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/main/detectors.ts Introduces detectAbilitySpike and wires it into the detector pipeline.
src/main/context.ts Adds AbilityLevels and lastAbilityLevels to context state initialization/type.
src/main/context.test.ts Extends snapshot builder to vary ability levels and adds tests for the new detector behavior.
data/prompts.json Adds two trading prompt templates that resolve via {ability} / {ability_level}.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI and others added 6 commits April 10, 2026 18:51
* Initial plan

* fix: add sanitizeText guard for prompt text in renderer.js

Defense-in-depth: strip HTML from prompt strings via DOMParser
before DOM insertion so future changes to innerHTML won't
introduce XSS. Also guards against null/undefined input.

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/4258d419-9607-411a-953a-1078f5aa6e8d

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>

* Rework: revert sanitizeText, add invariant test instead

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/78c779e4-6dd9-4ec6-b714-cd3064fe585f

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>

* fix: add renderer invariant test for innerText safety

Adds a static-analysis test (renderer.test.ts) that reads renderer.js
and asserts prompt text is set via innerText, not innerHTML.  This
protects against accidental XSS regression without mutating display text
at runtime.

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/78c779e4-6dd9-4ec6-b714-cd3064fe585f

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>
* Initial plan

* self-host Rajdhani font and remove unsafe-inline from CSP

- Add woff2 font files for Rajdhani (500, 600, 700) to assets/fonts/
- Create fonts.css with @font-face declarations
- Remove Google Fonts CDN links and preconnect from both HTML files
- Remove unsafe-inline from style-src and update font-src to 'self' in CSP

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/4659a37b-89be-454d-aa1d-e97931227613

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>

* add OFL.txt license for self-hosted Rajdhani font

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/9b20272a-0196-4444-a60e-c82d9a5034a8

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>
* Initial plan

* refactor: return state changes instead of mutating newState parameter

- processEvents now returns { newEvents, stateUpdates } instead of mutating newState
- detectVision and detectTabCheck return stateUpdates via DetectorResult
- detectItemCompleted no longer mutates newState; item tracking moved to deriveContext
- Removed newState from DetectorInput type
- deriveContext merges all state updates from processEvents and detectors
- Added DetectorResult.stateUpdates optional field
- Exported getRealItems for use in deriveContext
- Added 3 immutability tests verifying state parameter is never mutated

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/f01206eb-225b-423f-87d8-40450536f3d4

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>

* test: use deepFreeze and structuredClone in immutability tests

Shallow copy ({ ...state }) wouldn't catch mutations to nested arrays
like lastMyItemIds. Now deep-freeze the input state (so any mutation
throws) and use structuredClone for the comparison snapshot.

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/b5f93a2a-cf0e-4c5b-9297-1bad73959eff

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>
…polling while hidden (#38)

* Initial plan

* fix: clear cursor hit-test interval on overlay window close

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/2e9aa352-242a-45c9-a972-57c47476c060

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>

* perf: skip cursor hit-test polling while overlay is hidden

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/5b544e64-fa09-4a43-8318-f0f4f7ad555c

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>
* Initial plan

* chore: document intentional output mode toggle bypass in main.ts

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/80f417f6-e948-4493-b31e-72fcf212dc5f

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>
* Initial plan

* feat: distinguish 'waiting for game' from server errors

Categorize FETCH_ERROR by error type:
- ECONNREFUSED/ECONNRESET → game_not_running (waiting for game)
- Certificate errors → cert_error (config problem)
- Other errors → unknown

Only game_not_running resets to WAITING_FOR_GAME. Other errors
surface to the hub UI with an error state (red styling + reason).

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/3c40a88e-4902-4fbb-b2da-880452de9dc1

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>

* fix: reset prompt state and window visibility on non-game errors

Error transitions now call resetState() in handleServerMessage and
stopPromptLoop()/overlay.hide()/hub.show() in main.ts, ensuring
cooldown timers, prompt history, and window visibility are properly
cleaned up when cert or unknown errors occur mid-game.

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/473cd351-5bf6-4edf-bb16-11738d19ab65

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>

* fix: type-safe error narrowing and preserve error state on server exit

- Use typeof transition === 'object' narrowing before accessing .type
- Track lastAppStatus to prevent exit handler from overwriting error state
  with 'waiting'

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/6fedba23-f7b2-4b19-9188-1d3d53c5c492

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>

* refactor: use union type for lastAppStatus instead of loose string

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/6fedba23-f7b2-4b19-9188-1d3d53c5c492

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>
@Ilia01

Ilia01 commented Apr 10, 2026

Copy link
Copy Markdown
Owner

@copilot this branch is now stale against latest after #35 merged. Please rebase/update it and adapt the ability-spike work to the current state-update flow instead of relying on the older mutation path. Keep the first-observation seed-without-firing behavior for .

@Ilia01

Ilia01 commented Apr 10, 2026

Copy link
Copy Markdown
Owner

@copilot correction to my last note: this branch is now stale against latest main after #35 merged. Please rebase or update it and adapt the ability-spike work to the current state-update flow instead of relying on the older newState mutation path. Keep the first-observation seed-without-firing behavior for lastAbilityLevels.

Copilot AI and others added 2 commits April 10, 2026 15:05
…ility-spike-detector

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>
Merge latest main (post-PR #35) and adapt ability-spike work:

- Move ability-level tracking into deriveContext (like item tracking)
  so lastAbilityLevels is updated every snapshot regardless of detector
  result
- Remove newState mutation from detectAbilitySpike; it now reads
  directly from state and snapshot as a pure comparison function
- Remove unused AbilityLevels import from detectors.ts
- Keep first-observation seed-without-firing behavior via null guard

Agent-Logs-Url: https://github.com/Ilia01/Beacon/sessions/e6acf140-33ae-4815-9398-30ba93834e08

Co-authored-by: Ilia01 <155351862+Ilia01@users.noreply.github.com>

Copilot AI commented Apr 10, 2026

Copy link
Copy Markdown
Contributor Author

@copilot this branch is now stale against latest after #35 merged. Please rebase/update it and adapt the ability-spike work to the current state-update flow instead of relying on the older mutation ...

Merged latest main and adapted in 463271f. Ability-level tracking now lives in deriveContext (same pattern as item tracking), so lastAbilityLevels is updated every snapshot regardless of whether the detector fires. detectAbilitySpike is now a pure comparison function — no newState mutation. First-observation seed-without-firing behavior is preserved via the null guard.

@Ilia01

Ilia01 commented Apr 10, 2026

Copy link
Copy Markdown
Owner

@copilot This branch is not just stale, it is still built on the pre-#35 detector flow. writes through , and the branch still carries the older context shape, so I can't merge it on top of current main as-is. Rebuild this on current main and implement the ability-spike state update through the current state-update path instead of reviving the old mutable detector input shape.

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: ability spike detector using activePlayer.abilities

3 participants