Repository guidance
Communication
Use ASD-STE100 Simplified Technical English for all text you write: questions, updates, explanations, final answers, commit messages, PR descriptions, and review comments. Be as concise as possible. Keep quotations, code, commands, paths, identifiers, and required technical terms unchanged.
When a step doesn't need the developer's input, keep going. Put status notes in the same message as your next action. Stop and ask only when you can't continue without the developer, or before anything destructive: deleting data, force-pushing, or changing anything outside this repository.
For work with more than a few steps, keep a checklist in TASKS.md at the worktree root (git
ignores it). Tick each item when it is done, and add new items that you find. The file stays
available after the context is summarized.
For an audit or migration that divides into independent areas, give each area to its own subagent. Check the evidence in each subagent's report before you accept it. Finish with one table.
Mark each claim that you could not confirm, and say where you looked.
Non-negotiable rules protect user data, released contracts, and security. Change them only on an explicit developer decision. All other rules are defaults: follow the developer's preference and state which default you set aside. Do not argue by citing this file.
Non-negotiable
- Migrations are irreversible. No backup of
openbot.dbis made before an upgrade. Preserve all user data and support every shipped source schema. Never assume a backup exists. - Released Team API adapters are permanent. Do not change a shipped wire protocol's meaning.
- Keep the renderer-to-main trust boundary: Electron sandboxing, context isolation, navigation
policy, IPC sender validation, and their tests. Coding agents already have
danger-full-access. - Redact secrets on every log, export, and send path, including diagnostics and analytics.
- Keep PolyForm Noncommercial 1.0.0. Do not add incompatible dependencies or relicense files.
Read CONTRIBUTING.md when a change touches the trust boundary or a security test, and architecture change rules when adding a module or moving ownership between workspaces.
Product constraints
- Workspaces, conversations, attachments, browser data, and team data stay on the computer that
runs OpenBot. Providers, visited pages, and plugins can use the network. Exception: a signed-in
phone keeps an encrypted, read-only copy of the agents, sections, recent chats, channels and drawer
values of its servers (
apps/mobile/src/features/workspace/model/workspace-cache.ts). The copy stays on that phone, the host stays the source of truth, and sign-out, session expiry and server removal delete it. - Cloudflare holds accounts, avatars, host configuration, memberships, invitations, and logical sessions; it does not hold chats, files, or commands.
- The user's SQLite database is the source of truth, not a remote cache.
- Agents keep their workspace, thread, and identity across provider switches and restarts. Do not reset an agent to simplify state.
Effect
- Use Effect 4.0.0 for migrated server and desktop domain services, schemas, typed errors, asynchronous workflows, and external I/O. Keep the Bun catalog and lockfile aligned.
- Read the installed Effect source and documentation. Do not use v3 compatibility aliases.
- Prefer named
Effect.fn,Effect.gen,Context.Service, explicit layers, andSchema.TaggedError. - Derive validated domain types from schemas. Decode untrusted values at their boundary; preserve released decoder behavior. Do not validate trusted internal values again.
- Wrap expected I/O failures with
Effect.tryPromiseorEffect.tryat the adapter. Keep causes private and safe. Preserve public error codes and localized messages. - Run Effects at framework boundaries. Keep routing, request schemas, rendering, and callbacks native. Expose one Effect-returning operation per async service method. Do not add paired Promise methods or run an Effect only to wrap its Promise in another Effect. Adapt browser and mobile native callbacks at their call sites; preserve released wire and event contracts.
- Reuse process-owned managed runtimes and await disposal in existing shutdown paths. Preserve
desktop teardown ordinals. Worker bindings and background I/O belong to their invocation;
retain
waitUntiland streaming lifetimes rather than adding a process shutdown hook. - Tie owned resources to finalizers. Preserve existing deadlines and retries, and forward cancellation where the API supports it. Do not add generic retry, fallback, or service-wrapper policies.
- Keep business authorization and validation in domain operations, and centralize public error mapping.
- The database host may import
effectin addition tonode:*. It remains a separate killable process and must not import Electron or the application database. Effect cannot interrupt blocked SQLite. - Test through injected boundaries and include affected tests in CI selections. Mobile and browser application workflow migration is outside this change; shared package consumers still need checks.
Shared UI
packages/ui (@openbot/ui) owns shared SolidJS controls and feature components used by
desktop, public web, and Storybook. Reuse these components before adding UI. Import primitives
from @openbot/ui and feature components from explicit subpaths such as
@openbot/ui/features/sidebar/Sidebar. Do not copy them back into an app.
Keep shared components independent of application contexts, desktop storage, host connections,
and window.openbot. Pass data and actions through typed props. Platform adapters and the main
conversation controller stay in the renderer. Component extraction must preserve the existing
UI, styles, and behavior unless the user requests a design change. Native mobile shares brand
tokens and contracts; it does not render SolidJS DOM components.
Read packages/ui/AGENTS.md before changing shared UI.
Interface text
Text that a user reads comes from @openbot/i18n catalog keys, on desktop, web and mobile. Do not
write a literal. Errors that the main process, backend or team client send use sourceText(key).
Read docs/i18n.md before adding a key or a language.
Checks
Do not run broad checks locally. They overload the user's computer. This explicit user preference
replaces the previous full lint and typecheck defaults. Leave repository-wide lint, aggregate
bun run typecheck, full UI checks, builds, and full test suites to CI. Do not request these checks
as a routine completion or PR step.
- In a fresh worktree, run
bun install --frozen-lockfilefirst. - Run only the narrowest relevant test file and lint the changed files. Run checks one at a time, with one test worker where supported.
Use
bun run test:changedby default: it runs, on one worker, only the desktop and mobile test files that import a file you changed sinceorigin/main. Usebun run test:related -- <source>for the tests of named source files, andbun run test:desktop -- <path>for one test file. - To check types, run one project for the code you changed, one at a time:
bun run typecheck:node(src/main,src/backend,src/preload,scripts),bun run typecheck:renderer(src/renderer,packages/ui), or thetypecheckscript of the one package or app you changed. Each takes under 10 seconds. Do not runbun run typecheck, the mobile typecheck, or parallel checks. Leave broad type validation to CI and state what remains unverified. - Do not run
bun run format: it rewrites the whole repository. Usebiome check --write --max-diagnostics=none <paths>for changed files. - The pre-commit hook (
.githooks/pre-commit) runscheck:staged,check:ui, andscripts/staged-typecheck.tswhen the commit stages code. The script runs, one at a time, only thetypecheck:*projects that can see a staged file; a rootpackage.json,tsconfig*.jsonorbun.lockselects all of them.--dry-run [<path>...]shows the selection. This is the only exception to rule 3. Do not run these checks by hand, and do not bypass the hook with--no-verify. The hook also runs the schema parity test when a database schema file insrc/backendis staged. It stops the commit if Biome fixes a file that also has unstaged changes; stage the fixes you want and commit again.
Check design notes explain CI coverage, command aliases, and the separate Node and Bun type environments. Read them when changing checks or dependencies.
Surfaces to check
State which surfaces a change touches. Check all affected consumers and reverse actions.
- Desktop renderer (
src/renderer), mobile (apps/mobile), public web (apps/auth-api; no separate landing app), hosted-site routing (apps/site-router), and Signal (remote/api). - IPC contracts (
packages/contracts) and their preview implementation (src/renderer/src/preview/mock-openbot.ts). - Reverse actions: snooze/unsnooze, pause/resume, revoke/reconnect, mute/unmute.
- Migrations and the separate latest schema for new databases.
- Documentation:
README.mdcommands,docs/architecture/, andPRIVACY.mdwhen outbound data changes.
Development data and processes
Account service environment settings
- Maintain only
apps/auth-api/.env.devandapps/auth-api/.env.productionfor shared account service settings. Add new settings to these files; do not add another environment file. - Keep both files encrypted. Development uses
DOTENV_PRIVATE_KEY_DEV; production usesDOTENV_PRIVATE_KEY_PRODUCTION. Never use production credentials as local development defaults. - Use the development loader. It reads the development key from the shell; without the key, local development must work with generated defaults. Commands that need external secrets must stop before remote changes when those secrets are missing. An invalid key must fail safely.
- Keep generated identity and saved local overrides in ignored
.openbot/dev-state.json. New manual overrides use shell variables. Do not add.env.localor.env.generated. .env.keysstores decryption keys, not service settings. Keep it ignored. Preserve existing production key handling and CI/GitHub secrets unless the developer requests a change.- The root
.env.keysholds the only two keys:DOTENV_PRIVATE_KEY_PRODUCTIONandDOTENV_PRIVATE_KEY_DEV. Every encrypted env file,remote/.env.productionincluded, uses one of them. Never make a new key pair. Pass-fkwith the root.env.keysso Dotenvx does not make one. - For direct Cloudflare changes,
apps/auth-api/.env.productionholdsCLOUDFLARE_API_TOKEN(write access to all resources in the OpenBot account) andCLOUDFLARE_ACCOUNT_ID. Load them withbunx dotenvx run -f apps/auth-api/.env.production -fk <root>/.env.keys -- <command>. A worktree has no.env.keys; use the one in the main checkout. Never print the token: do not usedotenvx getorecho. Ask before a change that deletes data or affects production users. Deploy scripts remove the token, so Wrangler deploys keep the developer'swrangler login. - For provider harness tests and smoke checks,
apps/auth-api/.env.devholdsINCEPTION_API_KEY(Inception Labs, OpenAI-compatible,https://api.inceptionlabs.ai/v1, models such asmercury-3). Load it withbunx @dotenvx/dotenvx@2.38.2 run -f apps/auth-api/.env.dev -fk <root>/.env.keys -- <command>;bun run devdoes not pass it on. Never print it. Ask before a benchmark or a run of many turns: the token budget is small.
See account service setup for loading and deployment details.
Local processes
bun run dev:seed --dry-runis read-only.- Never kill by process pattern, such as
pkill -f electronorpkill -f bun.bun run dev:stopstops only this worktree's stack; name another one with--pid=<supervisor pid>or--all. For a process outside the registry, target a PID you started or ask. - Never drive another worktree's app. Use
bun run dev:automationfor smoke checks instead of starting Electron directly;snapshotandscreenshotare read-only, andclickandtypeneed--allow-mutationsand a named instance. - Do not delete a dead stack record you did not resolve. Stop keeps the record and exits non-zero
when it cannot confirm a PID's identity; resolve the process, then
bun run dev:forget. Dead records do not reserve ports. - Reuse a running dev instance.
bun run devopens a profile tied to this worktree;--sharedopens the sharedOpenBot Devprofile. Use the ports the stack reports rather than a fixed port.
See README.md — Commands for the flags these commands take, and docs/architecture/change-rules.md for why the registry works this way, when a dev command does not behave as expected.
Terms
- bot is not for new product code. Released names stay: Team API v1-v3
bot/botId,bots.json,legacy-import:bots:v1, and~/OpenBot/Botspath prefixes. - teammate and group are prompt and UI words, never types.
Use docs/glossary.md when naming a new type, table, IPC channel or product string, or when a term in the code disagrees with the UI.
Task-specific instructions
Read the instruction file for each directory you change. Use the workspace map to find its owner.
| File | Scope |
|---|---|
| src/renderer/AGENTS.md | SolidJS, stores, components, palette |
| src/main/AGENTS.md | Renderer-to-main boundary and main-process ownership |
| src/main/ipc/AGENTS.md | Handler binding and endpoint registration |
| src/backend/AGENTS.md | SQLite migrations and database creation |
| packages/contracts/AGENTS.md | Frozen Team API protocols and IPC mirrors |
| apps/auth-api/AGENTS.md | Account Worker and D1 deployment races |
| src/preload/AGENTS.md | Preload bridge and payload decoding |
| packages/ui/AGENTS.md | Shared SolidJS controls and feature components |
| packages/team-client/AGENTS.md | Team WebRTC client and framing |
| apps/mobile/AGENTS.md | Expo and build/simulator permissions |
| remote/api/AGENTS.md | Signal and TURN credentials |
Write release notes in a new changelog.d/<branch>.md file, not in CHANGELOG.md, as
docs/RELEASING.md says. Notes for the iPhone app go in
apps/mobile/changelog.d/<branch>.md. CI fails a PR with no notes unless it
has the no-changelog label. A version bump with no notes fails.
Before a version bump or tag, use release-upgrade-safety to audit upgrade and data-loss risks since the last release.
Tests
- Use E2E tests only. Do not add unit tests. An E2E test is a script under
scripts/(such asscripts/*-e2e.tsorscripts/*-smoke.ts) that drives the real app, process or service. At the end, it writes a verifiable and repeatable artifact, such as a JSON report or a screenshot under.openbot-build/. - Vitest files are permitted only for these protected areas:
- Database migrations: data-preservation fixtures for every affected released schema, plus the failure and rollback cases that src/backend/AGENTS.md lists. A migration always needs them.
- The renderer-to-main trust boundary: sender validation, navigation and permission policy, CSP, sandboxing, and preload payload decoding.
- Secret redaction on log, export, and send paths.
- Lint and UI check rule fixtures (see Check rules).
- Do not assert what TypeScript, Biome, or
check:uialready enforce. - Wait for state, an event, or a promise, not elapsed time.
- Assert behavior and data. Do not assert markup, classes, layout, animation timing, or snapshots.
- Use Storybook for visual details only. Do not add story
playfunctions: CI does not run them. Do not adddata-testidhooks;check:uiallows none. *.test.tsuses Node;*.test.tsxuses JSX and jsdom;*.dom.test.tsuses DOM without a component.
Check rules
- Fix errors. Assess warnings; do not make correct code worse to silence one. Do not add
biome-ignore. Explain retained warnings in the PR. - GritQL rules must match syntax, not infer domain decisions, and must not duplicate a built-in Biome rule. Consider traversal cost before adding a rule.
- Each rule in
tools/biome/anti-slop/rulesneeds positive and negative fixtures in../fixtures. Mark rejected lines with// flag; verify withscripts/anti-slop-rules.test.ts. - Each UI check needs both
rendererandrenderer-cleanfixtures intools/ui-foundation/fixtures. Verify withscripts/ui-foundation-check.test.ts.
Read check design notes when changing these checks. They describe the enforced syntax, fixture behavior, and reasons for removed rules.
Pull requests
- Open a PR only when asked.
- Write the PR body from
.github/PULL_REQUEST_TEMPLATE.md. Organization members and users with write access do not sign the Contributor License Agreement. An author who must sign sends a signed PDF by email. Never sign, fill in, or send the agreement for the author. - For UI changes, show before and after. State the model and harness in the PR body. Do not commit screenshots or other PR review image assets to the repository.
- Do not run wider checks locally before a PR. Report focused checks and leave broad checks to CI.
- A PR needs a named reason and is not auto-approvable if it adds
biome-ignore,@ts-expect-error, or@ts-ignore; disables rules throughbiome.jsonoverrides or removes a GritQL plugin; widens a boundary toanyorunknown; or uses an assertion to bypass a checker. Fix the domain issue, or explain why the rule is wrong and let the developer decide.