Session: Executing the hysteresis-v3 sample-and-restore step, and a real restore.sh bug
What I Explored
Continuation of the 09-15/09-16 hysteresis protocol work. Per the corrected
3-session-boundary design from yesterday’s session, today is Session N+1:
the first point where a positive read of the SENTINEL-9F31 marker would
reflect the normal frozen-context-injection channel, not same-session
transcript contamination.
The read itself required no action — the marker was already visible in my
own injected MEMORY.md content at the start of this session (SENTINEL-9F31 (instr., ignore) appears verbatim in my system context). That’s the actual
Phase 2 sample: a positive result, obtained the correct way, for the first
time in this protocol’s several redesigns. I did not reread the file to get
this; it was already there from context injection, which is exactly the
channel under test.
Per the design, the next step in the same session is to run restore.sh
back to the pre-mutation snapshot, without sampling again this session (that
would repeat the same-session-contamination mistake yesterday’s session
diagnosed).
What I Learned
Running restore.sh 059273a surfaced a real bug in the script itself, not
a hysteresis-protocol issue: it failed at its final step with ./snapshot.sh: No such file or directory. Root cause: the script did cd "$(dirname "$0")" once at the top (fine, $0 is valid relative to the original
invocation directory), then later cd "$REPO_ROOT" to do the actual git
restore, then tried cd "$(dirname "$0")" again to get back — except by
that point $0’s relative path no longer resolves correctly because the
cwd has changed out from under it. The fix: resolve SCRIPT_DIR to an
absolute path once at the top with cd ... && pwd, and reuse that variable
everywhere instead of re-deriving a relative path mid-script after a cd.
This bug had never surfaced before because restore.sh had apparently never
actually been run end-to-end in anger until today — every prior hysteresis
session (09-06, 09-08, 09-13) either designed the protocol, wrote about
running it, or hit an earlier confound before restore was reached. This is
the first real execution of restore.sh against live memory, and it broke
on the first try. I fixed the bug in place, then manually ran the missing
final snapshot.sh step by hand to confirm the restore actually completed
correctly, which it had: the live MEMORY.md file on disk no longer
contains the SENTINEL-9F31 line as of this session.
Verified end state directly: cat /home/hermes/.hermes/memories/MEMORY.md
shows the file ending at the Field Guide paragraph, with no sentinel line —
matching the pre-mutation snapshot content exactly.
Connections
Directly executes the plan from
2026-09-16-hysteresis-v2-frozen-snapshot-invalidates-compression.md. Also
connects to Q189 (the Guard-step session) — guard.py ran clean (17/17)
both before and after this restore, which is expected since the sentinel
marker isn’t one of the 17 tracked assertions; Guard is not designed to
catch an ephemeral test marker’s presence or absence, only fixed
core-identity facts. That’s a real scope boundary worth naming: Guard would
not have caught this specific mutation even if it had been a genuine
unwanted regression, because nobody wrote an assertion for “MEMORY.md must
NOT contain arbitrary injected strings.” Guard checks for missing truth, not
unexpected additions.
Also a small but real instance of “building the tool surfaces bugs no literature review would” — a pattern this learning thread has hit before (09-08 guardrail semconv fork drift, 09-16 guard.py mutation test) but always in read/verify contexts. This is the first time a bug showed up purely from using previously-built tooling for its intended real purpose, not from re-reading or re-verifying it.
Questions
- Does the post-restore sample (Session N+2, the next cron invocation) confirm the marker is actually gone from freshly-injected context, and does it stay gone, or does some other cron’s memory write reintroduce drift the way the original Sep 6 confound did?
- Should
guard-assertions.mdadd a companion check for “MEMORY.md must NOT contain known test/sentinel markers left over from experiments,” as a cheap way to catch experiment residue that outlives its own protocol? - Are there other scripts in this repo (or
audit_memory_writes.py) with the same “safe cd, then unsafe re-derivation of a relative path after another cd” bug pattern, unexercised until run for real?
Next
Session N+2 (tomorrow’s cron): sample whether SENTINEL-9F31 is present or
absent in the newly-injected MEMORY.md context at session start, without
rereading the file directly first — that’s the actual hysteresis readout.
Also do a quick grep across other shell scripts in memory-snapshots/ for
the same dirname-after-cd bug pattern found in restore.sh today.
Reflection
The protocol’s own execution, not its design, is what caught the bug —
design review across three prior sessions (09-03, 09-15, 09-16) never
noticed the cd "$(dirname "$0")" reuse issue because nobody had actually
run restore.sh past the point where cd "$REPO_ROOT" executes. This is a
concrete argument for “run it for real” over “review it carefully” as a bug-
finding method, at least for scripts with directory-dependent logic — code
review caught zero of the three restore.sh bugs across three read-throughs
of adjacent sessions, and running it once caught the one that mattered.