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) > Spec lives in [`docs/process.md`](https://git.fabledsword.com/bvandeusen/CI-runner/src/branch/main/docs/process.md)
> in the CI-Runner repo. > 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: 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 /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>` | | `main` | `:latest`, `:<sha>` |
| a `v*` tag | `:latest`, `:<version>`, `:<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. 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 # 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: instance into a first-class Claude Code extension:
- **MCP tools** over your notes, tasks, projects, milestones, systems, and - **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. 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 /plugin install grimoire@grimoire-plugin
``` ```
+1 -1
View File
@@ -1308,7 +1308,7 @@ grimoire_turn_fact() {
# Nothing Grimoire should be holding goes in it. # Nothing Grimoire should be holding goes in it.
# #
# {"instance": "https://grimoire.example.com", "project_id": 2, # {"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 # 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 # 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" # requires-python = ">=3.10"
# dependencies = ["httpx>=0.27"] # 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 Measures time-to-first-token, total wall time, and tokens/sec for each
(model, scenario) pair. Designed to be runnable against both local and (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. # per-instance via the Plugin marketplace setting.
PLUGIN_MARKETPLACE_URL: str = os.environ.get( PLUGIN_MARKETPLACE_URL: str = os.environ.get(
"PLUGIN_MARKETPLACE_URL", "PLUGIN_MARKETPLACE_URL",
"https://git.fabledsword.com/bvandeusen/FabledScribe.git", "https://git.fabledsword.com/bvandeusen/FabledGrimoire.git",
) )
# OIDC / OAuth2 SSO (e.g. Authentik) # 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. """Maps a git repository to the Grimoire project it represents.
The SessionStart hook sends the working repo's normalized remote 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 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 repos/projects without pinning a project id in plugin config. The key is a
*stable identifier* (the git remote), not a dir-name heuristic. *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( if all(
_path_matches((loc.get(key) or "").strip(), want) _path_matches((loc.get(key) or "").strip(), want)
if key == "path" if key == "path"
# Repo names are recorded free-form ("Grimoire" / "FabledScribe" / # Repo names are recorded free-form ("Grimoire" / "FabledGrimoire" /
# "fabledscribe") — case is never the distinguishing thing. # "fabledgrimoire") — case is never the distinguishing thing.
else (loc.get(key) or "").strip().lower() == want.lower() else (loc.get(key) or "").strip().lower() == want.lower()
if key == "repo" if key == "repo"
else (loc.get(key) or "").strip() == want 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.:: Collapses the equivalent clone URLs to one key, e.g.::
git@git.fabledsword.com:bvandeusen/FabledScribe.git git@git.fabledsword.com:bvandeusen/FabledGrimoire.git
https://git.fabledsword.com/bvandeusen/fabledscribe.git https://git.fabledsword.com/bvandeusen/fabledgrimoire.git
ssh://git@git.fabledsword.com:22/bvandeusen/fabledscribe 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". 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): 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 = tmp_path / "src" / "grimoire" / "services"
deep.mkdir(parents=True) deep.mkdir(parents=True)
assert _sh(f'grimoire_scope_query "{deep}"', tmp_path) == "project_id=2" 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 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() 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 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 tmp, env = sandbox
push = {"session_id": "s1", "tool_name": "Bash", push = {"session_id": "s1", "tool_name": "Bash",
"tool_input": {"command": "git push origin dev"}} "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 # 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 # auto-attribute and send this through the design branch, which is the
# opposite of what this test is about. # 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) design_system_id=None)
with patch("grimoire.services.plugin_context.projects_svc.get_project", with patch("grimoire.services.plugin_context.projects_svc.get_project",
AsyncMock(return_value=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 from grimoire.services.plugin_context import build_session_context
out = await build_session_context(user_id=7, project_id=2) out = await build_session_context(user_id=7, project_id=2)
assert out["project"] == {"id": 2, "title": "FabledScribe"} assert out["project"] == {"id": 2, "title": "FabledGrimoire"}
assert "## Active project: FabledScribe (id 2)" in out["context"] assert "## Active project: FabledGrimoire (id 2)" in out["context"]
assert "Open todo tasks: 4" 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 # No design system on the project -> no design block at all. An install with
# none is the ordinary case, not a degraded one. # 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( @pytest.mark.parametrize(
"raw", "raw",
[ [
"git@git.fabledsword.com:bvandeusen/fabledscribe.git", "git@git.fabledsword.com:bvandeusen/fabledgrimoire.git",
"git@git.fabledsword.com:bvandeusen/FabledScribe.git", "git@git.fabledsword.com:bvandeusen/FabledGrimoire.git",
"https://git.fabledsword.com/bvandeusen/fabledscribe.git", "https://git.fabledsword.com/bvandeusen/fabledgrimoire.git",
"https://git.fabledsword.com/bvandeusen/fabledscribe", "https://git.fabledsword.com/bvandeusen/fabledgrimoire",
"http://git.fabledsword.com/bvandeusen/fabledscribe.git", "http://git.fabledsword.com/bvandeusen/fabledgrimoire.git",
"ssh://git@git.fabledsword.com:22/bvandeusen/fabledscribe.git", "ssh://git@git.fabledsword.com:22/bvandeusen/fabledgrimoire.git",
"ssh://git@git.fabledsword.com/bvandeusen/fabledscribe", "ssh://git@git.fabledsword.com/bvandeusen/fabledgrimoire",
"https://user:token@git.fabledsword.com/bvandeusen/fabledscribe.git", "https://user:token@git.fabledsword.com/bvandeusen/fabledgrimoire.git",
], ],
) )
def test_normalize_collapses_equivalent_remotes(raw): 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]) @pytest.mark.parametrize("raw", ["", " ", None])