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,104 @@
"""Tests for :class:`AgentSkillFrontmatter` — the AgentSkill schema.
Lives under ``test_infra`` because :class:`AgentSkillFrontmatter` itself
lives under ``infra/.../mds`` (it carries business fields + the
directory-shape ClassVars). The schema-agnostic chassis tests live
under ``test_core/test_persistence/test_markdown/``.
"""
from __future__ import annotations
import pytest
from pydantic import ValidationError
from everos.infra.persistence.markdown import AgentSkillFrontmatter
def _kwargs(**overrides: object) -> dict[str, object]:
"""Minimal valid kwargs for AgentSkillFrontmatter."""
base: dict[str, object] = {
"id": "skill_contract_risk_scan",
"agent_id": "agent_zhang_legal",
"name": "contract_risk_scan",
"description": "Scan a contract draft for risk clauses.",
"confidence": 0.5,
"maturity_score": 0.5,
}
base.update(overrides)
return base
def test_skill_inherits_agent_scope() -> None:
"""Skills always live under ``agents/`` — track + SCOPE_DIR confirm."""
assert AgentSkillFrontmatter.SCOPE_DIR == "agents"
fm = AgentSkillFrontmatter(**_kwargs()) # type: ignore[arg-type]
assert fm.track == "agent"
assert fm.type == "agent_skill"
def test_skill_requires_name_and_description() -> None:
"""Tier-1 prompt injection demands both fields — schema enforces."""
bad = _kwargs()
del bad["name"]
with pytest.raises(ValidationError):
AgentSkillFrontmatter(**bad) # type: ignore[arg-type]
bad = _kwargs()
del bad["description"]
with pytest.raises(ValidationError):
AgentSkillFrontmatter(**bad) # type: ignore[arg-type]
def test_skill_requires_confidence_and_maturity_score() -> None:
"""LLM-emitted score fields are required (no default)."""
bad = _kwargs()
del bad["confidence"]
with pytest.raises(ValidationError):
AgentSkillFrontmatter(**bad) # type: ignore[arg-type]
bad = _kwargs()
del bad["maturity_score"]
with pytest.raises(ValidationError):
AgentSkillFrontmatter(**bad) # type: ignore[arg-type]
def test_skill_optional_fields_default() -> None:
"""``source_case_ids`` defaults to empty list; ``cluster_id`` to None."""
fm = AgentSkillFrontmatter(**_kwargs()) # type: ignore[arg-type]
assert fm.source_case_ids == []
assert fm.cluster_id is None
def test_skill_lineage_fields_round_trip() -> None:
"""``source_case_ids`` + ``cluster_id`` round-trip through model_dump."""
fm = AgentSkillFrontmatter(
**_kwargs(
source_case_ids=["case_a", "case_b"],
cluster_id="cl_x",
), # type: ignore[arg-type]
)
dumped = fm.model_dump()
assert dumped["source_case_ids"] == ["case_a", "case_b"]
assert dumped["cluster_id"] == "cl_x"
def test_skill_extra_fields_still_allowed() -> None:
"""L2 system metadata (md_sha256 / last_indexed_at) rides along."""
fm = AgentSkillFrontmatter(
**_kwargs(
md_sha256="deadbeef",
last_indexed_at="2026-05-07T08:00:00Z",
), # type: ignore[arg-type]
)
dumped = fm.model_dump()
assert dumped["md_sha256"] == "deadbeef"
assert dumped["last_indexed_at"] == "2026-05-07T08:00:00Z"
def test_skill_directory_shape_classvars() -> None:
"""Path-shape ClassVars pin the wiki layout for the writer/reader pair."""
assert AgentSkillFrontmatter.SKILLS_CONTAINER_NAME == "skills"
assert AgentSkillFrontmatter.SKILL_DIR_PREFIX == "skill_"
assert AgentSkillFrontmatter.SKILL_MAIN_FILENAME == "SKILL.md"
assert AgentSkillFrontmatter.SKILL_REFERENCES_DIR_NAME == "references"
assert AgentSkillFrontmatter.SKILL_SCRIPTS_DIR_NAME == "scripts"

View File

@ -0,0 +1,30 @@
"""Tests that every business frontmatter class reports the expected
``path_glob()`` — the cascade scanner reads these to enumerate eligible
files, so a wrong glob silently drops a whole kind from cascade.
"""
from __future__ import annotations
import pytest
from everos.infra.persistence.markdown import (
AgentCaseDailyFrontmatter,
AgentSkillFrontmatter,
AtomicFactDailyFrontmatter,
EpisodeDailyFrontmatter,
ForesightDailyFrontmatter,
)
@pytest.mark.parametrize(
("schema", "expected"),
[
(EpisodeDailyFrontmatter, "*/*/users/*/episodes/episode-*.md"),
(AtomicFactDailyFrontmatter, "*/*/users/*/.atomic_facts/atomic_fact-*.md"),
(ForesightDailyFrontmatter, "*/*/users/*/.foresights/foresight-*.md"),
(AgentCaseDailyFrontmatter, "*/*/agents/*/.cases/agent_case-*.md"),
(AgentSkillFrontmatter, "*/*/agents/*/skills/skill_*/SKILL.md"),
],
)
def test_path_glob(schema: type, expected: str) -> None:
assert schema.path_glob() == expected

View File

@ -0,0 +1,71 @@
"""Tests for the profile frontmatter duck-typed shape.
Profile schemas have no shared base class — they only need a
``PROFILE_FILENAME`` ClassVar plus inheritance from a scope mixin. This
test exercises that contract via a local fixture class.
"""
from __future__ import annotations
from typing import ClassVar, Literal
import pytest
from pydantic import ValidationError
from everos.core.persistence.markdown import UserScopedFrontmatter
class _SampleUserProfileFM(UserScopedFrontmatter):
"""Local fixture: a user-track profile schema."""
PROFILE_FILENAME: ClassVar[str] = "user.md"
type: Literal["sample_user_profile"] = "sample_user_profile"
display_name: str
bio: str
interests: list[str] = []
def test_schema_inherits_user_scope() -> None:
fm = _SampleUserProfileFM(
id="sample_user_profile_u_jason",
type="sample_user_profile",
user_id="u_jason",
display_name="Jason",
bio="hiker.",
)
assert fm.track == "user"
assert fm.SCOPE_DIR == "users"
def test_profile_filename_classvar() -> None:
"""Path-shape ClassVar is duck-typed onto the schema directly."""
assert _SampleUserProfileFM.PROFILE_FILENAME == "user.md"
def test_requires_display_name_and_bio() -> None:
with pytest.raises(ValidationError):
_SampleUserProfileFM( # type: ignore[call-arg]
id="x",
type="sample_user_profile",
user_id="u_jason",
bio="missing display_name",
)
with pytest.raises(ValidationError):
_SampleUserProfileFM( # type: ignore[call-arg]
id="x",
type="sample_user_profile",
user_id="u_jason",
display_name="missing bio",
)
def test_interests_default_empty() -> None:
fm = _SampleUserProfileFM(
id="x",
type="sample_user_profile",
user_id="u_jason",
display_name="Jason",
bio="hiker.",
)
assert fm.interests == []

View File

@ -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

View 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()

View File

@ -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()

View File

@ -0,0 +1,147 @@
"""Tests for :class:`AgentSkillWriter` — directory + progressive disclosure."""
from __future__ import annotations
from pathlib import Path
import pytest
from everos.core.persistence import MarkdownReader, MemoryRoot
from everos.infra.persistence.markdown import (
AgentSkillFrontmatter,
AgentSkillWriter,
)
def _make_fm(**overrides: object) -> AgentSkillFrontmatter:
"""Build an AgentSkillFrontmatter with sensible defaults for tests."""
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)
async def test_write_main_creates_directory_layout(
root: MemoryRoot, writer: AgentSkillWriter
) -> None:
fm = _make_fm()
path = await writer.write_main(
"agent_x", "alpha", frontmatter=fm, body="Step 1: do thing."
)
expected = root.agents_dir() / "agent_x" / "skills" / "skill_alpha" / "SKILL.md"
assert path == expected
assert expected.is_file()
async def test_write_main_writes_frontmatter_and_body(
root: MemoryRoot, writer: AgentSkillWriter
) -> None:
fm = _make_fm(
description="Contract risk scan.",
confidence=0.88,
maturity_score=0.82,
source_case_ids=["case_a", "case_b"],
cluster_id="cl_x",
)
await writer.write_main("agent_x", "alpha", frontmatter=fm, body="The body.")
parsed = await MarkdownReader.read(
root.agents_dir() / "agent_x" / "skills" / "skill_alpha" / "SKILL.md"
)
assert parsed.frontmatter["name"] == "alpha"
assert parsed.frontmatter["description"] == "Contract risk scan."
assert parsed.frontmatter["confidence"] == 0.88
assert parsed.frontmatter["maturity_score"] == 0.82
assert parsed.frontmatter["source_case_ids"] == ["case_a", "case_b"]
assert parsed.frontmatter["cluster_id"] == "cl_x"
assert parsed.body.rstrip("\n") == "The body."
async def test_write_main_is_upsert_full_replace(
root: MemoryRoot, writer: AgentSkillWriter
) -> None:
"""Second call overwrites both frontmatter and body — no append."""
fm1 = _make_fm(description="v1", maturity_score=0.4)
await writer.write_main("agent_x", "alpha", frontmatter=fm1, body="body v1")
fm2 = _make_fm(description="v2", maturity_score=0.7)
await writer.write_main("agent_x", "alpha", frontmatter=fm2, body="body v2")
parsed = await MarkdownReader.read(
root.agents_dir() / "agent_x" / "skills" / "skill_alpha" / "SKILL.md"
)
assert parsed.frontmatter["description"] == "v2"
assert parsed.frontmatter["maturity_score"] == 0.7
assert parsed.body.rstrip("\n") == "body v2"
# No "body v1" residue from the previous version.
assert "body v1" not in parsed.body
async def test_write_reference_uses_md_extension(
root: MemoryRoot, writer: AgentSkillWriter
) -> None:
path = await writer.write_reference(
"agent_x", "alpha", "termination_clauses", "## Termination\n..."
)
expected = (
root.agents_dir()
/ "agent_x"
/ "skills"
/ "skill_alpha"
/ "references"
/ "termination_clauses.md"
)
assert path == expected
assert path.read_text(encoding="utf-8").startswith("## Termination")
async def test_write_script_keeps_full_filename(
root: MemoryRoot, writer: AgentSkillWriter
) -> None:
path = await writer.write_script("agent_x", "alpha", "redline.py", "print('hi')\n")
expected = (
root.agents_dir()
/ "agent_x"
/ "skills"
/ "skill_alpha"
/ "scripts"
/ "redline.py"
)
assert path == expected
assert path.read_text(encoding="utf-8") == "print('hi')\n"
def test_main_path_does_not_create_anything(
root: MemoryRoot, writer: AgentSkillWriter
) -> None:
"""``main_path`` is a pure path resolver — no IO."""
p = writer.main_path("agent_x", "alpha")
assert p.name == "SKILL.md"
assert not root.agents_dir().exists()
async def test_write_main_normalises_trailing_newline(
root: MemoryRoot, writer: AgentSkillWriter
) -> None:
"""Body without a trailing newline still ends in exactly one newline."""
fm = _make_fm()
await writer.write_main("agent_x", "alpha", frontmatter=fm, body="no-newline-end")
text = (
root.agents_dir() / "agent_x" / "skills" / "skill_alpha" / "SKILL.md"
).read_text(encoding="utf-8")
assert text.endswith("no-newline-end\n")

View File

@ -0,0 +1,182 @@
"""Tests for ``BaseDailyWriter`` skeleton.
Uses a dummy ``UserScopedFrontmatter`` subclass to exercise the path
resolution + entry-id construction + today-by-default logic without
pulling in any concrete business schema.
"""
from __future__ import annotations
import datetime as dt
from pathlib import Path
from typing import ClassVar, Literal
import pytest
from everos.component.utils.datetime import today_with_timezone
from everos.core.persistence import (
AgentScopedFrontmatter,
MarkdownReader,
MemoryRoot,
UserScopedFrontmatter,
)
from everos.infra.persistence.markdown.writers import BaseDailyWriter
class _UserDemoFrontmatter(UserScopedFrontmatter):
ENTRY_ID_PREFIX: ClassVar[str] = "demo"
DIR_NAME: ClassVar[str] = "demos"
FILE_PREFIX: ClassVar[str] = "demo"
type: Literal["user_demo"] = "user_demo"
class _AgentDemoFrontmatter(AgentScopedFrontmatter):
ENTRY_ID_PREFIX: ClassVar[str] = "ademo"
DIR_NAME: ClassVar[str] = "demos"
FILE_PREFIX: ClassVar[str] = "demo"
type: Literal["agent_demo"] = "agent_demo"
class _UserDemoWriter(BaseDailyWriter):
schema = _UserDemoFrontmatter
class _AgentDemoWriter(BaseDailyWriter):
schema = _AgentDemoFrontmatter
@pytest.fixture
def root(tmp_path: Path) -> MemoryRoot:
return MemoryRoot(tmp_path)
def test_constructor_rejects_missing_schema(root: MemoryRoot) -> None:
class _NoSchema(BaseDailyWriter):
pass
with pytest.raises(TypeError, match="schema"):
_NoSchema(root)
def test_constructor_rejects_schema_missing_classvars(root: MemoryRoot) -> None:
class _IncompleteFrontmatter(UserScopedFrontmatter):
# Missing ENTRY_ID_PREFIX / DIR_NAME / FILE_PREFIX.
type: Literal["incomplete"] = "incomplete"
class _IncompleteWriter(BaseDailyWriter):
schema = _IncompleteFrontmatter
with pytest.raises(TypeError, match="ENTRY_ID_PREFIX"):
_IncompleteWriter(root)
async def test_append_writes_to_user_track(root: MemoryRoot) -> None:
writer = _UserDemoWriter(root)
eid = await writer.append("u_jason", "first", date=dt.date(2026, 4, 22))
assert eid.prefix == "demo"
assert eid.date == dt.date(2026, 4, 22)
assert eid.seq == 1
expected = root.users_dir() / "u_jason" / "demos" / "demo-2026-04-22.md"
assert expected.exists()
parsed = await MarkdownReader.read(expected)
assert parsed.entries[0].id == "demo_20260422_00000001"
assert parsed.entries[0].body == "first"
async def test_append_writes_to_agent_track(root: MemoryRoot) -> None:
writer = _AgentDemoWriter(root)
eid = await writer.append("agent_zhangsan", "trace", date=dt.date(2026, 4, 22))
assert eid.prefix == "ademo"
expected = root.agents_dir() / "agent_zhangsan" / "demos" / "demo-2026-04-22.md"
assert expected.exists()
async def test_append_increments_seq_across_calls(root: MemoryRoot) -> None:
writer = _UserDemoWriter(root)
eids = [
await writer.append("u_jason", f"body {i}", date=dt.date(2026, 4, 22))
for i in range(3)
]
assert [e.seq for e in eids] == [1, 2, 3]
async def test_append_date_defaults_to_today(root: MemoryRoot) -> None:
"""Omitting ``date`` falls back to today_with_timezone()."""
writer = _UserDemoWriter(root)
eid = await writer.append("u_jason", "body")
today = today_with_timezone()
assert eid.date == today
expected = root.users_dir() / "u_jason" / "demos" / f"demo-{today.isoformat()}.md"
assert expected.exists()
async def test_append_passes_frontmatter_updates(root: MemoryRoot) -> None:
writer = _UserDemoWriter(root)
await writer.append(
"u_jason",
"body",
date=dt.date(2026, 4, 22),
frontmatter_updates={"file_type": "user_demo_daily", "entry_count": 1},
)
path = root.users_dir() / "u_jason" / "demos" / "demo-2026-04-22.md"
parsed = await MarkdownReader.read(path)
assert parsed.frontmatter["file_type"] == "user_demo_daily"
assert parsed.frontmatter["entry_count"] == 1
async def test_current_count_hook_can_be_overridden(root: MemoryRoot) -> None:
"""Subclass override of ``_current_count`` controls seq."""
class _ConstantCount(BaseDailyWriter):
schema = _UserDemoFrontmatter
async def _current_count(self, path): # noqa: ANN001
return 41 # always claim 41 existing entries
writer = _ConstantCount(root)
eid = await writer.append("u_jason", "body", date=dt.date(2026, 4, 22))
assert eid.seq == 42 # 41 + 1
async def test_frontmatter_updates_hook_supplies_defaults(root: MemoryRoot) -> None:
"""Subclass override of ``_frontmatter_updates`` populates frontmatter."""
class _WithDefaults(BaseDailyWriter):
schema = _UserDemoFrontmatter
def _frontmatter_updates(self, scope_id, date, *, next_count): # noqa: ANN001
return {
"user_id": scope_id,
"entry_count": next_count,
"marker": "from-hook",
}
writer = _WithDefaults(root)
await writer.append("u_jason", "body", date=dt.date(2026, 4, 22))
path = root.users_dir() / "u_jason" / "demos" / "demo-2026-04-22.md"
parsed = await MarkdownReader.read(path)
assert parsed.frontmatter["marker"] == "from-hook"
assert parsed.frontmatter["entry_count"] == 1
assert parsed.frontmatter["user_id"] == "u_jason"
async def test_explicit_frontmatter_updates_skip_hook(root: MemoryRoot) -> None:
"""Caller-supplied ``frontmatter_updates`` overrides the hook entirely."""
class _WithDefaults(BaseDailyWriter):
schema = _UserDemoFrontmatter
def _frontmatter_updates(self, scope_id, date, *, next_count): # noqa: ANN001
return {"marker": "from-hook"}
writer = _WithDefaults(root)
await writer.append(
"u_jason",
"body",
date=dt.date(2026, 4, 22),
frontmatter_updates={"marker": "explicit"},
)
path = root.users_dir() / "u_jason" / "demos" / "demo-2026-04-22.md"
parsed = await MarkdownReader.read(path)
assert parsed.frontmatter["marker"] == "explicit"

View File

@ -0,0 +1,344 @@
"""Tests for AtomicFact / Foresight / AgentCase daily-log writers.
The 4 daily-log kinds (episode + these 3) all share ``BaseDailyWriter``
plumbing — exhaustive chassis tests live in ``test_base.py`` and
``test_episode_writer.py`` indirectly via the e2e flows. Here we focus
on the per-kind path resolution + frontmatter shape that each
subclass owns: ``schema``, ``_frontmatter_updates``, and the
writer ↔ reader round-trip on a fresh tmp memory_root.
"""
from __future__ import annotations
import datetime as _dt
from pathlib import Path
import pytest
from everos.core.persistence import MarkdownReader, MemoryRoot
from everos.infra.persistence.markdown import (
AgentCaseReader,
AgentCaseWriter,
AtomicFactReader,
AtomicFactWriter,
ForesightReader,
ForesightWriter,
)
@pytest.fixture
def memory_root(tmp_path: Path) -> MemoryRoot:
mr = MemoryRoot(tmp_path)
mr.ensure()
return mr
# ── AtomicFact ────────────────────────────────────────────────────────────
async def test_atomic_fact_writer_round_trip(memory_root: MemoryRoot) -> None:
writer = AtomicFactWriter(memory_root)
today = _dt.date(2026, 5, 15)
eid = await writer.append_entry(
"u1",
inline={
"owner_id": "u1",
"session_id": "s1",
"timestamp": "2026-05-15T10:00:00+00:00",
"parent_id": "mc_1",
"sender_ids": ["u1"],
},
sections={"Fact": "Alice prefers Italian."},
date=today,
)
path = (
memory_root.users_dir() / "u1" / ".atomic_facts" / "atomic_fact-2026-05-15.md"
)
parsed = await MarkdownReader.read(path)
# frontmatter
fm = parsed.frontmatter
assert fm["id"] == "atomic_fact_log_u1_2026-05-15"
assert fm["type"] == "atomic_fact_daily"
assert fm["file_type"] == "atomic_fact_daily"
assert fm["user_id"] == "u1"
assert fm["track"] == "user"
assert fm["date"] == "2026-05-15"
assert fm["entry_count"] == 1
# entry body
assert len(parsed.entries) == 1
entry = parsed.entries[0]
assert entry.id == eid.format()
structured = entry.as_structured()
assert structured.inline["owner_id"] == "u1"
assert structured.inline["parent_id"] == "mc_1"
assert structured.sections["Fact"] == "Alice prefers Italian."
# reader is symmetric
reader = AtomicFactReader(memory_root)
assert reader.path_for("u1", today) == path
found = await reader.find_structured("u1", eid)
assert found is not None
assert found.sections["Fact"] == "Alice prefers Italian."
async def test_atomic_fact_writer_appends_multiple(memory_root: MemoryRoot) -> None:
writer = AtomicFactWriter(memory_root)
today = _dt.date(2026, 5, 15)
eid1 = await writer.append_entry(
"u1",
inline={
"owner_id": "u1",
"session_id": "s1",
"timestamp": "2026-05-15T10:00:00+00:00",
"parent_id": "mc_1",
},
sections={"Fact": "fact 1"},
date=today,
)
eid2 = await writer.append_entry(
"u1",
inline={
"owner_id": "u1",
"session_id": "s1",
"timestamp": "2026-05-15T11:00:00+00:00",
"parent_id": "mc_2",
},
sections={"Fact": "fact 2"},
date=today,
)
assert eid1.format() != eid2.format()
assert eid2.format().endswith("0002")
# ── Foresight ─────────────────────────────────────────────────────────────
async def test_foresight_writer_round_trip(memory_root: MemoryRoot) -> None:
writer = ForesightWriter(memory_root)
today = _dt.date(2026, 5, 15)
eid = await writer.append_entry(
"u1",
inline={
"owner_id": "u1",
"session_id": "s1",
"timestamp": "2026-05-15T10:00:00+00:00",
"parent_id": "mc_1",
"start_time": "2026-05-15T12:00:00+00:00",
"end_time": "2026-05-15T13:00:00+00:00",
"duration_days": 1,
},
sections={
"Foresight": "User will book lunch at noon.",
"Evidence": "Past calendar pattern.",
},
date=today,
)
path = memory_root.users_dir() / "u1" / ".foresights" / "foresight-2026-05-15.md"
parsed = await MarkdownReader.read(path)
fm = parsed.frontmatter
assert fm["id"] == "foresight_log_u1_2026-05-15"
assert fm["type"] == "foresight_daily"
structured = parsed.entries[0].as_structured()
assert structured.sections["Foresight"] == "User will book lunch at noon."
assert structured.sections["Evidence"] == "Past calendar pattern."
assert structured.inline["duration_days"] == "1"
assert structured.inline["start_time"].startswith("2026-05-15T12:00:00")
reader = ForesightReader(memory_root)
found = await reader.find_structured("u1", eid)
assert found is not None
assert found.sections["Evidence"] == "Past calendar pattern."
# ── AgentCase ─────────────────────────────────────────────────────────────
async def test_agent_case_writer_round_trip(memory_root: MemoryRoot) -> None:
writer = AgentCaseWriter(memory_root)
today = _dt.date(2026, 5, 15)
eid = await writer.append_entry(
"a1",
inline={
"owner_id": "a1",
"session_id": "s1",
"timestamp": "2026-05-15T10:00:00+00:00",
"parent_id": "mc_agent",
"quality_score": 0.87,
},
sections={
"TaskIntent": "Scan contract for indemnity gaps.",
"Approach": "1. read sections;\n2. flag clauses;\n3. cross-check cap.",
"KeyInsight": "Indemnity cap missing in section 4.",
},
date=today,
)
path = memory_root.agents_dir() / "a1" / ".cases" / "agent_case-2026-05-15.md"
parsed = await MarkdownReader.read(path)
fm = parsed.frontmatter
assert fm["id"] == "agent_case_log_a1_2026-05-15"
assert fm["type"] == "agent_case_daily"
assert fm["agent_id"] == "a1"
assert fm["track"] == "agent"
structured = parsed.entries[0].as_structured()
assert structured.inline["quality_score"] == "0.87"
assert structured.sections["TaskIntent"].startswith("Scan contract")
assert structured.sections["Approach"].startswith("1. read sections")
assert structured.sections["KeyInsight"].startswith("Indemnity cap missing")
reader = AgentCaseReader(memory_root)
assert reader.path_for("a1", today) == path
found = await reader.find_structured("a1", eid)
assert found is not None
assert found.sections["TaskIntent"].startswith("Scan contract")
# ── round-trip with cascade handler (md → LanceDB row mapping) ─────────────
async def test_atomic_fact_writer_output_feeds_handler(
memory_root: MemoryRoot,
) -> None:
"""The writer's md is exactly what AtomicFactHandler expects to read."""
from everos.component.embedding import EmbeddingProvider
from everos.component.tokenizer import Tokenizer
from everos.memory.cascade.handlers import AtomicFactHandler, HandlerDeps
from everos.memory.cascade.handlers._daily_log_base import ParsedEntry
class _T(Tokenizer):
def tokenize(self, t): # type: ignore[no-untyped-def]
return [x for x in t.split() if x]
def tokenize_batch(self, ts): # type: ignore[no-untyped-def]
return [self.tokenize(x) for x in ts]
class _E(EmbeddingProvider):
dim = 1024
async def embed(self, t): # type: ignore[no-untyped-def]
return [0.0] * self.dim
async def embed_batch(self, ts): # type: ignore[no-untyped-def]
return [await self.embed(x) for x in ts]
today = _dt.date(2026, 5, 15)
eid = await AtomicFactWriter(memory_root).append_entry(
"u1",
inline={
"owner_id": "u1",
"session_id": "s1",
"timestamp": "2026-05-15T10:00:00+00:00",
"parent_id": "mc_1",
"sender_ids": ["u1"],
},
sections={"Fact": "Alice prefers Italian."},
date=today,
)
path = (
memory_root.users_dir() / "u1" / ".atomic_facts" / "atomic_fact-2026-05-15.md"
)
rel = path.relative_to(memory_root.root).as_posix()
parsed = await MarkdownReader.read(path)
entry = parsed.entries[0]
handler = AtomicFactHandler(
HandlerDeps(memory_root=memory_root, embedder=_E(), tokenizer=_T())
)
structured = entry.as_structured()
pe = ParsedEntry(entry.id, structured, handler._content_sha256(structured))
row = await handler._build_row(
owner_id="u1", owner_type="user", md_path=rel, entry=pe
)
assert row.id == f"u1_{eid.format()}"
assert row.fact == "Alice prefers Italian."
assert row.parent_id == "mc_1"
assert row.sender_ids == ["u1"]
assert len(row.vector) == 1024
# ── Display-tz contract for frontmatter timestamps (Gap #5) ────────────
async def test_atomic_fact_frontmatter_last_appended_at_carries_display_tz_offset(
memory_root: MemoryRoot,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""``last_appended_at`` in markdown frontmatter renders in the display tz.
Markdown frontmatter is a display-side artefact (users read the file
directly), so ``last_appended_at`` must use
:func:`get_now_with_timezone` not :func:`get_utc_now`. Pins that
contract end-to-end: configure ``EVEROS_MEMORY__TIMEZONE=Asia/Shanghai``,
write an entry, read the .md file, assert the literal string ends
with ``+08:00``.
Repeats the same check for ``ForesightWriter`` and
``AgentCaseWriter`` — they share ``BaseDailyWriter`` plumbing so a
regression on one would likely affect all three, but pinning each
rules out per-subclass shadowing of ``_frontmatter_updates``.
"""
from everos.component.utils import datetime as _dt_module
from everos.config import load_settings
monkeypatch.setenv("EVEROS_MEMORY__TIMEZONE", "Asia/Shanghai")
load_settings.cache_clear()
_dt_module._display_tz.cache_clear()
today = _dt.date(2026, 5, 15)
# AtomicFact
af_writer = AtomicFactWriter(memory_root)
await af_writer.append_entry(
"u1",
inline={
"owner_id": "u1",
"session_id": "s1",
"timestamp": "2026-05-15T10:00:00+00:00",
"parent_id": "mc_1",
"sender_ids": ["u1"],
},
sections={"Fact": "x"},
date=today,
)
af_path = (
memory_root.users_dir() / "u1" / ".atomic_facts" / "atomic_fact-2026-05-15.md"
)
af_fm = (await MarkdownReader.read(af_path)).frontmatter
assert af_fm["last_appended_at"].endswith("+08:00"), af_fm["last_appended_at"]
# Foresight
fs_writer = ForesightWriter(memory_root)
await fs_writer.append_entry(
"u1",
inline={
"owner_id": "u1",
"session_id": "s1",
"timestamp": "2026-05-15T10:00:00+00:00",
"scope": "today",
"horizon_days": 1,
},
sections={"Foresight": "x"},
date=today,
)
fs_path = memory_root.users_dir() / "u1" / ".foresights" / "foresight-2026-05-15.md"
fs_fm = (await MarkdownReader.read(fs_path)).frontmatter
assert fs_fm["last_appended_at"].endswith("+08:00"), fs_fm["last_appended_at"]
# AgentCase
ac_writer = AgentCaseWriter(memory_root)
await ac_writer.append_entry(
"a1",
inline={
"owner_id": "a1",
"session_id": "s1",
"timestamp": "2026-05-15T10:00:00+00:00",
"quality_score": 0.9,
},
sections={"Task intent": "x", "Approach": "y"},
date=today,
)
ac_path = memory_root.agents_dir() / "a1" / ".cases" / "agent_case-2026-05-15.md"
ac_fm = (await MarkdownReader.read(ac_path)).frontmatter
assert ac_fm["last_appended_at"].endswith("+08:00"), ac_fm["last_appended_at"]

View File

@ -0,0 +1,166 @@
"""Tests for :class:`ProfileWriter` — single-file rewrite layout."""
from __future__ import annotations
from pathlib import Path
from typing import ClassVar, Literal
import pytest
from everos.core.persistence import (
AgentScopedFrontmatter,
BaseFrontmatter,
MarkdownReader,
MemoryRoot,
UserScopedFrontmatter,
)
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 = ""
class _AgentProfileFM(AgentScopedFrontmatter):
PROFILE_FILENAME: ClassVar[str] = "agent.md"
type: Literal["demo_agent_profile"] = "demo_agent_profile"
name: str = ""
@pytest.fixture
def root(tmp_path: Path) -> MemoryRoot:
return MemoryRoot(tmp_path)
@pytest.fixture
def writer(root: MemoryRoot) -> ProfileWriter:
return ProfileWriter(root)
async def test_write_creates_user_profile(
root: MemoryRoot, writer: ProfileWriter
) -> None:
fm = _UserProfileFM(
id="demo_user_profile_u_jason",
type="demo_user_profile",
user_id="u_jason",
display_name="Jason",
bio="hiker.",
)
path = await writer.write("u_jason", frontmatter=fm, body="Long-form profile.")
expected = root.users_dir() / "u_jason" / "user.md"
assert path == expected
assert expected.is_file()
async def test_write_creates_agent_profile(
root: MemoryRoot, writer: ProfileWriter
) -> None:
fm = _AgentProfileFM(
id="demo_agent_profile_agent_x",
type="demo_agent_profile",
agent_id="agent_x",
name="zhang_legal",
)
path = await writer.write("agent_x", frontmatter=fm, body="Agent playbook.")
expected = root.agents_dir() / "agent_x" / "agent.md"
assert path == expected
assert expected.is_file()
async def test_write_writes_frontmatter_and_body(
root: MemoryRoot, writer: ProfileWriter
) -> None:
fm = _UserProfileFM(
id="demo_user_profile_u_jason",
type="demo_user_profile",
user_id="u_jason",
display_name="Jason",
bio="weekend hiker.",
)
await writer.write("u_jason", frontmatter=fm, body="The body.")
parsed = await MarkdownReader.read(root.users_dir() / "u_jason" / "user.md")
assert parsed.frontmatter["display_name"] == "Jason"
assert parsed.frontmatter["bio"] == "weekend hiker."
assert parsed.body.rstrip("\n") == "The body."
async def test_write_is_upsert_full_replace(
root: MemoryRoot, writer: ProfileWriter
) -> None:
"""Second call overwrites both frontmatter and body — no append."""
fm1 = _UserProfileFM(
id="demo_user_profile_u_jason",
type="demo_user_profile",
user_id="u_jason",
display_name="Jason v1",
bio="v1",
)
await writer.write("u_jason", frontmatter=fm1, body="body v1")
fm2 = _UserProfileFM(
id="demo_user_profile_u_jason",
type="demo_user_profile",
user_id="u_jason",
display_name="Jason v2",
bio="v2",
)
await writer.write("u_jason", frontmatter=fm2, body="body v2")
parsed = await MarkdownReader.read(root.users_dir() / "u_jason" / "user.md")
assert parsed.frontmatter["display_name"] == "Jason v2"
assert parsed.frontmatter["bio"] == "v2"
assert parsed.body.rstrip("\n") == "body v2"
assert "v1" not in parsed.body
def test_path_for_does_not_create_files(
root: MemoryRoot, writer: ProfileWriter
) -> None:
"""``path_for`` is a pure path resolver — no IO."""
p = writer.path_for("u_jason", schema=_UserProfileFM)
assert p == root.users_dir() / "u_jason" / "user.md"
assert not p.exists()
assert not root.users_dir().exists()
async def test_write_normalises_trailing_newline(
root: MemoryRoot, writer: ProfileWriter
) -> None:
fm = _UserProfileFM(
id="demo_user_profile_u_jason",
type="demo_user_profile",
user_id="u_jason",
)
await writer.write("u_jason", frontmatter=fm, body="no-newline-end")
text = (root.users_dir() / "u_jason" / "user.md").read_text(encoding="utf-8")
assert text.endswith("no-newline-end\n")
async def test_write_rejects_schema_missing_profile_filename(
writer: ProfileWriter,
) -> None:
"""Schema without ``PROFILE_FILENAME`` ClassVar raises a clear error."""
class _BadSchema(UserScopedFrontmatter):
type: Literal["bad"] = "bad"
fm = _BadSchema(id="x", type="bad", user_id="u_jason")
with pytest.raises(TypeError, match="PROFILE_FILENAME"):
await writer.write("u_jason", frontmatter=fm, body="body")
async def test_write_rejects_schema_missing_scope_dir(writer: ProfileWriter) -> None:
"""Schema without scope mixin (empty ``SCOPE_DIR``) raises a clear error."""
class _ScopelessSchema(BaseFrontmatter):
PROFILE_FILENAME: ClassVar[str] = "profile.md"
type: Literal["scopeless"] = "scopeless"
fm = _ScopelessSchema(id="x", type="scopeless")
with pytest.raises(TypeError, match="SCOPE_DIR"):
await writer.write("x", frontmatter=fm, body="body")