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:
@ -0,0 +1,129 @@
|
||||
"""Tests for :class:`AgentSkillReader` — typed read for the skill directory layout."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from everos.core.persistence import MemoryRoot
|
||||
from everos.infra.persistence.markdown import (
|
||||
AgentSkillFrontmatter,
|
||||
AgentSkillReader,
|
||||
AgentSkillWriter,
|
||||
)
|
||||
|
||||
|
||||
def _make_fm(**overrides: object) -> AgentSkillFrontmatter:
|
||||
base: dict[str, object] = {
|
||||
"id": "agent_x_skill_alpha",
|
||||
"agent_id": "agent_x",
|
||||
"name": "alpha",
|
||||
"description": "A test skill.",
|
||||
"confidence": 0.5,
|
||||
"maturity_score": 0.5,
|
||||
}
|
||||
base.update(overrides)
|
||||
return AgentSkillFrontmatter(**base) # type: ignore[arg-type]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def root(tmp_path: Path) -> MemoryRoot:
|
||||
return MemoryRoot(tmp_path)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def writer(root: MemoryRoot) -> AgentSkillWriter:
|
||||
return AgentSkillWriter(root)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def reader(root: MemoryRoot) -> AgentSkillReader:
|
||||
return AgentSkillReader(root)
|
||||
|
||||
|
||||
async def test_read_main_returns_typed_frontmatter_and_body(
|
||||
writer: AgentSkillWriter, reader: AgentSkillReader
|
||||
) -> None:
|
||||
fm_in = _make_fm(
|
||||
description="Contract risk scan.",
|
||||
confidence=0.88,
|
||||
maturity_score=0.82,
|
||||
source_case_ids=["case_a", "case_b"],
|
||||
)
|
||||
await writer.write_main("agent_x", "alpha", frontmatter=fm_in, body="The body.")
|
||||
|
||||
out = await reader.read_main("agent_x", "alpha", schema=AgentSkillFrontmatter)
|
||||
assert out is not None
|
||||
fm_out, body = out
|
||||
assert isinstance(fm_out, AgentSkillFrontmatter)
|
||||
assert fm_out.name == "alpha"
|
||||
assert fm_out.source_case_ids == ["case_a", "case_b"]
|
||||
assert fm_out.confidence == 0.88
|
||||
assert fm_out.maturity_score == 0.82
|
||||
assert body == "The body."
|
||||
|
||||
|
||||
async def test_read_main_returns_none_when_missing(reader: AgentSkillReader) -> None:
|
||||
assert (
|
||||
await reader.read_main("agent_x", "ghost", schema=AgentSkillFrontmatter) is None
|
||||
)
|
||||
|
||||
|
||||
async def test_read_main_round_trip_through_extra_fields(
|
||||
writer: AgentSkillWriter, reader: AgentSkillReader
|
||||
) -> None:
|
||||
"""L2 / L4 ride-along fields survive a write+read cycle (extra="allow")."""
|
||||
fm_in = _make_fm(md_sha256="abc", custom_label="ride-along")
|
||||
await writer.write_main("agent_x", "alpha", frontmatter=fm_in, body="b")
|
||||
out = await reader.read_main("agent_x", "alpha", schema=AgentSkillFrontmatter)
|
||||
assert out is not None
|
||||
fm_out, _ = out
|
||||
dumped = fm_out.model_dump()
|
||||
assert dumped["md_sha256"] == "abc"
|
||||
assert dumped["custom_label"] == "ride-along"
|
||||
|
||||
|
||||
async def test_read_main_validates_against_supplied_schema(
|
||||
writer: AgentSkillWriter, reader: AgentSkillReader
|
||||
) -> None:
|
||||
"""A stricter schema rejects loose existing data — proves typed parsing."""
|
||||
|
||||
class _StricterSkillFM(AgentSkillFrontmatter):
|
||||
# Required field with no default — written file lacks it.
|
||||
priority: int
|
||||
|
||||
fm_in = _make_fm()
|
||||
await writer.write_main("agent_x", "alpha", frontmatter=fm_in, body="b")
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
await reader.read_main("agent_x", "alpha", schema=_StricterSkillFM)
|
||||
|
||||
|
||||
async def test_read_reference_round_trip(
|
||||
writer: AgentSkillWriter, reader: AgentSkillReader
|
||||
) -> None:
|
||||
await writer.write_reference(
|
||||
"agent_x", "alpha", "termination", "## term clauses\n..."
|
||||
)
|
||||
content = await reader.read_reference("agent_x", "alpha", "termination")
|
||||
assert content == "## term clauses\n..."
|
||||
|
||||
|
||||
async def test_read_reference_returns_none_when_missing(
|
||||
reader: AgentSkillReader,
|
||||
) -> None:
|
||||
assert await reader.read_reference("agent_x", "alpha", "ghost") is None
|
||||
|
||||
|
||||
async def test_read_script_round_trip(
|
||||
writer: AgentSkillWriter, reader: AgentSkillReader
|
||||
) -> None:
|
||||
await writer.write_script("agent_x", "alpha", "redline.py", "print('hi')\n")
|
||||
content = await reader.read_script("agent_x", "alpha", "redline.py")
|
||||
assert content == "print('hi')"
|
||||
|
||||
|
||||
async def test_read_script_returns_none_when_missing(reader: AgentSkillReader) -> None:
|
||||
assert await reader.read_script("agent_x", "alpha", "ghost.py") is None
|
||||
182
tests/unit/test_infra/test_markdown/test_readers/test_base.py
Normal file
182
tests/unit/test_infra/test_markdown/test_readers/test_base.py
Normal file
@ -0,0 +1,182 @@
|
||||
"""Tests for ``BaseDailyReader`` chassis.
|
||||
|
||||
Symmetric to ``test_writers/test_base.py`` — exercises path resolution
|
||||
+ entry locating + structured-entry upgrading on a dummy schema.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
from pathlib import Path
|
||||
from typing import ClassVar, Literal
|
||||
|
||||
import pytest
|
||||
|
||||
from everos.core.persistence import (
|
||||
EntryId,
|
||||
MemoryRoot,
|
||||
StructuredEntry,
|
||||
UserScopedFrontmatter,
|
||||
render_structured_entry,
|
||||
)
|
||||
from everos.infra.persistence.markdown.readers import BaseDailyReader
|
||||
from everos.infra.persistence.markdown.writers import BaseDailyWriter
|
||||
|
||||
|
||||
class _DemoFrontmatter(UserScopedFrontmatter):
|
||||
ENTRY_ID_PREFIX: ClassVar[str] = "demo"
|
||||
DIR_NAME: ClassVar[str] = "demos"
|
||||
FILE_PREFIX: ClassVar[str] = "demo"
|
||||
type: Literal["user_demo"] = "user_demo"
|
||||
|
||||
|
||||
class _DemoWriter(BaseDailyWriter):
|
||||
schema = _DemoFrontmatter
|
||||
|
||||
|
||||
class _DemoReader(BaseDailyReader):
|
||||
schema = _DemoFrontmatter
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def root(tmp_path: Path) -> MemoryRoot:
|
||||
return MemoryRoot(tmp_path)
|
||||
|
||||
|
||||
# ── construction ────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_reader_rejects_missing_schema(root: MemoryRoot) -> None:
|
||||
class _NoSchemaReader(BaseDailyReader):
|
||||
pass
|
||||
|
||||
with pytest.raises(TypeError, match="schema"):
|
||||
_NoSchemaReader(root)
|
||||
|
||||
|
||||
def test_reader_rejects_schema_missing_classvars(root: MemoryRoot) -> None:
|
||||
class _IncompleteFrontmatter(UserScopedFrontmatter):
|
||||
# Missing DIR_NAME / FILE_PREFIX.
|
||||
type: Literal["incomplete"] = "incomplete"
|
||||
|
||||
class _IncompleteReader(BaseDailyReader):
|
||||
schema = _IncompleteFrontmatter
|
||||
|
||||
with pytest.raises(TypeError, match="missing ClassVar"):
|
||||
_IncompleteReader(root)
|
||||
|
||||
|
||||
# ── read_for ────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
async def test_read_for_returns_none_when_file_missing(root: MemoryRoot) -> None:
|
||||
reader = _DemoReader(root)
|
||||
assert await reader.read_for("u_jason", dt.date(2026, 4, 22)) is None
|
||||
|
||||
|
||||
async def test_read_for_returns_parsed_when_file_exists(
|
||||
tmp_path: Path, root: MemoryRoot
|
||||
) -> None:
|
||||
writer = _DemoWriter(root)
|
||||
await writer.append("u_jason", "first body", date=dt.date(2026, 4, 22))
|
||||
|
||||
reader = _DemoReader(root)
|
||||
parsed = await reader.read_for("u_jason", dt.date(2026, 4, 22))
|
||||
assert parsed is not None
|
||||
assert len(parsed.entries) == 1
|
||||
assert parsed.entries[0].body == "first body"
|
||||
|
||||
|
||||
async def test_read_for_today_default(root: MemoryRoot) -> None:
|
||||
"""Omitting ``date`` falls back to today_with_timezone()."""
|
||||
writer = _DemoWriter(root)
|
||||
await writer.append("u_jason", "today body")
|
||||
|
||||
reader = _DemoReader(root)
|
||||
parsed = await reader.read_for("u_jason")
|
||||
assert parsed is not None
|
||||
assert parsed.entries[0].body == "today body"
|
||||
|
||||
|
||||
# ── find_entry ──────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
async def test_find_entry_resolves_file_from_entry_id(root: MemoryRoot) -> None:
|
||||
writer = _DemoWriter(root)
|
||||
await writer.append("u_jason", "alpha", date=dt.date(2026, 4, 22))
|
||||
await writer.append("u_jason", "beta", date=dt.date(2026, 4, 22))
|
||||
|
||||
reader = _DemoReader(root)
|
||||
e = await reader.find_entry("u_jason", "demo_20260422_00000002")
|
||||
assert e is not None
|
||||
assert e.id == "demo_20260422_00000002"
|
||||
assert e.body == "beta"
|
||||
|
||||
|
||||
async def test_find_entry_returns_none_when_file_missing(root: MemoryRoot) -> None:
|
||||
reader = _DemoReader(root)
|
||||
assert await reader.find_entry("u_jason", "demo_20260422_00000001") is None
|
||||
|
||||
|
||||
async def test_find_entry_returns_none_when_entry_missing(root: MemoryRoot) -> None:
|
||||
writer = _DemoWriter(root)
|
||||
await writer.append("u_jason", "only", date=dt.date(2026, 4, 22))
|
||||
|
||||
reader = _DemoReader(root)
|
||||
assert await reader.find_entry("u_jason", "demo_20260422_00000099") is None
|
||||
|
||||
|
||||
async def test_find_entry_accepts_entryid_object(root: MemoryRoot) -> None:
|
||||
writer = _DemoWriter(root)
|
||||
await writer.append("u_jason", "alpha", date=dt.date(2026, 4, 22))
|
||||
|
||||
reader = _DemoReader(root)
|
||||
eid = EntryId(prefix="demo", date=dt.date(2026, 4, 22), seq=1)
|
||||
e = await reader.find_entry("u_jason", eid)
|
||||
assert e is not None
|
||||
assert e.body == "alpha"
|
||||
|
||||
|
||||
# ── find_structured ─────────────────────────────────────────────────────
|
||||
|
||||
|
||||
async def test_find_structured_parses_audit_form(root: MemoryRoot) -> None:
|
||||
writer = _DemoWriter(root)
|
||||
body = render_structured_entry(
|
||||
header="demo_20260422_00000001",
|
||||
inline={"type": "demo", "user_id": "u_jason"},
|
||||
sections={"Body": "the body"},
|
||||
)
|
||||
await writer.append("u_jason", body, date=dt.date(2026, 4, 22))
|
||||
|
||||
reader = _DemoReader(root)
|
||||
structured = await reader.find_structured("u_jason", "demo_20260422_00000001")
|
||||
assert structured is not None
|
||||
assert isinstance(structured, StructuredEntry)
|
||||
assert structured.id == "demo_20260422_00000001"
|
||||
assert structured.header == "demo_20260422_00000001"
|
||||
assert structured.inline == {"type": "demo", "user_id": "u_jason"}
|
||||
assert structured.sections == {"Body": "the body"}
|
||||
|
||||
|
||||
async def test_find_structured_returns_none_when_missing(root: MemoryRoot) -> None:
|
||||
reader = _DemoReader(root)
|
||||
assert await reader.find_structured("u_jason", "demo_20260422_00000001") is None
|
||||
|
||||
|
||||
# ── path_for ────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_path_for_matches_writer(tmp_path: Path, root: MemoryRoot) -> None:
|
||||
"""Reader and writer resolve to the same path for the same schema."""
|
||||
reader = _DemoReader(root)
|
||||
writer = _DemoWriter(root)
|
||||
d = dt.date(2026, 4, 22)
|
||||
assert reader.path_for("u_jason", d) == writer.path_for("u_jason", d)
|
||||
|
||||
|
||||
def test_path_for_does_not_create_files(tmp_path: Path, root: MemoryRoot) -> None:
|
||||
reader = _DemoReader(root)
|
||||
p = reader.path_for("u_jason", dt.date(2026, 4, 22))
|
||||
assert not p.exists()
|
||||
assert not (tmp_path / "users").exists()
|
||||
@ -0,0 +1,121 @@
|
||||
"""Tests for :class:`ProfileReader` — typed read for profile files."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import ClassVar, Literal
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from everos.core.persistence import MemoryRoot, UserScopedFrontmatter
|
||||
from everos.infra.persistence.markdown.readers import ProfileReader
|
||||
from everos.infra.persistence.markdown.writers import ProfileWriter
|
||||
|
||||
|
||||
class _UserProfileFM(UserScopedFrontmatter):
|
||||
PROFILE_FILENAME: ClassVar[str] = "user.md"
|
||||
type: Literal["demo_user_profile"] = "demo_user_profile"
|
||||
display_name: str = ""
|
||||
bio: str = ""
|
||||
interests: list[str] = []
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def root(tmp_path: Path) -> MemoryRoot:
|
||||
return MemoryRoot(tmp_path)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def writer(root: MemoryRoot) -> ProfileWriter:
|
||||
return ProfileWriter(root)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def reader(root: MemoryRoot) -> ProfileReader:
|
||||
return ProfileReader(root)
|
||||
|
||||
|
||||
async def test_read_returns_typed_frontmatter_and_body(
|
||||
writer: ProfileWriter, reader: ProfileReader
|
||||
) -> None:
|
||||
fm_in = _UserProfileFM(
|
||||
id="demo_user_profile_u_jason",
|
||||
type="demo_user_profile",
|
||||
user_id="u_jason",
|
||||
display_name="Jason",
|
||||
bio="weekend hiker.",
|
||||
interests=["hiking", "coffee"],
|
||||
)
|
||||
await writer.write("u_jason", frontmatter=fm_in, body="The body.")
|
||||
|
||||
out = await reader.read("u_jason", schema=_UserProfileFM)
|
||||
assert out is not None
|
||||
fm_out, body = out
|
||||
assert isinstance(fm_out, _UserProfileFM)
|
||||
assert fm_out.display_name == "Jason"
|
||||
assert fm_out.interests == ["hiking", "coffee"]
|
||||
assert body == "The body."
|
||||
|
||||
|
||||
async def test_read_returns_none_when_missing(reader: ProfileReader) -> None:
|
||||
assert await reader.read("u_ghost", schema=_UserProfileFM) is None
|
||||
|
||||
|
||||
async def test_read_round_trip_through_extra_fields(
|
||||
writer: ProfileWriter, reader: ProfileReader
|
||||
) -> None:
|
||||
"""L2 / L4 ride-along fields survive a write+read cycle."""
|
||||
fm_in = _UserProfileFM(
|
||||
id="demo_user_profile_u_jason",
|
||||
type="demo_user_profile",
|
||||
user_id="u_jason",
|
||||
md_sha256="abc", # extra
|
||||
custom_label="ride-along", # extra
|
||||
)
|
||||
await writer.write("u_jason", frontmatter=fm_in, body="b")
|
||||
out = await reader.read("u_jason", schema=_UserProfileFM)
|
||||
assert out is not None
|
||||
fm_out, _ = out
|
||||
dumped = fm_out.model_dump()
|
||||
assert dumped["md_sha256"] == "abc"
|
||||
assert dumped["custom_label"] == "ride-along"
|
||||
|
||||
|
||||
async def test_read_validates_against_supplied_schema(
|
||||
writer: ProfileWriter, reader: ProfileReader
|
||||
) -> None:
|
||||
"""A stricter schema rejects loose existing data — proves typed parsing."""
|
||||
|
||||
class _StricterFM(UserScopedFrontmatter):
|
||||
PROFILE_FILENAME: ClassVar[str] = "user.md"
|
||||
type: Literal["demo_user_profile"] = "demo_user_profile"
|
||||
# Required field with no default — written file lacks it.
|
||||
priority: int
|
||||
|
||||
fm_in = _UserProfileFM(
|
||||
id="demo_user_profile_u_jason",
|
||||
type="demo_user_profile",
|
||||
user_id="u_jason",
|
||||
)
|
||||
await writer.write("u_jason", frontmatter=fm_in, body="b")
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
await reader.read("u_jason", schema=_StricterFM)
|
||||
|
||||
|
||||
def test_path_for_matches_writer(
|
||||
tmp_path: Path,
|
||||
writer: ProfileWriter,
|
||||
reader: ProfileReader,
|
||||
) -> None:
|
||||
"""Reader and writer resolve to the same path for the same schema."""
|
||||
assert reader.path_for("u_jason", schema=_UserProfileFM) == writer.path_for(
|
||||
"u_jason", schema=_UserProfileFM
|
||||
)
|
||||
|
||||
|
||||
def test_path_for_does_not_create_files(tmp_path: Path, reader: ProfileReader) -> None:
|
||||
p = reader.path_for("u_jason", schema=_UserProfileFM)
|
||||
assert not p.exists()
|
||||
assert not (tmp_path / "users").exists()
|
||||
Reference in New Issue
Block a user