rename(547): the server is grimoire — package, image, DB defaults, backups
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / integration (push) Successful in 1m4s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 44s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / integration (push) Successful in 1m4s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 44s
Step 2 of the Scribe -> Grimoire rename. - src/scribe -> src/grimoire, and every import, patch target, module path, hypercorn entry point and `python -m grimoire.restore` with it; pyproject, uv.lock and the frontend package are named grimoire. - Image git.fabledsword.com/bvandeusen/fabledgrimoire; CI's Postgres service runs as grimoire / grimoire_test. - Compose files default the DB user, database and (prod) volume to grimoire, each overridable from .env, so an install created as Scribe keeps its data by setting POSTGRES_USER, POSTGRES_DB and PGDATA_VOLUME. - Backups are written as grimoire-<ts>.tar with format grimoire-backup-tar. Archives named scribe-* with the scribe-backup-tar manifest still restore and are still counted at the destination (folder and S3), with tests. - Exports, the MCP auth realm, the ASGI scope key and the persistent logger carry the new name. Kept on purpose: the secret-sealing key label stays "scribe.secrets.v1", because every stored secret was derived under it. Plugin identity (plugin name, hooks, env vars, .scribe marker, skills) is step 3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,16 +13,16 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from scribe.mcp.tools.milestones import get_milestone
|
||||
from scribe.mcp.tools.tasks import (
|
||||
from grimoire.mcp.tools.milestones import get_milestone
|
||||
from grimoire.mcp.tools.tasks import (
|
||||
elide, get_task, list_tasks, work_log_payload,
|
||||
)
|
||||
from scribe.services.notes import brief_row
|
||||
from grimoire.services.notes import brief_row
|
||||
# Bound at IMPORT time, which is before any fixture runs — so these names
|
||||
# keep pointing at the real implementations even though conftest's autouse
|
||||
# _no_task_log_arm replaces the module attributes for every test. Reaching
|
||||
# them as `task_logs.logs_for_task` would get the stub and test nothing.
|
||||
from scribe.services.task_logs import (
|
||||
from grimoire.services.task_logs import (
|
||||
count_logs_for_task,
|
||||
log_counts_for_tasks,
|
||||
logs_for_task,
|
||||
@@ -54,11 +54,11 @@ def fake_log(log_id: int = 1, content: str = "did the thing", **attrs):
|
||||
def _live_arm(logs=(), total=0, counts=None):
|
||||
"""Re-patch conftest's autouse stub so the arm under test is live."""
|
||||
return (
|
||||
patch("scribe.services.task_logs.logs_for_task",
|
||||
patch("grimoire.services.task_logs.logs_for_task",
|
||||
AsyncMock(return_value=list(logs))),
|
||||
patch("scribe.services.task_logs.count_logs_for_task",
|
||||
patch("grimoire.services.task_logs.count_logs_for_task",
|
||||
AsyncMock(return_value=total)),
|
||||
patch("scribe.services.task_logs.log_counts_for_tasks",
|
||||
patch("grimoire.services.task_logs.log_counts_for_tasks",
|
||||
AsyncMock(return_value=counts or {})),
|
||||
)
|
||||
|
||||
@@ -168,7 +168,7 @@ async def test_get_task_returns_the_work_log():
|
||||
fake = fake_task(id=4208, title="stale body", parent_id=None)
|
||||
p1, p2, p3 = _live_arm(logs=[fake_log(content="partially shipped in 04775c3")],
|
||||
total=1)
|
||||
with patch("scribe.mcp.tools.tasks.notes_svc.get_note_for_user",
|
||||
with patch("grimoire.mcp.tools.tasks.notes_svc.get_note_for_user",
|
||||
AsyncMock(return_value=(fake, "owner"))), p1, p2, p3:
|
||||
out = await get_task(task_id=4208)
|
||||
assert "work_log" in out
|
||||
@@ -180,7 +180,7 @@ async def test_get_task_returns_the_work_log():
|
||||
async def test_get_task_says_a_task_has_no_log_rather_than_omitting_the_key():
|
||||
fake = fake_task(id=1, parent_id=None)
|
||||
p1, p2, p3 = _live_arm(logs=[], total=0)
|
||||
with patch("scribe.mcp.tools.tasks.notes_svc.get_note_for_user",
|
||||
with patch("grimoire.mcp.tools.tasks.notes_svc.get_note_for_user",
|
||||
AsyncMock(return_value=(fake, "owner"))), p1, p2, p3:
|
||||
out = await get_task(task_id=1)
|
||||
assert out["work_log"] == {"total": 0, "entries": []}
|
||||
@@ -191,7 +191,7 @@ async def test_get_task_counts_every_entry_not_just_the_window():
|
||||
fake = fake_task(id=1, parent_id=None)
|
||||
window = [fake_log(i) for i in (9, 8, 7)]
|
||||
p1, p2, p3 = _live_arm(logs=window, total=9)
|
||||
with patch("scribe.mcp.tools.tasks.notes_svc.get_note_for_user",
|
||||
with patch("grimoire.mcp.tools.tasks.notes_svc.get_note_for_user",
|
||||
AsyncMock(return_value=(fake, "owner"))), p1, p2, p3:
|
||||
out = await get_task(task_id=1)
|
||||
assert out["work_log"]["total"] == 9
|
||||
@@ -207,10 +207,10 @@ async def test_get_task_default_window_is_three_entries():
|
||||
limit_seen["limit"] = limit
|
||||
return []
|
||||
|
||||
with patch("scribe.mcp.tools.tasks.notes_svc.get_note_for_user",
|
||||
with patch("grimoire.mcp.tools.tasks.notes_svc.get_note_for_user",
|
||||
AsyncMock(return_value=(fake, "owner"))), \
|
||||
patch("scribe.services.task_logs.logs_for_task", _capture_limit), \
|
||||
patch("scribe.services.task_logs.count_logs_for_task",
|
||||
patch("grimoire.services.task_logs.logs_for_task", _capture_limit), \
|
||||
patch("grimoire.services.task_logs.count_logs_for_task",
|
||||
AsyncMock(return_value=0)):
|
||||
await get_task(task_id=1)
|
||||
assert limit_seen["limit"] == 3
|
||||
@@ -223,9 +223,9 @@ async def test_log_limit_zero_asks_for_every_entry_and_skips_the_count_query():
|
||||
fake = fake_task(id=1, parent_id=None)
|
||||
counter = AsyncMock(return_value=99)
|
||||
p1, _, p3 = _live_arm(logs=[fake_log(1), fake_log(2)])
|
||||
with patch("scribe.mcp.tools.tasks.notes_svc.get_note_for_user",
|
||||
with patch("grimoire.mcp.tools.tasks.notes_svc.get_note_for_user",
|
||||
AsyncMock(return_value=(fake, "owner"))), \
|
||||
p1, p3, patch("scribe.services.task_logs.count_logs_for_task", counter):
|
||||
p1, p3, patch("grimoire.services.task_logs.count_logs_for_task", counter):
|
||||
out = await get_task(task_id=1, log_limit=0)
|
||||
assert out["work_log"]["total"] == 2
|
||||
assert counter.await_count == 0
|
||||
@@ -249,7 +249,7 @@ async def test_get_task_docstring_tells_a_reader_the_log_outranks_the_body():
|
||||
async def test_list_tasks_rows_carry_a_zero_filled_log_count():
|
||||
rows = [fake_task(id=1, milestone_id=None), fake_task(id=2, milestone_id=None)]
|
||||
_, _, p3 = _live_arm(counts={1: 4})
|
||||
with patch("scribe.mcp.tools.tasks.notes_svc.list_notes",
|
||||
with patch("grimoire.mcp.tools.tasks.notes_svc.list_notes",
|
||||
AsyncMock(return_value=(rows, 2))), p3:
|
||||
out = await list_tasks()
|
||||
assert out["tasks"][0]["log_count"] == 4
|
||||
@@ -262,9 +262,9 @@ async def test_list_tasks_rows_carry_a_zero_filled_log_count():
|
||||
async def test_list_tasks_counts_the_page_in_one_query():
|
||||
rows = [fake_task(id=i, milestone_id=None) for i in range(1, 6)]
|
||||
counter = AsyncMock(return_value={})
|
||||
with patch("scribe.mcp.tools.tasks.notes_svc.list_notes",
|
||||
with patch("grimoire.mcp.tools.tasks.notes_svc.list_notes",
|
||||
AsyncMock(return_value=(rows, 5))), \
|
||||
patch("scribe.services.task_logs.log_counts_for_tasks", counter):
|
||||
patch("grimoire.services.task_logs.log_counts_for_tasks", counter):
|
||||
await list_tasks()
|
||||
assert counter.await_count == 1
|
||||
assert sorted(counter.await_args.args[1]) == [1, 2, 3, 4, 5]
|
||||
@@ -279,17 +279,17 @@ async def test_get_milestone_steps_carry_a_log_count():
|
||||
steps = [fake_task(id=3735, milestone_id=385),
|
||||
fake_task(id=3736, milestone_id=385)]
|
||||
counter = AsyncMock(return_value={3735: 2})
|
||||
with patch("scribe.mcp.tools.milestones.milestones_svc.get_milestone",
|
||||
with patch("grimoire.mcp.tools.milestones.milestones_svc.get_milestone",
|
||||
AsyncMock(return_value=milestone)), \
|
||||
patch("scribe.mcp.tools.milestones.milestones_svc.get_milestone_progress",
|
||||
patch("grimoire.mcp.tools.milestones.milestones_svc.get_milestone_progress",
|
||||
AsyncMock(return_value={})), \
|
||||
patch("scribe.mcp.tools.milestones.notes_svc.list_notes",
|
||||
patch("grimoire.mcp.tools.milestones.notes_svc.list_notes",
|
||||
AsyncMock(return_value=(steps, 2))), \
|
||||
patch("scribe.mcp.tools.milestones.rulebooks_svc.get_applicable_rules",
|
||||
patch("grimoire.mcp.tools.milestones.rulebooks_svc.get_applicable_rules",
|
||||
AsyncMock(return_value=[])), \
|
||||
patch("scribe.mcp.tools.milestones.rulebooks_svc.rules_payload",
|
||||
patch("grimoire.mcp.tools.milestones.rulebooks_svc.rules_payload",
|
||||
MagicMock(return_value={})), \
|
||||
patch("scribe.services.task_logs.log_counts_for_tasks", counter):
|
||||
patch("grimoire.services.task_logs.log_counts_for_tasks", counter):
|
||||
out = await get_milestone(milestone_id=385)
|
||||
assert out["steps"][0]["log_count"] == 2
|
||||
assert out["steps"][1]["log_count"] == 0
|
||||
@@ -315,9 +315,9 @@ async def test_logs_for_task_is_not_filtered_by_who_wrote_the_entry():
|
||||
session.execute = AsyncMock(return_value=MagicMock(
|
||||
scalars=MagicMock(return_value=MagicMock(all=MagicMock(return_value=[])))
|
||||
))
|
||||
with patch("scribe.services.task_logs.can_read_note",
|
||||
with patch("grimoire.services.task_logs.can_read_note",
|
||||
AsyncMock(return_value=True)), \
|
||||
patch("scribe.services.task_logs.async_session",
|
||||
patch("grimoire.services.task_logs.async_session",
|
||||
MagicMock(return_value=session)):
|
||||
await logs_for_task(7, 42)
|
||||
# The WHERE clause specifically — `user_id` is a selected COLUMN of the
|
||||
@@ -333,9 +333,9 @@ async def test_logs_for_task_refuses_a_task_the_caller_cannot_read():
|
||||
"""Unscoped would be the mirror-image hole: access control is about
|
||||
routing the question through the access layer, in both directions."""
|
||||
opened = MagicMock()
|
||||
with patch("scribe.services.task_logs.can_read_note",
|
||||
with patch("grimoire.services.task_logs.can_read_note",
|
||||
AsyncMock(return_value=False)), \
|
||||
patch("scribe.services.task_logs.async_session", opened):
|
||||
patch("grimoire.services.task_logs.async_session", opened):
|
||||
out = await logs_for_task(7, 42)
|
||||
assert out == []
|
||||
assert opened.call_count == 0
|
||||
@@ -344,9 +344,9 @@ async def test_logs_for_task_refuses_a_task_the_caller_cannot_read():
|
||||
@pytest.mark.asyncio
|
||||
async def test_count_for_an_unreadable_task_is_zero_not_a_leak():
|
||||
opened = MagicMock()
|
||||
with patch("scribe.services.task_logs.can_read_note",
|
||||
with patch("grimoire.services.task_logs.can_read_note",
|
||||
AsyncMock(return_value=False)), \
|
||||
patch("scribe.services.task_logs.async_session", opened):
|
||||
patch("grimoire.services.task_logs.async_session", opened):
|
||||
assert await count_logs_for_task(7, 42) == 0
|
||||
assert opened.call_count == 0
|
||||
|
||||
@@ -359,7 +359,7 @@ async def test_log_counts_for_tasks_scopes_by_readability_in_the_same_query():
|
||||
session.execute = AsyncMock(return_value=MagicMock(
|
||||
all=MagicMock(return_value=[(1, 3)])
|
||||
))
|
||||
with patch("scribe.services.task_logs.async_session",
|
||||
with patch("grimoire.services.task_logs.async_session",
|
||||
MagicMock(return_value=session)):
|
||||
out = await log_counts_for_tasks(7, [1, 2])
|
||||
assert out == {1: 3}
|
||||
@@ -369,9 +369,9 @@ async def test_log_counts_for_tasks_scopes_by_readability_in_the_same_query():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_no_ids_asks_the_database_nothing():
|
||||
from scribe.services import task_logs as svc
|
||||
from grimoire.services import task_logs as svc
|
||||
|
||||
opened = MagicMock()
|
||||
with patch("scribe.services.task_logs.async_session", opened):
|
||||
with patch("grimoire.services.task_logs.async_session", opened):
|
||||
assert await log_counts_for_tasks(7, []) == {}
|
||||
assert opened.call_count == 0
|
||||
|
||||
Reference in New Issue
Block a user