fix(advice): guard the advice write on the comment as well as the token
The editing token catches every edit the application makes, because upsert_reading writes a fresh token on every write. A document edited outside the application - by a script, or by hand - keeps its empty token, so a stale thread's write would still match on the gravity alone and could put advice about the old comment back. The advice is generated from the comment as well as the gravity, so the comment is now a second, independent guard on the write. A stale write can only land if BOTH the gravity and the comment are unchanged. The stored comment and the one the view sees can differ by trailing whitespace - two of the live readings are padded - so the match is a stripped, anchored regex rather than an exact one. Also: the log message said the reading 'no longer holds the gravity it was written for', but the guard is the token and the comment. It now says what actually happened. 194 tests pass.
This commit is contained in:
@@ -127,6 +127,11 @@ def _start_advice(run_id: str, reading_id: str, corrected: bool = False) -> None
|
||||
# cannot overwrite a correction with advice about the old value.
|
||||
advice_sg = latest.get("sg")
|
||||
advice_token = latest.get("advice_token")
|
||||
# The comment is part of what the advice describes, so it is a
|
||||
# second, independent guard on the write. The token covers every
|
||||
# edit the application makes; this also covers a document edited
|
||||
# outside it.
|
||||
advice_comment = latest.get("comment") or ""
|
||||
|
||||
def write(read_id: str, ok: bool, text: str) -> None:
|
||||
# The client is SHARED, not built per reading. A MongoClient
|
||||
@@ -145,6 +150,7 @@ def _start_advice(run_id: str, reading_id: str, corrected: bool = False) -> None
|
||||
read_id, text, ok,
|
||||
expected_sg=advice_sg,
|
||||
expected_token=advice_token,
|
||||
expected_comment=advice_comment,
|
||||
)
|
||||
if not written:
|
||||
# The reading was corrected while the advice was being
|
||||
@@ -153,8 +159,8 @@ def _start_advice(run_id: str, reading_id: str, corrected: bool = False) -> None
|
||||
# reading will land in its own turn.
|
||||
log.info(
|
||||
"the advice for reading %s was not stored: the reading "
|
||||
"has been edited since the advice was generated, so the "
|
||||
"advice no longer matches it",
|
||||
"has been edited since the advice was generated, so its "
|
||||
"editing token or comment no longer matches the advice",
|
||||
read_id,
|
||||
)
|
||||
except Exception: # noqa: BLE001 - never let the thread die noisily
|
||||
|
||||
Reference in New Issue
Block a user