chore: initialize EverOS 1.0.0

md-first memory extraction framework for AI agents.

Markdown is the single source of truth; SQLite holds state and LanceDB
provides the rebuildable vector + BM25 + scalar index. The codebase follows
a single-direction DDD layering (entrypoints -> service -> memory -> infra,
with component / core / config cross-cutting) enforced by import-linter.

Engineering surface:
- Coding conventions in .claude/rules/ (path-scoped) and workflows in
  .claude/skills/ (/commit, /new-branch, /pr).
- GitHub Actions CI runs make lint + test + integration; pre-commit mirrors
  the gates locally (ruff, hygiene hooks, gitlint commit-msg).
- Commit messages follow Conventional Commits, enforced by gitlint.
- make lint also enforces datetime two-zone discipline and OpenAPI drift.
This commit is contained in:
Elliot Chen
2026-06-05 22:35:51 +08:00
commit 518b8eca85
636 changed files with 160553 additions and 0 deletions

View File

@ -0,0 +1,32 @@
# Architecture rule (always loaded)
EverOS is a DDD-layered framework. The dependency direction is **single, downward only**:
```
entrypoints → service → memory → infra
component / core / config
```
- `entrypoints/` — CLI + HTTP API (presentation). No business logic.
- `service/` — use-case orchestration (memorize / retrieve / evolve / manage).
- `memory/` — domain (extract / search / cascade / prompt_slots / models).
- `infra/` — storage adapters (markdown + sqlite + lancedb) and the OME subsystem.
- `component/` — injectable providers (llm / embedding / config / utils).
- `core/` — runtime base (observability / lifespan / context / persistence primitives).
- `config/` — configuration data (Settings + default TOML).
## Hard constraints (enforced by `import-linter`, run in `make lint`)
1. **Layering**: an outer layer may import an inner layer, never the reverse.
`entrypoints → service → memory → infra`.
2. **Private internals**: `service`, `memory`, and `entrypoints` must not import
`infra.persistence.{markdown,lancedb,sqlite}.**` internals — go through the
package facade (`from everos.infra.persistence.markdown import ...`).
3. **OME isolation**: `infra.ome` must not import `persistence`, `memory`,
`service`, or `entrypoints`. It is a low-level scheduler with no domain knowledge.
If a change needs to cross a boundary the wrong way, the design is wrong — refactor,
don't add an exception.
Full rationale: [docs/architecture.md](../../docs/architecture.md).

View File

@ -0,0 +1,21 @@
---
paths:
- "src/**/*.py"
- "tests/**/*.py"
---
# Async programming rule
The write/read paths are async end-to-end. Keep them non-blocking.
- **No blocking calls in async functions** — no synchronous file I/O, no `time.sleep`,
no blocking DB/network calls inside `async def`. Ruff `ASYNC` flags the common cases.
- **Offload CPU/blocking work** with `anyio.to_thread.run_sync` (or the established
helper) rather than blocking the event loop.
- **Concurrency** via `asyncio.gather` / `asyncio.TaskGroup` for independent awaits;
don't `await` in a loop when the calls are independent.
- **Tests**: `pytest-asyncio` is in `auto` mode — an `async def test_*` just works,
no `@pytest.mark.asyncio` needed.
- **Don't fire-and-forget** without holding a reference (`asyncio.create_task` results
must be tracked, or you lose exceptions). The OME subsystem owns the long-running
background loops — application code shouldn't spawn its own.

View File

@ -0,0 +1,17 @@
# Code style rule (always loaded)
- **Formatter & linter**: `ruff` is the single tool (replaces black / isort / flake8).
Line length 88, target `py312`. Run `make format` to auto-fix; `make lint` checks.
- **Active ruff rule sets**: `E F I N UP B SIM ASYNC`. Don't disable a rule inline
unless there's a genuine reason — prefer fixing the code.
- **Type hints**: annotate every public function signature (params + return). The
codebase is ~100% typed; keep it that way.
- **`from __future__ import annotations`** at the top of every module — annotations
are strings, so forward refs and `X | None` unions are free.
- **Prefer `collections.abc`** (`Sequence`, `Mapping`) over concrete `list`/`dict`
in signatures; use `Protocol` for structural interfaces.
- **No dead code**: no commented-out blocks, no unused imports, no speculative
abstractions. Delete rather than comment out.
- **Naming**: `*Manager` (orchestrators), `*Provider` (injectable services),
`*Reader`/`*Writer` (persistence), `*Recaller` (search routes). Follow the
established suffix when adding a sibling.

View File

@ -0,0 +1,33 @@
---
paths:
- "src/**/*.py"
- "tests/**/*.py"
---
# Datetime handling rule (two-zone discipline)
**Never** construct or read "now" directly. All datetime flows through
`everos.component.utils.datetime`. This is a **hard CI gate**
(`make check-datetime`, wired into `make lint`).
## Banned (the checker fails the build on these)
- `datetime.now()`, `datetime.utcnow()`, `datetime.today()`
- `time.time()`, `time.time_ns()`
- `datetime(YYYY, ...)` without `tzinfo=`
- `.astimezone(...)` / `.replace(tzinfo=...)` outside the helper module
## Use instead
| Need | Helper |
|---|---|
| "now" for **storage** (UTC) | `get_utc_now()` |
| "now" for **display** (configured TZ) | `get_now_with_timezone()` |
| today's date, display TZ | `today_with_timezone()` |
| normalize a value to UTC | `ensure_utc(d)` |
| render to display TZ | `to_display_tz(d)` |
| parse ISO / epoch / str | `from_iso_format(v)`, `from_timestamp(ts)` |
| serialize | `to_iso_format(d)`, `to_date_str(d)`, `to_timestamp_ms(d)` |
**Two zones**: persist in UTC, present in the configured display TZ. Crossing them
goes through the helpers — never ad-hoc. See [docs/datetime.md](../../docs/datetime.md).

22
.claude/rules/imports.md Normal file
View File

@ -0,0 +1,22 @@
---
paths:
- "src/**/*.py"
- "tests/**/*.py"
---
# Imports rule
- **`from __future__ import annotations`** is the first import in every module.
- **Import order** (ruff `I` enforces, `make format` fixes): stdlib → third-party
→ first-party (`everalgo`, then `everos`). One group per blank-line-separated block.
- **Absolute imports** for cross-package references (`from everos.memory import ...`).
Relative imports (`from .models import ...`) only **within** a package, typically
in its `__init__.py`.
- **`TYPE_CHECKING` guard** for import cycles and type-only imports:
```python
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from everos.config import Settings
```
- Never import a private internal across a package boundary — respect the
`import-linter` contracts (see [architecture.md](architecture.md)).

View File

@ -0,0 +1,37 @@
---
paths:
- "src/**/__init__.py"
- "src/**/*.py"
---
# `__init__.py` and re-export rule
A package's `__init__.py` is its **public facade**. Consumers import from the
package, never from its internal modules.
## Pattern
```python
"""One-paragraph module docstring: what this package is and how to use it."""
from .models import Episode as Episode
from .models import MemCell as MemCell
__all__ = [
"Episode",
"MemCell",
]
```
- **Explicit `X as X` redundant-alias form** on each re-export. This is intentional:
it marks the name as a deliberate public re-export (ruff `F401` / `PLC0414` aware)
rather than an accidental unused import.
- **`__all__`** lists every public name, alphabetically sorted, matching the
re-exports. It is the contract; keep it in sync.
- **Internal modules stay private** — don't re-export helpers that aren't part of
the public API.
- New subpackage? Add an `__init__.py` with a docstring + `__all__` even if it
starts small. Empty-but-documented beats missing.
This facade discipline is what lets `import-linter` forbid deep imports across
package boundaries (see [architecture.md](architecture.md)).

View File

@ -0,0 +1,12 @@
# Language policy rule (always loaded)
The project targets a global audience and is **English-first**.
- **Code, comments, docstrings, docs, commit messages, identifiers**: English only.
- **CJK characters are allowed only in**:
- test fixtures under `tests/` and `data/` (multilingual input is the point), and
- locale-suffixed mirror files (e.g. `*_zh.json`).
- Do **not** introduce CJK into `src/`, `docs/`, or config.
Enforcement: `make check-cjk` scans for stray CJK outside the allowlist (advisory).
Keep user-facing strings and error messages in English.

View File

@ -0,0 +1,23 @@
---
paths:
- "src/**/*.py"
---
# Logging & observability rule
- **Use the project logger**, never `print` or the stdlib `logging` directly:
```python
from everos.core.observability.logging import get_logger
logger = get_logger(__name__)
```
- **Structured logging** (`structlog`): pass context as keyword fields, not f-strings.
```python
logger.info("memory.search.completed", owner_type=owner, n_results=len(items))
```
Event name first (dotted, stable), structured kwargs after. This keeps logs
queryable and avoids leaking interpolated PII into the message string.
- **Levels**: `debug` for developer detail, `info` for lifecycle milestones,
`warning` for recoverable anomalies, `error` for failures with a stack/context.
- **Metrics** go through `core.observability.metrics` (Prometheus); don't invent
ad-hoc counters. Histograms/counters/gauges have registry helpers.
- Don't log secrets, API keys, or full memory content at `info`/above.

View File

@ -0,0 +1,35 @@
---
paths:
- "src/everos/infra/**/*.py"
- "src/everos/memory/**/*.py"
- "src/everos/service/**/*.py"
- "src/everos/component/**/*.py"
- "src/everos/core/**/*.py"
---
# Module docstring rule
Every non-trivial module in the domain/infra layers opens with a docstring that
explains **intent and contract**, not just a one-line label.
A good module docstring states:
- **What** the module is responsible for (one sentence).
- **The load-bearing invariants** — the rules a reader must know to change it
safely (partition keys, what is/isn't written, defaults, ignored flags).
- **External usage** when the module is a package facade (a short import example).
Example (abbreviated, from `memory/search/manager.py`):
```python
"""SearchManager — top-level orchestrator for POST /api/v1/memory/search.
Hard partition by owner_type: user → episodes (+ profiles), agent →
agent_cases + agent_skills. The manager never writes to storage; it only
reads LanceDB + markdown.
"""
```
Prefer prose that would save the next engineer a debugging session over
boilerplate. If a module is genuinely trivial (a 3-line constant), a one-liner
is fine — but most modules here are not.

27
.claude/rules/testing.md Normal file
View File

@ -0,0 +1,27 @@
---
paths:
- "tests/**/*.py"
---
# Testing rule
Tests mirror the source layout: `tests/unit/test_<layer>/...`,
`tests/integration/...`, `tests/e2e/...`.
- **Structure**: `tests/unit/` mirrors `src/everos/` package-for-package. Put a test
next to where its subject lives in the mirror.
- **Async**: `pytest-asyncio` is in `auto` mode — write `async def test_*` directly,
no marker needed.
- **Markers** (default run excludes both — `-m "not slow and not live_llm"`):
- `@pytest.mark.slow` — tests ≥ ~10s.
- `@pytest.mark.live_llm` — needs real LLM/embedder credentials.
Keep unit tests fast and credential-free; push anything needing real services
behind a marker or into `integration`/`e2e`.
- **Fixtures**: shared fixtures live in the nearest `conftest.py`. The root conftest
resets module caches (settings/logging/datetime) per test — rely on that for
isolation rather than mutating globals.
- **Module docstring** on each test file stating what contract it pins (see existing
tests for the style).
- **Coverage gate**: `make cov` enforces 80% (`--cov-fail-under=80`). New code should
not drop coverage below the gate.
- Run `make test` (unit) and `make integration` before pushing; both run in CI.

19
.claude/settings.json Normal file
View File

@ -0,0 +1,19 @@
{
"permissions": {
"allow": [
"Bash(make:*)",
"Bash(uv sync:*)",
"Bash(uv run:*)",
"Bash(uv pip:*)",
"Bash(ruff:*)",
"Bash(pytest:*)",
"Bash(git status:*)",
"Bash(git diff:*)",
"Bash(git log:*)",
"Bash(git branch:*)",
"Bash(git show:*)",
"Bash(gh pr view:*)",
"Bash(gh pr list:*)"
]
}
}

View File

@ -0,0 +1,54 @@
---
name: commit
description: Stage and create a Conventional Commits message for the current change
---
# /commit
Create a well-formed commit following the [Conventional Commits](https://www.conventionalcommits.org)
standard. The format is enforced by `gitlint` in the `commit-msg` hook.
## Steps
1. Run `git status` and `git diff` (and `git diff --staged`) to see what changed.
2. Review recent history for style: `git log --oneline -10`.
3. Group the change into a single focused commit. If the working tree mixes
unrelated changes, stage selectively (`git add -p` / specific paths) rather
than committing everything at once.
4. Write the message in **Conventional Commits** form:
```
<type>[(scope)][!]: <imperative summary, ≤72 chars>
<optional body: what & why, wrapped at 72>
<optional footer: BREAKING CHANGE: …, Refs: #123>
```
5. Never use `--no-verify`. If pre-commit hooks fail, fix the cause and re-commit.
6. Do not commit secrets, generated artifacts, or work-in-progress to a
protected branch (`main` / `dev` / `master`).
## Types
| Type | Use for |
|---|---|
| `feat` | new feature |
| `fix` | bug fix |
| `refactor` | behavior-preserving restructure |
| `test` | add / update tests |
| `docs` | documentation |
| `style` | formatting only |
| `perf` | performance |
| `chore` | config / build / tooling |
| `build` | build system or dependencies |
| `ci` | CI configuration |
| `revert` | revert a previous commit |
## Notes
- No emoji — the title must start with the `type`.
- One logical change per commit; keep the history bisectable.
- The summary is imperative mood: "add", not "added" / "adds".
- `scope` is optional: `fix(search): …`. A `!` before the colon (or a
`BREAKING CHANGE:` footer) marks a breaking change.

View File

@ -0,0 +1,43 @@
---
name: new-branch
description: Create a branch following the project's GitFlow Lite model
---
# /new-branch
Cut a new branch under the GitFlow Lite model.
## Branch model
```
master = released / stable (tagged on release; protected)
dev = integration branch (protected)
feat/* = cut from dev → PR → merge into dev
fix/* = cut from dev → PR → merge into dev
hotfix/* = cut from master → merge into master AND synced into dev (double merge)
release = dev → master + tag on master (no separate release branch)
```
## Steps
1. Ask (or infer) the change type: `feat`, `fix`, or `hotfix`.
2. Pick the parent:
- `feat/*`, `fix/*` → branch from **`dev`**.
- `hotfix/*` → branch from **`master`**.
3. Update the parent first:
```bash
git checkout <parent>
git pull --ff-only
```
4. Create the branch with a kebab-case slug:
```bash
git checkout -b feat/<short-slug>
```
5. For a `hotfix`, remember it must later merge into **both** `master` and `dev`.
## Naming
- `feat/add-agentic-rerank`, `fix/empty-profile-crash`, `hotfix/lancedb-conn-leak`.
- Lowercase, hyphen-separated, no spaces, concise.
Never commit directly to `master` or `dev` — always via a branch + PR.

View File

@ -0,0 +1,41 @@
---
name: pr
description: Open a GitHub PR targeting the correct branch with the project template
---
# /pr
Open a pull request on GitHub using the `gh` CLI and the repo's PR template.
## Steps
1. Confirm the branch and target:
- `feat/*`, `fix/*` → base **`dev`**.
- `hotfix/*` → base **`master`** (then a follow-up PR/sync into `dev`).
2. Ensure local checks pass first:
```bash
make ci
```
Do not open a PR with failing lint/tests.
3. Push the branch:
```bash
git push -u origin HEAD
```
4. Create the PR, filling the template
([.github/PULL_REQUEST_TEMPLATE.md](../../../.github/PULL_REQUEST_TEMPLATE.md)):
```bash
gh pr create --base dev --fill-first
```
Then edit the body to complete each section:
- **Summary** — what changed and why.
- **Area** — tick the relevant box (architecture / benchmark / use case /
docs / DX / CI-build-release).
- **Verification** — paste the commands you ran (`make ci`, manual checks).
- **Checklist** — tick honestly; don't tick boxes you didn't satisfy.
- **Notes for Reviewers** — anything subtle.
## Notes
- Keep the PR scoped to one area. Split unrelated changes.
- If `make ci` was not fully run, say so in Verification rather than implying it passed.
- A `hotfix` is not done until it has landed on **both** `master` and `dev`.