init is idempotent by test; put it in the command table
This commit is contained in:
parent
bd2a799500
commit
2623c95294
2 changed files with 42 additions and 11 deletions
27
memo
27
memo
|
|
@ -366,13 +366,13 @@ from another agent, you are that subagent: skip this section.
|
|||
"""
|
||||
|
||||
|
||||
def cmd_init(args):
|
||||
def cmd_init(d, args):
|
||||
"""The one command that may create the memory directory, and the whole
|
||||
setup: make the store, write the size knobs, print the block the user
|
||||
pastes into their agent's instruction file."""
|
||||
pastes into their agent's instruction file. Re-running it is safe: it
|
||||
only ever creates what is missing, and never rewrites what is there."""
|
||||
if args:
|
||||
die("usage: memo init")
|
||||
d = memory_dir()
|
||||
fresh = not os.path.isdir(d)
|
||||
os.makedirs(os.path.join(d, "TREE"), exist_ok=True)
|
||||
open(log_path(d), "a").close()
|
||||
|
|
@ -595,24 +595,29 @@ def cmd_import(d, args):
|
|||
print("%s pending. Run: memo sleep" % plural(n, "compression"))
|
||||
|
||||
|
||||
COMMANDS = {"wake": cmd_wake, "note": cmd_note, "sleep": cmd_sleep,
|
||||
"recall": cmd_recall, "forget": cmd_forget, "import": cmd_import}
|
||||
COMMANDS = {"init": cmd_init, "wake": cmd_wake, "note": cmd_note,
|
||||
"sleep": cmd_sleep, "recall": cmd_recall, "forget": cmd_forget,
|
||||
"import": cmd_import}
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__.strip())
|
||||
sys.exit(0)
|
||||
if sys.argv[1] == "init":
|
||||
cmd_init(sys.argv[2:])
|
||||
return
|
||||
if sys.argv[1] not in COMMANDS:
|
||||
print("No such command: %s\n" % sys.argv[1], file=sys.stderr)
|
||||
cmd = sys.argv[1]
|
||||
if cmd not in COMMANDS:
|
||||
print("No such command: %s\n" % cmd, file=sys.stderr)
|
||||
print(__doc__.strip(), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
# `init` is the only command that may run without an existing memory:
|
||||
# it is the one that creates it. Every other command refuses, so a typo
|
||||
# in MEMORY_DIR is an error instead of a second, empty identity.
|
||||
if cmd == "init":
|
||||
cmd_init(memory_dir(), sys.argv[2:])
|
||||
return
|
||||
d = store()
|
||||
config(d)
|
||||
COMMANDS[sys.argv[1]](d, sys.argv[2:])
|
||||
COMMANDS[cmd](d, sys.argv[2:])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
26
test.py
26
test.py
|
|
@ -385,6 +385,32 @@ r = run("wake", store=d2)
|
|||
check(r.stdout.rstrip().endswith("You are awake."),
|
||||
"a one-part wake never says `You are awake.`:\n" + r.stdout)
|
||||
|
||||
# re-running init on a lived-in store must not touch one byte of it: the
|
||||
# whole setup is idempotent, so it is safe on every provision. `init` only
|
||||
# ever makedirs(exist_ok), opens LOG.txt for APPEND, and writes `config`
|
||||
# when absent -- never truncating, never overwriting a tuned config.
|
||||
def fingerprint(path):
|
||||
out = {}
|
||||
for root, _, files in os.walk(path):
|
||||
for f in files:
|
||||
if f == ".lock":
|
||||
continue
|
||||
p = os.path.join(root, f)
|
||||
out[os.path.relpath(p, path)] = open(p, "rb").read()
|
||||
return out
|
||||
|
||||
|
||||
with open(os.path.join(d, "config"), "a") as f:
|
||||
f.write("WAKE_LINES=120\n") # a size the user tuned by hand
|
||||
before = fingerprint(d)
|
||||
check(len(before) > 3 and before["LOG.txt"], "the store under test is empty")
|
||||
for _ in range(3):
|
||||
r = run("init")
|
||||
check(r.returncode == 0 and "Found" in r.stdout, "init on a live store failed")
|
||||
check(fingerprint(d) == before, "init modified an existing memory")
|
||||
r = run("wake")
|
||||
check(r.stdout.rstrip().endswith("You are awake."), "wake broke after re-init")
|
||||
|
||||
shutil.rmtree(d2)
|
||||
shutil.rmtree(d)
|
||||
print("\n%d passed, %d failed" % (ok, fail))
|
||||
|
|
|
|||
Loading…
Reference in a new issue