docs: lead decision 71, S4 round 2 scope and the notify journal's torn tail (sage)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-08 18:52:11 -05:00
co-authored by Claude Opus 5.5
parent 49f99f99aa
commit b9b6cf008a
+37
View File
@@ -1359,3 +1359,40 @@ which stay with him. Each item names who decided it and what happened.
fail-closed Vikunja checks, about 15 requests, before the boot
reply, so the host waits 30 s for `{ok: true}`. A refusal comes
back as `{ok: false, error}`.
71. **S4 round 2 scope and the notify journal's torn tail (2026-10-08).**
Source: Filbert's round 1 verdict on #1521 (comment 26848, B1), and
Rocko's point that a newline alone doesn't fix it.
- A final line without its newline is a write that never finished.
No confirmed record lives in it, so removing it doesn't rewrite
evidence. Decision 70's append-only rule covers complete lines.
- On open, a torn tail is handled in this order:
1. Copy the torn bytes to
`<dataRoot>/notify/<business>/torn-<UTC stamp>.bin` (0600, new
file, fsync).
2. Truncate `sent.jsonl` to its last newline and fsync.
3. Log both steps.
- A crash between steps 1 and 2 leaves the tail torn. The next open
repeats both steps, and the second copy is harmless.
- Appending a newline isn't enough: the fragment becomes a malformed
middle line, and the next open refuses.
- A malformed complete line still refuses with exit 3. Only the final
unterminated line gets this treatment.
- If the torn write was a send that reached Discord, the decision
gets DM'd again. Decision 70 already prefers a duplicate DM to a
missed one, and the per-decision nonce lets Discord dedupe it
inside its window.
- Round 2 must fix:
- B1, with the rule above. Tests: open, append, reopen; and the
state after a crash between steps 1 and 2.
- F1. Refuse a journal directory looser than 0700, as the file
mode is checked.
- R2. A test that two decisions get different DM nonces.
- D1. Put the business in the digest nonce.
- The host startup race. Check each child's exit state after the
listeners attach.
- J3. Refuse a symlinked `sent.jsonl`.
- F3. Drop `network-online.target` from the user unit.
- Tests for mutants M22 to M24, M26 and M29.
- Follow-ups, not round 2: F2 (a refused DM retries every 30 min with
no limit) and J4 (loose types in confirmed lines).