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

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:
2026-10-22 10:50:45 -04:00
co-authored by Claude Opus 5.5
parent e99f1169f7
commit 0f5ef0be63
447 changed files with 3611 additions and 3558 deletions
+12 -12
View File
@@ -7,7 +7,7 @@ doors hand out the same shapes.
"""
import pytest
from scribe.services import moments, reply_shapes
from grimoire.services import moments, reply_shapes
from tests.helpers import FakeMCP, dev_only_hits
# Bound before conftest's autouse stub replaces the module attribute.
@@ -114,14 +114,14 @@ def test_the_payload_carries_every_shape_with_its_moments_meaning():
async def test_the_tool_returns_the_catalog():
from scribe.mcp.tools import moments as tool
from grimoire.mcp.tools import moments as tool
assert await tool.list_reply_shapes() == reply_shapes.catalog()
def test_the_tool_is_registered_as_a_read():
from scribe.mcp.server import _READ_ONLY_TOOLS
from scribe.mcp.tools import moments as tool
from grimoire.mcp.server import _READ_ONLY_TOOLS
from grimoire.mcp.tools import moments as tool
mcp = FakeMCP()
tool.register(mcp)
@@ -132,8 +132,8 @@ def test_the_tool_is_registered_as_a_read():
def test_both_doors_read_one_catalog():
"""Parity: the session and the Settings view cannot show
different defaults."""
from scribe.mcp.tools import moments as tool
from scribe.routes import retrieval as routes
from grimoire.mcp.tools import moments as tool
from grimoire.routes import retrieval as routes
assert tool.shapes_svc is reply_shapes
assert routes.shapes_svc is reply_shapes
@@ -145,7 +145,7 @@ async def test_the_route_returns_the_operators_overview_with_a_bounded_window():
from quart import Quart, g
from scribe.routes import retrieval as routes
from grimoire.routes import retrieval as routes
view = AsyncMock(return_value={"shapes": [], "total": 0})
app = Quart(__name__)
@@ -175,7 +175,7 @@ async def test_the_overview_lists_the_preferences_mounted_on_each_shapes_moment(
counts = {k: {"full": 0, "pointer": 0} for k in reply_shapes.SHAPES}
counts["core"] = {"full": 2, "pointer": 9}
with patch("scribe.services.rulebooks.rules_on_moments", mounted), \
with patch("grimoire.services.rulebooks.rules_on_moments", mounted), \
patch.object(reply_shapes, "delivery_counts", AsyncMock(return_value=counts)):
data = await reply_shapes.overview(7, days=30)
rows = {r["key"]: r for r in data["shapes"]}
@@ -191,7 +191,7 @@ async def test_counts_that_fail_read_as_unknown_not_zero():
async def mounted(user_id, moments, project_id=None):
return []
with patch("scribe.services.rulebooks.rules_on_moments", mounted), \
with patch("grimoire.services.rulebooks.rules_on_moments", mounted), \
patch.object(reply_shapes, "delivery_counts", AsyncMock(side_effect=RuntimeError("db"))):
data = await reply_shapes.overview(7)
assert data["deliveries_failed"] is True
@@ -219,7 +219,7 @@ async def test_delivery_counts_tally_each_shape_by_form_and_skip_what_does_not_p
async def execute(self, _stmt):
return result
with patch("scribe.models.async_session", lambda: _Session()):
with patch("grimoire.models.async_session", lambda: _Session()):
counts = await reply_shapes.delivery_counts(7, days=30)
assert counts["core"] == {"full": 1, "pointer": 2}
assert counts["completion"] == {"full": 1, "pointer": 0}
@@ -293,7 +293,7 @@ async def test_a_delivery_is_one_plugin_row_naming_each_shape_and_its_form():
async def __aexit__(self, *exc):
return False
with patch("scribe.models.async_session", lambda: _Ctx()):
with patch("grimoire.models.async_session", lambda: _Ctx()):
await _REAL_RECORD(7, {"core": "pointer", "asks": "full"}, via="turn")
assert len(added) == 1
row = added[0]
@@ -309,7 +309,7 @@ async def test_the_delivery_row_fails_open_and_skips_an_empty_delivery():
def _boom():
raise RuntimeError("db down")
with patch("scribe.models.async_session", _boom):
with patch("grimoire.models.async_session", _boom):
await _REAL_RECORD(7, {"core": "full"}, via="turn") # no raise
await _REAL_RECORD(7, {}, via="turn")