| name | nekocap-runtime-cautions |
| description | Use when working on Next.js routing/middleware, error handling, Sentry/logging, user-facing notifications, performance-sensitive paths (Octopus subtitle renderer, large lists, history-state debouncing, React Compiler / memoization), or any change to caption parsing or the caption editor. Also load this when verifying a change since there are no automated tests. |
This skill covers the runtime behaviors that don't fit cleanly elsewhere: routing, error handling, logging, performance hot-paths, and the project-specific gotchas that will silently bite agents who don't know about them.
Routing (web)
- File-based under
pages/. SSG with getStaticProps (often wrapped via wrapper.getStaticProps() from next-redux-wrapper) is the default; use getServerSideProps only when you genuinely need per-request data.
middleware.ts handles /browse → /browse/1, Firebase auth rewrites under /__/auth/*, and YouTube Shorts / watch URL → /create redirects. If you add a new URL shortcut or platform deep link, update this file.
react-router-dom@5 is used only inside the extension popup — don't import it in web code.
Error handling
- User-facing notifications: use antd's
message API (message.info, message.error). The content script already wires ChromeMessageType.InfoMessage → message.info(payload.message, payload.duration).
- Saga errors: wrap with
safe() from src/common/redux-utils.ts (see the nekocap-redux-feature skill). Unwrapped sagas kill the entire saga middleware silently.
- Web errors: Sentry is configured via
sentry.client.config.js / sentry.server.config.js and NEXT_PUBLIC_SENTRY_DSN.
- Extension errors: no Sentry coverage. Anything thrown in
background/, content/, popup/, or canvas-iframe/ only reaches the devtools console.
- No global error boundary.
react-error-boundary is installed but unused. If you wrap a risky tree, do it locally; don't add a global boundary without a discussion.
Logging
console.error / console.warn / console.log used directly — no project logger utility.
redux-logger is enabled in dev only (process.env.PRODUCTION ? undefined : logger in src/common/store/store.tsx). Don't enable it in production.
- No analytics (no Mixpanel, no GA). Don't add one without a discussion.
Performance hot-paths
- Octopus subtitle renderer is isolated in a sandboxed iframe (
src/extension/content/canvas-iframe/ + src/extension/content/containers/octopus-renderer-iframe-proxy.tsx) to keep its workload off the host page's main thread. Anything you add that runs per subtitle frame must stay inside that iframe or be cheap enough to not affect playback.
- YouTube history-state navigation is debounced 1s in
src/extension/background/index.tsx (~line 257). Reducing or removing the debounce causes double-injection.
- Lists: use
rc-virtual-list or react-virtualized (both already installed) for anything over ~50 rows. Don't render thousands of subtitle entries unvirtualized.
- Memoization: the project relies on
babel-plugin-react-compiler (React Compiler RC). Don't sprinkle useMemo / useCallback defensively — the compiler handles it. Add manual memoization only with a measured reason.
Project-specific cautions — these will bite you
-
NO TEST COVERAGE EXISTS. Vitest is wired up (vitest.config.ts) but the test suite is empty. npm run test passes because there's nothing to run. Manual verification in a real browser is the only safety net. After any change, load the unpacked extension on an actual video page (or run npm run watch:web for web changes), exercise the code path you touched, and watch the devtools console for errors. Don't trust "it builds" as proof of correctness.
-
Caption-editor mutations fail silently. There's a // TODO send error message to content script at src/common/feature/caption-editor/sagas.ts:304. Failed validation or parsing does not surface a toast — the user sees nothing. When touching caption editing, watch the background console; don't trust the absence of an error message.
-
Persisted Redux state can poison users. State is written to chrome.storage.local (see the nekocap-redux-feature skill). A bad shape ships to every existing user and stays there until they reset the extension. Migrations matter.
-
MV3 CSP forbids external scripts — see the nekocap-extension-messaging skill if you're adding runtime dependencies.
When you change X, also check Y
| If you change… | Also verify… |
|---|
| Caption parsing / Octopus rendering | Manually test ASS, SRT, and VTT — there are no tests |
| Caption-editor flow | Watch the background console for silent mutation errors |
middleware.ts | The redirect chain still works for YouTube Shorts/watch links |
| Performance-sensitive code | Profile in devtools; React Compiler output may not match your assumptions |
| Any extension change | Tested in both Chrome AND Firefox unpacked builds |
Anti-patterns
- Don't add
useMemo / useCallback defensively. Do trust the React Compiler unless you've measured.
- Don't introduce a global error boundary unannounced.
- Don't add analytics, a custom logger, or a second notification system. Use
message from antd.
- Don't rely on
npm run test passing as a correctness signal — the suite is empty.
- Don't ship a Redux state shape change without a migration plan.
- Don't render >50 rows unvirtualized.