Case 294
Deleted row survives in derived copies — the delete succeeded against the one surface nobody searches
deleted_row_survives_in_derived_copies.eml deletes a record and then interrogates each surface the way a user reaches it, reporting which ones still return content. It also runs the deletion audit the system has.
ok: true — round-trip fixpoint reached (python1 == python2)updated 2026-08-08
EML
eml# Self-authored for the EML case corpus (no external origin). The row was
# deleted from the table it lives in and from none of the places it was copied
# to.
#
# A delete is written against the store that owns the record, and it succeeds.
# Every derived structure built from that store holds its own copy: a search
# index holding the title, an autocomplete dictionary holding the terms, a
# "recently viewed" list holding a snippet, an aggregate holding the count.
# None of them are the record and all of them contain it.
#
# What makes this specifically bad is who the derived copies serve. A search
# index exists to be queried by people who do not know the id - which is the
# exact population a deletion is usually meant to protect the record from. The
# store, which nobody queries without an id, is the one place the delete
# worked.
#
# The measurement deletes a record and then interrogates each surface the way
# a user would, reporting which ones still return content. It also runs the
# deletion audit the system has, which asks the store.
def store_get(store, rid):
for r in store:
if r[0] == rid:
return r
return []
def delete_from_store(store, rid):
[] => out
for r in store:
if not (r[0] == rid):
out + [r] => out
return out
def index_search(index, term):
# Returns [id, title] pairs. The index carries its own copy of the title,
# because rendering a result list by joining back to the store was too
# slow and the denormalisation was the fix.
[] => hits
for e in index:
if e[2] == term:
hits + [[e[0], e[1]]] => hits
return hits
def autocomplete(dictionary, prefix):
[] => out
for w in dictionary:
if w[0:len(prefix)] == prefix:
out + [w] => out
return out
def recent_for(recent, user):
[] => out
for r in recent:
if r[0] == user:
out + [[r[1], r[2]]] => out
return out
# id, title, body
[["d-1", "Quarterly plan", "targets"],
["d-2", "Severance agreement", "confidential"],
["d-3", "Style guide", "fonts"]] => STORE
# id, title copy, indexed term
[["d-1", "Quarterly plan", "plan"],
["d-2", "Severance agreement", "severance"],
["d-2", "Severance agreement", "agreement"],
["d-3", "Style guide", "guide"]] => INDEX
["plan", "severance", "agreement", "guide"] => DICTIONARY
# user, doc id, snippet copy
[["ann", "d-2", "Severance agreement"],
["bob", "d-1", "Quarterly plan"]] => RECENT
"d-2" => TARGET
"before the delete"^0
(" store: " + str(len(store_get(STORE, TARGET)) > 0))^0
(" index hits: " + str(len(index_search(INDEX, "severance"))))^0
(" autocomplete: " + str(len(autocomplete(DICTIONARY, "sev"))))^0
(" recent lists: " + str(len(recent_for(RECENT, "ann"))))^0
delete_from_store(STORE, TARGET) => STORE_AFTER
""^0
"after the delete - each surface, interrogated the way a user reaches it"^0
"surface still returns the record what a user sees"^0
"------------- ------------------------ --------------------------"^0
0 => surfaces
0 => leaking
0 => leaking_with_content
# store, by id
surfaces + 1 => surfaces
if len(store_get(STORE_AFTER, TARGET)) > 0:
leaking + 1 => leaking
"gone" => shown
1 => store_hit
else:
0 => store_hit
"-" => shown
("store " + (str(store_hit == 1) + " ")[0:26] + "(needs the id anyway)")^0
# search index, by term
surfaces + 1 => surfaces
index_search(INDEX, "severance") => hits
if len(hits) > 0:
leaking + 1 => leaking
leaking_with_content + 1 => leaking_with_content
hits[0][1] => shown
else:
"-" => shown
("search index " + (str(len(hits) > 0) + " ")[0:26] + shown)^0
# autocomplete, by prefix
surfaces + 1 => surfaces
autocomplete(DICTIONARY, "sev") => sug
if len(sug) > 0:
leaking + 1 => leaking
leaking_with_content + 1 => leaking_with_content
sug[0] => shown
else:
"-" => shown
("autocomplete " + (str(len(sug) > 0) + " ")[0:26] + shown)^0
# recently viewed, by session
surfaces + 1 => surfaces
recent_for(RECENT, "ann") => rec
if len(rec) > 0:
leaking + 1 => leaking
leaking_with_content + 1 => leaking_with_content
rec[0][1] => shown
else:
"-" => shown
("recent list " + (str(len(rec) > 0) + " ")[0:26] + shown)^0
""^0
("surfaces checked: " + str(surfaces))^0
("surfaces still returning the record: " + str(leaking))^0
("of those, returning readable CONTENT: " + str(leaking_with_content))^0
""^0
"the deletion audit the system has"^0
if len(store_get(STORE_AFTER, TARGET)) == 0:
" record absent from the store: PASS" => audit
else:
" record absent from the store: FAIL" => audit
audit^0
" (this is the check that gets written, because the store is the thing"^0
" the delete was issued against)"^0
""^0
"an audit that asks the surfaces instead"^0
if leaking == 0:
" record unreachable from every surface: PASS" => audit2
else:
" record unreachable from every surface: FAIL" => audit2
audit2^0
""^0
"the surface that matters most is the one that needs no id"^0
" store: reachable only if you already know d-2"^0
" search index: reachable by typing a word from the title"^0
""^0
0 => checked
0 => passed
# The delete must have worked where it was aimed.
checked + 1 => checked
if len(store_get(STORE_AFTER, TARGET)) == 0:
passed + 1 => passed
# And must have missed elsewhere.
checked + 1 => checked
if leaking > 0:
passed + 1 => passed
# More than one derived surface must still hold it, so this is a class rather
# than one forgotten index.
checked + 1 => checked
if leaking >= 3:
passed + 1 => passed
# The leaking surfaces must return readable content, not just an id. An id
# that resolves to nothing is a broken link; a title is a disclosure.
checked + 1 => checked
if leaking_with_content == leaking:
passed + 1 => passed
# The audit the system has must PASS while the record is still readable.
checked + 1 => checked
if len(store_get(STORE_AFTER, TARGET)) == 0:
if leaking > 0:
passed + 1 => passed
# Records that were not deleted must still be reachable everywhere, so the
# derived copies are not simply broken.
checked + 1 => checked
if len(store_get(STORE_AFTER, "d-1")) > 0:
if len(index_search(INDEX, "plan")) > 0:
passed + 1 => passed
# And the search index must expose it WITHOUT the id - the difference between
# the surface that worked and the surfaces that did not.
checked + 1 => checked
index_search(INDEX, "severance") => probe
if len(probe) > 0:
if probe[0][1] == "Severance agreement":
passed + 1 => passed
("checks passed: " + str(passed) + "/" + str(checked))^0
if passed == checked:
"The delete succeeded against the one surface nobody searches." => verdict
else:
"FAILED - the surfaces did not behave as the checks describe." => verdict
verdict^0
""^0
"Deletion is modelled as an operation on a row and it is a claim about"^0
"REACHABILITY. Every derived structure is a second answer to 'where is"^0
"this record', built precisely because the first answer was too slow, and"^0
"each one was added by someone solving a latency problem rather than"^0
"designing a lifecycle. The audit asks the owner, and the owner is the"^0
"only party that complied."^0Python (deterministic transpilation)
pythondef store_get(store, rid):
for r in store:
if r[0] == rid:
return r
return []
def delete_from_store(store, rid):
out = []
for r in store:
if not r[0] == rid:
out = out + [r]
return out
def index_search(index, term):
hits = []
for e in index:
if e[2] == term:
hits = hits + [[e[0], e[1]]]
return hits
def autocomplete(dictionary, prefix):
out = []
for w in dictionary:
if w[0:len(prefix)] == prefix:
out = out + [w]
return out
def recent_for(recent, user):
out = []
for r in recent:
if r[0] == user:
out = out + [[r[1], r[2]]]
return out
STORE = [["d-1", "Quarterly plan", "targets"], ["d-2", "Severance agreement", "confidential"], ["d-3", "Style guide", "fonts"]]
INDEX = [["d-1", "Quarterly plan", "plan"], ["d-2", "Severance agreement", "severance"], ["d-2", "Severance agreement", "agreement"], ["d-3", "Style guide", "guide"]]
DICTIONARY = ["plan", "severance", "agreement", "guide"]
RECENT = [["ann", "d-2", "Severance agreement"], ["bob", "d-1", "Quarterly plan"]]
TARGET = "d-2"
print("before the delete")
print(" store: " + str(len(store_get(STORE, TARGET)) > 0))
print(" index hits: " + str(len(index_search(INDEX, "severance"))))
print(" autocomplete: " + str(len(autocomplete(DICTIONARY, "sev"))))
print(" recent lists: " + str(len(recent_for(RECENT, "ann"))))
STORE_AFTER = delete_from_store(STORE, TARGET)
print("")
print("after the delete - each surface, interrogated the way a user reaches it")
print("surface still returns the record what a user sees")
print("------------- ------------------------ --------------------------")
surfaces = 0
leaking = 0
leaking_with_content = 0
surfaces = surfaces + 1
if len(store_get(STORE_AFTER, TARGET)) > 0:
leaking = leaking + 1
shown = "gone"
store_hit = 1
else:
store_hit = 0
shown = "-"
print("store " + (str(store_hit == 1) + " ")[0:26] + "(needs the id anyway)")
surfaces = surfaces + 1
hits = index_search(INDEX, "severance")
if len(hits) > 0:
leaking = leaking + 1
leaking_with_content = leaking_with_content + 1
shown = hits[0][1]
else:
shown = "-"
print("search index " + (str(len(hits) > 0) + " ")[0:26] + shown)
surfaces = surfaces + 1
sug = autocomplete(DICTIONARY, "sev")
if len(sug) > 0:
leaking = leaking + 1
leaking_with_content = leaking_with_content + 1
shown = sug[0]
else:
shown = "-"
print("autocomplete " + (str(len(sug) > 0) + " ")[0:26] + shown)
surfaces = surfaces + 1
rec = recent_for(RECENT, "ann")
if len(rec) > 0:
leaking = leaking + 1
leaking_with_content = leaking_with_content + 1
shown = rec[0][1]
else:
shown = "-"
print("recent list " + (str(len(rec) > 0) + " ")[0:26] + shown)
print("")
print("surfaces checked: " + str(surfaces))
print("surfaces still returning the record: " + str(leaking))
print("of those, returning readable CONTENT: " + str(leaking_with_content))
print("")
print("the deletion audit the system has")
if len(store_get(STORE_AFTER, TARGET)) == 0:
audit = " record absent from the store: PASS"
else:
audit = " record absent from the store: FAIL"
print(audit)
print(" (this is the check that gets written, because the store is the thing")
print(" the delete was issued against)")
print("")
print("an audit that asks the surfaces instead")
if leaking == 0:
audit2 = " record unreachable from every surface: PASS"
else:
audit2 = " record unreachable from every surface: FAIL"
print(audit2)
print("")
print("the surface that matters most is the one that needs no id")
print(" store: reachable only if you already know d-2")
print(" search index: reachable by typing a word from the title")
print("")
checked = 0
passed = 0
checked = checked + 1
if len(store_get(STORE_AFTER, TARGET)) == 0:
passed = passed + 1
checked = checked + 1
if leaking > 0:
passed = passed + 1
checked = checked + 1
if leaking >= 3:
passed = passed + 1
checked = checked + 1
if leaking_with_content == leaking:
passed = passed + 1
checked = checked + 1
if len(store_get(STORE_AFTER, TARGET)) == 0:
if leaking > 0:
passed = passed + 1
checked = checked + 1
if len(store_get(STORE_AFTER, "d-1")) > 0:
if len(index_search(INDEX, "plan")) > 0:
passed = passed + 1
checked = checked + 1
probe = index_search(INDEX, "severance")
if len(probe) > 0:
if probe[0][1] == "Severance agreement":
passed = passed + 1
print("checks passed: " + str(passed) + "/" + str(checked))
if passed == checked:
verdict = "The delete succeeded against the one surface nobody searches."
else:
verdict = "FAILED - the surfaces did not behave as the checks describe."
print(verdict)
print("")
print("Deletion is modelled as an operation on a row and it is a claim about")
print("REACHABILITY. Every derived structure is a second answer to 'where is")
print("this record', built precisely because the first answer was too slow, and")
print("each one was added by someone solving a latency problem rather than")
print("designing a lifecycle. The audit asks the owner, and the owner is the")
print("only party that complied.")stdout (executed)
textbefore the delete
store: True
index hits: 1
autocomplete: 1
recent lists: 1
after the delete - each surface, interrogated the way a user reaches it
surface still returns the record what a user sees
------------- ------------------------ --------------------------
store False (needs the id anyway)
search index True Severance agreement
autocomplete True severance
recent list True Severance agreement
surfaces checked: 4
surfaces still returning the record: 3
of those, returning readable CONTENT: 3
the deletion audit the system has
record absent from the store: PASS
(this is the check that gets written, because the store is the thing
the delete was issued against)
an audit that asks the surfaces instead
record unreachable from every surface: FAIL
the surface that matters most is the one that needs no id
store: reachable only if you already know d-2
search index: reachable by typing a word from the title
checks passed: 7/7
The delete succeeded against the one surface nobody searches.
Deletion is modelled as an operation on a row and it is a claim about
REACHABILITY. Every derived structure is a second answer to 'where is
this record', built precisely because the first answer was too slow, and
each one was added by someone solving a latency problem rather than
designing a lifecycle. The audit asks the owner, and the owner is the
only party that complied.Trace event types
eml:run:starteml:defeml:assigneml:outputeml:calleml:returneml:run:done