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,152 @@
"""StrategyRegistry — registration + DAG cycle detection.
Mutated at startup via :meth:`register` / :meth:`validate`, and at
runtime via :meth:`replace` (config hot-reload). Cycle detection is a
Kahn-style topological pass on the event-flow DAG implied by
``trigger.on`` (incoming) and ``emits`` (outgoing).
"""
from __future__ import annotations
from collections import defaultdict, deque
from collections.abc import Callable
from typing import Any
from everos.infra.ome.decorator import StrategyMeta
from everos.infra.ome.events import BaseEvent, CronTick, IdleTick
from everos.infra.ome.exceptions import StartupValidationError
from everos.infra.ome.triggers import Cron, Idle, Immediate, Trigger
class StrategyRegistry:
"""Startup-time registry for offline strategies with cycle detection."""
def __init__(self) -> None:
self._strategies: dict[str, StrategyMeta] = {}
def register(self, func: Callable[..., Any]) -> None:
"""Register a strategy function (reads ``_ome_strategy_meta``).
Raises ``StartupValidationError`` if ``func`` is not decorated
with ``@offline_strategy`` or if its name is already registered.
"""
meta = getattr(func, "_ome_strategy_meta", None)
if not isinstance(meta, StrategyMeta):
fn_name = getattr(func, "__name__", repr(func))
raise StartupValidationError(
f"register: {fn_name} is not decorated with @offline_strategy"
)
if meta.name in self._strategies:
raise StartupValidationError(
f"register: duplicate strategy name {meta.name!r}"
)
self._strategies[meta.name] = meta
def replace(self, name: str, new_meta: StrategyMeta) -> None:
"""Swap an already-registered strategy's meta in place (hot-reload entry).
Cycle / gate validation is **not** re-run; callers (currently
:func:`apply_overrides`) must only feed metas where the
DAG-shaping fields (``trigger.on``, ``emits``, trigger type)
match the original. Raises ``KeyError`` if ``name`` is not yet
registered.
"""
if name not in self._strategies:
raise KeyError(name)
self._strategies[name] = new_meta
def get(self, name: str) -> StrategyMeta:
"""Return meta by name (raises ``KeyError`` if absent)."""
return self._strategies[name]
def all(self) -> list[StrategyMeta]:
"""Return a snapshot list of every registered strategy."""
return list(self._strategies.values())
def lookup_by_event(self, event_cls: type[BaseEvent]) -> list[StrategyMeta]:
"""Return strategies that may receive an event of ``event_cls``.
Resolution:
* ``Immediate`` strategy listening on the class → match
* ``CronTick`` → all Cron strategies (narrowed later by name)
* ``IdleTick`` → all Idle strategies (narrowed later by name)
Engine-emitted ticks carry a ``strategy_name`` field; dispatcher
narrows the returned set to the single target via ``_routes_to``.
"""
out: list[StrategyMeta] = []
for m in self._strategies.values():
if (
(isinstance(m.trigger, Immediate) and event_cls in m.trigger.on)
or (isinstance(m.trigger, Cron) and event_cls is CronTick)
or (isinstance(m.trigger, Idle) and event_cls is IdleTick)
):
out.append(m)
return out
def validate(self) -> None:
"""Validate the strategy DAG for cycles and gate field existence."""
self._validate_no_cycles()
self._validate_gate_event_fields()
def _validate_no_cycles(self) -> None:
"""Kahn topological sort over the event-flow DAG.
Edge ``s_a → s_b`` exists iff ``s_a.emits`` intersects
``s_b.trigger.on``.
"""
adj: dict[str, set[str]] = defaultdict(set)
indeg: dict[str, int] = dict.fromkeys(self._strategies, 0)
for src in self._strategies.values():
for ev in src.emits:
for dst in self._strategies.values():
if (
isinstance(dst.trigger, Immediate)
and ev in dst.trigger.on
and dst.name not in adj[src.name]
):
adj[src.name].add(dst.name)
indeg[dst.name] += 1
queue = deque(n for n, d in indeg.items() if d == 0)
visited = 0
while queue:
n = queue.popleft()
visited += 1
for nbr in adj[n]:
indeg[nbr] -= 1
if indeg[nbr] == 0:
queue.append(nbr)
if visited < len(self._strategies):
raise StartupValidationError("cycle detected in strategy DAG")
def _validate_gate_event_fields(self) -> None:
"""Reject any ``gate.event_field`` missing from a receivable event class.
Without this check a typo silently collapses every event into one
shared bucket and the rate gate stops segmenting.
"""
for meta in self._strategies.values():
if meta.gate is None or meta.gate.event_field is None:
continue
field = meta.gate.event_field
for ev_cls in _event_classes_for_trigger(meta.trigger):
if field not in ev_cls.model_fields: # type: ignore[operator] # Pydantic model_fields → dict via @deprecated_instance_property (pydantic/main.py:277)
raise StartupValidationError(
f"strategy {meta.name!r}: gate.event_field {field!r} "
f"not found in {ev_cls.__name__} fields "
f"(available: {list(ev_cls.model_fields)})" # type: ignore[arg-type] # same as above
)
def _event_classes_for_trigger(trigger: Trigger) -> list[type[BaseEvent]]:
"""Enumerate event classes a strategy with the given trigger receives."""
if isinstance(trigger, Immediate):
return list(trigger.on)
if isinstance(trigger, Cron):
return [CronTick]
if isinstance(trigger, Idle):
return [IdleTick]
raise NotImplementedError(f"unknown trigger type: {type(trigger).__name__}")