fix(mascot): complete Yue locale coverage and konami sliding window #156

Merged
ellamizuki merged 1 commit from fix/yue-mascot-audit-fixes into main 2026-09-09 00:27:16 +00:00
ellamizuki commented 2026-09-09 00:22:44 +00:00 (Migrated from github.com)

Summary

Mascot audit fixes: Yue's AI chat was missing response-language instructions for three of the seven supported locales, and the Konami easter egg used a broken key buffer that could never trigger the code in realistic use.

Evidence / Reproduction

build_yue_system_message looked up the response-language instruction in a table keyed by en, pt, fr, de, zh:

  • es and ja were absent entirely: users of /es/ and /ja/ got an empty instruction and the mascot answered in English.
  • zh did not match any website locale: the canonical locale is zh-CN, so the Chinese instruction was also unreachable.

The Konami handler pushed every key into a buffer that only reset once it exceeded the code length. After a single wrong key, the buffer held a poisoned window (e.g. [x, ArrowUp, ArrowUp, ArrowDown, ...]) and every() compared it against the code from index 0, so the code could only ever be detected if typed as the very first 10 keys of a session.

Root cause

  1. Locale table drifted from packages/i18n/src/config.ts (missing es, ja; stale zh key instead of zh-CN).
  2. The Konami buffer kept non-prefix garbage instead of sliding to the longest suffix that is a prefix of the code.

Fix

  1. localeInstructions now covers all seven locales with the canonical zh-CN key, typed Record<string, string>.
  2. Konami detection keeps the longest suffix of typed keys matching a prefix of the code (classic sliding window). The sequence lives in a ref so rapid key events never read a stale closure, and the localStorage side effect moved out of the state updater (updaters must stay pure).

Tests

  • New yue-locale-coverage.test.ts: asserts every locale from the i18n registry produces a respond-in-language instruction, with explicit cases for zh-CN, ja, and es. Failed before the fix (3/3 red), passes now.
  • use-mascot-state.test.tsx: new case activating the Konami code when preceded by a wrong key. Failed on the old buffer (red), passes with the sliding window.

Validation

pnpm check:ci     # exit 0 (lint, typecheck, prettier, knip)
pnpm test:unit    # 63 files, 271 tests passed

Live verification on production before the fix: mascot widget renders, opens, and the AI chat answers (PT); /api/mascot/latest-post and /api/ai/chat/status return 200.

Residual risks

  • konamiSequenceRef is module-instance state: it intentionally survives re-renders but resets on full page reload (pre-existing behavior).
## Summary Mascot audit fixes: Yue's AI chat was missing response-language instructions for three of the seven supported locales, and the Konami easter egg used a broken key buffer that could never trigger the code in realistic use. ## Evidence / Reproduction `build_yue_system_message` looked up the response-language instruction in a table keyed by `en, pt, fr, de, zh`: - `es` and `ja` were absent entirely: users of `/es/` and `/ja/` got an empty instruction and the mascot answered in English. - `zh` did not match any website locale: the canonical locale is `zh-CN`, so the Chinese instruction was also unreachable. The Konami handler pushed every key into a buffer that only reset once it exceeded the code length. After a single wrong key, the buffer held a poisoned window (e.g. `[x, ArrowUp, ArrowUp, ArrowDown, ...]`) and `every()` compared it against the code from index 0, so the code could only ever be detected if typed as the very first 10 keys of a session. ## Root cause 1. Locale table drifted from `packages/i18n/src/config.ts` (missing `es`, `ja`; stale `zh` key instead of `zh-CN`). 2. The Konami buffer kept non-prefix garbage instead of sliding to the longest suffix that is a prefix of the code. ## Fix 1. `localeInstructions` now covers all seven locales with the canonical `zh-CN` key, typed `Record<string, string>`. 2. Konami detection keeps the longest suffix of typed keys matching a prefix of the code (classic sliding window). The sequence lives in a ref so rapid key events never read a stale closure, and the `localStorage` side effect moved out of the state updater (updaters must stay pure). ## Tests - New `yue-locale-coverage.test.ts`: asserts every locale from the i18n registry produces a respond-in-language instruction, with explicit cases for `zh-CN`, `ja`, and `es`. Failed before the fix (3/3 red), passes now. - `use-mascot-state.test.tsx`: new case activating the Konami code when preceded by a wrong key. Failed on the old buffer (red), passes with the sliding window. ## Validation ``` pnpm check:ci # exit 0 (lint, typecheck, prettier, knip) pnpm test:unit # 63 files, 271 tests passed ``` Live verification on production before the fix: mascot widget renders, opens, and the AI chat answers (PT); `/api/mascot/latest-post` and `/api/ai/chat/status` return 200. ## Residual risks - `konamiSequenceRef` is module-instance state: it intentionally survives re-renders but resets on full page reload (pre-existing behavior).
isyuricunha (Migrated from github.com) reviewed 2026-09-09 00:22:44 +00:00
vercel[bot] commented 2026-09-09 00:22:47 +00:00 (Migrated from github.com)

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
website-web Ready Ready Preview Sep 9, 2026 12:26am UTC
1 Skipped Deployment
Project Deployment Actions Updated
website-docs Ignored Ignored Preview Sep 9, 2026 12:26am UTC
[vc]: #fm3JyL4o7jccdgRWsFMqRv0uivm7nRYcJtO4HYefGbE=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJ3ZWJzaXRlLWRvY3MiLCJwcm9qZWN0SWQiOiJwcmpfMmVVMnNUTTBpZFk3MHJUYnNjQ0N0RXQ2TUZPdyIsInJvb3REaXJlY3RvcnkiOiJhcHBzL2RvY3MiLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20veXVyaS1jdW5oYXMtcHJvamVjdHMvd2Vic2l0ZS1kb2NzL0R0UDE0ZzhYOWNnRlNyVnc5TXpOUm1uY255d00iLCJwcmV2aWV3VXJsIjoid2Vic2l0ZS1kb2NzLWdpdC1maXgteXVlLW1hc2NvdC1hdWQtNTQyNDgwLXl1cmktY3VuaGFzLXByb2plY3RzLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiSUdOT1JFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiIifX0seyJuYW1lIjoid2Vic2l0ZS13ZWIiLCJwcm9qZWN0SWQiOiJwcmpfYjZyRmhBQVBISVhSNzNjZW9kRTB5dmxCdzM4MiIsInJvb3REaXJlY3RvcnkiOiJhcHBzL3dlYiIsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS95dXJpLWN1bmhhcy1wcm9qZWN0cy93ZWJzaXRlLXdlYi85TW1rYjVEUG51OWNDdjdrOVJ6a0p4Yk1IODRIIiwicHJldmlld1VybCI6IndlYnNpdGUtd2ViLWdpdC1maXgteXVlLW1hc2NvdC1hdWRpdC1maXhlcy15dXJpLWN1bmhhcy1wcm9qZWN0cy52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6IndlYnNpdGUtd2ViLWdpdC1maXgteXVlLW1hc2NvdC1hdWRpdC1maXhlcy15dXJpLWN1bmhhcy1wcm9qZWN0cy52ZXJjZWwuYXBwIn19XX0= The latest updates on your projects. Learn more about [Vercel for GitHub](https://vercel.link/github-learn-more). | Project | Deployment | Actions | Updated | | :--- | :----- | :------ | :------ | | <a href="https://vercel.com/yuri-cunhas-projects/website-web"><sup><img src="https://vercel.com/api/www/avatar?projectId=prj_b6rFhAAPHIXR73ceodE0yvlBw382&teamId=team_g087rqPZJa9s4DWPeZtQhxa9&s=32" width="16" height="16" align="middle" alt="" /></sup></a> [website-web](https://vercel.com/yuri-cunhas-projects/website-web) | ![Ready](https://vercel.com/static/status/ready.svg) [Ready](https://vercel.com/yuri-cunhas-projects/website-web/9Mmkb5DPnu9cCv7k9RzkJxbMH84H) | [Preview](https://website-web-git-fix-yue-mascot-audit-fixes-yuri-cunhas-projects.vercel.app) | <relative-time datetime="2026-09-09T00:26:59.706Z">Sep 9, 2026 12:26am UTC</relative-time> | <details><summary>1 Skipped Deployment</summary> | Project | Deployment | Actions | Updated | | :--- | :----- | :------ | :------ | | <a href="https://vercel.com/yuri-cunhas-projects/website-docs"><sup><img src="https://vercel.com/api/www/avatar?projectId=prj_2eU2sTM0idY70rTbscCCtEt6MFOw&teamId=team_g087rqPZJa9s4DWPeZtQhxa9&s=32" width="16" height="16" align="middle" alt="" /></sup></a> [website-docs](https://vercel.com/yuri-cunhas-projects/website-docs) | ![Ignored](https://vercel.com/static/status/canceled.svg) [Ignored](https://vercel.com/yuri-cunhas-projects/website-docs/DtP14g8X9cgFSrVw9MzNRmncnywM) | [Preview](https://website-docs-git-fix-yue-mascot-aud-542480-yuri-cunhas-projects.vercel.app) | <relative-time datetime="2026-09-09T00:26:59.705Z">Sep 9, 2026 12:26am UTC</relative-time> | </details>
Sign in to join this conversation.
No description provided.