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:
+10
-10
@@ -3,7 +3,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from scribe.mcp.auth import resolve_bearer
|
||||
from grimoire.mcp.auth import resolve_bearer
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -22,7 +22,7 @@ async def test_resolve_bearer_malformed_header_returns_none():
|
||||
@pytest.mark.asyncio
|
||||
async def test_resolve_bearer_unknown_token_returns_none():
|
||||
with patch(
|
||||
"scribe.mcp.auth.lookup_key",
|
||||
"grimoire.mcp.auth.lookup_key",
|
||||
AsyncMock(return_value=None),
|
||||
):
|
||||
assert await resolve_bearer("Bearer fmcp_doesnotexist") is None
|
||||
@@ -34,7 +34,7 @@ async def test_resolve_bearer_valid_token_returns_user_id():
|
||||
fake_key.user_id = 42
|
||||
fake_key.scope = "write"
|
||||
with patch(
|
||||
"scribe.mcp.auth.lookup_key",
|
||||
"grimoire.mcp.auth.lookup_key",
|
||||
AsyncMock(return_value=fake_key),
|
||||
):
|
||||
uid, scope = await resolve_bearer("Bearer fmcp_validkey")
|
||||
@@ -48,7 +48,7 @@ async def test_resolve_bearer_calls_lookup_with_stripped_token():
|
||||
fake_key.user_id = 1
|
||||
fake_key.scope = "write"
|
||||
mock_lookup = AsyncMock(return_value=fake_key)
|
||||
with patch("scribe.mcp.auth.lookup_key", mock_lookup):
|
||||
with patch("grimoire.mcp.auth.lookup_key", mock_lookup):
|
||||
await resolve_bearer("Bearer fmcp_abc123 ")
|
||||
mock_lookup.assert_awaited_once_with("fmcp_abc123")
|
||||
|
||||
@@ -60,13 +60,13 @@ async def test_resolve_bearer_returns_user_id_and_scope():
|
||||
fake_key = MagicMock()
|
||||
fake_key.user_id = 9
|
||||
fake_key.scope = "read"
|
||||
with patch("scribe.mcp.auth.lookup_key", AsyncMock(return_value=fake_key)):
|
||||
with patch("grimoire.mcp.auth.lookup_key", AsyncMock(return_value=fake_key)):
|
||||
assert await resolve_bearer("Bearer fmcp_x") == (9, "read")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_resolve_bearer_none_for_invalid():
|
||||
with patch("scribe.mcp.auth.lookup_key", AsyncMock(return_value=None)):
|
||||
with patch("grimoire.mcp.auth.lookup_key", AsyncMock(return_value=None)):
|
||||
assert await resolve_bearer("Bearer nope") is None
|
||||
assert await resolve_bearer(None) is None
|
||||
|
||||
@@ -75,7 +75,7 @@ async def test_resolve_bearer_none_for_invalid():
|
||||
|
||||
def test_body_calls_write_tool_classifies_correctly():
|
||||
import json
|
||||
from scribe.mcp.server import _body_calls_write_tool
|
||||
from grimoire.mcp.server import _body_calls_write_tool
|
||||
|
||||
def call(name):
|
||||
return json.dumps({"jsonrpc": "2.0", "id": 1, "method": "tools/call",
|
||||
@@ -99,7 +99,7 @@ def test_body_calls_write_tool_classifies_correctly():
|
||||
def _registered_tool_names() -> set[str]:
|
||||
"""What the server actually mounts — not a glob of function names, which
|
||||
would count helpers and miss anything registered another way."""
|
||||
from scribe.mcp.server import build_mcp_server
|
||||
from grimoire.mcp.server import build_mcp_server
|
||||
|
||||
return {tool.name for tool in build_mcp_server()._tool_manager.list_tools()}
|
||||
|
||||
@@ -118,7 +118,7 @@ def test_every_registered_tool_is_classified_exactly_once():
|
||||
not look like reads (`rules_due_for_verification`, `rule_history`). So the
|
||||
candidates are EVERY registered tool.
|
||||
"""
|
||||
from scribe.mcp.server import (
|
||||
from grimoire.mcp.server import (
|
||||
_DELIBERATELY_WRITE_SCOPED, _READ_ONLY_TOOLS, _WRITE_TOOLS,
|
||||
)
|
||||
|
||||
@@ -156,7 +156,7 @@ def test_every_registered_tool_is_classified_exactly_once():
|
||||
def test_the_completeness_check_can_fail():
|
||||
"""A guard that cannot fail is indistinguishable from one that is broken
|
||||
Drop a real tool from its set and the check must notice."""
|
||||
from scribe.mcp.server import _READ_ONLY_TOOLS, _WRITE_TOOLS
|
||||
from grimoire.mcp.server import _READ_ONLY_TOOLS, _WRITE_TOOLS
|
||||
|
||||
registered = _registered_tool_names()
|
||||
assert "rule_history" in _READ_ONLY_TOOLS
|
||||
|
||||
Reference in New Issue
Block a user