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
+29 -29
View File
@@ -45,7 +45,7 @@ def _bind_user():
``pytestmark = pytest.mark.usefixtures("_bind_user")`` and builds its fakes
with user_id=7 so ownership checks see the caller as the owner.
"""
from scribe.mcp._context import _user_id_ctx
from grimoire.mcp._context import _user_id_ctx
token = _user_id_ctx.set(7)
yield
@@ -63,7 +63,7 @@ async def _dispose_engine():
pool cleanly. The import is deferred so merely collecting a module that
mixes unit and integration tests never builds an engine.
"""
from scribe.models import engine
from grimoire.models import engine
yield
await engine.dispose()
@@ -82,8 +82,8 @@ def _no_embedding():
# Milestones embed too; a plan created in a test would
# otherwise detach the same model-loading task.
with patch("scribe.services.notes.embed_note", MagicMock()), \
patch("scribe.services.milestones.embed_milestone", MagicMock()):
with patch("grimoire.services.notes.embed_note", MagicMock()), \
patch("grimoire.services.milestones.embed_milestone", MagicMock()):
yield
@@ -97,7 +97,7 @@ def _no_supersession():
lie about what it does. The label's own behaviour is covered in
tests/test_supersession_ranking.py.
"""
with patch("scribe.services.plugin_context.superseded_ids",
with patch("grimoire.services.plugin_context.superseded_ids",
AsyncMock(return_value=set())):
yield
@@ -112,7 +112,7 @@ def _no_reply_shape_log():
tests/test_reply_shapes.py binds the real function at import time,
before this patch runs.
"""
with patch("scribe.services.reply_shapes.record_delivery", AsyncMock()):
with patch("grimoire.services.reply_shapes.record_delivery", AsyncMock()):
yield
@@ -125,7 +125,7 @@ def _no_system_labels():
to "no labels", the state of any untagged record. Tests of the label
itself patch it with a value.
"""
with patch("scribe.services.plugin_context.system_names_for",
with patch("grimoire.services.plugin_context.system_names_for",
AsyncMock(return_value={})):
yield
@@ -140,7 +140,7 @@ def _no_rulings_arm():
tests/test_system_rulings.py binds the real function at import time,
before this patch runs.
"""
with patch("scribe.services.system_rulings.rulings_for_paths",
with patch("grimoire.services.system_rulings.rulings_for_paths",
AsyncMock(return_value={"lines": [], "system_ids": []})):
yield
@@ -154,12 +154,12 @@ def _no_system_usage_readout():
the zero shape for whatever ids were asked. tests/test_system_usage_readout.py
binds the real function at import time, before this patch runs.
"""
from scribe.services.system_usage import empty_system_usage
from grimoire.services.system_usage import empty_system_usage
async def _zeros(system_ids):
return {int(s): empty_system_usage() for s in system_ids}
with patch("scribe.services.system_usage.usage_for_systems",
with patch("grimoire.services.system_usage.usage_for_systems",
AsyncMock(side_effect=_zeros)):
yield
@@ -181,11 +181,11 @@ def _no_task_log_arm():
tests/test_integration_task_work_log.py. A test that wants the arm live
re-patches it, same as the rules arm.
"""
with patch("scribe.services.task_logs.logs_for_task",
with patch("grimoire.services.task_logs.logs_for_task",
AsyncMock(return_value=[])), \
patch("scribe.services.task_logs.count_logs_for_task",
patch("grimoire.services.task_logs.count_logs_for_task",
AsyncMock(return_value=0)), \
patch("scribe.services.task_logs.log_counts_for_tasks",
patch("grimoire.services.task_logs.log_counts_for_tasks",
AsyncMock(return_value={})):
yield
@@ -218,13 +218,13 @@ def _no_lesson_rule_links(request):
if request.node.get_closest_marker("integration"):
yield
return
with patch("scribe.services.lesson_rules.attach_lesson_rules", AsyncMock()), \
patch("scribe.services.lesson_rules.attach_rule_lessons", AsyncMock()), \
patch("scribe.services.lesson_rules.rule_candidates", AsyncMock(return_value=[])), \
patch("scribe.services.lesson_rules.co_surfaced", AsyncMock(return_value="")), \
patch("scribe.services.plugin_context._rules_via_lessons",
with patch("grimoire.services.lesson_rules.attach_lesson_rules", AsyncMock()), \
patch("grimoire.services.lesson_rules.attach_rule_lessons", AsyncMock()), \
patch("grimoire.services.lesson_rules.rule_candidates", AsyncMock(return_value=[])), \
patch("grimoire.services.lesson_rules.co_surfaced", AsyncMock(return_value="")), \
patch("grimoire.services.plugin_context._rules_via_lessons",
AsyncMock(return_value=([], []))), \
patch("scribe.services.lesson_rules.convergence_for", AsyncMock(return_value=None)):
patch("grimoire.services.lesson_rules.convergence_for", AsyncMock(return_value=None)):
yield
@@ -245,10 +245,10 @@ def _no_family_triggers(request):
if request.node.get_closest_marker("integration"):
yield
return
with patch("scribe.services.family.citation_trigger", AsyncMock(return_value=None)), \
patch("scribe.services.family.repeat_trigger", AsyncMock(return_value=None)), \
patch("scribe.services.family.milestone_trigger", AsyncMock(return_value=None)), \
patch("scribe.services.family.milestone_is_open", AsyncMock(return_value=False)):
with patch("grimoire.services.family.citation_trigger", AsyncMock(return_value=None)), \
patch("grimoire.services.family.repeat_trigger", AsyncMock(return_value=None)), \
patch("grimoire.services.family.milestone_trigger", AsyncMock(return_value=None)), \
patch("grimoire.services.family.milestone_is_open", AsyncMock(return_value=False)):
yield
@@ -266,9 +266,9 @@ def _no_moment_delivery(request):
if request.node.get_closest_marker("integration"):
yield
return
from scribe.services.retrieval_pipeline import RuleResult
from grimoire.services.retrieval_pipeline import RuleResult
with patch("scribe.services.moment_delivery.deliver_for_act",
with patch("grimoire.services.moment_delivery.deliver_for_act",
AsyncMock(return_value=([], RuleResult()))):
yield
@@ -289,7 +289,7 @@ def _no_rule_arm():
channel in tests/test_write_path_trigger.py. A test that wants the arm
live can re-patch it.
"""
with patch("scribe.services.plugin_context.semantic_search_rules",
with patch("grimoire.services.plugin_context.semantic_search_rules",
AsyncMock(return_value=[])):
yield
@@ -306,7 +306,7 @@ def _no_rule_overlap():
tests/test_rule_overlap_gate.py, which binds the real function at import
time — before this patch runs — and stubs the search beneath it instead.
"""
with patch("scribe.services.dedup.find_overlapping_rules",
with patch("grimoire.services.dedup.find_overlapping_rules",
AsyncMock(return_value=[])):
yield
@@ -323,10 +323,10 @@ def _default_gate_bars():
read itself is tested in tests/test_gate_settings.py, which binds the real
function at import time, before this patch runs.
"""
from scribe.services.dedup import GATE_DEFAULT_THRESHOLDS
from grimoire.services.dedup import GATE_DEFAULT_THRESHOLDS
async def _default(user_id, key, lo):
return GATE_DEFAULT_THRESHOLDS[key]
with patch("scribe.services.dedup._gate_setting", AsyncMock(side_effect=_default)):
with patch("grimoire.services.dedup._gate_setting", AsyncMock(side_effect=_default)):
yield