Skip to content

fix(autobiographical): a removal or a view filter no longer halts compression; the debt report says when the chain-break guard does - #423

Open
nissa-seru wants to merge 1 commit into
anima-research:mainfrom
nissa-seru:cedric/d2-removal-guard
Open

nissa-seru wants to merge 1 commit into
anima-research:mainfrom
nissa-seru:cedric/d2-removal-guard

Conversation

@nissa-seru

Copy link
Copy Markdown
Contributor

With this change, removing the messages behind most of AutobiographicalStrategy's chunk records, or hiding them with a viewFilter, no longer stops compression of the messages that follow. While the chain-break guard does stop it, getCompressionDebt() reads critical and says why. The guard now counts a chunk record against the store only when the store can't show it ever held any of the record's message ids. It does that with a new MessageStore.held(id). Records naming ids the store never held, the case the guard exists for, still stop compression.

Fixes #421.

@antra-tess, we're asking you to review this before it's merged, since it changes whether a resident's memory goes on compressing after a removal or under a viewFilter, and what getCompressionDebt() reports while the guard stops it.

Problem

rebuildChunks' guard (src/strategies/autobiographical.ts:17901–17920 at fb4cf1046) counted a chunk record as orphaned when none of its messages was in the strategy's view. Once at least three records existed and more than half were orphaned, no new chunk formed. A removal (removeMessage, removeMessages) or a viewFilter hiding old history gives that count from ids the store did hold. #421 shows it by a test file it carries, at fb4cf1046, on Linux with Node 24.22.2. These are its lines after a removal of the messages behind 5 of 8 records, and after a reopen under a filter hiding them:

remove-5-of-8 after 40 more: records 8 (compressed 8); L1s 8; calls 9; queue 0; orphaned true; isReady true; debt healthy pending 0; messages 58; fail-closed errors 1
remove-5-of-8 reopened, after 40 more: records 8 (compressed 8); L1s 8; calls 9; queue 0; orphaned true; isReady true; debt healthy pending 0; messages 98; fail-closed errors 2
hide-5-of-8-then-show reopened: records 8 (compressed 8); L1s 8; calls 9; queue 0; orphaned true; isReady true; debt healthy pending 0; messages 38; fail-closed errors 1
hide-5-of-8-then-show after 40 more: records 8 (compressed 8); L1s 8; calls 9; queue 0; orphaned true; isReady true; debt healthy pending 0; messages 78; fail-closed errors 1

Changes

  • MessageStore.held(id) (src/message-store.ts:1175) answers whether the store ever held a message under an id, on any of its branches. It answers true for a live message and for one removed since, and false for an id that names no Append to this store's messages state, including one chronicle refuses as a record id. MessageStoreView gains it as the optional held? (src/types/strategy.ts:36). filterMessageStoreView passes it through, so a filtered view answers for the messages it hides (src/message-view.ts:62). mergeMessageStoreViews answers when every source can (:152).
  • The guard counts a record with no message in the view only when the view can't show that the store held any of the record's ids (src/strategies/autobiographical.ts:17848, :17925). A view without held counts as before. The answer is kept per record for the load, except a negative over a record number the store hasn't used yet, which a later write can give a message (:17857).
  • getCompressionDebt(), while the guard holds, reads critical, with failure: 'chunk-records', an addition to failure's union, and an error saying what happened, with Cause:, Effect: and Fix: lines (:237, :8549). When the quarantine ledger can't be read at the same time, error holds both messages.
  • The FAIL-CLOSED log line and the guard's comments now say the records name ids the store can't show it ever held.
  • Tests: test/removal-guard.test.ts is new, and one comment in test/chunk-persistence.test.ts changes.
  • Changelog: two fragments, changelog.d/removal-keeps-compressing.fixed.md and changelog.d/removal-keeps-compressing.added.md.

In all, 8 files, +413 −14. Merged at 19:04 UTC on October 30 with each of the 13 open pull requests' heads, this head conflicts where main does, except with #350 (below).

With #350

@tel-0s, by git merge-tree at 19:04 UTC on October 30, your #350 at 4d04b425ae merges into main fb4cf1046 cleanly and conflicts with this head in src/types/strategy.ts, where each adds an optional method to MessageStoreView after sequenceOf: this head held? and yours positionOf?. Both can stand.

Tests

On Linux with Node 24.22.2, after npm ci from the lockfile, which pins @animalabs/membrane 0.9.0 and @animalabs/chronicle 0.5.1. Each run is in a clean checkout of the commit named, with a file added where one is named.

  • At this head, c1dcc0fd0: npm run build exits 0. test/removal-guard.test.ts passes 11 of 11, and test/chunk-persistence.test.ts 6 of 6, its chain-break test unchanged.
  • Red first: with fb4cf1046's src and this head's tests, npm ci and npm run build each exit 2 on 14 type errors, all at the tests' calls of held, and the tests then run from what tsc emitted. All 11 of removal-guard's tests fail. Five removal and filter cases fail at "an L1 covers messages added after it". The numbers-given-later case and the two never-held cases fail at the debt report, which reads healthy where they expect critical, and the ledger case where it reads unknown. held's two tests fail where fb4cf1046 has no held.
  • The issue's test file at this head: after the removal and 40 more messages, and after the reopen under the filter, compression goes on, with no FAIL-CLOSED line:
remove-5-of-8 after 40 more: records 18 (compressed 18); L1s 18; calls 21; queue 0; orphaned false; isReady true; debt healthy pending 0; messages 58; fail-closed errors 0
remove-5-of-8 reopened, after 40 more: records 28 (compressed 28); L1s 28; calls 32; queue 0; orphaned false; isReady true; debt healthy pending 0; messages 98; fail-closed errors 0
hide-5-of-8-then-show reopened: records 8 (compressed 8); L1s 8; calls 9; queue 0; orphaned false; isReady true; debt healthy pending 0; messages 38; fail-closed errors 0
hide-5-of-8-then-show after 40 more: records 18 (compressed 18); L1s 18; calls 21; queue 0; orphaned false; isReady true; debt healthy pending 0; messages 78; fail-closed errors 0
  • What removal-guard pins:
    1. removing the messages of 3 of 5 records one by one, or of both of 2 as a range, leaves later messages compressing;
    2. a store reopened under a viewFilter hiding the messages of 5 of 8 records compresses later messages;
    3. a store whose records a removal emptied before it closed compresses later messages after it reopens;
    4. records each naming a removed message beside ids the store never held leave later messages compressing, and records naming record numbers the store gives messages later in the same load stop halting compression once it has;
    5. records naming ids the store never held, of another form or record numbers it never assigned, still stop compression, with the debt report critical, carrying the ledger's error too when the ledger can't be read;
    6. held answers as above, through filtered and merged views, after compactState, after a reopen and on a fork.

The whole suite runs in this pull request's CI.

Not verified


  • Changelog fragments added: changelog.d/removal-keeps-compressing.fixed.md and changelog.d/removal-keeps-compressing.added.md.

Fern-2426 found the defect. Cedric-2315 made the change and measured where the guard trips, Alfred-2570 checked that each part is needed, and Oscar-1005 placed it. Lottie-2661 ran the tests at this head and wrote this description.

…pression; the debt report says when the chain-break guard does

rebuildChunks' fail-closed guard counted a chunk record as orphaned when none
of its messages was in the strategy's view, and halted all compression once
more than half of at least three records were. Its case is a store whose
messages were renumbered: records naming ids the store never held. A host's
removal (removeMessage, removeMessages, removeBodyGroup, an operator's
suppress) or a viewFilter hiding old history produced the same count from ids
the store did hold, so compression of every later message stopped for good,
across reopens, while getCompressionDebt() read healthy with nothing pending.

- MessageStore.held(id): whether the store ever held a message under an id,
  on any of its branches: true for a live message and for one removed since
  (removal is a Redact state operation; the message's append record stays in
  the chronicle, compaction included), false for an id that names no Append
  to this store's messages slot. MessageStoreView gains it as optional held?;
  filterMessageStoreView passes it through unfiltered (a hidden message is
  held) and mergeMessageStoreViews answers when every source can.
- The guard counts a record with no message in the view only when the store
  can't show it held any of the record's ids; a view without held counts as
  before. The answer is memoized per record for the load, except a negative
  over a record number the store hasn't used yet, the only kind of id a later
  write can give a message.
- While the guard holds, getCompressionDebt() reads critical, with error and
  failure: 'chunk-records', an addition to failure's union; if the quarantine
  ledger can't be read then too, error carries both messages.

Requirements, each pinned in test/removal-guard.test.ts:
1. Removing the messages of more than half the chunk records (3 of 5 one by
   one; 2 of 2 as a range) leaves later messages compressing.
2. A store reopened under a viewFilter hiding the messages of 5 of 8 records
   compresses later messages.
3. A store whose records a removal emptied before it closed compresses later
   messages after it reopens.
4. Records each naming a removed message beside ids the store never held
   leave later messages compressing; records naming numbers the store gives
   messages later in the same load stop halting compression once it has.
5. Records naming ids the store never held, of another form or record
   numbers it never assigned, still halt compression, and the debt report
   reads critical with failure 'chunk-records', carrying the ledger's error
   too when the ledger can't be read.
6. held answers as above, through filtered and merged views, after
   compactState, after a reopen and on a fork.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AutobiographicalStrategy stops compressing once most of its chunk records' messages are removed or hidden, while its compression debt reads healthy

1 participant