rename(547): the plugin is grimoire — manifest, MCP key, hooks, env, skills
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m2s
CI & Build / Python tests (push) Failing after 1m26s
CI & Build / Build & push image (push) Skipped
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m2s
CI & Build / Python tests (push) Failing after 1m26s
CI & Build / Build & push image (push) Skipped
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>
This commit is contained in:
@@ -42,9 +42,9 @@ from tests.helpers import need_tools
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
HOOKS = ROOT / "plugin" / "hooks"
|
||||
DEFS = HOOKS / "scribe_defs.sh"
|
||||
PRECOMPACT = HOOKS / "scribe_precompact_preserve.sh"
|
||||
RECORDER = HOOKS / "scribe_record_outcome.sh"
|
||||
DEFS = HOOKS / "grimoire_defs.sh"
|
||||
PRECOMPACT = HOOKS / "grimoire_precompact_preserve.sh"
|
||||
RECORDER = HOOKS / "grimoire_record_outcome.sh"
|
||||
HOOKS_JSON = HOOKS / "hooks.json"
|
||||
|
||||
|
||||
@@ -61,34 +61,34 @@ def sh(script: str) -> str:
|
||||
def ledgers(tmp_path, *, named=(), opened=(), acted=(), held=()) -> Path:
|
||||
"""The four ledgers, written THROUGH THE REAL WRITERS.
|
||||
|
||||
Not hand-rolled files: `scribe_rules_append` is what creates the evidence
|
||||
Not hand-rolled files: `grimoire_rules_append` is what creates the evidence
|
||||
twin these read from, so a fixture that wrote the bytes itself would keep
|
||||
passing if the twin stopped being written.
|
||||
"""
|
||||
d = tmp_path / "scribe-priorart"
|
||||
d = tmp_path / "grimoire-priorart"
|
||||
d.mkdir(parents=True, exist_ok=True)
|
||||
script = []
|
||||
for name, ids in (("rules", named), ("opened", opened), ("acted", acted)):
|
||||
if ids:
|
||||
body = "".join(f"{i}\n" for i in ids)
|
||||
script.append(
|
||||
f"printf '%s' '{body}' | scribe_rules_append \"{d}/s.{name}.ids\""
|
||||
f"printf '%s' '{body}' | grimoire_rules_append \"{d}/s.{name}.ids\""
|
||||
)
|
||||
for i in held:
|
||||
script.append(f'scribe_checkpoint_allowed "{d}/s.checkpoint.ids" {i} >/dev/null')
|
||||
script.append(f'grimoire_checkpoint_allowed "{d}/s.checkpoint.ids" {i} >/dev/null')
|
||||
if script:
|
||||
sh("\n".join(script))
|
||||
return d
|
||||
|
||||
|
||||
def readout(d: Path) -> str:
|
||||
return sh(f'scribe_slippage_lines "{d}" "s"')
|
||||
return sh(f'grimoire_slippage_lines "{d}" "s"')
|
||||
|
||||
|
||||
# ── The arithmetic ────────────────────────────────────────────────────────
|
||||
|
||||
def minus(a: str, b: str) -> str:
|
||||
return sh(f'scribe_ids_minus "{a}" "{b}"').strip()
|
||||
return sh(f'grimoire_ids_minus "{a}" "{b}"').strip()
|
||||
|
||||
|
||||
def test_the_difference_keeps_order_and_drops_members():
|
||||
@@ -159,7 +159,7 @@ def test_a_session_that_resolved_everything_makes_no_accusation(tmp_path):
|
||||
def test_a_session_no_rule_touched_says_nothing_at_all(tmp_path):
|
||||
"""A fresh install must not be told something is
|
||||
wrong when the truth is that nothing has happened yet."""
|
||||
d = tmp_path / "scribe-priorart"
|
||||
d = tmp_path / "grimoire-priorart"
|
||||
d.mkdir(parents=True)
|
||||
assert readout(d).strip() == ""
|
||||
|
||||
@@ -216,13 +216,13 @@ def test_declaring_an_outcome_is_recorded(tmp_path):
|
||||
tmp_path,
|
||||
)
|
||||
assert r.returncode == 0
|
||||
assert (tmp_path / "scribe-priorart" / "s.acted.ids").read_text().startswith("156\t")
|
||||
assert (tmp_path / "grimoire-priorart" / "s.acted.ids").read_text().startswith("156\t")
|
||||
|
||||
|
||||
def test_the_entry_is_stamped_like_its_siblings(tmp_path):
|
||||
"""One reader ages all three ledgers, so all three carry a timestamp."""
|
||||
run_recorder({"session_id": "s", "tool_input": {"rule_id": 9}}, tmp_path)
|
||||
line = (tmp_path / "scribe-priorart" / "s.acted.ids").read_text().strip()
|
||||
line = (tmp_path / "grimoire-priorart" / "s.acted.ids").read_text().strip()
|
||||
assert "\t" in line and line.split("\t")[1].isdigit()
|
||||
|
||||
|
||||
@@ -235,7 +235,7 @@ def test_an_unusable_event_records_nothing_and_still_exits_zero(event, tmp_path)
|
||||
hook error."""
|
||||
r = run_recorder(event, tmp_path)
|
||||
assert r.returncode == 0
|
||||
assert not (tmp_path / "scribe-priorart" / "s.acted.ids").exists()
|
||||
assert not (tmp_path / "grimoire-priorart" / "s.acted.ids").exists()
|
||||
|
||||
|
||||
def test_the_recorder_is_registered_on_the_rule_outcome_tool():
|
||||
@@ -245,7 +245,7 @@ def test_the_recorder_is_registered_on_the_rule_outcome_tool():
|
||||
entries = cfg["hooks"]["PostToolUse"]
|
||||
assert any(
|
||||
"rule_outcome" in e.get("matcher", "")
|
||||
and any("scribe_record_outcome.sh" in h["command"] for h in e["hooks"])
|
||||
and any("grimoire_record_outcome.sh" in h["command"] for h in e["hooks"])
|
||||
for e in entries
|
||||
)
|
||||
|
||||
@@ -268,7 +268,7 @@ def test_the_readout_is_unchanged_by_the_sweep(tmp_path):
|
||||
reported all of it."""
|
||||
d = ledgers(tmp_path, named=[9, 34, 156], opened=[9, 156], acted=[156], held=[156])
|
||||
before = readout(d)
|
||||
sh(f'TMPDIR="{tmp_path}" scribe_clear_session_ledgers s')
|
||||
sh(f'TMPDIR="{tmp_path}" grimoire_clear_session_ledgers s')
|
||||
assert readout(d) == before
|
||||
assert "READ WITH NO OUTCOME RECORDED: 9" in readout(d)
|
||||
|
||||
@@ -279,7 +279,7 @@ def test_the_sweep_still_clears_what_the_context_no_longer_holds(tmp_path):
|
||||
the strength of the old ledger would be silent about a rule the context
|
||||
has lost."""
|
||||
d = ledgers(tmp_path, named=[9], opened=[9])
|
||||
sh(f'TMPDIR="{tmp_path}" scribe_clear_session_ledgers s')
|
||||
sh(f'TMPDIR="{tmp_path}" grimoire_clear_session_ledgers s')
|
||||
assert not (d / "s.rules.ids").exists()
|
||||
assert not (d / "s.opened.ids").exists()
|
||||
assert (d / "s.opened.keep.ids").exists()
|
||||
@@ -288,7 +288,7 @@ def test_the_sweep_still_clears_what_the_context_no_longer_holds(tmp_path):
|
||||
def test_the_evidence_twin_is_not_aged_out(tmp_path):
|
||||
"""An exclusion ledger ages by TTL, and should: a rule named two hours ago
|
||||
is not in this context. A rule OPENED two hours ago was still opened."""
|
||||
d = tmp_path / "scribe-priorart"
|
||||
d = tmp_path / "grimoire-priorart"
|
||||
d.mkdir(parents=True, exist_ok=True)
|
||||
stale = int(time.time()) - 99999
|
||||
(d / "s.opened.keep.ids").write_text(f"9\t{stale}\n")
|
||||
@@ -300,11 +300,11 @@ def test_every_ledger_written_through_the_appender_gets_a_twin(tmp_path):
|
||||
"""Derived in one place rather than listed at the call sites — a list is
|
||||
what broke this before, and the next ledger somebody adds should be born
|
||||
on the right side without anyone remembering to say so."""
|
||||
d = tmp_path / "scribe-priorart"
|
||||
d = tmp_path / "grimoire-priorart"
|
||||
d.mkdir(parents=True, exist_ok=True)
|
||||
sh(f"""printf '7\n' | scribe_rules_append "{d}/s.brandnew.ids" """)
|
||||
sh(f"""printf '7\n' | grimoire_rules_append "{d}/s.brandnew.ids" """)
|
||||
assert (d / "s.brandnew.keep.ids").read_text().startswith("7\t")
|
||||
sh(f'TMPDIR="{tmp_path}" scribe_clear_session_ledgers s')
|
||||
sh(f'TMPDIR="{tmp_path}" grimoire_clear_session_ledgers s')
|
||||
assert (d / "s.brandnew.keep.ids").exists()
|
||||
|
||||
|
||||
@@ -312,15 +312,15 @@ def test_a_checkpoint_budget_is_restored_by_a_compaction(tmp_path):
|
||||
"""The cap counts the SWEPT file on purpose. After a compaction this
|
||||
context has not read the rule, so the budget to stop an act on it is
|
||||
honestly fresh — while the record that a stop already happened stays."""
|
||||
d = tmp_path / "scribe-priorart"
|
||||
d = tmp_path / "grimoire-priorart"
|
||||
d.mkdir(parents=True, exist_ok=True)
|
||||
f = d / "s.checkpoint.ids"
|
||||
sh(f'scribe_checkpoint_allowed "{f}" 156 >/dev/null')
|
||||
sh(f'grimoire_checkpoint_allowed "{f}" 156 >/dev/null')
|
||||
r = subprocess.run(
|
||||
["bash", "-c", f'. "{DEFS}"\nscribe_checkpoint_allowed "{f}" 156'],
|
||||
capture_output=True, text=True, timeout=30,
|
||||
)
|
||||
assert r.returncode != 0, "a rule does not get to stop the same session twice"
|
||||
sh(f'TMPDIR="{tmp_path}" scribe_clear_session_ledgers s')
|
||||
sh(f'scribe_checkpoint_allowed "{f}" 156 >/dev/null')
|
||||
sh(f'TMPDIR="{tmp_path}" grimoire_clear_session_ledgers s')
|
||||
sh(f'grimoire_checkpoint_allowed "{f}" 156 >/dev/null')
|
||||
assert (d / "s.checkpoint.keep.ids").read_text().count("156") == 2
|
||||
|
||||
Reference in New Issue
Block a user