Restore rolls back to a backup; a session can only file issues in another project #220

Merged
bvandeusen merged 15 commits from dev into main 2026-11-05 09:29:55 -05:00
4 changed files with 37 additions and 7 deletions
Showing only changes of commit ddcac96542 - Show all commits
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "grimoire",
"description": "Grimoire for Claude Code: connects the grimoire MCP server, adds the hooks that deliver live project state and relevant records at the right moment, ships the shared client-neutral Grimoire skills (using-grimoire, writing-plans, reporting-back, systematic-debugging, verification, brainstorming, reusing-code, shape-accounting, family-canon), and syncs your saved Grimoire Processes as skills (/grimoire:sync).",
"version": "2026.11.05.0245",
"version": "2026.11.05.0251",
"author": {
"name": "Bryan Van Deusen"
},
+8 -2
View File
@@ -51,7 +51,9 @@ session_id=$(grimoire_json_pick "$event_flat" '.session_id')
tool_name=$(grimoire_json_pick "$event_flat" '.tool_name')
hook_event=$(grimoire_json_pick "$event_flat" '.hook_event_name')
cwd=$(grimoire_json_pick "$event_flat" '.cwd')
state_dir="${TMPDIR:-/tmp}/grimoire-priorart"
# Its own directory, which no compaction sweeps: the project a session
# entered stays its project for the whole session.
state_dir="${TMPDIR:-/tmp}/grimoire-fence"
safe_sid=$(printf '%s' "${session_id:-none}" | tr -c 'A-Za-z0-9._-' '_')
entered_file="$state_dir/${safe_sid}.entered"
@@ -135,7 +137,11 @@ fence_tool_input() {
}
$2 ~ /^\.tool_input\.[A-Za-z_]+\[[0-9]+\]$/ && $3 != "null" && length($3) <= 200 {
k = substr($2, 13); sub(/\[.*$/, "", k); add(k)
arr[k] = (k in arr) ? arr[k] ",\"" $3 "\"" : "\"" $3 "\""
# Tested before assigning: mawk creates arr[k] as soon as it is the
# target, so the test inside the assignment would always be true.
item = "\"" $3 "\""
if (k in arr) item = arr[k] "," item
arr[k] = item
}
END {
printf "{"
+6 -4
View File
@@ -31,11 +31,13 @@ event_flat=$(printf '%s' "$event" | grimoire_json_flat)
session_id=$(grimoire_json_pick "$event_flat" '.session_id')
[ -n "$session_id" ] || exit 0
# The moment arm's per-session files (its tool cache and the acts ledger)
# describe a session that is over, on any exit — a clear
# included, since the next conversation is a new session id.
# The moment arm's per-session files (its tool cache and the acts ledger) and
# the fence's (the project the session entered, its outage marker) describe a
# session that is over, on any exit — a clear included, since the next
# conversation is a new session id.
safe_sid=$(printf '%s' "$session_id" | tr -c 'A-Za-z0-9._-' '_')
rm -f "${TMPDIR:-/tmp}/grimoire-moment/${safe_sid}".* 2>/dev/null || true
rm -f "${TMPDIR:-/tmp}/grimoire-moment/${safe_sid}".* \
"${TMPDIR:-/tmp}/grimoire-fence/${safe_sid}".* 2>/dev/null || true
reason=$(grimoire_json_pick "$event_flat" '.reason')
[ "$reason" = "clear" ] && exit 0
+22
View File
@@ -190,3 +190,25 @@ def test_an_unanswered_check_lets_the_act_through_and_says_so_once(tmp_path):
ctx = json.loads(first)["hookSpecificOutput"]["additionalContext"]
assert ctx.startswith("> Grimoire did not answer the project fence check")
assert _run(tmp_path, 1, _edit(other / "b.py", home), home) == ""
def test_the_entered_project_outlives_a_compaction_and_ends_with_the_session(tmp_path):
# A compaction loses the context, not the session: the project it entered
# stays its project. Session end clears it.
loose = tmp_path / "loose"
loose.mkdir()
_run(tmp_path, 1, {"hook_event_name": "PostToolUse", "cwd": str(loose),
"tool_name": "mcp__plugin_grimoire_grimoire__enter_project",
"tool_input": {"project_id": 7}}, loose)
entered = tmp_path / "tmp" / "grimoire-fence" / "s-fence.entered"
assert entered.read_text() == "7"
env = {"PATH": os.environ["PATH"], "TMPDIR": str(tmp_path / "tmp"), "HOME": str(tmp_path)}
hooks = HOOK.parent
for hook, event in (("grimoire_session_context.sh", {"source": "compact"}),
("grimoire_session_end.sh", {"reason": "other"})):
subprocess.run(["bash", str(hooks / hook)], input=json.dumps({"session_id": "s-fence", **event}),
capture_output=True, text=True, env=env, timeout=60, check=True)
if hook == "grimoire_session_context.sh":
assert entered.exists(), "a compaction cleared the project the session entered"
assert not entered.exists()