426e9eeabd
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Waiting to run
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Waiting to run
Build Agent Image / build-and-push (push) Waiting to run
Chat shell gestures / Chat shell gesture + parity e2e (push) Waiting to run
ci / test (push) Waiting to run
ci / lint-and-format (push) Waiting to run
ci / build (push) Waiting to run
ci / dev-startup (push) Waiting to run
Cloud Gateway Discord / Test (push) Waiting to run
Cloud Gateway Webhook / Test (push) Waiting to run
Cloud Tests / lint-and-types (push) Waiting to run
Cloud Tests / unit-tests (push) Waiting to run
Cloud Tests / integration-tests (push) Waiting to run
Cloud Tests / e2e-tests (push) Blocked by required conditions
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Deploy Apps Worker (Product 2) / Determine environment (push) Waiting to run
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Blocked by required conditions
Deploy Eliza Provisioning Worker / Determine environment (push) Waiting to run
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Blocked by required conditions
Dev Smoke / Classify changed paths (push) Waiting to run
Dev Smoke / bun run dev onboarding chat (push) Blocked by required conditions
Dev Smoke / Vite HMR dependency-level smoke (push) Blocked by required conditions
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Waiting to run
gitleaks / gitleaks (push) Waiting to run
Markdown Links / Relative Markdown Links (push) Waiting to run
Publish @elizaos/example-code / check_npm (push) Waiting to run
Publish @elizaos/example-code / publish_npm (push) Blocked by required conditions
Publish @elizaos/plugin-elizacloud / verify_version (push) Waiting to run
Publish @elizaos/plugin-elizacloud / publish_npm (push) Blocked by required conditions
Quality (Extended) / Homepage Build (PR smoke) (push) Waiting to run
Quality (Extended) / Comment-only diff guard (push) Waiting to run
Quality (Extended) / Format + Type Safety Ratchet (push) Waiting to run
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Waiting to run
Quality (Extended) / Develop Gate (lint) (push) Waiting to run
Sandbox Live Smoke / Sandbox live smoke (push) Waiting to run
Snap Build & Test / Build Snap (amd64) (push) Waiting to run
Snap Build & Test / Build Snap (arm64) (push) Waiting to run
supply-chain / sbom (push) Waiting to run
supply-chain / vulnerability-scan (push) Waiting to run
Build, Push & Deploy to Phala Cloud / build-and-push (push) Waiting to run
Test Packaging / Validate Packaging Configs (push) Waiting to run
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Waiting to run
Test Packaging / Pack & Test JS Tarballs (push) Waiting to run
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Waiting to run
UI Fixture E2E / ui-fixture-e2e (push) Waiting to run
UI Fixture E2E / fixture-e2e (push) Waiting to run
UI Story Gate / story-gate (push) Waiting to run
vault-ci / test (macos-latest) (push) Waiting to run
vault-ci / test (ubuntu-latest) (push) Waiting to run
vault-ci / test (windows-latest) (push) Waiting to run
vault-ci / app-core wiring tests (push) Waiting to run
verify-patches / verify patches/CHECKSUMS.sha256 (push) Waiting to run
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Waiting to run
Voice Benchmark Smoke / voice bench smoke summary (push) Blocked by required conditions
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Waiting to run
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Waiting to run
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Waiting to run
Test Packaging / Build & Test PyPI Package (push) Waiting to run
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
524 lines
31 KiB
Markdown
524 lines
31 KiB
Markdown
# elizaOS — repository guide for agents
|
||
|
||
This is the **elizaOS** monorepo: an open-source framework for building and
|
||
deploying autonomous AI agents, plus the runtime, CLI, dashboard, cloud
|
||
backend, native bridges, and first-party plugins built on top of it. The repo
|
||
is **self-contained** — everything needed to run, test, and ship an Eliza agent
|
||
lives here.
|
||
|
||
`CLAUDE.md` and `AGENTS.md` in every directory are **identical** — author
|
||
`CLAUDE.md`, then copy it to `AGENTS.md`. Read the package-local `CLAUDE.md`
|
||
before working inside any package or plugin; this root file is the map.
|
||
|
||
## Naming
|
||
|
||
Write **elizaOS** (not `ElizaOS`). npm scope is `@elizaos/*`. In plain language,
|
||
say **Eliza agents**. Exception: the **Eliza Classic** plugin keeps `Eliza`
|
||
(the 1966 chatbot it reimplements).
|
||
|
||
## Toolchain
|
||
|
||
- **Runtime:** [Bun](https://bun.sh) (`packageManager` is pinned in
|
||
`package.json`) on **Node 24** (`engines.node`). ESM only (`"type": "module"`).
|
||
- **Monorepo:** [Turbo](https://turbo.build) drives `build` / `typecheck` /
|
||
`lint` / `test` across workspaces. Workspace globs are in `package.json`
|
||
(`packages/*`, `plugins/*`, `packages/native/*`, `packages/os/*`,
|
||
`packages/examples/*`, `packages/cloud/services/*`, …).
|
||
- **Lint/format:** [Biome](https://biomejs.dev) (`biome.json`). Ignore globs in
|
||
`.biomeignore`.
|
||
- **Tests:** Vitest, orchestrated by `packages/scripts/run-all-tests.mjs`.
|
||
- **TypeScript:** project-references build; root `tsconfig.json`,
|
||
`tsconfig.base.json`, per-package `tsconfig.json`.
|
||
|
||
## Root commands
|
||
|
||
```bash
|
||
bun install # workspace install (runs postinstall: submodules, patches)
|
||
bun run install:light # install without downloading the large artifact bundle
|
||
bun run dev # boot the API + dashboard UI (packages/app-core dev-ui)
|
||
bun run build # turbo build across the workspace
|
||
bun run verify # typecheck + lint (alias: bun run check) — run before "done"
|
||
bun run lint # biome lint via turbo
|
||
bun run format # biome format via turbo
|
||
bun run typecheck # tsc across workspace (8 GB heap)
|
||
bun run test # full suite (run-all-tests.mjs)
|
||
bun run test:server # core/agent/app-core/shared/vault/elizaos/skills/scenario-runner
|
||
bun run test:client # app/ui + lifeops/training plugins
|
||
bun run test:e2e # end-to-end lane
|
||
bun run start # run an agent (packages/agent start)
|
||
bun run clean # nuke dist/.turbo/node_modules and local state
|
||
bun run reset # clean, reinstall, rebuild
|
||
bun run cloud:mock # boot the full local cloud stack with mocks
|
||
```
|
||
|
||
Scope any command to one package with `--cwd`:
|
||
`bun run --cwd packages/core test`. The repo has 188 root scripts; the list
|
||
above is the day-to-day set. Use `bun run` with no args to print them all.
|
||
|
||
### Shared dev server for parallel lanes
|
||
|
||
When several worktrees are active on the same VPS, do **not** have every lane bind
|
||
the default app UI port (`2138`). `packages/app` keeps `bun run dev` unchanged
|
||
for single-lane local work, but concurrent agents should use the shared scripts:
|
||
|
||
```bash
|
||
cd packages/app
|
||
bun run dev:shared # long-lived Vite server on a deterministic worktree port
|
||
bun run dev:status # list running shared dev servers (port, worktree, pid)
|
||
bun run dev:rebuild # explicit Vite full-reload trigger for this worktree
|
||
```
|
||
|
||
Ports are reserved in `~/.eliza/dev-server-registry.json` (override with
|
||
`ELIZA_DEV_SERVER_REGISTRY`) from the normalized worktree path, with registry
|
||
locking and linear probing so active lanes do not collide. See
|
||
[`packages/docs/development/shared-dev-server.md`](packages/docs/development/shared-dev-server.md).
|
||
|
||
### Removed Root Command Migrations
|
||
|
||
| Removed command | Use instead |
|
||
| --- | --- |
|
||
| `bun run test:ci` | `bun run test` |
|
||
| `bun run test:cloud:playwright` | `bun run --cwd packages/app test:e2e` |
|
||
| `bun run test:ui:playwright` | `bun run --cwd packages/app test:e2e` |
|
||
| `bun run test:lifeops` | `bun run test:plugin 'plugin-personal-assistant'` |
|
||
| `bun run trajectory:inspect:test` | `bun test packages/scripts/__tests__/trajectory-validate.test.ts` |
|
||
| `bun run audit:e2e-coverage:test` | `bun test packages/scripts/e2e-coverage/check-e2e-coverage.test.ts` |
|
||
| `bun run test:browser-bridge` | `bun run --cwd packages/browser-extension test` |
|
||
| `bun run test:browser-bridge:safari` | `bun run --cwd packages/browser-extension test:smoke:safari` |
|
||
| `bun run voice:latency-report` | `bun run --cwd packages/app-core voice:latency-report` |
|
||
| `bun run voice:interactive` | `bun run --cwd packages/app-core voice:interactive` |
|
||
| `bun run voice:duet` | `bun run --cwd packages/app-core voice:duet` |
|
||
| `bun run voice:create-profile` | `bun run --cwd packages/app-core voice:create-profile` |
|
||
| `bun run smartglasses:hardware:doctor` | `bun run --cwd packages/examples/smartglasses hardware:doctor` |
|
||
| `bun run smartglasses:hardware:status` | `bun run --cwd packages/examples/smartglasses hardware:status-latest` |
|
||
| `bun run smartglasses:hardware:validate` | `bun run --cwd packages/examples/smartglasses hardware:validate-latest` |
|
||
| `bun run smartglasses:hardware:prove` | `bun run --cwd packages/examples/smartglasses hardware:prove:bleak` |
|
||
| `bun run smartglasses:hardware:prove:watch` | `bun run --cwd packages/examples/smartglasses hardware:prove:bleak:watch` |
|
||
| `bun run smartglasses:hardware:prove:noble` | `bun run --cwd packages/examples/smartglasses hardware:prove:noble` |
|
||
| `bun run smartglasses:hardware:prove:noble:watch` | `bun run --cwd packages/examples/smartglasses hardware:prove:noble:watch` |
|
||
| `bun run smartglasses:dev:hardware` | `bun run --cwd packages/examples/smartglasses dev:hardware` |
|
||
| `bun run smartglasses:dev:simulator` | `bun run --cwd packages/examples/smartglasses dev:simulator` |
|
||
| `bun run smartglasses:simulator` | `bun run --cwd packages/examples/smartglasses simulator` |
|
||
| `bun run smartglasses:smoke:simulator` | `bun run --cwd packages/examples/smartglasses smoke:simulator` |
|
||
| `bun run test:ci:live` | `bun run test:live` |
|
||
| `bun run test:lint` | `bun run audit:test-integrity:all` |
|
||
| `bun run test:lint:no-vi-mocks` | `bun run audit:test-integrity:no-vi-mocks` |
|
||
| `bun run test:lint:lane-coverage` | `bun run audit:test-integrity:lane-coverage` |
|
||
| `bun run test:lint:test-integrity` | `bun run audit:test-integrity` |
|
||
| `bun run test:lint:test-integrity:self-test` | `bun run audit:test-integrity:self-test` |
|
||
| `bun run verify:smartglasses-software` | `bun run audit:smartglasses-software` |
|
||
| `bun run personality:judge` | `bun run bench:personality` |
|
||
| `bun run personality:bench:calibrate` | `bun run bench:personality:calibrate` |
|
||
| `bun run lint:all` | `bun run verify` |
|
||
| `bun run build:typescript` | `node packages/scripts/run-turbo.mjs run build` |
|
||
| `bun run audit:mvp-board` | `bun run mvp:closeout-audit` |
|
||
| `bun run mvp:board-readiness` | `bun run mvp:closeout-audit` |
|
||
| `bun run mvp:evidence-matrix` | `bun run mvp:closeout-audit` |
|
||
|
||
## Repo map — where to find what
|
||
|
||
```
|
||
packages/ framework, shared libraries, and product surfaces
|
||
core/ @elizaos/core — runtime, types, agent loop, memory/state, model layer
|
||
agent/ @elizaos/agent — AgentRuntime, plugin loader, default plugin map
|
||
app-core/ API + dashboard host; dev/build orchestration (scripts/dev-ui.mjs)
|
||
elizaos/ the `elizaos` CLI — create / info / upgrade / version; project + plugin templates
|
||
prompts/ shared prompt scaffolding
|
||
shared/ cross-package utilities + brand assets
|
||
ui/ shared React component library
|
||
app/ web + desktop dashboard, desktop shell, and current cloud apex UI
|
||
tui/ terminal UI
|
||
skills/ runtime skills knowledge base (USE_SKILL)
|
||
scenario-runner/ scenario + eval harness
|
||
cloud/api/ managed backend API (Hono on Cloudflare Workers)
|
||
cloud/docs-redirect/ Eliza Cloud docs redirects
|
||
cloud/shared/ shared cloud backend: db (Drizzle), billing, services, types
|
||
cloud/sdk/ cloud/routing/ cloud/infra/ cloud client SDK, model routing, IaC
|
||
contracts/ on-chain contracts + ABIs
|
||
security/ security/soc2-verify/ vault/ secrets, key management, compliance tooling
|
||
os/ robot/ device/OS images, OS landing, robotics
|
||
plugin-remote-manifest/ plugin-worker-runtime/
|
||
remote plugin manifests, host shims, and worker runtime support
|
||
homepage/ docs/ marketing site and docs site
|
||
examples/ 30+ standalone runnable examples (each has its own README)
|
||
benchmarks/ 30+ evaluation suites (each has its own README + harness)
|
||
|
||
plugins/ runtime plugins and app plugins
|
||
plugin-<model>/ openai, anthropic, google-genai, groq, openrouter, xai, ollama, …
|
||
plugin-<connector>/ discord, telegram, farcaster, slack, imessage, whatsapp, x, …
|
||
plugin-native-*/ native device bridges (camera, contacts, calendar, location, …)
|
||
plugin-local-inference/ on-device llama.cpp (Kokoro TTS folded in) / whisper (git submodules under native/)
|
||
plugin-sql/ plugin-localdb/ plugin-inmemorydb/ storage adapters
|
||
plugin-documents/ plugin-personal-assistant/ plugin-health/ … app plugins
|
||
|
||
scripts/ repo automation patches/ dependency patches
|
||
turbo.json knip.json build + dead-code config
|
||
```
|
||
|
||
Every package and plugin carries its own `CLAUDE.md` / `AGENTS.md` (identical)
|
||
and `README.md`. **Read the package-local doc first** — it lists that package's
|
||
layout, exports, scripts, env vars, and gotchas.
|
||
|
||
## Runtime architecture in 60 seconds
|
||
|
||
- **`@elizaos/core`** is the framework: the agent loop, the plugin model, and
|
||
the message / memory / state primitives, with a model-agnostic LLM layer.
|
||
If your code depends on `@elizaos/core`, you are using the framework.
|
||
- **`@elizaos/agent`** wires a runnable agent: `AgentRuntime`, the plugin
|
||
loader, and the default plugin map.
|
||
- A **plugin** is `src/index.ts` exporting a `Plugin` object that registers:
|
||
- **actions** — things the agent can *do* (validate + handler),
|
||
- **providers** — context injected into the prompt,
|
||
- **services** — long-lived singletons (clients, schedulers, connectors),
|
||
- **evaluators** — post-response processing,
|
||
- plus routes, events, and model handlers.
|
||
- **`@elizaos/app-core`** hosts the HTTP API + dashboard that runs agents.
|
||
- The **`elizaos` CLI** is intentionally minimal: scaffolding (`create`),
|
||
info, and template upgrades. Project/plugin scaffolds live in
|
||
`packages/elizaos/templates/` (`min-project`, `min-plugin` have `SCAFFOLD.md`
|
||
contracts).
|
||
|
||
To build on the runtime from your own TypeScript with no CLI/UI, import
|
||
`@elizaos/core` directly — see `packages/examples/` (30+ standalone references).
|
||
|
||
## Repo-wide conventions
|
||
|
||
- **Logger only, never `console`** in server code. Use the structured logger,
|
||
prefix messages with `[ClassName]`, attach context objects on errors.
|
||
- **ESM only.** No CommonJS.
|
||
- **No business computation in proxy/route layers.** Derive values in
|
||
use-cases and return DTO fields the client just renders. Clients display,
|
||
never compute.
|
||
- **DTO fields are required by default;** don't paper over a broken pipeline
|
||
with `?? 0` or `as` casts.
|
||
- Keep weak types (`any` / `unknown` / unsafe casts) out; validate at runtime
|
||
boundaries and type the validated result.
|
||
|
||
## GitHub project coordination
|
||
|
||
For agent/human work coordinated through GitHub Projects, read
|
||
[`CONTRIBUTING.md`](CONTRIBUTING.md) before claiming work. Permanent process
|
||
lives in that doc, not in a long issue-comment thread.
|
||
|
||
- Issues are scoped work cards with acceptance criteria and evidence.
|
||
- GitHub Projects are the live kanban state: `Todo` -> `Claimed` ->
|
||
`In progress` -> `Needs-agent-verify` -> `needs-human-verify` -> `Done`.
|
||
- Set the Project `Claimed by` field to your lane/agent tag when you claim a
|
||
card, and keep `Status` accurate.
|
||
- Discussions are for coordination, handoffs, and noisy multi-card chat; roll
|
||
durable decisions back into docs, issue bodies, or Project readmes.
|
||
- PRs carry the code and the proof required by this guide; link the issue
|
||
or Project card they resolve.
|
||
- Do not move cards to `Done` unless the board explicitly grants that authority
|
||
to your role. Human verification owns final done for launch/QA boards.
|
||
|
||
Current Launch QA routing: Project
|
||
<https://github.com/orgs/elizaOS/projects/12>, Discussion
|
||
<https://github.com/orgs/elizaOS/discussions/14292>, tracker/history
|
||
<https://github.com/elizaOS/eliza/issues/13406>.
|
||
|
||
LifeOps Personal Assistant MVP routing: Project
|
||
<https://github.com/orgs/elizaOS/projects/15>. Product scope, the seven
|
||
personas, and the per-workstream acceptance bar live in
|
||
[`packages/docs/ongoing-development/mvp/MVP.md`](packages/docs/ongoing-development/mvp/MVP.md);
|
||
in-flight design docs (per-workstream research + status snapshots) live under
|
||
[`packages/docs/ongoing-development/`](packages/docs/ongoing-development/README.md).
|
||
This folder adds a **design-doc layer** to the workflow above: discussion →
|
||
design doc here → issues on the board → PR with evidence → doc updated on merge.
|
||
|
||
Evidence attaches **inline in the issue/PR**, not committed to the repo: MP4
|
||
video (renders inline in GitHub), JPG over PNG for screenshots, logs in a
|
||
`<details>` block. Bug reports include a screenshot or recording of the *wrong*
|
||
behavior. `.github/issue-evidence/` is retired; the full standard is
|
||
in this guide and `CONTRIBUTING.md`.
|
||
|
||
## Error-Handling Simplification
|
||
|
||
Binding policy for all error handling (parent #12182, foundation #12263). The
|
||
codebase is full of defensive sludge — empty catches, log-and-continue that
|
||
fabricates a result, `return <default>` from catch, `.catch(() => {})` on writes
|
||
that matter, `?? <literal>` standing in for failed/missing data — that
|
||
**swallows failures and makes broken pipelines look healthy**. Remove it.
|
||
|
||
**Doctrine — fail fast inside, handle at the boundary.** Inner code throws typed
|
||
errors; it does not catch-and-continue. Only designated boundaries translate
|
||
those errors into a structured failure, a user-facing error state, or an
|
||
escalation. This is crash-only design (Candea & Fox, "Crash-Only Software",
|
||
HotOS IX 2003): transparent recovery at a designed boundary beats ad-hoc
|
||
continue-on-error in every function. A failure must surface **observably** —
|
||
either the **agent** sees it (and can retry / reconfigure / disable the failing
|
||
feature) or it is **raised to the owner/developers** when systemic.
|
||
|
||
**"Not loaded" must never read as "zero"/"empty".** A `?? 0`, `?? []`, `?? ""`,
|
||
or `return 0`/`return []` from a catch that substitutes for failed or missing
|
||
data conflates a broken pipeline with a legitimately empty result. Banned. DTO
|
||
fields are required by default; fix the pipeline, don't paper over it.
|
||
|
||
**Fast-fail on data paths; throw, never fabricate.** Precedent: issue #9324
|
||
(closed) removed fabricated zero/marker embedding vectors in favor of throwing;
|
||
`plugins/plugin-embeddings/AGENTS.md:32` codifies "THROW, never fabricate".
|
||
|
||
**UI three-state rule.** `loading` / designed-`empty` / `error` are three
|
||
distinguishable renders — never render healthy-empty from a catch. A
|
||
404-from-unloaded-plugin may degrade to a designed "unavailable" state (J4);
|
||
5xx/transport/parse failures set an error state. Canonical pattern:
|
||
`packages/ui/src/components/pages/StreamView.tsx:55-63`; repaired load shape:
|
||
`packages/ui/src/state/usePluginsSkillsState.ts:195-220`. See the view audit
|
||
`scripts/view-audit/output/MASTER-REPORT.md` §6.A/§6.D.
|
||
|
||
**Use the foundation.** New/rewritten throw sites use `ElizaError`
|
||
(`packages/core/src/errors.ts`, `{ code, context, cause, severity }`).
|
||
Diagnostic call sites outside the action path (providers, services, background
|
||
jobs, event handlers) call `runtime.reportError(scope, error, context?)` — it
|
||
logs, emits `EventType.ERROR_REPORTED`, surfaces the failure to the agent via
|
||
the `RECENT_ERRORS` provider, and drives owner escalation on repeated systemic
|
||
failure. Action/tool failures already reach the model via the planner loop —
|
||
keep that.
|
||
|
||
### Justified categories (J1–J7) — keep, annotated `// error-policy:J<N> <reason>`
|
||
|
||
Every kept handler carries a grep-able `// error-policy:J<N> <reason>` comment
|
||
so "remaining handlers each have a documented justification" is mechanically
|
||
checkable. A justified handler still may not fabricate a success value: J1
|
||
returns a *failure*, J3 returns an explicit *invalid* signal, J4 renders an
|
||
*error/unavailable* state.
|
||
|
||
- **J1 boundary translation** — one outermost handler per process/transport
|
||
boundary producing a structured failure.
|
||
- **J2 context-adding rethrow** — must use `cause`.
|
||
- **J3 untrusted-input sanitizing** — parse failures produce an explicit typed
|
||
"invalid" result, never a fake-valid default.
|
||
- **J4 explicit user-facing degrade** — designed, visually-distinguishable
|
||
unavailable/error states; only expected error shapes degrade.
|
||
- **J5 unhandled-rejection suppression** — with a comment naming where the
|
||
rejection IS observed.
|
||
- **J6 best-effort teardown** — debug/warn, teardown paths only.
|
||
- **J7 diagnostics-must-not-kill-the-loop** — trajectory/telemetry writes may
|
||
catch but must warn + `runtime.reportError`.
|
||
|
||
Everything else is slop — including every empty catch, log-and-continue that
|
||
fabricates the function's result, `return <default>` from catch,
|
||
`.catch(() => {})` on writes that matter, `?? <literal>` substituting for
|
||
failed/missing data, optional-chaining-as-guard on required collaborators, and
|
||
fallback code paths whose only purpose is masking a primary failure. Every catch
|
||
without an annotation must be either newly-obvious slop or a J1 route boundary in
|
||
a directory documented as such in the batch PR.
|
||
|
||
**Regression guard (diff-scoped ratchet).** `bun run audit:error-policy-ratchet`
|
||
compares every production source file the branch touches against that file's own
|
||
content at the merge-base with `origin/develop`, and fails only when a touched
|
||
file **adds** an empty catch or server-side `console.*` call. It is immune to
|
||
unrelated `develop` drift (files the branch does not touch are never counted)
|
||
and is a no-op on `develop` itself. Run `... --report` for the repo-wide totals
|
||
the #12182 sweeps drive down. Logger only, never `console`, in server code.
|
||
|
||
## Slop and Comment Cleanup
|
||
|
||
Every file is legible on its own: a purpose-explaining prose header at the top,
|
||
then in-body comments that explain **why the code is the way it is** — the
|
||
design rationale, the constraint that forced the approach, what consumes it —
|
||
never a restatement of *what* it does. The reader can read the code; a comment
|
||
earns its place only by adding what the code cannot show. No change-narration.
|
||
Write for the next engineer opening this file cold in a **greenfield** codebase:
|
||
there is no legacy to apologize for and no diff history to narrate — these
|
||
comments are the codified, durable explanation of the system. The rules below
|
||
are binding for new code and for the repo-wide cleanup (#12181).
|
||
|
||
1. **Header form — one `/** … */` prose block at the very top** (after a `#!`
|
||
shebang; after a third-party license block in the few files that carry one;
|
||
before imports). Plain prose, not a template: no `@fileoverview`, no
|
||
`@author`/`@date`/`@version`. Position is the signal.
|
||
2. **First sentence states what the file does in system terms; never repeat the
|
||
filename** ("Local content-addressed media store for chat attachments.").
|
||
Then, only as warranted, give the reader a sense of the file's place in the
|
||
system: **who consumes it and what it consumes** (the boundary it sits on),
|
||
the invariants/constraints it must uphold, why it is shaped the way it is, and
|
||
gotchas to know before editing. Issue refs like `(#9948)` are welcome when
|
||
they anchor non-obvious rationale — never as a substitute for stating it.
|
||
3. **Length scales with weight, hard ceiling ~25 lines.** Barrel `index.ts` /
|
||
tiny type files: 1 line. Typical modules: 2–6 lines. Load-bearing modules:
|
||
2–3 short paragraphs. Longer than that belongs in the package
|
||
`CLAUDE.md`/`README.md` — reference it instead. Test files: 1–3 lines — what
|
||
surface is under test and how real the harness is (live model vs
|
||
deterministic proxy, real DB vs in-memory).
|
||
4. **In-body comments explain the *why*, never the *what*.** The reader can see
|
||
what the code does; a comment earns its place only by adding what the code
|
||
cannot show — the design rationale (why this approach and not the obvious
|
||
alternative), the invariant or constraint that forced it, units and boundary
|
||
conditions, protocol quirks, ordering constraints, and non-obvious
|
||
consequences for callers. Keep the two-tier split — `/** JSDoc */` on exported
|
||
symbols (for callers), `//` for implementation notes. Delete restatement,
|
||
change-narration, status updates, migration stories, and commented-out code.
|
||
Do not blanket-comment: a file whose code is clear needs a header and nothing
|
||
else.
|
||
5. **Churn test = durability test.** Would the comment be true and useful to
|
||
someone who never saw the previous version? If it only makes sense as a diff
|
||
annotation, delete it; if the fact is durable but churn-phrased, rewrite it to
|
||
present tense. History lives in git.
|
||
6. **Accuracy over coverage.** A wrong header is worse than no header. Read the
|
||
package's `CLAUDE.md` first; if a file's purpose can't be determined, flag it
|
||
in the PR instead of guessing.
|
||
|
||
Copy the tone from these in-repo exemplars, don't invent one:
|
||
[`packages/agent/src/api/media-store.ts:1`](packages/agent/src/api/media-store.ts)
|
||
(service), [`packages/ui/src/components/RoleGate.tsx:1`](packages/ui/src/components/RoleGate.tsx)
|
||
(React component), [`packages/scripts/run-all-tests.mjs:1`](packages/scripts/run-all-tests.mjs)
|
||
(script — but don't copy its filename-repetition opener), and `.gitmodules`
|
||
(config prose). Never touch code, string/template literals, third-party license
|
||
blocks (header goes below them), or generated files. Comment-only changes are
|
||
machine-checked by `bun run check:comment-only`
|
||
([`scripts/assert-comment-only-diff.mjs`](scripts/assert-comment-only-diff.mjs)):
|
||
it asserts the code token stream is byte-for-byte unchanged.
|
||
|
||
## App visual review — REQUIRED for UI changes in `packages/app/`
|
||
|
||
Any change in `packages/app/` (or a shared package whose UI bleeds into it) MUST
|
||
pass the screenshot + manual-review loop before it is "done":
|
||
|
||
```bash
|
||
bun run --cwd packages/app audit:app
|
||
```
|
||
|
||
This walks the app views (desktop + mobile, rest + hover), captures the
|
||
populated UI, and auto-stubs `aesthetic-audit-output/manual-review/<slug>.md`
|
||
per view. Use those Markdown files for human notes and eyeballing; CI enforces
|
||
the computed verdicts in `report.json` / the Playwright run, not edited
|
||
manual-review Markdown. Review every page you touched or can reach via shared
|
||
layout/theme/components.
|
||
|
||
- No computed page verdict may stay `needs-work` / `broken` when a UI task is
|
||
declared done.
|
||
- Iterate the loop ≥5× for any meaningful redesign.
|
||
- Orange is accent only; no blue anywhere; orange-resting → darker-orange hover
|
||
(never orange→black). Full package rules: `packages/app/AGENTS.md`.
|
||
|
||
## LifeOps + health: one scheduler, structural behavior
|
||
|
||
`@elizaos/plugin-personal-assistant` and `@elizaos/plugin-health` share one
|
||
scheduled-item architecture. Reminders, check-ins, follow-ups, watchers,
|
||
recaps, approvals, and outputs are all `ScheduledTask` records routed through a
|
||
single runner (`plugins/plugin-scheduling/src/scheduled-task/runner.ts`),
|
||
which pattern-matches on structural fields (`kind`, `trigger`, `shouldFire`,
|
||
`completionCheck`, `pipeline`, …), never on `promptInstructions` text. Health
|
||
contributes through registries; LifeOps does not import its internals.
|
||
|
||
For the full automation vocabulary — how a **workflow**, **trigger**, **task**,
|
||
**scheduled item**, **coding task**, and **automation** differ and what fires
|
||
each (one clock, two consumers) — see [`docs/automation-glossary.md`](docs/automation-glossary.md).
|
||
|
||
**Do not add:** a second LifeOps scheduling mechanism, a second knowledge-graph
|
||
store (use `EntityStore` / `RelationshipStore`), behavior driven by
|
||
`promptInstructions` string content, a `boolean` return from a connector
|
||
dispatch (use the typed `DispatchResult`), or an identity-merge that bypasses
|
||
the merge engine. Architecture, frozen contracts, and contribution paths live in
|
||
`plugins/plugin-personal-assistant/README.md` and `plugins/plugin-health/README.md`.
|
||
|
||
## Attachments & files: one content-addressed store, additive model
|
||
|
||
Attachment bytes live in a **single content-addressed store** —
|
||
`packages/agent/src/api/media-store.ts` (`${STATE_DIR}/media/<sha256>.<ext>`,
|
||
served at `/api/media/<sha256>.<ext>`). The sha256 URL is the canonical,
|
||
unguessable, deduped handle; `Media` (`packages/core/src/types/primitives.ts`)
|
||
is the in-message reference and is widened **additively only**. Bytes are served
|
||
pre-auth (the hash is the capability), with `nosniff` + a download
|
||
`Content-Disposition` for SVG/active types; every server-side attachment fetch
|
||
must go through the SSRF guard (`packages/core/src/network` + `media/fetch.ts`).
|
||
|
||
**Do not add:** a second file store or storage abstraction/selector; a
|
||
`files`/`file_references` DB table or a refcount/GC engine (the store already
|
||
GCs via `gcUnreferencedMedia` + a grace window); a `fileId` on `Media`; a
|
||
rewrite/rehost on the pre-auth serve path (rehost only on authenticated write);
|
||
or a repurpose/removal of a `ContentType` enum value (it is **frozen,
|
||
append-only** — derive fine-grained kind from `mimeType` at read time). Scope,
|
||
deferrals, and rationale live in issue #8876.
|
||
|
||
## Definition of Done — sync, PR, and human-verifiable evidence
|
||
|
||
Every fix/feature ships through a **PR against `develop`**, and a reviewer must
|
||
be able to confirm it works **without reading the code**. This section is the
|
||
binding standard. **The same standard is restated in every package's
|
||
`CLAUDE.md` / `AGENTS.md` and is non-negotiable.**
|
||
|
||
**The three laws of "done"** (the whole standard expands these):
|
||
|
||
1. **Prove the real thing happened — and look at it yourself.** Record the
|
||
actual model trajectories (inputs *and* outputs from a **live** model, not the
|
||
proxy, not a mock), the real client + server logs, the real pixels/audio, and
|
||
the real domain artifacts (memories, knowledge, DB rows, scheduled tasks,
|
||
wallet balance, on-chain results, generated files). Then **open every artifact
|
||
and review it by hand.** Capturing is not reviewing; green CI is not proof.
|
||
2. **Test everything for real — no larp.** Every change ships detailed,
|
||
full-featured **end-to-end** tests that drive the *real* path — not the happy
|
||
"front door" only. Cover error paths, edges, empty/invalid input, concurrency,
|
||
roles/permissions, and adversarial input. A test asserting against a
|
||
mock/stub standing in for the thing under test does **not** count; if the real
|
||
model/device/chain/connector is hard to reach, make it reachable — that's the
|
||
work. If the existing tests you touch are shallow or mocked, fixing them is
|
||
part of your change. (See the standing backlog: #9943, #9950, #9954, #9958,
|
||
#9967, #9970.)
|
||
3. **No residuals, no shortcuts.** The goal is not "done," it is *everything*
|
||
done. Clear blockers by the **hard path** — build the real architecture, stand
|
||
up the real model/device/service, actually test it. No TODOs, stubs,
|
||
stepping-stones, or "follow-ups." When unsure, research, weigh options, and
|
||
ship the best production-ready version. Keep going until every possibility is
|
||
exhausted.
|
||
|
||
The non-negotiables in practice:
|
||
|
||
- **Always PR; never push feature/fix work straight to `develop`.** Branch as
|
||
`feat|fix|docs|chore/<slug>`; open an issue first for anything non-trivial.
|
||
- **Always sync before opening or updating a PR.** `git fetch origin &&
|
||
git rebase origin/develop`, resolve **every** conflict, `bun install`, then
|
||
`bun run verify`. A branch that can't fast-forward onto `develop` is not ready.
|
||
- **Frontend-testable changes are not done without rendered proof.** Any change a
|
||
user can exercise in the web, desktop, mobile, or cloud UI must attach a video
|
||
walkthrough; before and after full-page screenshots for desktop and mobile;
|
||
backend structured logs; frontend console and network logs; and real-LLM
|
||
trajectories when agent/action/provider/prompt/model behavior changes. If one
|
||
row does not apply, keep it visible and write `N/A - <reason>` in the PR and
|
||
issue evidence.
|
||
- **Attach complete, real, manually-reviewed evidence** — prove the real thing
|
||
happened, not a mock of it:
|
||
- **Real-LLM trajectories** for agent/action/prompt/model changes —
|
||
`packages/scenario-runner/bin/eliza-scenarios run <scenario> --report <out>`
|
||
against a **live** model (JSON report + run viewer + native jsonl) — **and
|
||
read them.**
|
||
- **Backend logs** (structured `[ClassName] …`) and **frontend logs**
|
||
(console + network) showing the actual code path firing.
|
||
- **Before/after full-page screenshots** (desktop + mobile) + a **video
|
||
walkthrough** of the whole flow — default to `bun run test:matrix:review`
|
||
so the full matrix writes `evidence/matrix-run.json` and opens the unified
|
||
reviewer. For scoped UI evidence, use `bun run test:e2e:record:review`;
|
||
for app UI, use `bun run --cwd packages/app audit:app`.
|
||
- **Per-platform capture** (screenshot + recording + logs) for native/mobile/
|
||
desktop changes — `bun run --cwd packages/app capture:ios-sim` /
|
||
`capture:android-emu` / `capture:linux-desktop` / `capture:windows-desktop`,
|
||
electrobun `GET /api/dev/cursor-screenshot`. Run native features on the real
|
||
device/simulator/platform matrix, not mocked-bridge desktop Chromium. The
|
||
command matrix lives in `CONTRIBUTING.md`.
|
||
- **Always build + deploy the latest before capturing.** Capture helpers
|
||
screenshot whatever is **already installed/running** — they do not build.
|
||
Before any on-device/simulator/desktop capture, rebuild and redeploy the
|
||
current tree (mobile: `build:android` / `build:ios` cap sync **and
|
||
reinstall** — a Capacitor app bakes the web bundle into the APK/IPA at build
|
||
time, so restarting the old app never picks up a renderer change). Confirm
|
||
the running build is yours (`versionName` / a known on-screen change) — a
|
||
screenshot of a stale install proves nothing.
|
||
- **Audio + narrated walkthrough** for voice/transcript/TTS/STT changes.
|
||
- **Domain artifacts** — the things the change produced (memory/knowledge/DB
|
||
rows, scheduled tasks, wallet balance before/after, on-chain tx hashes,
|
||
generated files, device output) — inspected by hand and shown.
|
||
- Evidence is posted **inline in the issue/PR itself** (drag-and-drop):
|
||
videos as **MP4** (GitHub renders MP4 inline — convert `.mov`/`.webm`),
|
||
screenshots as **JPG** rather than PNG where possible, long logs in a
|
||
`<details>` block. Do not commit evidence files to the repo
|
||
(`.github/issue-evidence/` is retired). Each evidence type is attached
|
||
**or** explicitly marked N/A with a reason — never left blank. If `develop`
|
||
moved and changed behavior, **re-capture** evidence; stale proof is worse
|
||
than none.
|
||
|
||
## Contributing
|
||
|
||
Open an issue before a non-trivial PR. License: MIT (`LICENSE`). Security
|
||
policy: `SECURITY.md`. Shipping workflow: `CONTRIBUTING.md`.
|