Skip to content

fix(store): a retired belief no longer gains evidence — get_belief filters valid_to (#1210) - #1214

Merged
github-actions[bot] merged 3 commits into
mainfrom
fix/issue-1210-get-belief-valid-to
Jul 30, 2026
Merged

fix(store): a retired belief no longer gains evidence — get_belief filters valid_to (#1210)#1214
github-actions[bot] merged 3 commits into
mainfrom
fix/issue-1210-get-belief-valid-to

Conversation

@robotrocketscience

@robotrocketscience robotrocketscience commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Closes #1210.

get_belief had no valid_to filter. aelf retire sets valid_to and prunes the FTS row, so keyword search correctly stopped finding a retired belief — but everything reached by id rather than by search still saw it: BFS, propagate_valence, feedback, context assembly.

Reproduced, and the consequence measured

valid_to           : 2026-07-30T19:51:18+00:00
get_belief         : RETURNS IT
search finds gone  : False      <-- correct
retired alpha      : 9.0 -> 9.9 <-- from one `aelf feedback` on a NEIGHBOUR
audit rows on gone : 1
still soft-deleted : True

A belief the user retired gained confidence and had an audit row written claiming evidence for it, while staying invisible in search — so nobody could see it happening. aelf restore then returned it at a posterior the user never endorsed. Propagation is on the default path; no flag was needed to reach this.

After:

get_belief default     : None
get_belief opt-in      : returns it
retired alpha          : 9.0 -> 9.0
audit rows on retired  : 0
restore still works    : True
after restore, visible : True

The one-line fix would have been wrong

The issue says so and it holds up: a blanket AND valid_to IS NULL breaks aelf restore, turning reversible curation into a one-way door — a worse defect than the one being fixed. So the filter is the default and the opt-in is explicit and greppable.

include_retired=True at the callers that must address a tombstone:

caller why
insert_belief id-collision guard a tombstone still owns its primary key
_cmd_delete hard-delete must be able to address a retired belief
_cmd_retire the already retired branch reads belief.valid_to
lock re-lock path (CLI + MCP) a tombstone reading as absent skips the lock-upgrade branch
migrate existence checks same PK argument as the collision guard
replay drift audit reconciles ingest_log against the rows that exist

Everything else — ~50 sites across retrieval, BFS, propagation, feedback, promotion, classification, context assembly, wonder, aelf core — takes the new default.

Two of these are load-bearing rather than defensive, and both are pinned: reverting the insert_belief opt-in trips sqlite3.IntegrityError: UNIQUE constraint failed: beliefs.id, and test_retire_already_retired_is_noop already covers the retire one.

get_belief_in_scope needed the filter in both branches

Its peer branch runs its own SELECT * FROM beliefs WHERE id = ?. A retired belief in a peer store was as reachable through the walk as a local one, so filtering only the local branch would have left federated stores as a way back in.

spine_neighbors opts in for the opposite reason

It already implements the correct policy itself — emit only active beliefs, but traverse through retired ones so a GC'd segment does not sever the chain:

if belief.valid_to is not None:
    # skip-but-continue: traverse through GC'd segments.
    next_frontier.append(nid)
    continue

Taking the default collapsed that distinction into the is None branch and dropped the rest of the chain — test_neighbors_skip_but_continue_soft_deleted went ["b3"] -> []. That test caught it, and None there now means a genuinely dangling edge, which correctly stops the walk. Flagging it because it is the clearest case of the hazard the issue warned about: the blanket filter is wrong at sites that were already handling lifecycle correctly.

Tests assert the invariant, not the call sites

Per the issue: with ~60 callers, a suite that pins each one says nothing about whether the sixty-first is safe. tests/test_retired_belief_evidence_1210.py asserts that a retired belief's posterior cannot move and that the callers which must reach a tombstone still can.

Verified distinguishing rather than assumed: reverting the valid_to filter fails 6 of the 9. The 3 that survive are the opt-in paths and the negative control, which should pass either way. The control is deliberate — without test_propagation_still_reaches_a_live_neighbour, a bug that disabled propagation outright would satisfy both "gained no alpha" assertions on a store where nothing propagates to anything.

Acceptance criteria

  • get_belief excludes soft-deleted beliefs by default.
  • Call sites that need retired rows opt in explicitly; aelf restore still works end to end (asserted, including re-entry into keyword search).
  • A retired belief cannot gain alpha or accrue feedback_history rows via propagation from a neighbour.
  • BFS does not surface soft-deleted beliefs — local and peer branch.
  • soft_delete_belief's docstring claim about read-side filtering is now true, and says where it is enforced.

Out of scope per the issue: dangling-edge handling once a node is retired.

Test-side changes worth a look

Nine probes that exist to inspect a tombstone now pass include_retired=True. Each was verified as a probe artifact rather than a product regression before being touched — aelf retire was confirmed to still set valid_to via raw SQL while the test's own _valid_to helper reported None. Two comments asserting "get_belief does not filter on valid_to" are corrected rather than left describing the old behaviour.

Verification

  • Full suite: 6415 passed, 69 skipped, 71 xfailed.
  • scripts/check_migration_policy.py against github/main: OK: no new migration entries (no schema change).

Noted, not fixed

aelf lock on a retired statement leaves the belief retired but locked. That is unchanged by this PR — the opt-in preserves it rather than introducing it — but it looks like an unintended state. Separable from #1210; say the word and I will file it.

Summary by Sourcery

Prevent retired beliefs from being treated as active content and ensure only explicit callers can access tombstones.

New Features:

  • Add an explicit include_retired flag to belief retrieval APIs to allow opt-in access to soft-deleted rows for lifecycle and audit paths.

Bug Fixes:

  • Exclude soft-deleted beliefs from get_belief and peer-scope retrieval by default so retired beliefs no longer gain evidence or appear in BFS and propagation results.
  • Ensure lock, retire, delete, migration, and replay code paths can still operate correctly on retired beliefs by opting into tombstone visibility.
  • Fix temporal spine neighbor traversal to continue through retired beliefs without emitting them, preserving chain integrity.

Enhancements:

  • Clarify soft_delete_belief documentation to reflect that read-side filtering of retired beliefs is now enforced in get_belief.
  • Strengthen invariants around belief lifecycle with tests that assert retired beliefs remain stable and that required callers still reach tombstones.

Documentation:

  • Document the new retired-belief handling in get_belief and soft_delete_belief, and record the lifecycle fix in the v4 changelog.

Tests:

  • Add a focused test suite ensuring retired beliefs cannot gain alpha or feedback via default paths while still being reachable from lifecycle and audit callers.
  • Update existing tests and helpers to use include_retired when intentionally inspecting soft-deleted beliefs.

Summary by CodeRabbit

  • Bug Fixes
    • Retired beliefs are now excluded from normal ID-based lookups, preventing propagation and feedback from modifying them.
    • Lock, delete, retire, restore, replay, and migration workflows can still correctly access retired records when required.
    • Traversal continues through retired beliefs without surfacing them in results.
    • Retired records no longer cause duplicate insertion or identifier-collision issues.
  • Tests
    • Added coverage for retired-belief retrieval, evidence protection, traversal, restoration, and lifecycle behavior.

`get_belief` had no `valid_to` filter, so a soft-deleted belief stayed
reachable by id even though the FTS prune kept it out of search. Everything
downstream of it — BFS, `propagate_valence`, feedback, context assembly —
still saw the tombstone.

Measured consequence: one `aelf feedback` on a *neighbour* propagated into a
retired belief, taking alpha 9.0 -> 9.9 and writing an audit row against it,
invisibly. `aelf restore` then returned a belief at a posterior the user
never endorsed. Propagation is on the default path, so this needed no flag
to reach.

`include_retired=False` is the new default; the opt-in is explicit and
greppable at the callers that must address a tombstone: the id-collision
guard in `insert_belief` (a tombstone still owns its primary key), the
`retire`/`delete` lifecycle commands, the `lock` re-lock path, the migration
existence checks, and the replay drift audit. `get_belief_in_scope` carries
the flag through to its peer branch too — a retired belief in a peer store
was as reachable through the walk as a local one.

`spine_neighbors` opts in for the opposite reason: it already implements the
correct policy itself — emit only active beliefs, but traverse *through*
retired ones so a GC'd segment does not sever the chain. Taking the default
collapsed that into the `is None` branch and dropped the rest of the chain.

Test probes that exist to inspect a tombstone opt in likewise, and two
comments asserting "get_belief does not filter on valid_to" are corrected.
`soft_delete_belief`'s docstring claimed read-side queries already filtered
`valid_to`; that was aspirational, and is now true.

Closes #1210
Written against the invariant rather than the call sites, per the issue:
with ~60 `get_belief` callers, a suite that pins each one individually says
nothing about whether the sixty-first is safe.

Distinguishing, not decorative — reverting the `valid_to` filter fails 6 of
the 9, and reverting the `insert_belief` opt-in on its own fails the
collision test with the UNIQUE constraint it exists to prevent. The three
that survive a revert are the opt-in paths and the negative control, which
should pass either way.

The control matters: without `test_propagation_still_reaches_a_live
_neighbour`, a bug that disabled propagation entirely would satisfy both
"the retired belief gained no alpha" assertions on a store where nothing
propagates to anything.
@robotrocketscience robotrocketscience added the author-Gylf PR coordination mutex label Jul 30, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @robotrocketscience, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@robotrocketscience robotrocketscience added the attn:review Needs review (PR open, awaiting reviewer) label Jul 30, 2026
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 936f2941-c9b3-41bc-99ce-455afe6e08fa

📥 Commits

Reviewing files that changed from the base of the PR and between d8a8856 and 87bf0b1.

📒 Files selected for processing (17)
  • CHANGELOG/v4.md
  • src/aelfrice/cli.py
  • src/aelfrice/mcp_server.py
  • src/aelfrice/migrate.py
  • src/aelfrice/replay.py
  • src/aelfrice/store.py
  • src/aelfrice/temporal_spine.py
  • tests/test_cli_retire_restore.py
  • tests/test_cli_review.py
  • tests/test_retired_belief_evidence_1210.py
  • tests/test_review_module.py
  • tests/test_review_store.py
  • tests/test_soft_deleted_retrieval_exclusion_980.py
  • tests/test_speculative_phantom_trust.py
  • tests/test_store_crud.py
  • tests/test_wonder_autogc_hook.py
  • tests/test_wonder_lifecycle.py

📝 Walkthrough

Walkthrough

get_belief now hides retired beliefs by default, with explicit tombstone access for restoration, migration, replay, locking, and related lifecycle operations. Traversal and propagation avoid retired targets, while tests cover filtering, evidence invariants, restoration, and existing soft-delete behavior.

Changes

Retired belief lifecycle

Layer / File(s) Summary
Store retrieval contract
src/aelfrice/store.py
get_belief and scoped lookup exclude valid_to rows by default, while include_retired=True supports tombstone access and ID-collision handling.
Propagation and traversal policy
src/aelfrice/temporal_spine.py, tests/test_retired_belief_evidence_1210.py
Retired beliefs are not surfaced or mutated through default propagation paths; spine traversal continues through them without emitting them.
Tombstone-aware callers
src/aelfrice/cli.py, src/aelfrice/mcp_server.py, src/aelfrice/migrate.py, src/aelfrice/replay.py
Lifecycle operations explicitly retrieve retired rows for locking, deletion, retirement, migration, and replay comparisons.
Lifecycle regression coverage
tests/test_*
Tests cover lookup filtering, evidence and audit invariants, restoration, ID collisions, and post-soft-delete assertions. CHANGELOG/v4.md records the fix.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant Feedback caller
  participant MemoryStore
  participant spine_neighbors
  participant Audit events
  Feedback caller->>MemoryStore: Apply feedback to a live neighbour
  MemoryStore->>spine_neighbors: Traverse related belief IDs
  spine_neighbors->>MemoryStore: get_belief(id)
  MemoryStore-->>spine_neighbors: Active belief or None for retired belief
  MemoryStore->>Audit events: Record evidence only for active targets
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the core fix: retired beliefs are filtered by default in get_belief, preventing them from gaining evidence.
Description check ✅ Passed The PR description covers the bug, rationale, verification, and reviewer notes, though a few template sections are not filled in explicitly.
Linked Issues check ✅ Passed The changes match #1210 by default-filtering retired beliefs, preserving restore, and keeping required tombstone opt-ins where needed.
Out of Scope Changes check ✅ Passed The diff stays focused on retired-belief handling, test updates, docs, and required opt-ins; no clear unrelated changes stand out.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-1210-get-belief-valid-to

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

PR-size soft cap

This PR is over the advisory size threshold:

  • 351 changed lines (limit: 200)
  • 17 changed files (limit: 3)

Bigger PRs collide with more open work, which under the parallel-session workflow tends to produce repeated attn:merge-conflict cycles (see #602). When practical, split into smaller PRs that each touch a focused surface.

This is advisory only — nothing is blocked. If the size is intentional (large refactor, module removal, generated code), apply the size:override label and this comment will be removed on the next push.

@sourcery-ai

sourcery-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Reviewer's Guide

Introduce lifecycle-aware filtering to get_belief so retired (soft-deleted) beliefs are excluded from default retrieval, with explicit include_retired opt-ins at a small set of lifecycle, audit, and migration call sites; ensure BFS, propagation, feedback, and peer-store traversal no longer surface retired beliefs while restore and tombstone-oriented operations continue to work, with tests asserting the invariant that retired beliefs cannot gain evidence and that required tombstone access paths remain functional.

Sequence diagram for BFS peer traversal using lifecycle-aware get_belief_in_scope

sequenceDiagram
    participant BFSWalker
    participant Store
    participant PeerDB

    BFSWalker->>Store: edges_from_in_scope(belief_id)
    loop for each neighbour_id
        Store->>Store: get_belief_in_scope(neighbour_id, owning_scope, include_retired=False)
        alt owning_scope is None
            Store->>Store: get_belief(neighbour_id, include_retired=False)
            Store-->>BFSWalker: Belief or None (retired excluded by valid_to IS NULL)
        else owning_scope is peer
            Store->>PeerDB: SELECT * FROM beliefs WHERE id = neighbour_id AND valid_to IS NULL
            PeerDB-->>Store: row or None (retired excluded)
            Store-->>BFSWalker: Belief or None
        end
    end
Loading

File-Level Changes

Change Details Files
Make get_belief lifecycle-aware and add an include_retired opt-in, then thread this behavior through scoped retrieval and BFS.
  • Add include_retired: bool = False parameter to get_belief and conditionally append AND b.valid_to IS NULL to the SQL when include_retired is False
  • Update soft_delete_belief docstring to reflect that read-side filtering on valid_to is now enforced via get_belief
  • Extend get_belief_in_scope to accept include_retired, delegating to get_belief for local scope and adding a matching valid_to filter in the peer-DB SELECT
src/aelfrice/store.py
Opt in to tombstone visibility only where necessary for lifecycle, locking, migration, and replay logic.
  • Use get_belief(..., include_retired=True) in insert_or_corroborate’s id-collision guard to avoid UNIQUE constraint failures on retired IDs
  • Update CLI and MCP lock commands to use include_retired=True when checking pre-existing lock IDs and resolving/tiering beliefs so re-locks correctly operate on retired rows
  • Make _cmd_delete and _cmd_retire use include_retired=True to allow hard delete and retire-noop behavior on already-retired beliefs
  • Update migrate’s existence checks and edge endpoint filtering to treat tombstones as existing rows by calling get_belief(..., include_retired=True)
  • Change replay_full_equality to use include_retired=True when reconciling ingest_log against the canonical store so retired beliefs aren’t misclassified as orphans
src/aelfrice/store.py
src/aelfrice/cli.py
src/aelfrice/mcp_server.py
src/aelfrice/migrate.py
src/aelfrice/replay.py
Preserve correct traversal semantics in temporal spine BFS while still hiding retired beliefs from BFS results.
  • Update spine_neighbors to call get_belief(..., include_retired=True) so the walk can traverse through retired nodes while still skipping emission of retired beliefs based on belief.valid_to
  • Clarify in comments that None from get_belief now means a genuinely dangling edge, not a retired node, preserving skip-but-continue behavior
src/aelfrice/temporal_spine.py
Align tests and helpers with lifecycle-aware retrieval and add a focused regression suite asserting the retired-belief evidence invariant.
  • Introduce tests/test_retired_belief_evidence_1210.py to cover default exclusion of retired beliefs by get_belief, scoped filtering, prevention of evidence propagation/audit rows to retired beliefs, maintenance of propagation to live neighbours, BFS exclusion of retired beliefs, id-collision handling on retired IDs, and restore behavior preserving posterior
  • Update various tests and helpers that intentionally inspect soft-deleted rows (e.g., retire/restore helpers, audit and GC tests, review flows, CRUD and lifecycle tests) to pass include_retired=True when calling get_belief
  • Adjust comments in existing tests to reflect that get_belief now filters on valid_to by default rather than returning soft-deleted rows
  • Add a CHANGELOG entry describing the retired-belief evidence bug, the default filter plus explicit include_retired=True opt-ins, and the invariant-based testing strategy
tests/test_retired_belief_evidence_1210.py
tests/test_cli_retire_restore.py
tests/test_soft_deleted_retrieval_exclusion_980.py
tests/test_review_store.py
tests/test_wonder_autogc_hook.py
tests/test_cli_review.py
tests/test_review_module.py
tests/test_speculative_phantom_trust.py
tests/test_store_crud.py
tests/test_wonder_lifecycle.py
CHANGELOG/v4.md

Assessment against linked issues

Issue Objective Addressed Explanation
#1210 Modify get_belief to exclude soft-deleted beliefs (valid_to IS NOT NULL) by default, add an explicit include_retired flag, update necessary call sites (restore/introspect/audit/migration/etc.), and align soft_delete_belief’s docstring with actual behavior.
#1210 Ensure a retired belief cannot gain alpha or accrue feedback/audit rows via propagation from neighbours, and that BFS (including peer-store traversal) does not surface soft-deleted beliefs.
#1210 Add tests that assert the invariant that a retired belief cannot gain evidence via any default-path operation, rather than pinning individual get_belief call sites.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:garsecg:2026-07-30T20:17:50Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Review: approved

Verified independently rather than taking the PR body's word for it.

The distinguishing check reproduces. Reverting the one line that matters
(lifecycle = "" if include_retired else "AND b.valid_to IS NULL""")
fails 6 of the 9 new tests, including the reproduction itself:

AssertionError: assert 9.9 == 9.0
  where 9.9 = Belief(id='B222...', content='deploy target is heroku', alpha=9.9, ...)

The 3 survivors are the two opt-in paths and the negative control, which
should pass either way. So the suite pins the invariant, not the call sites,
which is what the issue asked for. tests/test_retired_belief_evidence_1210.py
passes 9/9 on the branch; CI is green on both 3.12 and 3.13.

Spot-checked the call sites that take the new default and could plausibly
need the opt-in.
restore_belief never routes through get_belief (raw SQL
on valid_to IS NOT NULL), so reversibility is structurally unaffected.
assign_belief_to_category, _cmd_demote, and review.apply_decisions all
read a belief in order to act on it — a tombstone reading as "not found"
there is the correct answer, and review --apply degrades to a non-fatal
errors entry rather than re-soft-deleting. The spine_neighbors opt-in is
the right call for the reason given; the default genuinely does collapse
skip-but-continue into the dangling-edge branch.

The f-string in the SQL is a fixed literal chosen by a bool, not
caller-controlled — no injection surface.

Commits are atomic and signed. Discretion grep on added lines is clean.

One adjacent gap — not this PR's, filing separately

get_belief_by_content_hash has no valid_to filter either, and it is the
same hole one door down. Measured on this branch:

re-assert of a retired statement -> id B333333333333333  inserted? False
resolves to the tombstone?  True
still retired: True | corroborations: 1
visible by default get_belief: False
in search: []

Re-asserting a statement you previously retired is swallowed: insert_or_corroborate
matches the tombstone by content hash, writes a corroboration row against the
retired belief
, and returns inserted=False. The statement never re-enters
the store or search. So a retired belief can still gain evidence — a
corroboration row rather than alpha — and the user's re-assertion is lost
silently.

Pre-existing, not introduced here, and outside #1210's scope (which is
get_belief). Not a blocker. I'll file it with this reproduction.

On the "noted, not fixed" item — aelf lock on a retired statement leaving it
retired-but-locked — yes, file it. Same family, same door.

Adding ready-to-merge.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Toug:2026-07-30T20:22:06Z]

@robotrocketscience robotrocketscience added ready-to-merge Trigger merge-train: FF main to this PR's head and removed attn:review Needs review (PR open, awaiting reviewer) labels Jul 30, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Toug:2026-07-30T20:22:11Z]

@github-actions
github-actions Bot merged commit 87bf0b1 into main Jul 30, 2026
41 of 51 checks passed
@github-actions github-actions Bot removed the ready-to-merge Trigger merge-train: FF main to this PR's head label Jul 30, 2026
@github-actions

Copy link
Copy Markdown

merge-train: merged 87bf0b1main via FF push.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author-Gylf PR coordination mutex

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(store): a retired belief still gains evidence — get_belief does not filter valid_to

1 participant