Snippet locations are keyed by path too, so a snippet recorded under a
moved directory reads `missing` at its next check. The tool is now
move_path(project_id, from_path, to_path). It moves the ledger rows as
before, and re-paths the project's snippet locations under from_path
through update_snippet, so each body and its indexed mirror stay one
record. A snippet the caller can only read is listed in
snippets_read_only. The tool refuses only when nothing at all is recorded
under from_path. The path test is the one snippets already uses
(_path_touches), and the plugin version is minted for the skill edit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ledger rows are keyed by path, so after a directory moves the next sync
marks every judged row vanished and lists the same shapes again, unjudged.
move_shapes(project_id, from_path, to_path) re-keys the rows under one path
to the other, in every repo of the project, keeping each row's judgment,
uses edges and history and adding a `moved` event that records the path it
left. A row already at the target is replaced when nobody judged it; a
judged one stands, and the old row is reported in `kept`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A binding and every row of its project's code-shape ledger are keyed by the
repo's remote. A rename on the forge changes the remote, so binding the new name
started an empty ledger beside the old one and left every judgment behind.
bind_repo now takes moved_from, the old remote. Its binding and the project's
ledger rows move to the new key in one transaction, and the reply says how many
shapes moved. The move is refused when:
- the old name isn't bound to that project;
- the new name is already bound;
- the project already has a ledger under the new name.
Each refusal leaves both names exactly as they were.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
theme.css loaded Fraunces, Inter and JetBrains Mono from Google Fonts. That sent
every visitor's address to Google on every page load, and an instance with no
internet drew in fallback type. All three families are now vendored under
frontend/src/assets/fonts with every subset Google publishes for them, plus their
OFL licences. The Content-Security-Policy no longer lists fonts.googleapis.com or
fonts.gstatic.com.
A new guard test fails if any stylesheet, page or component links a remote asset
or preconnects to one, or if the CSP names another host.
The theme.css header now describes the sheet as it is instead of how it used to
read.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Step 3 of the Scribe -> Grimoire rename.
- Plugin `grimoire` in marketplace `grimoire-plugin`, MCP server key
`grimoire`, so tools reach a session as mcp__plugin_grimoire_grimoire__*.
- hooks/scribe_* -> hooks/grimoire_*, their shell functions and the TMPDIR
ledgers with them; SCRIBE_URL / SCRIBE_TOKEN / SCRIBE_HOOK_DIR /
SCRIBE_LEDGER_DIRS -> GRIMOIRE_*; the project marker is `.grimoire`.
- skills/using-scribe -> using-grimoire, /grimoire:sync, stored processes
arrive as grimoire-proc-<slug>; moment_actions names them the same way.
- Migration 0124 rewrites stored skill names (rule_moments,
rule_moment_judgments, moment_mappings) from scribe:/scribe-proc- to
grimoire:/grimoire-proc-, so a rule mounted on a skill keeps firing.
- Settings' install commands and MCP-only defaults, the docs and the
tests follow.
- scripts/check_plugin.py drops the smoke event for a report-check hook
that has no file in plugin/hooks.
Kept on purpose: the secret-sealing label, and the names that let backups
written as Scribe restore and be counted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>