Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
3299c16
Docs/i18n batch c1 developing foundation (#2504)
JAYcodr May 22, 2026
4a76f22
docs(i18n): add zh-CN translations for developing modules (C2) (#2505)
JAYcodr May 22, 2026
533208b
docs(i18n): add zh-CN translation for developing/README.md (C2b) (#2506)
JAYcodr May 22, 2026
ae0464b
feat(memory): two-lane user preferences (save_preference) + model-awa…
sanil-23 May 22, 2026
1f9a23d
fix(cef): auto-disable prewarm webview on Wayland/XWayland to prevent…
M3gA-Mind May 22, 2026
0826de4
feat(composio): add GitHub as a native memory provider (#2488)
M3gA-Mind May 22, 2026
e7e660f
fix(inference): fail closed when BYOK intent cannot resolve a provide…
M3gA-Mind May 22, 2026
9ac9613
feat: make CORS origin configurable for cloud deployments (#2344)
hobostay May 23, 2026
03d1e25
feat(e2e): complete E2E v2 suite — 66 specs, orchestrator, bug fixes …
YellowSnnowmann May 23, 2026
d6ee4aa
Fix expired pending approvals lingering after restart (#2357)
Alexxigang May 23, 2026
9b2eb90
chore(shortcuts): self-assign PR in pnpm review fix (#2510)
senamakel May 23, 2026
b0facb5
fix(config): preserve built-in reserved-slug cloud_providers across s…
YellowSnnowmann May 23, 2026
a6b19ce
fix(agent-harness): dedup visible tool specs in all provider-call pat…
YellowSnnowmann May 23, 2026
50c84f9
feat(composio): add Linear as a native memory provider (#2452)
M3gA-Mind May 23, 2026
49528de
fix(integrations): distinguish mid-OAuth / expired / failed states in…
CodeGhost21 May 23, 2026
50172f4
Fix provider setup error wrapping (#2369)
graycyrus May 23, 2026
04e1bcb
docs(model-routing): document per-agent model pins (#2431)
aqilaziz May 23, 2026
4572972
packaging(arch): add openhuman-bin AUR recipe (#2428)
aqilaziz May 23, 2026
d42835f
docs(cloud): document remote web UI preview (#2434)
aqilaziz May 23, 2026
33dc4ec
docs(search): document SearXNG setup (#2435)
aqilaziz May 23, 2026
1a852cd
docs: clarify managed backend defaults (#2426)
sunilkumarvalmiki May 23, 2026
05e1e5d
test(e2e): add stable UI hooks (#2421)
aqilaziz May 23, 2026
7e9c29d
fix(triage): defer malformed cloud replies (#2415)
aqilaziz May 23, 2026
299e54d
fix(memory): localize background LLM prompts (#2447)
sunilkumarvalmiki May 23, 2026
2f5db77
feat(approval): persist post-execution audit row alongside approval (…
CodeGhost21 May 23, 2026
27bea24
fix(auth): deliver OAuth JWT to remote core in cloud mode (#2453)
M3gA-Mind May 23, 2026
333bd52
fix(config): log RPC URL and core mode as strings, not object wrapper…
M3gA-Mind May 23, 2026
ae6909f
feat(tauri): support workspace file links (#2476)
YOMXXX May 23, 2026
b087d70
fix(observability,composio): demote direct-mode Composio 401/Invalid …
oxoxDev May 23, 2026
f346476
chore(staging): v0.54.8
github-actions[bot] May 23, 2026
2c68eae
i18n: polish Indonesian UI translations (#2475)
aqilaziz May 23, 2026
e9c9374
feat(composio): curate OneDrive/Excel/Todoist + UI preview badge for …
CodeGhost21 May 23, 2026
0b2e9fe
fix(memory): bound ingestion queue to prevent OOM under runaway produ…
M3gA-Mind May 23, 2026
1cb6a14
fix(tauri): forward deep-link URLs on Linux before CEF preflight exit…
M3gA-Mind May 23, 2026
5b29725
feat(composio): add Linear provider for Memory Tree ingest (#2402)
justinhsu1477 May 23, 2026
934546b
chore(staging): v0.54.9
github-actions[bot] May 23, 2026
bbec0d6
feat(auth): loopback OAuth redirect with deep-link fallback (#2511)
senamakel May 23, 2026
e53204c
test(e2e): expand e2e coverage for 12 missing high-priority flows (#2…
senamakel May 23, 2026
67c943b
chore(staging): v0.54.10
github-actions[bot] May 23, 2026
9bae6d5
feat(analytics): forward x-tauri-version and x-core-version on every …
senamakel May 23, 2026
25da30f
test(e2e): expand agent-harness coverage for channels + prompt flows …
senamakel May 23, 2026
ac67a95
feat(keyring): OS keychain for secrets with dev-mode file backend (#2…
senamakel May 23, 2026
f29a20f
fix(agent): remove welcome agent flow (#2517)
senamakel May 23, 2026
6a09030
feat(search): unified search-engine selector + Brave Search (#2516)
senamakel May 23, 2026
838309e
Docs/i18n batch fix link localization (#2508)
JAYcodr May 23, 2026
95aeab5
docs(i18n): add zh-CN translations for features cloud-deploy, tool-me…
JAYcodr May 23, 2026
48548a2
feat(app): UI control for max_actions_per_hour (#2486) (#2500)
EvanCarson May 23, 2026
c6f5a8b
Add custom GIF mascot avatar override (#2347)
vaddisrinivas May 23, 2026
0d83851
fix(release): build linux arm64 target artifact (#2404)
aqilaziz May 23, 2026
ae164f5
fix(installer): let linux arm64 dry-run report unsupported asset (#2405)
aqilaziz May 23, 2026
2f913a2
fix(composio): show waiting state before opening browser (#2406)
aqilaziz May 23, 2026
f62bc96
Feat/2358 clear reembed skipped (#2443)
MrMrVlad May 23, 2026
8371755
fix(logging): print stored RPC URL as string in configPersistence (#2…
leighstillard May 23, 2026
ebdd03e
Document Linux/Wayland AppImage launch-crash workaround in README (#2…
distorx May 23, 2026
97553ca
feat(wallet): complete multi-chain signing + broadcast (EVM/BTC/Solan…
senamakel May 23, 2026
5aa6fbb
feat(analytics): enable low-rate browser Sentry tracing (#2520)
senamakel May 23, 2026
186498b
fix(linux): include libxdo3 in deb dependencies (#2498)
NgoQuocViet2001 May 23, 2026
a2d1b12
fix(i18n): add missing settings.mascot.customGif* keys for de (#2522)
senamakel May 23, 2026
fe37b13
fix(observability): demote OpenHuman backend unknown-model 400 (TAURI…
CodeGhost21 May 23, 2026
3e5a083
feat(ios): iOS client with QR pairing, E2E-encrypted tunnel, and push…
senamakel May 23, 2026
974abba
fix(memory/ingestion): bound the job channel + reject submits at cap …
obchain May 23, 2026
27f8d77
Fix/channels i18n hardcoded text (#2509)
JAYcodr May 23, 2026
b47b71e
fix(core): clean up startup timeout task (#2407)
NgoQuocViet2001 May 23, 2026
f4ea6a4
fix(docker): lower default build memory (#2420)
aqilaziz May 23, 2026
d93f834
feat(mascot): react to conversation cues (#2423)
aqilaziz May 23, 2026
7745d58
refactor(config): optimize backend URL resolution & local AI endpoint…
manucian-official May 23, 2026
f8c9698
feat(inference): OpenAI-compatible /v1 router with user-managed API k…
senamakel May 23, 2026
2a5d821
fix(docker): normalize core entrypoint line endings (#2545)
YOMXXX May 23, 2026
4c6007b
fix(oauth): make loopback redirect actually work, plus settings clean…
senamakel May 23, 2026
6a06bae
fix(windows): make port/process takeover actually free the port (#2552)
senamakel May 23, 2026
cf600a9
feat(memory_tree): consolidate module + add agentic walk tool + tests…
senamakel May 24, 2026
85e8421
feat: prod-test 2026-05-23 — composer, settings reorg, MCP/search, al…
senamakel May 24, 2026
f66e7e4
feat(encapsulation): cross-platform directory jail for agents/tools (…
senamakel May 24, 2026
e9ca97c
feat(mcp): split mcp_client/registry, multi-registry, boot-spawn + se…
senamakel May 24, 2026
d52abe5
fix(ai): add OpenRouter OAuth provider flow (#2571)
senamakel May 24, 2026
59c1687
Fix provider model testing and add MCP coming soon placeholder (#2570)
senamakel May 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
339 changes: 339 additions & 0 deletions .claude/scratch/agent-harness-e2e-plan.md

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,7 @@ Thumbs.db
tests/
scripts/
# Re-include the Docker entrypoint for the core image (Dockerfile COPYs it).
# The negation must come after the broad exclusion above to take effect.
# Re-include the parent directory first so older Docker pattern matchers that
# prune excluded directories still see the leaf exception below.
!scripts/
!scripts/docker-entrypoint-core.sh
17 changes: 17 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,20 @@ JWT_TOKEN=
# [optional] Default: 127.0.0.1 (use 0.0.0.0 for Docker / cloud).
# Leave unset to keep the default; the Docker image sets 0.0.0.0 automatically.
# OPENHUMAN_CORE_HOST=
# [optional] Extra CORS origins (comma-separated) allowed to reach the
# JSON-RPC server. The Tauri webview and loopback hosts are always allowed.
# For Docker / cloud deployments where the server binds to 0.0.0.0, add the
# canonical frontend origin(s) here to prevent cross-origin abuse from
# arbitrary sites (e.g. OPENHUMAN_CORE_ALLOWED_ORIGINS=https://app.example.com).
# OPENHUMAN_CORE_ALLOWED_ORIGINS=
# [optional] Default: 7788
OPENHUMAN_CORE_PORT=7788
# [optional] Default: http://127.0.0.1:7788/rpc
OPENHUMAN_CORE_RPC_URL=http://127.0.0.1:7788/rpc
# [optional] Comma-separated browser origins allowed to call /rpc with the
# Authorization bearer. Tauri and loopback Vite origins are allowed by default.
# Set this when serving a private web UI preview from a non-loopback origin.
# OPENHUMAN_CORE_ALLOWED_ORIGINS=https://openhuman-ui.example.com
# Core RPC bearer token. Single source of truth for /rpc auth.
# - Tauri desktop: set automatically by the shell — leave blank.
# - Docker / cloud / VPS: REQUIRED. Generate with `openssl rand -hex 32`.
Expand Down Expand Up @@ -72,6 +82,10 @@ OPENHUMAN_MODEL=
OPENHUMAN_WORKSPACE=
# [optional] Default: 0.7
OPENHUMAN_TEMPERATURE=0.7
# [optional] Language for background LLM artifacts such as memory-tree summaries,
# entity-extraction reasons, and learning reflections. Accepts UI locale tags
# such as zh-CN or a language name. Leave unset for default behavior.
# OPENHUMAN_OUTPUT_LANGUAGE=zh-CN
# [optional] Skill + agent tool execution timeout in seconds (default 120, max 3600)
# OPENHUMAN_TOOL_TIMEOUT_SECS=
# [optional] Headless update restart contract: self_replace | supervisor
Expand Down Expand Up @@ -167,6 +181,9 @@ OLLAMA_BIN=
# ---------------------------------------------------------------------------
# [optional] Bot username for managed Telegram DM linking (default: openhuman_bot)
OPENHUMAN_TELEGRAM_BOT_USERNAME=openhuman_bot
# [optional] Override Telegram Bot API base URL (defaults to https://api.telegram.org).
# Used by E2E tests to redirect Telegram API calls to the mock server.
# OPENHUMAN_TELEGRAM_API_BASE=http://127.0.0.1:18473

# ---------------------------------------------------------------------------
# Wallet RPC overrides
Expand Down
6 changes: 6 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.sh text eol=lf
*.bash text eol=lf
Dockerfile text eol=lf
.dockerignore text eol=lf
docker-compose*.yml text eol=lf
*.ps1 text eol=crlf
64 changes: 64 additions & 0 deletions .github/workflows/android-compile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
name: Android Compile Sanity

on:
pull_request:
paths:
- 'app/src-tauri-mobile/**'
- 'packages/tauri-plugin-ptt/**'
- 'src/openhuman/devices/**'
- 'app/src/services/transport/**'
- 'app/src/lib/tunnel/**'
- 'app/src/pages/ios/**'
- '.github/workflows/android-compile.yml'
workflow_dispatch:

permissions:
contents: read
pull-requests: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.head_ref || github.ref }}
cancel-in-progress: true

jobs:
android-compile:
name: Android Compile Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 1
# Mobile crate uses stock Tauri (no CEF) — no submodules needed.
submodules: false

- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: '1.93.0'
targets: aarch64-linux-android

- name: Cache Rust build artifacts
uses: Swatinem/rust-cache@v2
with:
workspaces: |
app/src-tauri-mobile -> target
packages/tauri-plugin-ptt -> target
cache-on-failure: true

- name: Set up pnpm
uses: pnpm/action-setup@v4

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'pnpm'

- name: Install dependencies
run: pnpm install --frozen-lockfile

# Hard gate: mobile Tauri host compiles for Android.
- name: cargo check -- mobile host (aarch64-linux-android)
run: cargo check --manifest-path app/src-tauri-mobile/Cargo.toml --target aarch64-linux-android
4 changes: 4 additions & 0 deletions .github/workflows/deploy-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ on:
paths:
- Dockerfile
- .dockerignore
- .gitattributes
- docker-compose.yml
- scripts/docker-entrypoint-core.sh
- .do/app.yaml
- gitbooks/developing/cloud-deploy.md
- .github/workflows/deploy-smoke.yml
Expand All @@ -18,7 +20,9 @@ on:
paths:
- Dockerfile
- .dockerignore
- .gitattributes
- docker-compose.yml
- scripts/docker-entrypoint-core.sh
- .do/app.yaml
- gitbooks/developing/cloud-deploy.md
- .github/workflows/deploy-smoke.yml
Expand Down
37 changes: 29 additions & 8 deletions .github/workflows/e2e-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,16 +150,37 @@ jobs:

- name: Run E2E (full suite)
if: ${{ inputs.full }}
env:
E2E_BAIL_ON_FAILURE: ${{ vars.E2E_BAIL_ON_FAILURE || '' }}
run: |
BAIL_FLAG=""
if [[ "${E2E_BAIL_ON_FAILURE:-}" == "1" ]]; then
BAIL_FLAG="--bail"
fi
xvfb-run -a --server-args="-screen 0 1280x960x24" \
bash app/scripts/e2e-run-session.sh

# Artifact uploads intentionally omitted — this reusable workflow
# is invoked from release-staging.yml and release-production.yml,
# and uploaded logs can carry mock-backend payloads, env-var
# echoes, and CDP transcripts that we don't want pinned to a
# release artifact. Local repro: rerun the spec via Docker and
# the same logs land in /tmp.
bash app/scripts/e2e-run-all-flows.sh --skip-preflight $BAIL_FLAG

- name: Upload E2E failure artifacts
if: failure()
uses: actions/upload-artifact@v5
with:
name: e2e-failure-logs-${{ runner.os }}-${{ github.run_id }}
path: |
/tmp/openhuman-e2e-app-*.log
app/test/e2e/artifacts/
retention-days: 7
if-no-files-found: ignore

- name: Write job summary
if: always()
run: |
echo "## E2E Results (${{ runner.os }})" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ -f /tmp/e2e-summary.txt ]; then
cat /tmp/e2e-summary.txt >> $GITHUB_STEP_SUMMARY
else
echo "No summary file found." >> $GITHUB_STEP_SUMMARY
fi

# Rust-side E2E counterpart to the Tauri runs above. Same Linux-only
# scope (CI does not run this on macOS or Windows — the Rust core is
Expand Down
93 changes: 93 additions & 0 deletions .github/workflows/ios-compile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
name: iOS Compile Sanity

on:
pull_request:
paths:
- 'app/src-tauri-mobile/**'
- 'packages/tauri-plugin-ptt/**'
- 'src/openhuman/devices/**'
- 'app/src/services/transport/**'
- 'app/src/lib/tunnel/**'
- 'app/src/pages/ios/**'
- '.github/workflows/ios-compile.yml'
workflow_dispatch:

permissions:
contents: read
pull-requests: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.head_ref || github.ref }}
cancel-in-progress: true

jobs:
ios-compile:
name: iOS Compile Check
runs-on: macos-latest
env:
# Pin the deployment target so swift-rs invokes the Swift compiler with
# `-target arm64-apple-ios16.0`. Matches Package.swift in
# packages/tauri-plugin-ptt/ios/, which uses iOS 14+ APIs (OSLog).
IPHONEOS_DEPLOYMENT_TARGET: '16.0'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 1
# The mobile crate uses stock Tauri (no CEF), so we don't need
# `submodules: recursive` — which would try to clone the
# `app/src-tauri/vendor/tauri-cef` submodule, a step that
# intermittently fails on macOS runners for fork PRs.
submodules: false

- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: '1.93.0'
targets: aarch64-apple-ios

- name: Cache Rust build artifacts
uses: Swatinem/rust-cache@v2
with:
workspaces: |
. -> target
app/src-tauri-mobile -> target
packages/tauri-plugin-ptt -> target
cache-on-failure: true

- name: Set up pnpm
uses: pnpm/action-setup@v4

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'pnpm'

- name: Install dependencies
run: pnpm install --frozen-lockfile

# Hard gate: mobile Tauri host compiles for iOS. No more soft-gate
# `continue-on-error` — the mobile crate uses stock Tauri without CEF
# so cef-dll-sys is not in the dependency graph.
- name: cargo check -- mobile host (aarch64-apple-ios)
run: cargo check --manifest-path app/src-tauri-mobile/Cargo.toml --target aarch64-apple-ios

# Hard gate: PTT plugin (host-target check; Swift sources are built
# lazily by swift-rs during the iOS-target check above).
- name: cargo check -- tauri-plugin-ptt
run: cargo check --manifest-path packages/tauri-plugin-ptt/Cargo.toml

# Hard gate: TypeScript compile.
- name: pnpm compile
run: pnpm --dir app compile

# Hard gate: iOS-relevant Vitest suites.
- name: pnpm test (iOS suites)
run: >
pnpm --dir app test --
src/services/transport
src/lib/tunnel
src/pages/ios
src/components/settings/panels/devices
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ remove_dupe_nonsense_issues
# Diagnostic harness output (scripts/diagnose-cef-runtime.mjs)
diagnosis-*.json

# Dev-mode keyring file backend (plain-text secrets, dev artifact only)
dev-keychain.json

# Logs
logs
*.log
Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ Follow this order so behavior is **specified**, **proven in Rust**, **proven ove
- **Pre-merge checks** (when touching code): Prettier, ESLint, `tsc --noEmit` in `app/`; `cargo fmt` + `cargo check` for changed Rust (`Cargo.toml` at root and/or `app/src-tauri/Cargo.toml` as appropriate).
- **No dynamic imports** in production **`app/src`** code — use **static** `import` / `import type` at the top of the module. Do **not** use `import()` (async dynamic import), `React.lazy(() => import(...))`, or `await import('…')` to load app modules, Tauri APIs, or RPC clients. **Why:** predictable chunk graph, simpler static analysis, fewer surprises in Tauri + Vite, and easier code review. **If a module must not run at load time** (e.g. heavy optional path), use a static import and **guard the call site** with `try/catch` or an explicit runtime check instead of deferring module load via dynamic import. **Exceptions:** Vitest harness patterns (`vi.importActual`, dynamic imports **only** inside `*.test.ts` / `__tests__` / `test/setup.ts` when required by the runner); ambient `typeof import('…')` in `.d.ts`; config files (e.g. `tailwind.config.js` JSDoc).- **Type-only imports**: `import type` where appropriate.
- **Dual socket / tool sync**: If you change realtime protocol, keep **frontend** (`socketService` / MCP transport) and **core** socket behavior aligned (see [`gitbooks/developing/architecture.md`](gitbooks/developing/architecture.md) dual-socket section).
- **i18n for all UI text**: Every user-visible string in `app/src/**` (headings, labels, button text, placeholders, status chips, toasts, dialog copy, `aria-label`, etc.) must go through `useT()` from `app/src/lib/i18n/I18nContext`. Hard-coded literals in JSX or `label=`/`placeholder=`/`aria-label=` props are not allowed. Add the new key to [`app/src/lib/i18n/en.ts`](app/src/lib/i18n/en.ts) in the same PR — other locales fall back to English. **Exceptions:** developer-only debug logs, code identifiers, and non-display data (URLs, slugs, technical sentinel values).

---

Expand Down
22 changes: 22 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,27 @@ Commands assume the **repo root**; `pnpm dev` delegates to the `app` workspace.

---

## iOS client (experimental)

The iOS client is an **in-progress, non-shipping** target in this repo. It does not ship a Rust core on-device; instead it connects to the desktop core via one of three transports selected by a `ConnectionProfile`.

**Transport strategies** (see `app/src/services/transport/`):
- `LanHttpTransport` — direct HTTP to the desktop core on the same LAN.
- `TunnelTransport` — socket.io relay through the backend; E2E encrypted with XChaCha20-Poly1305 over X25519 key agreement.
- `CloudHttpTransport` — fallback via the cloud backend API.

**Key paths:**
- PTT plugin: `packages/tauri-plugin-ptt/` (Swift + Rust, iOS-only).
- iOS screens: `app/src/pages/ios/` and `app/src/components/ios/`.
- Devices domain (Rust): `src/openhuman/devices/`.
- Tunnel crypto (TS): `app/src/lib/tunnel/`.
- iOS build entry: `pnpm tauri:ios:dev` — uses stock `@tauri-apps/cli@^2` via `npx`, **not** the vendored CEF CLI.
- Setup guide: `docs/ios/SETUP.md`.

**Backend dependency:** `tinyhumansai/backend#709` (tunnel socket.io contract) must be merged and deployed for end-to-end pairing to work.

---

## Commands (from repo root)

```bash
Expand Down Expand Up @@ -300,6 +321,7 @@ Specify → prove in Rust → prove over RPC → surface in the UI → test.
- **Pre-merge** (code changes): Prettier, ESLint, `tsc --noEmit` in `app/`; `cargo fmt` + `cargo check` for changed Rust.
- **No dynamic imports** in production `app/src` code — static `import` / `import type` only. No `import()`, `React.lazy(() => import(...))`, `await import(...)`. For heavy optional paths, use a static import and guard the call site with `try/catch` or a runtime check. *Exceptions*: Vitest harness patterns in `*.test.ts` / `__tests__` / `test/setup.ts`; ambient `typeof import('…')` in `.d.ts`; config files (e.g. `tailwind.config.js` JSDoc).
- **Dual socket sync**: when changing the realtime protocol, keep `socketService` / MCP transport aligned with core socket behavior (see `gitbooks/developing/architecture.md` dual-socket section).
- **i18n for all UI text**: every user-visible string in `app/src/**` (headings, labels, button text, placeholders, status chips, toasts, error messages, dialog copy) must go through `useT()` from `app/src/lib/i18n/I18nContext`. Hard-coded literals in JSX or `label=`/`placeholder=`/`aria-label=` props are not allowed. Add the key to [`app/src/lib/i18n/en.ts`](app/src/lib/i18n/en.ts) in the same PR — other locales fall back to English. Exceptions: developer-only debug logs, code identifiers, and non-display data (URLs, slugs, technical sentinel values).

---

Expand Down
Loading