Skip to content

feat: implement issue #2302 phases 1-3 easter egg mode#2303

Open
github-actions[bot] wants to merge 23 commits into
mainfrom
ai/easter-egg-phases-1-3-ec46518c4c4e0462
Open

feat: implement issue #2302 phases 1-3 easter egg mode#2303
github-actions[bot] wants to merge 23 commits into
mainfrom
ai/easter-egg-phases-1-3-ec46518c4c4e0462

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented Mar 10, 2026

Summary

This PR implements phases 1–3 requested in issue #2302 as an opt-in easter egg layer over existing observability pages (no route rewrite, off by default).

Phase 1 — foundation

  • Added persisted easterEggMode state via new store:
    • peek/src/store/useEasterEggStore.ts
  • Added world-map metadata and route matching helpers:
    • peek/src/features/easterEgg/worldMap.ts
    • peek/src/features/easterEgg/routeMatching.ts
    • peek/src/features/easterEgg/types.ts
  • Added isometric world map rendered only when mode is enabled:
    • peek/src/features/easterEgg/IsometricMap.tsx
    • peek/src/features/easterEgg/GoToMapFab.tsx
    • Wired in peek/src/App.tsx

Phase 2 — progression + discoverability

  • Added quest definitions and progression logic:
    • peek/src/features/easterEgg/quests.ts
    • peek/src/features/easterEgg/progress.ts
  • Added progression tracking (visited pages, objective confirmations, reward acknowledgements) in store.
  • Added subtle discoverability when enabled:
    • Sidebar quest chip + settings menu entry in peek/src/components/AppSidebar.tsx
    • Settings toggle in peek/src/components/SettingsPage.tsx

Phase 3 — narrative polish + docs

  • Added narrative reward content:
    • peek/src/features/easterEgg/content.ts
  • Added docs section and docs navigation wiring:
    • peek/src/docs/easter-egg-mode.md
    • peek/src/docs/sections.ts
    • peek/src/components/DocsNavSidebar.tsx
    • peek/src/components/DocsPage.tsx

Tests

Added/updated tests for new behavior:

  • peek/tests/unit/easterEggProgress.test.ts
  • peek/tests/unit/useEasterEggStore.test.ts
  • peek/tests/unit/storeResetters.test.ts
  • peek/tests/component/SettingsPage.test.tsx
  • peek/tests/component/AppSidebar.test.tsx

Validation run

  • make lint
  • cd peek && npm run test:unit -- tests/unit/easterEggProgress.test.ts tests/unit/useEasterEggStore.test.ts tests/unit/storeResetters.test.ts tests/component/SettingsPage.test.tsx tests/component/AppSidebar.test.tsx
  • make build

All passed.


The body of this PR is automatically managed by the workflow runtime.


The body of this PR is automatically managed by the Update PR Body workflow.

…er-egg layer with persisted mode state,\nworld map metadata, quest progression, narrative rewards, docs updates,\nand UI integrations in settings/sidebar/app overlay.\n\nValidation:\n- make lint\n- cd peek && npm run test:unit -- tests/unit/easterEggProgress.test.ts tests/unit/useEasterEggStore.test.ts tests/unit/storeResetters.test.ts tests/component/SettingsPage.test.tsx tests/component/AppSidebar.test.tsx\n- make build\n\nCo-authored-by: Copilot <[email protected]>
@github-actions
Copy link
Copy Markdown
Contributor Author

github-actions Bot commented Mar 10, 2026

PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://elastic.github.io/ai-github-actions-playground/pr-preview/pr-2303/

Built to branch gh-pages at 2026-03-10 21:18 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@strawgate
Copy link
Copy Markdown
Contributor

/ai I get the following when activating the Easter mode

Something went wrong

Minified React error #185; visit https://reactjs.org/docs/error-decoder.html?invariant=185 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.

github-actions Bot and others added 2 commits March 10, 2026 15:40
Use a shallow selector in IsometricOverlay to avoid useSyncExternalStore snapshot churn, which caused maximum update depth errors when Easter Egg mode was enabled. Also make progression actions idempotent to avoid no-op store updates and add regression tests.

Co-authored-by: Copilot <[email protected]>
@strawgate strawgate marked this pull request as ready for review March 10, 2026 16:01
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 10, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an opt-in Easter Egg Mode: a persisted Zustand store (useEasterEggStore) with resetter registration; domain types, world map, quests, rewards, route-matching, and quest-progress utilities; a new IsometricOverlay React component and UI wiring in App to mark visited pages and conditionally render the overlay. Integrates the mode into Settings, Docs, and sidebar (including a quest progress chip), updates reset registry wiring, and adds unit and component tests for the store, progress logic, route matching, and UI integrations.

Possibly related PRs

  • elastic/ai-github-actions-playground PR 2298: Refactors the centralized reset registry/storeResetters and touches the same resetter registration patterns used to register the new Easter Egg store.
  • elastic/ai-github-actions-playground PR 477: Modifies the centralized store reset registry and import/register patterns, directly intersecting with the added easterEgg reset wiring.
  • elastic/ai-github-actions-playground PR 1762: Registers additional Zustand stores in storeResetters.ts, touching the same file and reset-scope mechanics updated by this change.
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed PR implements all core objectives from #2302: isometric overlay layer, world-map metadata, quest/progression mechanics, persistence, and discoverability UI across phases 1-3.
Out of Scope Changes check ✅ Passed All changes align with issue #2302 objectives. Additions include easter egg feature (store, components, logic), documentation, tests, and integration with existing UI—no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ai/easter-egg-phases-1-3-ec46518c4c4e0462
  • 🛠️ Update Documentation: Commit on current branch
  • 🛠️ Update Documentation: Create PR

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]

This comment was marked as resolved.

github-actions[bot]

This comment was marked as resolved.

- Gate page-visit tracking on `connected` state to prevent false completions
- Wrap IsometricOverlay in ErrorBoundary to isolate render failures
- Fix dual-selected menu items in settings menu (mutually exclusive)
- Move easter-egg docs to alphabetical position in sections array
- Fix invalid `success.50` palette key → use `success.main` with alpha
- Simplify pendingReward derivation (remove redundant null check)
- Make buildQuestProgress order-independent with two-pass approach
- Improve test to cover runtime activation path, not just cold-start

Co-Authored-By: Claude Opus 4.6 <[email protected]>
coderabbitai[bot]

This comment was marked as resolved.

@github-actions github-actions Bot mentioned this pull request Mar 10, 2026
@github-actions
Copy link
Copy Markdown
Contributor Author

github-actions Bot commented Mar 10, 2026

✅ UI Smoke Test Review — 07e4de8

🎭 Smoke Test Results

Status: ✅ All passed

Total Passed Failed Flaky Skipped
11 11 0 0 0
Individual test results
Status Test Duration
onboarding user reaches the connect entrypoint from the welcome screen 589ms
add data entrypoint exposes the technology picker and step 2 1596ms
metrics user connects, picks a metric, and gets a line chart-ready result 1883ms
traces user opens a trace and pivots from service map context into Query Lab 2248ms
security-focused user validates auth tab switching before submitting credentials 653ms
ops user confirms connection guardrails and can reset back to the landing state 2454ms
query lab preserves query text and results after navigating to Console and back 2335ms
logs explorer route is available and runs a logs query 1814ms
logs landing page dimension drill-down opens query lab with selected filter 1778ms
task manager, ILM, and templates pages render with headings 1581ms
pages have no axe accessibility violations 15292ms

📸 Screenshot Preflight

Result: ✅ Clean — no errors detected

No console errors, page errors, or UI alerts detected.

💡 Recommendations

  • ✨ All smoke tests pass and no UI errors detected. Ready for review.

📎 Full diagnostics and screenshots: workflow run

@github-actions

This comment has been minimized.

@github-actions
Copy link
Copy Markdown
Contributor Author

Addressed all currently unresolved review threads with surgical fixes and pushed commit 2c39d2b.

  • Fixed persistence-reset test coverage in peek/tests/unit/useEasterEggStore.test.ts by asserting localStorage.removeItem("elastic-peek-easter-egg") is called during resetEasterEggState.
  • Updated reward panel background in peek/src/features/easterEgg/IsometricOverlay.tsx to use alpha(theme.palette.success.main, 0.08) instead of hex-string concatenation, ensuring compatibility with non-hex theme color formats.
  • Resolved addressed threads: PRRT_kwDORVGiRc5zWjXL, PRRT_kwDORVGiRc5zWpEZ, PRRT_kwDORVGiRc5zXgHc.
  • No inline reply-only threads were needed in this pass.
Validation run
  • cd peek && npm ci --quiet
  • make lint
  • cd peek && npm run test:unit -- tests/unit/useEasterEggStore.test.ts
  • make build

Notes:

  • make lint-full currently reports pre-existing warnings in unrelated files and was not modified in this fix.
  • Existing smoke-review CI failures point to a detached-element timeout in Logs E2E (smoke.spec.ts services card click); no clear one-line/surgical fix was identifiable from this change set, so not altered here.

What is this? | From workflow: Address PR Review Feedback

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

coderabbitai[bot]

This comment was marked as resolved.

- split stable store actions out of useShallow selection in overlay
- add overlay collapse toggle and responsive mobile positioning
- export STORE_NAME and use it in unit test expectations

Co-authored-by: Copilot <[email protected]>
Comment thread peek/src/features/easterEgg/routeMatching.ts Outdated
coderabbitai[bot]

This comment was marked as resolved.

github-actions Bot and others added 2 commits March 10, 2026 17:37
- rank static routes ahead of parameterized routes in easter egg page matching
- expose overlay collapse state and objective-specific button labels for a11y
- ensure reset clears persisted easter egg store key
- add regression tests for route matching and persistence reset

Co-authored-by: Copilot <[email protected]>
@github-actions
Copy link
Copy Markdown
Contributor Author

Addressed all currently unresolved review threads with a focused follow-up and pushed commit 385b9a0.

Implemented fixes:

  • peek/src/features/easterEgg/routeMatching.ts: route ranking now prioritizes more static routes over parameterized ones, preventing /hosts/linux from resolving as hostDetail.
  • peek/src/features/easterEgg/IsometricOverlay.tsx: added aria-expanded + aria-controls on the collapse toggle and objective-specific aria-labels for Go / Complete action buttons.
  • peek/src/store/useEasterEggStore.ts: reordered reset operations so state resets first, then persisted storage key is removed.
  • Added regression coverage in peek/tests/unit/routeMatching.test.ts and strengthened persistence reset assertion in peek/tests/unit/useEasterEggStore.test.ts.

No inline reply-only threads were needed; all unresolved items were addressed directly and resolved.

Validation
  • cd peek && npm ci --quiet
  • cd peek && npm run test:unit -- tests/unit/routeMatching.test.ts tests/unit/useEasterEggStore.test.ts tests/component/App.test.tsx
  • make lint (reported success; source-file lint step skipped due missing local main ref in this runner)
  • make build
  • cd peek && npx oxfmt --check ...changed files...
  • cd peek && npx oxlint ...changed files...
  • Re-ran cd peek && npm run test:unit -- tests/unit/routeMatching.test.ts tests/unit/useEasterEggStore.test.ts tests/component/App.test.tsx

All executed test targets passed.


What is this? | From workflow: Address PR Review Feedback

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@peek/tests/unit/useEasterEggStore.test.ts`:
- Around line 5-77: Add a component-level regression test in the same test file
that mounts the routed app, toggles the easter-egg mode via the store, and
asserts the UI overlay mounts without crashing: use
useEasterEggStore.getState().setEasterEggMode(true) (and reset with
resetEasterEggState() in beforeEach/afterEach), render the app component used by
your app's routing (the routed app or <App /> used elsewhere in tests), then
assert the easter egg overlay element is present (query for the overlay DOM node
or component that appears when mode is enabled); ensure the test cleans up
localStorage/STORE_NAME as other tests do so it doesn't leak state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2473b643-fd2b-4016-8ee1-9d2754d2cc8c

📥 Commits

Reviewing files that changed from the base of the PR and between 355e050 and d1aa5f8.

📒 Files selected for processing (5)
  • peek/src/features/easterEgg/IsometricOverlay.tsx
  • peek/src/features/easterEgg/routeMatching.ts
  • peek/src/store/useEasterEggStore.ts
  • peek/tests/unit/routeMatching.test.ts
  • peek/tests/unit/useEasterEggStore.test.ts

Comment thread peek/tests/unit/useEasterEggStore.test.ts
strawgate and others added 2 commits March 10, 2026 10:56
Replace the quest overlay with a full-screen isometric map where the
wizard character walks to locations representing app pages. Each page
is a themed building with tooltips and visited indicators. Add a FAB
button (bottom-right) to open the map when easter egg mode is active.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@github-actions
Copy link
Copy Markdown
Contributor Author

Addressed review feedback with a surgical test update.

  • ✅ Fixed thread PRRT_kwDORVGiRc5zYN49 with code changes in peek/tests/unit/useEasterEggStore.test.ts.
  • Added a component-level regression in that file that mounts routed App, toggles Easter Egg mode after mount, and asserts the overlay renders without error.
  • Added local test setup in the same file (jsdom env + store reset + fetch stub) so the regression is deterministic and isolated.
  • No inline reply-only threads in this pass.
Validation
  • make lint
  • cd peek && npm run test:unit -- tests/unit/useEasterEggStore.test.ts
  • cd peek && npx playwright test tests/e2e/smoke.spec.ts -g "logs landing page dimension drill-down opens query lab with selected filter|pages have no axe accessibility violations" --project=chromium ❌ (still failing in current branch state; no additional smoke-test code changes were kept in this push)

What is this? | From workflow: Address PR Review Feedback

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

@github-actions
Copy link
Copy Markdown
Contributor Author

TL;DR

CI run 22917005733 failed in Unit Tests because one test cannot find the expected overlay label and the app hit a nuqs adapter runtime error during render. Immediate action: update the test harness to provide a nuqs adapter (or avoid rendering the DashboardsLandingPage path without one), then rerun unit tests.

Remediation

  • In peek/tests/unit/useEasterEggStore.test.ts (failure at :100), render App with a nuqs testing adapter (e.g., NuqsTestingAdapter) so hooks using useQueryStates have required context.
  • Re-run: cd peek && npm run test:unit -- tests/unit/useEasterEggStore.test.ts (or the changed-files unit test command used in CI) and confirm green before rerunning CI.
Investigation details

Root Cause

The failing step is Unit tests (changed files only) in job Unit Tests. The test tests/unit/useEasterEggStore.test.ts > ... renders the easter egg overlay when mode is enabled after mount fails because the expected labeled element is never rendered. Logs also show a runtime error: [nuqs] nuqs requires an adapter to work with your framework, originating from useDashboardFilters in DashboardsLandingPage, which indicates missing adapter context in the test render path.

Evidence

  • Workflow: https://github.com/elastic/ai-github-actions-playground/actions/runs/22917005733
  • Job/step: Unit TestsUnit tests (changed files only)
  • Key log excerpt:
    • ErrorBoundary caught: Error: [nuqs] nuqs requires an adapter to work with your framework.
    • FAIL unit-node tests/unit/useEasterEggStore.test.ts > ... renders the easter egg overlay when mode is enabled after mount
    • TestingLibraryElementError: Unable to find a label with the text of: /isometric quest overlay/i
    • failure location: tests/unit/useEasterEggStore.test.ts:100

Validation

  • Ran log-level investigation only (read workflow job metadata and failed job logs).
  • No local test rerun was performed in this detective pass.

Follow-up

  • I checked the most recent prior detective report on this PR; it diagnosed a different failure (UI Smoke Test PR Review timeout). This is a new, materially different failure and remediation.

What is this? | From workflow: PR Actions Detective

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

Add a close button to the isometric map header so users can dismiss the
overlay without navigating. Fix stale aria-label in the store test that
still referenced the removed IsometricOverlay component.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Copy link
Copy Markdown
Contributor Author

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Request changes.

peek/src/features/easterEgg/IsometricMap.tsx (around line 170) schedules delayed navigation with setTimeout but never cancels it when the map is closed/unmounted. Repro: click a destination, then close the map immediately; navigation still fires ~1s later. Please track/clear the timeout and guard the callback.

peek/src/features/easterEgg/quests.ts introduces multiple confirmAction objectives (e.g. line 26), but there is no runtime caller of completeObjective in app code (only store/tests). This makes those objectives impossible to complete in normal UI flow, blocking quest progression. Please wire a user action path to completeObjective and cover it with a component/integration test.


What is this? | From workflow: PR Review

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

…celebrations

Redesign the easter egg as a fun onboarding experience:

- Add Quest Journal panel (left side of map) showing active quest with
  objective checklist, progress bars, lock/complete indicators, and
  earned reward badges
- Highlight quest-target buildings with pulse animation and warning-
  colored labels so users know where to go next
- Show reward celebration overlay with pop animation when a quest
  completes (Scout Badge, Signal Keeper, Navigator Sigil)
- Add quest progress badge on the map FAB button (e.g. "1/3")
- Auto-open the map when easter egg mode is first enabled
- Simplify all objectives to visitPage (drop confirmAction) so
  progress happens naturally as users explore the app
- Rewrite quest narratives to teach observability concepts: dashboards,
  queries, cluster health, logs, metrics, hosts, traces, services,
  and the dev console

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Copy link
Copy Markdown
Contributor Author

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Requesting changes for one confirmed behavior bug.

In peek/src/features/easterEgg/IsometricMap.tsx (around lines 564–568 and 615), map travel uses setTimeout to navigate after the walk animation, but closing the map does not cancel that pending timer. Repro: click a destination, immediately click Close map before animation completes; the app still navigates anyway. Please cancel the pending timeout on close/unmount (and ideally guard in the callback) so closing the map truly aborts travel.


What is this? | From workflow: PR Review

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

strawgate and others added 4 commits March 10, 2026 13:54
Extract MapBuilding, QuestJournal, and RewardCelebration into their own
files to improve readability and maintainability. IsometricMap.tsx goes
from ~730 lines to ~320 lines.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
# Conflicts:
#	peek/src/components/AppSidebar.tsx
setWalking(true);
setCharacterPosition({ x: location.mapX, y: location.mapY });

setTimeout(() => {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Clicking a location schedules a delayed navigation, but closing the map does not cancel that pending timer. Repro: click a building, then immediately click Close map before the walk delay completes; the app still navigates after the map is dismissed.

This creates surprising state transitions and can pull users away from their current view.

Consider storing the timeout handle in a ref and clearing it when the map closes/unmounts (and treating close as cancel).

Copy link
Copy Markdown
Contributor Author

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Requesting changes due to two user-facing issues:

  1. peek/src/features/easterEgg/QuestJournal.tsx (line 80): on xs, the journal is width: 100% with higher z-index than the map, while the map area in IsometricMap also starts at left: 0 for xs. This fully covers the map and blocks location interaction on small screens.

  2. peek/src/features/easterEgg/IsometricMap.tsx (around lines 127-140): reward detection stores only one pendingReward while advancing prevProgressRef immediately. If multiple completions are processed before reward acknowledgement, earlier reward moments can be overwritten and never surfaced.

Please address these before merge.


What is this? | From workflow: PR Review

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

.filter((objective) => isObjectiveComplete(objective, state))
.map((objective) => objective.id);
const complete =
quest.objectives.length > 0 && completedObjectiveIds.length === quest.objectives.length;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

complete is computed without considering unlocked, so a locked quest can be counted as complete before prerequisites are met.

Concrete repro from current code:
buildQuestProgress(EASTER_EGG_QUESTS, { visitedPages: ['traces','services','console'], completedObjectiveIds: [] })
returns quest 3 as { unlocked: false, complete: true }.

That leaks into user-facing state (GoToMapFab counts p.complete) and can trigger out-of-order reward moments in IsometricMap (cur.complete && !old.complete) before the prerequisite quest is finished.

Please gate completion on unlock (for example, const complete = unlocked && ...) or otherwise prevent locked quests from being treated as complete/rewardable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add isometric 2d video game

1 participant