- Make code changes on
mv2first. mainis the real MV3 branch now.mv3is historical. Do not use it as the normal MV3 source branch for new work or cross-repo sync.- Do not implement feature/fix work directly on
main. - If a task starts on another branch, switch to
mv2before editing unless the user explicitly asks otherwise. - If a task touches both HyperChat and LiveTL, HyperChat still goes first.
- Cross-repo order is mandatory:
- HyperChat
mv2 - HyperChat
main - HyperChat
mv3-ltl - LiveTL
develop - LiveTL
mv3-fr - LiveTL
release
- HyperChat
- Never start cross-repo work in LiveTL when the HyperChat submodule also needs to change.
- If the task also requires syncing YtcFilter (YTCF), do it after HyperChat
mainis updated:- merge HyperChat
maininto YTCFmaster - keep YTCF release notes and its in-product changelog in the strict one-line, lowercase, user-facing style documented in YTCF's
AGENTS.md
- merge HyperChat
- Commit messages should be short, direct, and readable in
git log --oneline. - Prefer active voice and concrete verbs:
hide @ in namesfix lingering yt visualsorder matters
- Avoid padded scopes, issue-number prefixes, and changelog-style essays in commit subjects.
- A slightly dry or funny commit is fine if it is still clear at a glance.
- Prefer proper merges when carrying
mv2work intomainandmv3-ltl. - Carry
mainintomv3-ltl. Do not treatmv3as the normal hop between them. - If MV3 needs follow-up adaptation, keep that as a small, explicit commit after the merge instead of rewriting history or hand-copying changes.
- Prefer editing existing modules and utilities over creating one-off files for tiny helpers.
- If a helper obviously belongs in an existing shared utility file, put it there.
- Put shared behavior on
mv2first;mainandmv3-ltlshould usually be merge-plus-adaptation branches, not separate feature branches. - Keep
mv3-ltlbranching from the currentmainline oncemainhas the intended HC changes. - Keep MV3 adaptation narrow:
- preserve branch-specific build/runtime wiring
- change only what is required for manifest/background/injection differences
- Prefer render-edge formatting over mutating raw identity data:
- keep parsed message/channel ids untouched
- transform display text at component or view-model boundaries
- Prefer resilient lookups over brittle positions:
- endpoint/type detection over fixed menu indices
- semantic selectors/utilities over DOM-order assumptions
- When a bug appears in multiple surfaces, prefer fixing the shared parser/messaging/util layer before patching several components by hand.
- Keep release bullets short and user-facing.
- Prefer active voice:
Fix admin block/report actionsHide leading @ in names
- Avoid passive voice, filler, and overly technical internal wording unless the release note is specifically for maintainers.
- Run
scripts/codex-dev.sh setup-mcponce (or per fresh machine) to register the Codex MCP server:- name:
chrome-devtools - command:
npx -y chrome-devtools-mcp@latest --browserUrl=http://127.0.0.1:9222
- name:
- Use
scripts/codex-dev.sh watchonce per session to keep Chrome extension builds live in the background. - On
main, this resolves to MV3 scripts (dev:chrome/build:chrome) andbuild/chromeoutput automatically. - On
mv2, watcher mode prefersnpm run start:noneso build watch stays alive without separate browser autolaunch. - Start headless browser testing only when explicitly requested (for example: "go test", "test this", "run browser test").
- For test runs, use
scripts/codex-dev.sh go-test. This guarantees:- MCP configuration is present
- watcher is running
- headless Chromium is restarted with a fresh profile and extension reload
- If Chromium fails to start in a sandboxed/snap environment, set
CHROME_BINto a non-snap Chrome/Chromium binary beforego-test.
- After significant extension-runtime changes, run
scripts/codex-dev.sh reloadbefore validation. - Treat these as significant by default:
src/scripts/**src/components/**src/manifest.jsonvite.config.ts- settings/storage/messaging code under
src/ts/**
- The reload is intentionally hard (full browser restart) to avoid stale MV3 service-worker state, extension cache artifacts, and mixed-profile debugging drift.
- For chat author display, hide a leading
@in UI text while keeping underlying identity data unchanged. - Use
src/ts/component-utils.ts(formatAuthorName) for this transformation and apply it at render points.
- Treat legacy member emoji placeholders (
U+25A1, rendered as□) as emoji-equivalent for filtering. - In
HIDE_ALLmode, do not render these placeholders inMessageRuns.svelte. - For emoji-only spam detection, count placeholder-only text runs as emoji in
isAllEmoji.
- Do not assume fixed menu item indices from
get_item_context_menu(YouTube may reorder menu items). - Resolve block/report actions by searching for endpoint types (
moderateLiveChatEndpoint,getReportFormEndpoint) in the response tree. - Always post
chatUserActionResponseeven when message context params are missing so UI state can fail gracefully. - Keep proxy fetch request/response events correlated by request id; do not use unscoped global listeners.
- For deeper notes on implementing new YouTube chat actions (headers, tracking params, endpoint discovery, SAPISIDHASH, and debugging), see
docs/YOUTUBE_ACTIONS.md.
- Headless validation should open the same
startUrlused byvite.config.ts. scripts/codex-dev.sh go-testnow does this automatically (defaulting by detected mode), andTEST_URLcan override when needed.
- Always rebuild for the target browser before runtime validation:
yarn build:chromeyarn build:firefox
- Chromium extension validation is most reliable in CI/headless shells with:
- Playwright Chromium persistent context
headless=falseplus--ozone-platform=headless- extension args:
--disable-extensions-except=<build>and--load-extension=<build>
- Firefox validation in this environment must set
HOME=/rootbefore launching browser automation as root, or Firefox exits early with a root/session ownership error. - For Firefox runtime checks, prefer
https://www.youtube.com/live_chat?v=jfKfPfyJRdk&is_popout=1for deterministic chat-frame loading in headless mode. - Packaged LiveTL Firefox translation is a special case: keep the request bridge in HC, but host the actual translator iframe on the YouTube page side.
- For LiveTL MV2 (webpack),
iframe-translator'sgetClient()is safe to use as long as the bundler rewritesimport.meta.env.DEVtofalsefornode_modules/iframe-translator/index.js(otherwiseimport.meta.envcan be undefined at runtime).
- The MV3 embed fallback page (
/embed/hyperchat_embed) can render a centered YouTube logo/error artifact if page elements are not fully removed. - In
src/scripts/chat-mounter.ts, treat the HyperChat mount root as the only allowed directbodychild and aggressively remove fallback embed artifacts, including#player-controls. - If the logo reappears in browser tests, prioritize checking
chat-mounter.tscleanup selectors and page timing behavior before touching parser/UI code.
scripts/codex-dev.sh statusshows watcher/MCP/browser states.scripts/codex-dev.sh logsprints watcher/browser log file locations.scripts/codex-dev.sh stopshuts down watcher and the headless browser.