Developer Notes
These rules are current repository contracts. Historical incident details belong in the owning rule, test, or design record rather than copied model/version tables.
Mandatory rules
Python through uv
uv run python launcher.py
uv run pytest
uv run ruff check .Do not document or run project Python with bare python, pytest, or ad-hoc pip.
Eight-locale i18n
The runtime locale set is:
en, ja, ko, zh-CN, zh-TW, ru, pt, esA user-visible i18n change updates every file in static/locales/. Plugin-manager locale changes follow their own synchronized locale group. CI enforces lockstep diffs.
Privacy-sensitive output
Raw conversation or other privacy-sensitive user text may use print only; do not send it through project logger calls. System events may use the configured logger when they contain no raw private content.
Prompt watermark
Translations or rearrangements of system prompts must preserve ======以上为.
Structural symmetry
Provider/backend/feature implementations remain structurally paired. Review peer providers whenever one path is split, renamed, configured, or packaged.
Runtime boundaries
- Browser development uses
/, one page/window, and preferred port 48911. - Electron loads separate routes/windows such as
/chatand/subtitle. - Static paths, initialization, IPC, and build outputs must work in both contexts.
frontend/react-neko-chat/is the single chat implementation.index.htmlandchat.htmlmount it at#react-chat-window-root.- Legacy
#chat-containeris hidden/deprecated;app-chat-adapter.jsbridges oldappendMessage()calls.
Backend boundaries
- API resources do not end in
/; avoid Starlette redirects behind proxies. - Blocking work must not run directly in async handlers.
- Sync configuration writes used during startup should have an async
a*counterpart for async paths. - Main package layering and
main_logic/core/facade contracts are CI-enforced. - Do not pass
temperature=to restricted LLM construction/call sites; observe output, timeout, and input budgets.
Memory
Conversation event persistence, projections, recall candidates, evidence/reflection, persona, and maintenance queues are separate layers. Read Memory System and the implemented design records before modifying them. Do not describe one-hour in-memory plugin context as semantic memory recall.
Frontend boundaries
The project is not “vanilla JS only.” It includes static/Jinja JavaScript, React chat, and a Vue plugin manager. Check the owning subtree and its tests. Avoid fixed-delay DOM assumptions; follow existing lifecycle/event patterns.
Catgirl visit: local development loopback
Visits (design record: Catgirl visiting infrastructure; Servers contract: Servers contract) are off unless NEKO_VISIT_ENABLED=1. What the code supports today:
Servers is still required. Credentials (
POST /api/visit/credentials) and the ticket public keys (GET /api/visit/pubkeys) are always fetched fromNEKO_SOCIAL_BASE_URL(default: the production community site) with the signed-in community OAuth session. There is no built-in local Servers stand-in, and ticket verification fails closed when the public-key list has never been fetched, so a loopback test needs a Servers test environment that implements the contract, plus one community account per side (redeeming your own invite is rejected).Dev signing key.
NEKO_VISIT_DEV_KEYFILE=<path>adds that Ed25519 key's public half under the reserved dev kid. Verification follows the production code path; there is no skip-verify branch. Create a key and sign tickets with the same key usingscripts/visit_dev_mint.py. A Servers test environment can return those tickets asidentity_ticket:bashuv run python scripts/visit_dev_mint.py keygen --out <path outside the repo>/visit_dev_key.pem uv run python scripts/visit_dev_mint.py pubkey --keyfile <path>/visit_dev_key.pem uv run python scripts/visit_dev_mint.py mint --keyfile <path>/visit_dev_key.pem \ --role guest --visit-id <22 chars> --visit-uid <24 hex>Local LiveKit. Run
livekit-server --dev(dev key pair, plainws://127.0.0.1:7880), or use the LiveKit deployment template on a host with real domains. That template lives in the private N.E.K.O.Connect repository together with the other server-side visit services, not in this repository.NEKO_VISIT_DEV_LIVEKIT_URL=ws://127.0.0.1:7880adds that host to the accepted LiveKit hosts. Plainws://is accepted only for this configured host and only when it is a loopback literal.NEKO_VISIT_DEV_LIVEKIT_API_KEY/_SECRETare read intoconfig.visit_settingsbut no backend code path signs LiveKit JWTs with them. The room JWT still comes from Servers. Note that--devkeeps LiveKit's default automatic room creation, unlike theauto_create: falsedeployment.Two backends on one machine. Give each side its own runtime root (
NEKO_STORAGE_SELECTED_ROOT) and its own port set (NEKO_MAIN_SERVER_PORT,NEKO_MEMORY_SERVER_PORT,NEKO_MONITOR_SERVER_PORT,NEKO_COMMENTER_SERVER_PORT,NEKO_TOOL_SERVER_PORT,NEKO_USER_PLUGIN_SERVER_PORT). Start each withuv run python launcher.py, then open each UI athttp://127.0.0.1:<main port>/.
Deployment limits
- Reverse-proxy deployments do not support visits. This includes the Docker image, which sets
NEKO_BEHIND_PROXY=trueindocker/entrypoint.sh. Proxy headers rewriteclient.host, so the local-origin check cannot be trusted. Visits answer 403VISIT_E_UNAUTHORIZED, and the settings page hides the visit group and shows the reason. - Non-loopback access (Docker, LAN) is unsupported by default. The visit gate requires the browser and the backend to be on the same machine, talking over
127.0.0.1/::1. NEKO_VISIT_ALLOW_NONLOCAL=1is an escape hatch. It disables the loopback check, the proxy-mode rejection and the forwarding-header rejection. The operator must authenticate at the proxy and overwrite (not append)X-Forwarded-For, at their own risk. Without that, any client that can reach the backend can read invite codes and transcripts and send visit lines on the user's behalf.
Steam and packaging
Achievements and cloud state can have external irreversible effects. Use existing test hooks and staging flows; do not test destructive behavior on a real account casually. Packaging changes must follow Nuitka Packaging and the current build workflow.
Validation
Run the narrowest relevant tests first, then broader tests/builds proportional to risk. The authoritative static gate is .github/workflows/analyze.yml; plugin tests, docs build, desktop packaging, and Docker have separate workflows.
