claire/tests/test_scheduler.py
Natalie c1e6f7dbe5 feat: initial Clare scaffold — project manager for the Claude agent fleet
Push A (single-machine):
- HLC + event-sourced SQLite (events table is source of truth, projections rebuildable)
- Pydantic v2 domain models (Project, Task, Assignment, Session, Group, Update)
- rclaude subprocess wrapper (local_sessions via _claude-projects --sessions)
- Typer CLI: init, project, task, assign, pull, status, broadcast, serve, sync
- FastAPI + Jinja2 + HTMX dashboard
- 26 unit tests passing

Push B (HTTP API + sync substrate):
- /api/v1/* JSON routes (projects, tasks, assignments, sessions, status, broadcast, sync)
- CLI refactored as thin httpx client over the API — single business-logic codepath
- web/service.py: every business op defined once; HTML routes + API routes both call into it
- sync.py: peer-to-peer sync via /api/v1/sync/events with HLC + uuid-based dedup
- 32 tests passing including two-Clare convergence test

Push C (cross-host deployment):
- apricot install via uv (Python 3.12.12)
- systemd --user unit for clare-serve on apricot
- Cross-host sync demoed plum (10.9.0.3) ↔ apricot (10.9.0.2) over wg
- .local → .lan rename for forge URLs

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 02:20:23 -07:00

67 lines
1.9 KiB
Python

from __future__ import annotations
import uuid as _uuid
from clare.domain import Session, Task, TaskStatus
from clare.scheduler import next_task_for_session, rank_open_tasks, session_attention_score
def _task(prio: int, status: TaskStatus = TaskStatus.TODO, hlc: str = "0") -> Task:
return Task(
id=_uuid.uuid4(),
project_id=_uuid.uuid4(),
title="t",
status=status,
priority=prio,
created_hlc=hlc,
updated_hlc=hlc,
)
def test_rank_open_tasks_drops_done() -> None:
tasks = [
_task(0, TaskStatus.DONE),
_task(2),
_task(1),
]
ranked = rank_open_tasks(tasks)
assert [t.priority for t in ranked] == [1, 2]
def test_rank_open_tasks_orders_by_priority_then_created() -> None:
t_a = _task(1, hlc="00000000001")
t_b = _task(1, hlc="00000000002")
t_c = _task(0, hlc="00000000003")
ranked = rank_open_tasks([t_a, t_b, t_c])
assert ranked == [t_c, t_a, t_b]
def test_next_task_for_session_returns_top_owned() -> None:
sid = _uuid.uuid4()
other_sid = _uuid.uuid4()
t1 = _task(2)
t2 = _task(0)
t3 = _task(1)
active = {t1.id: sid, t2.id: other_sid, t3.id: sid}
nxt = next_task_for_session([t1, t2, t3], active, sid)
assert nxt == t3 # priority 1 (owned by sid) beats priority 2
def test_next_task_for_session_none_when_no_assignments() -> None:
sid = _uuid.uuid4()
nxt = next_task_for_session([_task(0)], {}, sid)
assert nxt is None
def test_session_attention_score_inverts_priority() -> None:
def make_session(prio: int | None) -> Session:
return Session(
uuid=_uuid.uuid4(),
host="local",
last_triage_priority=prio,
updated_hlc="0",
)
assert session_attention_score(make_session(0)) == 4
assert session_attention_score(make_session(4)) == 0
assert session_attention_score(make_session(None)) == 0