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
+15 -15
View File
@@ -18,20 +18,20 @@ import pytest
def test_retrieval_blueprint_registered():
from scribe.routes.retrieval import retrieval_bp
from grimoire.routes.retrieval import retrieval_bp
assert retrieval_bp.name == "retrieval"
assert retrieval_bp.url_prefix == "/api/retrieval"
def test_retrieval_blueprint_registered_in_app():
from scribe.app import create_app
from grimoire.app import create_app
app = create_app()
assert "retrieval" in app.blueprints
def test_every_endpoint_is_reachable_on_the_app():
"""Handlers existing is not the same as them being routed."""
from scribe.app import create_app
from grimoire.app import create_app
app = create_app()
rules = {
str(r.rule) for r in app.url_map.iter_rules()
@@ -52,9 +52,9 @@ def test_every_endpoint_is_reachable_on_the_app():
def test_the_browser_and_the_agent_call_the_same_service():
"""Parity. Two callers, one service — or the two surfaces drift and
the guard that a reason is required exists on only one of them."""
from scribe.mcp.tools import retrieval_tuning as tool
from scribe.routes import retrieval as routes
from scribe.services import retrieval_tuning as svc
from grimoire.mcp.tools import retrieval_tuning as tool
from grimoire.routes import retrieval as routes
from grimoire.services import retrieval_tuning as svc
assert routes.set_dial is svc.set_dial
assert tool.tuning_svc is svc
@@ -64,7 +64,7 @@ def test_the_route_does_not_take_the_actor_from_the_caller():
"""`actor` is the one field a reviewer leans on to answer "did I do this,
or did the session?". A payload-supplied actor would let a model claim to
be the operator, which turns the column into decoration."""
from scribe.routes import retrieval as routes
from grimoire.routes import retrieval as routes
src = inspect.getsource(routes.tune_surface_route)
assert 'actor="human"' in src
@@ -76,7 +76,7 @@ def test_the_route_does_not_take_the_actor_from_the_caller():
def test_every_registry_key_is_recognised_as_a_dial():
"""Derived from the registry, both directions. A seventh surface added
without a row here is a floor that can be written silently again."""
from scribe.services.retrieval_surfaces import SURFACES, dial_for_key
from grimoire.services.retrieval_surfaces import SURFACES, dial_for_key
for name, surface in SURFACES.items():
assert dial_for_key(surface.floor_key) == (name, "floor")
@@ -87,7 +87,7 @@ def test_an_ordinary_setting_is_not_mistaken_for_a_dial():
"""The interception must be narrow. A false positive here would send an
unrelated setting through a service that parses it as a float and rejects
the save."""
from scribe.services.retrieval_surfaces import dial_for_key
from grimoire.services.retrieval_surfaces import dial_for_key
for key in ("smtp_password", "kb_autoinject_enabled", "theme",
"kb_planmatch_threshold", ""):
@@ -99,7 +99,7 @@ def test_the_settings_endpoint_routes_a_dial_through_the_recorder():
request-context round trip for a branch whose whole content is "which
function gets called" — and because what must not regress is precisely
that this module reaches for `set_dial` at all."""
from scribe.routes import settings as routes
from grimoire.routes import settings as routes
src = inspect.getsource(routes.update_settings_route)
assert "dial_for_key" in src, (
@@ -119,7 +119,7 @@ async def test_setting_a_dial_to_the_value_it_already_has_records_nothing():
reads."""
from unittest.mock import AsyncMock, MagicMock, patch
from scribe.services import retrieval_tuning as rt
from grimoire.services import retrieval_tuning as rt
from tests.helpers import make_mock_session
session = make_mock_session()
@@ -144,7 +144,7 @@ async def _moments_call(handler_name, path, method="GET", **kw):
from quart import Quart, g
from scribe.routes import retrieval as routes
from grimoire.routes import retrieval as routes
app = Quart(__name__)
async with app.test_request_context(path, method=method, **kw):
@@ -158,7 +158,7 @@ async def _moments_call(handler_name, path, method="GET", **kw):
async def test_the_view_gets_the_catalog_with_counts_beside_it():
from unittest.mock import AsyncMock, patch
from scribe.routes import retrieval as routes
from grimoire.routes import retrieval as routes
usage = AsyncMock(return_value={"by_moment": {"work.finish": {"delivered": 2}}})
with patch.object(routes.moment_actions_svc, "actions_by_moment",
@@ -177,7 +177,7 @@ async def test_the_view_gets_the_catalog_with_counts_beside_it():
async def test_a_failed_mount_count_is_flagged_not_shown_as_nothing():
from unittest.mock import AsyncMock, patch
from scribe.routes import retrieval as routes
from grimoire.routes import retrieval as routes
with patch.object(routes.moment_actions_svc, "actions_by_moment",
AsyncMock(return_value={"actions": {}, "removed_defaults": []})), \
@@ -194,7 +194,7 @@ async def test_a_mapping_removal_can_come_as_query_parameters():
mapping it removes arrives in the query string."""
from unittest.mock import AsyncMock, patch
from scribe.routes import retrieval as routes
from grimoire.routes import retrieval as routes
unmap = AsyncMock(return_value={"change": "removed this install's mapping"})
with patch.object(routes.moment_actions_svc, "unmap_action", unmap):