Known Surprises
This file tracks repository-specific confusion points that caused agent mistakes.
Entry Criteria
Add an entry only if all are true:
- It is specific to this repository (not generic advice).
- It is likely to recur for future agents.
- It has a concrete mitigation that can be followed.
If uncertain, ask the developer before adding an entry.
Entry Template
### [Short title]
- **Date:** YYYY-MM-DD
- **Observed by:** agent name or contributor
- **Context:** where/when it happened
- **What was surprising:** concrete unexpected behavior
- **Impact:** what went wrong or could go wrong
- **Mitigation:** exact step future agents should take
- **Status:** confirmed | superseded
Entries
Vercel app production domains can drift back to Git master deployments
- Date: 2026-04-28
- Observed by: Tommaso + Codex
- Context: Verifying Seedit and 5chan app mirrors in the Bitsocial Web app directory.
- What was surprising: The Vercel
seeditand5chanprojects hadgitProviderOptions.createDeployments = "enabled", so GitHubmasterpushes were promoted to the production domains even though the repo policy expects production app mirrors to serve release artifacts only. - Impact: App-directory verified mirror badges can become false because production domains serve the latest development commit instead of the GitHub release ZIP whose
index.htmlhash is recorded inabout/src/lib/apps-data.ts. - Mitigation: Before adding or refreshing mirror verification metadata, check the Vercel project with
vercel api /v9/projects/<project-id>and confirmgitProviderOptions.createDeployments = "disabled". Deploy release ZIP contents withvercel deploy --prebuilt --prodand useseedit-omega.vercel.appor5chan-omega.vercel.appfor development deployments. - Status: confirmed
Portless 0.11 reuses legacy proxy state unless the launcher forces HTTPS
- Date: 2026-04-28
- Observed by: Tommaso + Codex
- Context: Upgrading the normal
yarn startflow from the oldhttp://bitsocial.localhost:1355proxy URL tohttps://bitsocial.localhost. - What was surprising: Even with
portless@0.11.1installed, Portless reused the existing~/.portless/proxy.port = 1355HTTP proxy and printed the legacy:1355URL. - Impact: Updating package versions and docs is not enough;
yarn startcan still advertise and use the old URL when a contributor has legacy Portless state running. - Mitigation: Keep the start scripts explicitly starting the Portless HTTPS proxy on port
443before registering app routes, so the runtime flow migrates away from persisted1355state instead of inheriting it. - Status: confirmed
Portless changes the canonical local app URL
- Date: 2026-03-18
- Observed by: Codex
- Context: Browser verification and smoke flows
- What was surprising: The default local URL is not the usual Vite port. The repo expects
https://bitsocial.localhostthrough Portless, so checkinglocalhost:3000orlocalhost:5173can hit the wrong app or nothing at all. - Impact: Browser checks can fail or validate the wrong target even when the dev server is healthy.
- Mitigation: Use
https://bitsocial.localhostfirst. Only bypass it withPORTLESS=0 corepack yarn startwhen you explicitly need a direct Vite port. - Status: confirmed
Commitizen hooks block non-interactive commits
- Date: 2026-03-18
- Observed by: Codex
- Context: Agent-driven commit workflows
- What was surprising:
git committriggers Commitizen through Husky and waits for interactive TTY input, which hangs non-interactive agent shells. - Impact: Agents can stall indefinitely during what should be a normal commit.
- Mitigation: Use
git commit --no-verify -m "message"for agent-created commits. Humans can still usecorepack yarn commitorcorepack yarn exec cz. - Status: confirmed
Corepack is required to avoid Yarn classic
- Date: 2026-03-19
- Observed by: Codex
- Context: Package manager migration to Yarn 4
- What was surprising: The machine still has a global Yarn classic install on
PATH, so running plainyarncan resolve to v1 instead of the pinned Yarn 4 version. - Impact: Developers can accidentally bypass the repo's package-manager pinning and get different install behavior or lockfile output.
- Mitigation: Use
corepack yarn ...for shell commands, or runcorepack enablefirst so plainyarnresolves to the pinned Yarn 4 version. - Status: confirmed
Fixed Portless app names collide across Bitsocial Web worktrees
- Date: 2026-03-30
- Observed by: Codex
- Context: Starting
yarn startin one Bitsocial Web worktree while another worktree was already serving through Portless - What was surprising: Using the literal Portless app name
bitsocialin every worktree makes the route itself collide, even when the backing ports are different, so the second process fails becausebitsocial.localhostis already registered. - Impact: Parallel Bitsocial Web branches can block each other even though Portless is meant to let them coexist safely.
- Mitigation: Keep Portless startup behind
scripts/start-dev.mjs, which now uses a branch-scoped*.bitsocial.localhostroute outside the canonical case and falls back to a branch-scoped route when the barebitsocial.localhostname is already occupied. - Status: confirmed
Docs preview used to hard-code port 3001
- Date: 2026-03-30
- Observed by: Codex
- Context: Running
yarn startalongside other local repos and agents - What was surprising: The root dev command ran the docs workspace with
docusaurus start --port 3001, so the whole dev session failed whenever another process already owned3001, even though the main app already used Portless. - Impact:
yarn startcould kill the web process immediately after it booted, interrupting unrelated local work over a docs-port collision. - Mitigation: Keep docs startup behind
yarn start:docs, which now uses Portless plusscripts/start-docs.mjsto honor an injected free port or fall back to the next available port when run directly. - Status: confirmed
Fixed docs Portless hostname was hard-coded
- Date: 2026-04-03
- Observed by: Codex
- Context: Running
yarn startin a secondary Bitsocial Web worktree while another worktree was already serving docs through Portless - What was surprising:
start:docsstill registered the literaldocs.bitsocial.localhosthostname, soyarn startcould fail even though the about app already knew how to avoid Portless route collisions for its own hostname. - Impact: Parallel worktrees could not reliably use the root dev command because the docs process exited first and
concurrentlythen killed the rest of the session. - Mitigation: Keep docs startup behind
scripts/start-docs.mjs, which now derives the same branch-scoped Portless hostname as the about app and injects that shared public URL into the/docsdev proxy target. - Status: confirmed
Worktree shells can miss the repo's pinned Node version
- Date: 2026-04-03
- Observed by: Codex
- Context: Running
yarn startin Git worktrees such as.claude/worktrees/*or sibling worktree checkouts - What was surprising: Some worktree shells resolved
nodeandyarn nodeto Homebrew Node25.2.1even though the repo pins22.12.0in.nvmrc, soyarn startcould silently run the dev launchers under the wrong runtime. - Impact: Dev-server behavior can drift between the main checkout and worktrees, making bugs hard to reproduce and violating the repo's expected Node 22 toolchain.
- Mitigation: Keep the dev launchers behind
scripts/start-dev.mjsandscripts/start-docs.mjs, which now re-exec under the.nvmrcNode binary when the current shell is on the wrong version. Shell setup should still prefernvm use. - Status: confirmed
docs-site/ leftovers can hide missing docs source after the refactor
- Date: 2026-04-01
- Observed by: Codex
- Context: Post-merge monorepo cleanup after moving the Docusaurus project from
docs-site/todocs/ - What was surprising: The old
docs-site/folder can remain on disk with stale but important files likei18n/, even after the tracked repo moved todocs/. That makes the refactor look duplicated locally and can hide the fact that tracked docs translations were not actually moved intodocs/. - Impact: Agents can delete the old folder as “junk” and accidentally lose the only local copy of docs translations, or keep editing scripts that still point at the dead
docs-site/path. - Mitigation: Treat
docs/as the only canonical docs project. Before deleting any localdocs-site/leftovers, restore tracked source likedocs/i18n/and update scripts and hooks to stop referencingdocs-site. - Status: confirmed
Multilocale docs preview can spike RAM during verification
- Date: 2026-04-01
- Observed by: Codex
- Context: Fixing docs i18n, locale routing, and Pagefind behavior with
yarn start:docsplus Playwright - What was surprising: The default docs preview mode now does a full multilocale docs build plus Pagefind indexing before serving, and keeping that process alive alongside multiple Playwright or Chrome sessions can consume much more RAM than a normal Vite or single-locale Docusaurus dev loop.
- Impact: The machine can become memory-constrained, browser sessions can crash, and interrupted runs can leave stale docs servers or headless browsers behind that keep consuming memory.
- Mitigation: For docs work that does not need locale-route or Pagefind verification, prefer
DOCS_START_MODE=live yarn start:docs. Only use the default multilocale preview when you need to validate translated routes or Pagefind. Keep a single Playwright session, close old browser sessions before opening new ones, and stop the docs server after verification if you no longer need it. - Status: confirmed
translate-docs.py can leave docs locales half-translated or with broken link targets
- Date: 2026-04-06
- Observed by: Codex
- Context: Fixing localized docs routes and content after
yarn start:docsserved English detail pages or failed to build locale output - What was surprising: The docs translation pipeline had two repo-specific failure modes at once:
scripts/translate-docs.pyonly extracted a small subset ofDocsHomemessages whentr(...)calls used forms it did not parse, and translated markdown underdocs/i18n/**could contain machine-translated slugs orZXQPLACEHOLDERartifacts inside link targets. - Impact: Localized homepages can silently fall back to English, localized detail pages can appear untranslated, and full
yarn docs:buildcan fail on broken locale links even though the source docs are valid. - Mitigation: After changing docs translations or regenerating locale files, always run
yarn docs:buildfrom the repo root, scandocs/i18n/**markdown forZXQPLACEHOLDER, and verify translated links still point to canonical doc slugs such as/apps/5chan/instead of translated URL paths. IfDocsHomecopy changed, confirmscripts/translate-docs.pystill extracts alldocs.home.*messages. - Status: confirmed
About-site no-JS checks must use the Portless route, not a standalone SSR preview
- Date: 2026-04-12
- Observed by: Codex
- Context: Verifying no-JS support for the
about/site from a branch worktree - What was surprising: A standalone SSR preview can look healthy while the actual branch-scoped Portless route is still serving the wrong app shell or an older process. In this repo, the real local contract is the Portless hostname from
yarn start, not an ad hoc preview server. - Impact: Agents can incorrectly claim no-JS support works, or miss regressions that only show up on
*.bitsocial.localhost. - Mitigation: For
about/browser verification, always start the real local server withyarn startoryarn start:aboutand test the branch-scoped Portless URL first. If a Portless hostname looks stale, inspect and stop the old process before retesting. - Status: confirmed
chain/ was invisible to yarn build:verify and yarn doctor
- Date: 2026-07-05
- Observed by: Codex
- Context: Verifying a chain/-only diff after the
chain/workspace (standalone Vite app forchain.bitsocial.net) was added to the monorepo. - What was surprising:
scripts/verify-build.mjsonly recognizedabout/,docs/, andstats/path prefixes, so a chain/-only diff printed "No targeted build checks matched the current diff" and ran no build at all, even thoughbuild:chainalready existed in the rootpackage.json. Separately,yarn doctorwas hard-coded toreact-doctor about -y, so React changes underchain/srcgot zero React Doctor coverage. - Impact: Agents verifying chain changes had to know to call
yarn build:chaindirectly instead of trustingyarn build:verify, and React issues inchain/src(effects, hooks, dead code) went undetected byyarn doctor. - Mitigation:
scripts/verify-build.mjsnow has achain/branch mirroring theabout/one, anddoctor/doctor:verbosenow runreact-doctor --project about,chain -yin a single invocation.doctor:scorestaysabout-only because--scoresilently prints nothing when combined with--projectfor more than one project; useyarn react-doctor --project about,chain --verbose -y(or--json) if a chain score is needed. - Status: confirmed
Browser P2P runs on secure WebSockets; pkc-js denies WebRTC and WebTransport by default
- Date: 2026-08-02
- Observed by: Claude
- Context: Writing landing-page and docs copy about how Bitsocial browser P2P works
- What was surprising:
@pkcprotocol/pkc-jsships a default connection gater that rejects WebRTC and WebTransport dials in the browser —dist/browser/helia/dial-transport-filter.jsexportsDENIED_DIAL_TRANSPORTS_BY_DEFAULT = ["webrtc", "webrtc-direct", "webtransport"]. Its source comment gives the reason: in the browser those transports add long, often-failing connection-establishment paths (STUN/ICE, certhash rotation) that slow loads, while WebSocket is direct and reliable. Every live peer on the blog's P2P status panel shows "Secure WebSocket". The gater lives innode_modules, so nothing in the repo hints at it. - Impact: It is very easy to write technically plausible but false public copy — for example crediting WebTransport reaching browser Baseline in March 2026 for making Bitsocial browser P2P possible. That claim shipped into the landing page, the comparison table, and two docs pages before the developer caught it. Wrong architecture claims on public pages are checked by exactly the developer audience the site targets.
- Mitigation: Never infer which transports Bitsocial uses from what libp2p or the browser platform supports in principle. Check
node_modules/@pkcprotocol/pkc-js/dist/browser/helia/dial-transport-filter.jsfor the current deny list, confirm noconnectionGateroverride exists underabout/src/, and read the live transport labels in the blog's "P2P status" panel before making any public claim. The upstream change that actually unblocked browser publishing was the gossipsub monotonic-seqno fix in@libp2p/gossipsub15.0.21 (May 2026); pkc-js currently ships 16.0.4. - Status: confirmed
Relative ./page.md links from an untranslated docs page break every localized build
- Date: 2026-08-02
- Observed by: Claude
- Context: Adding a new English-only page,
docs/browser-p2p.md, that linked to existing docs with./peer-to-peer-protocol.mdand./apps/5chan.md - What was surprising: Each locale under
docs/i18n/<lang>/docusaurus-plugin-content-docs/current/mirrors the docs tree. A new page absent from those mirrors still renders in every locale via English fallback, but its relative markdown links no longer resolve — Docusaurus emits/ar/browser-p2p/peer-to-peer-protocol.md/and fails the build with "Docusaurus found broken links!". Crucially,yarn build:verifyandyarn docs:build:verifybuild onlyenand pass cleanly; only a fullyarn docs:buildsurfaces it, and it aborts on the first locale alphabetically (ar). - Impact: A docs change can pass every fast local check and still break the production multilocale build. The failure also looks unrelated to the change, since the error names a locale path the author never touched.
- Mitigation: In any docs page that is not mirrored into
docs/i18n/**, use root-relative links (/peer-to-peer-protocol/,/apps/5chan/) instead of relative.mdlinks; Docusaurus prefixes them with the locale automatically.docs/build-your-own-client.mdis the existing example. Run a fullyarn docs:build— not justbuild:verify— before handing off any change that adds or links a docs page. - Status: confirmed
Concurrent update-translations.js runs silently lose keys
- Date: 2026-08-02
- Observed by: Claude
- Context: Applying 26 translated i18next keys across all 36 locales via the
translateskill - What was surprising: Each invocation is a read-modify-write over all 36 locale files, so two invocations running at once clobber each other and one key vanishes with no error. The
translateskill explicitly instructs spawning up to 4 subagents concurrently, each of which would call the script. - Impact: Fails silently: keys go missing from arbitrary locales, and the diff still looks plausible.
- Mitigation: Never let translator subagents write locale files concurrently — have them emit dictionary JSON files only, then apply every key serially from the parent agent. After applying, verify programmatically that each key exists in all 35 non-English locales and that no value is byte-identical to the English source.
- Status: confirmed
- Update (2026-08-10): The script used to also resolve its target as
path.join(process.cwd(), "public", "translations"), so the documented repo-root command failed with "Translations directory not found" and had to be run fromabout/. It now resolves the workspace from the current directory or from its own location, and works from anywhere. The concurrency trap above is unchanged.
The dev react-scan toolbar swallows driven clicks in the bottom-right corner
- Date: 2026-08-10
- Observed by: Claude
- Context: Verifying the fixed scroll button on the about home page with
playwright-cli - What was surprising:
playwright-cli clickon the fixed scroll button times out with<div id="react-scan-root"></div> intercepts pointer events. The host element is a zero-height<div>at the end of<body>, so it looks harmless in a DOM dump; the toolbar actually lives in its open shadow root and is fixed to the bottom-right corner — the same corner as the app's scroll button.element.click()fromevalbypasses hit-testing and still works, so a scripted check can pass while every real pointer click fails. - Impact: A whole verification pass reads as a product bug in the control being tested.
navigator.webdriverisfalsein playwright-cli sessions, so the app cannot detect automation on its own. - Mitigation:
scripts/pw-session.sh opennow registerswindow.__NO_DEV_TOOLBAR__ = trueviapage.addInitScriptand reloads, so sessions opened through the wrapper never mount the toolbar. Only the toolbar is suppressed —react-scanitself stays enabled, so__getReactScanReportand__ELEMENT_SOURCE__keep working for the profiler and element-inspection skills. When drivingplaywright-clidirectly, set the same flag before load, or expect corner clicks to be intercepted. - Status: confirmed
skills add installs Codex and Cursor copies into the gitignored .agents/ directory
- Date: 2026-08-18
- Observed by: Tommaso + Claude
- Context: Installing the
improve-threejsskill frommillionco/react-doctorwith theskillsCLI (vercel-labs/skills). - What was surprising:
npx skills add <repo> --skill <name> --agent codexand--agent cursorboth write to.agents/skills/<name>/, not to.codex/skills/or.cursor/skills/.AGENTS.mdforbids a repo-level.agents/directory and.gitignore:29ignores it, so both copies are silently untracked. Only--agent claude-codewrites to the expected.claude/skills/. Separately, the documented comma-separated form (--agent claude-code,codex,cursor) fails with "Invalid agents" and installs nothing, even though each name is valid on its own. - Impact: The install reports success while two of the three toolchain copies land somewhere that will never be committed, so Codex and Cursor silently lack the skill after a fresh clone. The comma form can also produce a no-op install that reads as a success.
- Mitigation: Run
skills addonce per agent with a single--agentvalue. Use--agent claude-codefor the.claude/skills/copy, thencp -R .claude/skills/<name> .codex/skills/<name>andcp -R .claude/skills/<name> .cursor/skills/<name>, andrm -rf .agents. Confirm withgit statusthat all three copies show as untracked additions before committing. - Status: confirmed