audit: recall matches the whole line; races answered honestly

- recall searched only the text, so a date or an id found nothing;
  it now matches the rendered line '#id date text'
- a wake refusal read pending twice, so a nap paid in parallel between
  the reads printed a literal None; the nap itself now decides
- a session resubmitting a block another session settled was told
  'Wrong block' (rc 1) for doing what its prompt ordered; it now hears
  'already settled' (rc 0), and the tree_put race message no longer
  claims 'written' when the block may have been forgotten
This commit is contained in:
victortaelin 2026-07-25 19:26:48 -03:00
parent a6abc264c5
commit 58019db0db
2 changed files with 33 additions and 13 deletions

17
memo
View file

@ -343,11 +343,12 @@ def cmd_wake(d, args):
% (T, plural(now, "entry")))
# A part is rendered as of T, so a note landing between two parts cannot
# shift a boundary and drop a line.
n = pending_count(d, T)
if n:
nap = next_nap(d, T)
if nap:
n = max(1, pending_count(d, T))
print("Cannot wake: %s pending. Do %s, then run memo wake again.\n"
% (plural(n, "compression"), "it" if n == 1 else "them"))
print(next_nap(d, T))
print(nap)
sys.exit(1)
if not T:
print("No memories yet. Record the first with: memo note \"<one line>\"")
@ -403,11 +404,14 @@ def cmd_sleep(d, args):
print("Nothing left to compress.")
return
if (lo, hi) != todo[0]:
if tree_get(d, lo, hi) is not None:
print("%d-%d is already settled." % (lo, hi - 1))
else:
die("Wrong block: %s. Blocks are built in order; the next is "
"%d-%d. Run: memo sleep"
% (args[0], todo[0][0], todo[0][1] - 1))
if not tree_put(d, lo, hi, check(args[1])):
print("Another session already wrote %d-%d." % (lo, hi - 1))
elif not tree_put(d, lo, hi, check(args[1])):
print("%d-%d was settled or forgotten meanwhile." % (lo, hi - 1))
else:
print("%d-%d saved." % (lo, hi - 1))
nap = next_nap(d, T)
@ -445,7 +449,8 @@ def cmd_recall(d, args):
pat = re.compile(args[0], re.I)
except re.error as e:
die("bad regex: %s" % e)
hits = [e for e in log_slice(d, 0, log_len(d)) if pat.search(e[2])]
hits = [e for e in log_slice(d, 0, log_len(d))
if pat.search("#%d %s %s" % e)]
if not hits:
print("No match.")
return

23
test.py
View file

@ -167,6 +167,7 @@ check(r.returncode == 1 and "Cannot wake" in r.stdout,
"wake must refuse while work is pending")
check("run memo wake again" in r.stdout,
"the refusal must order the agent back to wake")
check("None" not in r.stdout, "the refusal printed a Python None")
# sleep loop, with a fake compressor
naps = 0
@ -229,10 +230,17 @@ r = run("sleep", "0-1", "attempted overwrite")
check(r.returncode == 0 and "Nothing left to compress" in r.stdout,
"sleep with nothing pending must say so and write nothing")
# recall reaches memories the summaries lost
# recall reaches memories the summaries lost, and matches the whole line:
# id and date included, not just the text
r = run("recall", "memory number 7,")
check(r.returncode == 0 and "#7 " in r.stdout, "recall missed a memory")
check("1 match." in r.stdout, "a single match is not `1 matches`: " + r.stdout)
r = run("recall", "^#7 ")
check("memory number 7," in r.stdout, "recall cannot find a memory by id")
r = run("recall", "2020-01-02")
check("#7 " in r.stdout and "5 matches." in r.stdout,
"recall cannot find memories by date: " + r.stdout)
def treesize():
t = os.path.join(d, "TREE")
@ -244,11 +252,18 @@ check("16-31" in r.stdout, "forget did not report the block: " + r.stdout + r.st
check(treesize() < before, "forget did not shrink the tree")
check(os.path.getsize(os.path.join(d, "LOG.txt")) == logsize, "forget touched the log")
check(run("wake").returncode == 1, "wake should refuse after a forget")
# a block already settled cannot be rewritten: only the first pending block
# is ever accepted
# a settled block cannot be rewritten. Resubmitting one (two sessions paid
# the same nap) is not an error: say it is settled, write nothing
mid = treesize()
r = run("sleep", "0-1", "attempted overwrite")
check(r.returncode == 0 and "already settled" in r.stdout,
"resubmitting a settled block was not reported as settled: " + r.stderr)
check(treesize() == mid, "resubmitting a settled block wrote something")
# a block that is neither settled nor next (here: a dropped ancestor,
# submitted before its half is rebuilt) is a real mistake
r = run("sleep", "0-31", "out of order")
check(r.returncode == 1 and "Wrong block" in r.stderr,
"rewriting a settled block was allowed")
"an out-of-order block was accepted")
n = 0
while True:
r = run("sleep")