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
|
|
|
from __future__ import annotations
|
|
|
|
|
|
2026-05-20 19:54:05 -07:00
|
|
|
from claire.db import migrate, open_db
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_migrate_is_idempotent() -> None:
|
|
|
|
|
conn = open_db(":memory:")
|
|
|
|
|
first = migrate(conn)
|
|
|
|
|
second = migrate(conn)
|
2026-05-20 03:43:35 -07:00
|
|
|
assert first == [
|
|
|
|
|
"0001_initial", "0002_chat", "0003_pm", "0004_fleet",
|
2026-05-20 04:31:55 -07:00
|
|
|
"0003_pm_alter", "0005_session_liveness", "0006_project_org",
|
2026-05-22 16:58:30 -07:00
|
|
|
"0007_usage", "0008_task_blocked_by", "0009_decisions",
|
2026-05-31 18:15:59 -06:00
|
|
|
"0010_role_clare_to_claire", "0011_host_telemetry",
|
2026-06-01 02:36:07 -06:00
|
|
|
"0012_decision_pinned",
|
2026-05-20 03:43:35 -07:00
|
|
|
]
|
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
|
|
|
assert second == [] # already applied
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_schema_has_expected_tables() -> None:
|
|
|
|
|
conn = open_db(":memory:")
|
|
|
|
|
migrate(conn)
|
|
|
|
|
rows = conn.execute(
|
|
|
|
|
"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
|
|
|
|
|
).fetchall()
|
|
|
|
|
names = {r[0] for r in rows}
|
|
|
|
|
expected = {
|
|
|
|
|
"schema_migrations",
|
|
|
|
|
"events",
|
|
|
|
|
"projects",
|
|
|
|
|
"tasks",
|
|
|
|
|
"assignments",
|
|
|
|
|
"sessions",
|
|
|
|
|
"groups",
|
|
|
|
|
"updates",
|
2026-05-18 07:47:06 -07:00
|
|
|
"chat_messages",
|
|
|
|
|
"task_splits",
|
2026-05-18 22:43:13 -07:00
|
|
|
"orgs",
|
|
|
|
|
"people",
|
|
|
|
|
"epics",
|
|
|
|
|
"tags",
|
|
|
|
|
"categories",
|
|
|
|
|
"domains",
|
|
|
|
|
"task_tags",
|
|
|
|
|
"task_state_history",
|
2026-05-20 02:20:26 -07:00
|
|
|
"agent_status",
|
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
|
|
|
}
|
|
|
|
|
assert expected <= names
|
2026-05-18 22:43:13 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_pm_alter_adds_task_columns() -> None:
|
|
|
|
|
"""Migration 0003_pm_alter adds the new task columns idempotently."""
|
|
|
|
|
conn = open_db(":memory:")
|
|
|
|
|
migrate(conn)
|
|
|
|
|
cols = {row[1] for row in conn.execute("PRAGMA table_info(tasks)")}
|
|
|
|
|
expected = {
|
|
|
|
|
"epic_id", "owner_person_id", "task_type",
|
|
|
|
|
"category_id", "domain_id", "color", "emoji",
|
|
|
|
|
"apply_color_rule",
|
|
|
|
|
}
|
|
|
|
|
assert expected <= cols
|
|
|
|
|
# Idempotent — a second migrate is a no-op.
|
|
|
|
|
again = migrate(conn)
|
|
|
|
|
assert again == []
|