rename(547): the repo is FabledGrimoire, and no stray "scribe" is left
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 1m1s
CI & Build / Python tests (push) Successful in 2m2s
CI & Build / Build & push image (push) Successful in 28s

The forge repo is now bvandeusen/FabledGrimoire. The plugin marketplace default,
the install commands in the docs and plugin README, the CI requirements title and
the example repo URLs in comments and tests follow it. docs/development.md now
names the image the build actually publishes, fabledgrimoire.

Two test names still said "scribes_own"; they now say "grimoires_own".

What still says scribe does so on purpose:
- the backup reader's accepted archive names and prefix;
- the label every stored secret is sealed under;
- the notes on keeping an old install's database names;
- the old migrations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 13:29:00 -04:00
co-authored by Claude Opus 5.5
parent e45e4cd063
commit a07547447f
15 changed files with 30 additions and 30 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
# CI Requirements — FabledScribe
# CI Requirements — FabledGrimoire
> Spec lives in [`docs/process.md`](https://git.fabledsword.com/bvandeusen/CI-runner/src/branch/main/docs/process.md)
> in the CI-Runner repo.
+1 -1
View File
@@ -53,7 +53,7 @@ without a read-shaped name are spelled out here. A `write`-scoped key may call e
The plugin connects the MCP server and adds the hooks and skills that put Grimoire's records in front of Claude as it works. Install it from this repo:
```
/plugin marketplace add https://git.fabledsword.com/bvandeusen/FabledScribe.git
/plugin marketplace add https://git.fabledsword.com/bvandeusen/FabledGrimoire.git
/plugin install grimoire@grimoire-plugin
```
+1 -1
View File
@@ -57,7 +57,7 @@ CI runs on Gitea Actions (workflow in `.forgejo/workflows/ci.yml`) in the shared
| `main` | `:latest`, `:<sha>` |
| a `v*` tag | `:latest`, `:<version>`, `:<sha>` |
`:<sha>` never moves, so it is the rollback point. Images publish to `git.fabledsword.com/bvandeusen/fabledscribe`, with a registry layer cache at `:cache`. The build needs the `REGISTRY_USER` and `REGISTRY_TOKEN` (a token with `write:packages`) repository secrets.
`:<sha>` never moves, so it is the rollback point. Images publish to `git.fabledsword.com/bvandeusen/fabledgrimoire`, with a registry layer cache at `:cache`. The build needs the `REGISTRY_USER` and `REGISTRY_TOKEN` (a token with `write:packages`) repository secrets.
Work happens on `dev`. A release merges `dev` into `main` with a plain merge commit; a dated `vYYYY.MM.DD.HHMM` tag, matching the version the image reports, can mark it.
+2 -2
View File
@@ -1,6 +1,6 @@
# Grimoire plugin for Claude Code
Turns a self-hosted [Grimoire](https://git.fabledsword.com/bvandeusen/FabledScribe)
Turns a self-hosted [Grimoire](https://git.fabledsword.com/bvandeusen/FabledGrimoire)
instance into a first-class Claude Code extension:
- **MCP tools** over your notes, tasks, projects, milestones, systems, and
@@ -41,7 +41,7 @@ The plugin ships inside the Grimoire app repo, so the marketplace *is* that repo
you always get the plugin version that matches your Grimoire instance.
```
/plugin marketplace add https://git.fabledsword.com/bvandeusen/FabledScribe.git
/plugin marketplace add https://git.fabledsword.com/bvandeusen/FabledGrimoire.git
/plugin install grimoire@grimoire-plugin
```
+1 -1
View File
@@ -1308,7 +1308,7 @@ grimoire_turn_fact() {
# Nothing Grimoire should be holding goes in it.
#
# {"instance": "https://grimoire.example.com", "project_id": 2,
# "project": "FabledScribe"}
# "project": "FabledGrimoire"}
#
# instance WHICH Grimoire the id belongs to, and the reason this file is
# not just a number. A project id means nothing on its own: id 2
+1 -1
View File
@@ -3,7 +3,7 @@
# requires-python = ">=3.10"
# dependencies = ["httpx>=0.27"]
# ///
"""Benchmark Ollama models for FabledScribe chat / curator workloads.
"""Benchmark Ollama models for FabledGrimoire chat / curator workloads.
Measures time-to-first-token, total wall time, and tokens/sec for each
(model, scenario) pair. Designed to be runnable against both local and
+1 -1
View File
@@ -45,7 +45,7 @@ class Config:
# per-instance via the Plugin marketplace setting.
PLUGIN_MARKETPLACE_URL: str = os.environ.get(
"PLUGIN_MARKETPLACE_URL",
"https://git.fabledsword.com/bvandeusen/FabledScribe.git",
"https://git.fabledsword.com/bvandeusen/FabledGrimoire.git",
)
# OIDC / OAuth2 SSO (e.g. Authentik)
+1 -1
View File
@@ -9,7 +9,7 @@ class RepoBinding(Base, TimestampMixin):
"""Maps a git repository to the Grimoire project it represents.
The SessionStart hook sends the working repo's normalized remote
(`repo_key`, e.g. ``git.fabledsword.com/bvandeusen/fabledscribe``) and the
(`repo_key`, e.g. ``git.fabledsword.com/bvandeusen/fabledgrimoire``) and the
server resolves it to a project — so the operator can work across many
repos/projects without pinning a project id in plugin config. The key is a
*stable identifier* (the git remote), not a dir-name heuristic.
+2 -2
View File
@@ -77,8 +77,8 @@ def location_matches(data: dict | None, parts: dict[str, str]) -> bool:
if all(
_path_matches((loc.get(key) or "").strip(), want)
if key == "path"
# Repo names are recorded free-form ("Grimoire" / "FabledScribe" /
# "fabledscribe") — case is never the distinguishing thing.
# Repo names are recorded free-form ("Grimoire" / "FabledGrimoire" /
# "fabledgrimoire") — case is never the distinguishing thing.
else (loc.get(key) or "").strip().lower() == want.lower()
if key == "repo"
else (loc.get(key) or "").strip() == want
+4 -4
View File
@@ -20,11 +20,11 @@ def normalize_repo_key(raw: str) -> str:
Collapses the equivalent clone URLs to one key, e.g.::
git@git.fabledsword.com:bvandeusen/FabledScribe.git
https://git.fabledsword.com/bvandeusen/fabledscribe.git
ssh://git@git.fabledsword.com:22/bvandeusen/fabledscribe
git@git.fabledsword.com:bvandeusen/FabledGrimoire.git
https://git.fabledsword.com/bvandeusen/fabledgrimoire.git
ssh://git@git.fabledsword.com:22/bvandeusen/fabledgrimoire
all normalize to ``git.fabledsword.com/bvandeusen/fabledscribe``.
all normalize to ``git.fabledsword.com/bvandeusen/fabledgrimoire``.
Returns "" for empty/garbage input so callers can treat it as "no repo".
"""
+1 -1
View File
@@ -65,7 +65,7 @@ def test_a_bare_integer_is_a_valid_marker(tmp_path):
def test_the_marker_is_found_from_a_subdirectory(tmp_path):
_marker(tmp_path, instance=INSTANCE, project_id=2, project="FabledScribe")
_marker(tmp_path, instance=INSTANCE, project_id=2, project="FabledGrimoire")
deep = tmp_path / "src" / "grimoire" / "services"
deep.mkdir(parents=True)
assert _sh(f'grimoire_scope_query "{deep}"', tmp_path) == "project_id=2"
+1 -1
View File
@@ -292,7 +292,7 @@ def test_the_hook_and_the_routes_agree_on_every_name():
assert name in route
def test_the_hook_leaves_scribes_own_tools_to_their_responses():
def test_the_hook_leaves_grimoires_own_tools_to_their_responses():
assert "mcp__*grimoire*__*) exit 0" in HOOK.read_text()
+1 -1
View File
@@ -75,7 +75,7 @@ def _acts(tmp: Path, sid: str = "s1") -> list[tuple[int, dict]]:
return rows
def test_every_call_is_written_to_the_acts_ledger_scribes_own_included(sandbox):
def test_every_call_is_written_to_the_acts_ledger_grimoires_own_included(sandbox):
tmp, env = sandbox
push = {"session_id": "s1", "tool_name": "Bash",
"tool_input": {"command": "git push origin dev"}}
+3 -3
View File
@@ -111,7 +111,7 @@ async def test_build_session_context_includes_project_when_scoped():
# design_system_id explicitly None: a bare MagicMock would hand back a truthy
# auto-attribute and send this through the design branch, which is the
# opposite of what this test is about.
project = MagicMock(id=2, title="FabledScribe", goal="ship it",
project = MagicMock(id=2, title="FabledGrimoire", goal="ship it",
design_system_id=None)
with patch("grimoire.services.plugin_context.projects_svc.get_project",
AsyncMock(return_value=project)), \
@@ -120,8 +120,8 @@ async def test_build_session_context_includes_project_when_scoped():
from grimoire.services.plugin_context import build_session_context
out = await build_session_context(user_id=7, project_id=2)
assert out["project"] == {"id": 2, "title": "FabledScribe"}
assert "## Active project: FabledScribe (id 2)" in out["context"]
assert out["project"] == {"id": 2, "title": "FabledGrimoire"}
assert "## Active project: FabledGrimoire (id 2)" in out["context"]
assert "Open todo tasks: 4" in out["context"]
# No design system on the project -> no design block at all. An install with
# none is the ordinary case, not a degraded one.
+9 -9
View File
@@ -6,18 +6,18 @@ from grimoire.services.repo_bindings import normalize_repo_key
@pytest.mark.parametrize(
"raw",
[
"git@git.fabledsword.com:bvandeusen/fabledscribe.git",
"git@git.fabledsword.com:bvandeusen/FabledScribe.git",
"https://git.fabledsword.com/bvandeusen/fabledscribe.git",
"https://git.fabledsword.com/bvandeusen/fabledscribe",
"http://git.fabledsword.com/bvandeusen/fabledscribe.git",
"ssh://git@git.fabledsword.com:22/bvandeusen/fabledscribe.git",
"ssh://git@git.fabledsword.com/bvandeusen/fabledscribe",
"https://user:token@git.fabledsword.com/bvandeusen/fabledscribe.git",
"git@git.fabledsword.com:bvandeusen/fabledgrimoire.git",
"git@git.fabledsword.com:bvandeusen/FabledGrimoire.git",
"https://git.fabledsword.com/bvandeusen/fabledgrimoire.git",
"https://git.fabledsword.com/bvandeusen/fabledgrimoire",
"http://git.fabledsword.com/bvandeusen/fabledgrimoire.git",
"ssh://git@git.fabledsword.com:22/bvandeusen/fabledgrimoire.git",
"ssh://git@git.fabledsword.com/bvandeusen/fabledgrimoire",
"https://user:token@git.fabledsword.com/bvandeusen/fabledgrimoire.git",
],
)
def test_normalize_collapses_equivalent_remotes(raw):
assert normalize_repo_key(raw) == "git.fabledsword.com/bvandeusen/fabledscribe"
assert normalize_repo_key(raw) == "git.fabledsword.com/bvandeusen/fabledgrimoire"
@pytest.mark.parametrize("raw", ["", " ", None])