Skip to content

fix(store): OR the rarest query tokens instead of ANDing all of them (#1177) - #1230

Merged
github-actions[bot] merged 3 commits into
mainfrom
fix/issue-1177-fts5-or-recall
Jul 31, 2026
Merged

fix(store): OR the rarest query tokens instead of ANDing all of them (#1177)#1230
github-actions[bot] merged 3 commits into
mainfrom
fix/issue-1177-fts5-or-recall

Conversation

@robotrocketscience

@robotrocketscience robotrocketscience commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Implements the ratified operator disposition on #1177: pursue the recall fix on the existing numpy lane, price the trade explicitly, and do not carry the substrate swap along with it. The FTS5-for-numpy/scipy swap is untouched and stays parked.

The defect is also written up as a sub-item of the #1158 umbrella ("FTS5 MATCH is built as an implicit AND over every whitespace token"), so this closes that sub-item rather than opening a third issue for the same surface.

The defect

_escape_fts5_query quotes each whitespace token and joins with spaces, which FTS5 reads as an implicit AND. No single belief contains every word of a natural-language question, so the lane goes silent on exactly the query shape a UserPromptSubmit prompt has.

Measured over 503 distinct logged prompts against a live 44,584-belief store. Arms reported separately, because pooling them is misleading in both directions — 66% of the corpus is harness blocks, and they behave nothing like user turns:

MATCH form arm zero-hit p50 p95
AND (today) user 28.7% 1.01 ms 5.01 ms
AND (today) harness 100.0% 0.37 ms 0.63 ms
AND (today) pooled 75.7% 0.42 ms 2.37 ms
OR (all tokens) user 0.0% 43.67 ms 84.13 ms
OR (all tokens) harness 0.0% 0.76 ms 1.48 ms
OR rarest-3 (this PR) user 0.0% 4.91 ms 23.31 ms
OR rarest-3 (this PR) harness 0.0% 1.99 ms 2.70 ms
OR rarest-5 user 0.0% 10.10 ms 45.69 ms

Pricing the trade, which is what the disposition asked for

The full OR is the option the issue's Mechanism section actually specifies, and it is the expensive one — 43.67 ms p50 on the user arm against the conjunctive lane's 1.01 ms. It also buys nothing over the trim. Agreement with the conjunctive lane's top-20, on the 122 user queries where that lane returned anything at all:

variant top-20 preservation user p50
OR (all tokens) 91.4% 43.67 ms
OR rarest-3 91.3% 4.91 ms
OR rarest-5 91.4% 10.10 ms

So the middle option the disposition anticipated is not a compromise — it is the same answer for a ninth of the cost, and going past 3 tokens is measurably free of benefit. That is why the constant is 3 and not a round number.

What that agreement figure does and does not say. It is measured only where the conjunctive lane returned hits, because there is no reference set anywhere else. On the 49 user queries and 332 harness blocks where it returned nothing, every hit is new and unverified against a ground truth — this PR establishes that the lane stops being silent, not that the new hits are well-ranked. Ranking quality on those is a bench question, not a claim made here.

Rarity is corpus df, not a stopword list. On a memory store about retrieval, retrieval carries df 2,443 and how only 516 — so how survives the trim and retrieval is dropped. That reads wrong and measures right: the trim is candidate generation feeding bm25, which then ranks. I checked this specifically because the output looked like a bug.

No migration, no new dependency

Document frequencies come from two per-connection TEMP virtual tables — an fts5vocab over beliefs_fts, and an empty probe table declared with the same porter unicode61 tokenizer. Nothing is written to the database file, there is no schema change, and the path works on a read-only DB. Given #1161, avoiding a migration was the whole risk profile. An SQLite built without fts5vocab degrades to a full OR rather than to an exception or back to zero recall.

Two facts established by measurement, both of which would have been silent defects

Tokens are resolved through FTS5's own tokenizer, not aelfrice.bm25.tokenize_stemmed. The two disagree on underscores and diacritics. Ranking query tokens against the index with the Python tokenizer resolves 62.9% of them against the native path's 99.7% — and every unresolved token collapses to df 0 and masquerades as the rarest. That is precisely the failure #1158 records for the IDF-clip lane, and my first measurement pass had it: the rarest-N numbers were wrong until I checked the resolution rate and redid them.

The expression carries the original tokens, never the stems it ranked by. The porter stemmer is not idempotent. Checked across the whole live vocabulary rather than spot-checked: 416 of 15,208 terms stem again to something else (abusabu, acceleraccel). Re-emitting a stem would silently stop matching those documents.

Tests

tests/test_fts5_recall_1177.py — 15 tests over six properties, each chosen so that reverting the corresponding piece of the fix fails one of them. Mutation-checked rather than assumed:

mutation result
revert search_beliefs to the implicit AND 3 failed
trim in query order instead of by df 1 failed
emit stems instead of original tokens 1 failed
drop the probe-unavailable fallback 1 failed

The cliff test asserts the conjunctive builder still returns empty on the same fixture, so it cannot pass by the corpus happening to contain every token.

One existing test asserted the old contract and is rewritten rather than deleted: test_multi_word_query_implicit_and pinned ids == {"b1"}. It now asserts the disjunctive contract and that the belief containing every token still ranks first, which is the property that makes partial matching safe.

Full suite: 6544 passed, 69 skipped, 71 xfailed.

Refs #1177, #1158

Summary by Sourcery

Change FTS5 belief search to use an OR over the rarest query tokens instead of an implicit AND over all tokens, eliminating zero-recall behavior for natural-language queries while keeping latency acceptable.

New Features:

  • Introduce a disjunctive FTS5 MATCH builder that ORs the rarest query tokens based on corpus document frequency for belief search.
  • Add support for building per-connection TEMP FTS5 vocab and probe tables to derive document frequencies and tokenizer behavior without schema migrations.

Bug Fixes:

  • Fix FTS5 search returning no results for many multi-word natural-language queries by replacing the implicit AND over all tokens with an OR over the rarest few tokens.
  • Ensure query token rarity calculation uses FTS5's native tokenizer and preserves original tokens rather than stems, avoiding mis-ranked queries and silent mismatches.
  • Provide a safe fallback to a full OR MATCH expression when FTS5 vocabulary probing is unavailable, preventing errors while preserving recall.

Enhancements:

  • Refine FTS5 query escaping utilities by separating conjunctive and disjunctive builders and sharing robust token-quoting behavior.
  • Update peer belief search to reuse the rarest-token disjunctive MATCH expression built from the local corpus, avoiding per-peer probing while keeping results consistent.
  • Improve inline documentation around FTS5 behavior, tokenizer differences, and the rationale for the rarest-token trim constant.

Documentation:

  • Document the FTS5 recall fix, rarest-token disjunction strategy, and its measured impact on zero-hit rates and latency in the v4 changelog, including behavior under environments lacking fts5vocab and the distinction between conjunctive and disjunctive query builders.

Tests:

  • Add a dedicated test suite validating FTS5 recall behavior for multi-word queries, token rarity ordering, tokenizer usage, stem handling, escaping, and fallbacks, including mutation-style coverage of key properties.
  • Update existing FTS5 query tests to assert disjunctive behavior and that beliefs containing all tokens still rank first under BM25 scoring.

`_escape_fts5_query` joined every whitespace token with spaces, which
FTS5 reads as an implicit AND. Requiring every token to be present is
zero-recall on the multi-word natural-language queries the lane exists
to serve: measured over 503 distinct logged prompts against a live
44,584-belief store, it returned nothing for 28.7% of user turns and
100% of harness blocks.

`search_beliefs`, `search_beliefs_scored` and the federated peer search
now build the MATCH expression by ORing the three lowest-document-
frequency tokens. Zero-hit rate goes to 0.0% on both arms while holding
91.3% of the conjunctive lane's top-20 hits, indistinguishable from a
full OR's 91.4% and at a ninth of its cost (4.91 ms vs 43.67 ms p50 on
the user arm).

Rarity is resolved through FTS5's own tokenizer via two per-connection
TEMP virtual tables — no migration, nothing written to the database
file, and safe on a read-only DB. Reusing the Python tokenizer instead
would resolve only 62.9% of tokens against 99.7%, and unresolved tokens
collapse to df 0 and masquerade as the rarest, which is the failure
#1158 already records for the IDF-clip lane. The expression carries the
original tokens rather than the stems it ranked by, because the porter
stemmer is not idempotent: 416 of that store's 15,208 terms stem again
to something else.

`_escape_fts5_query` is retained for callers that do want every token
present. `test_multi_word_query_implicit_and` asserted the old contract
and is rewritten to assert the new one, including that the belief
containing every token still ranks first.

Refs #1177, #1158
Six properties, each chosen so that reverting the corresponding piece of
the fix fails one of them: the cliff itself (a multi-word query where no
belief holds every term returns hits, with the conjunctive builder shown
still reproducing the empty result on the same fixture, so the test
cannot pass by the corpus happening to contain every token); the trim
ordering by document frequency rather than query position; resolution
through FTS5's own tokenizer for the underscore and diacritic cases
where the Python tokenizer disagrees; the expression carrying original
tokens rather than re-emitted stems; escaping still holding for FTS5
operators, punctuation and embedded quotes; and the fallback to a full
OR when the probe is unavailable.

Refs #1177, #1158
@robotrocketscience robotrocketscience added the author-Setr PR coordination mutex label Jul 31, 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

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@robotrocketscience, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 35 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 28568cb0-7771-4ee3-98bc-02914a45bc28

📥 Commits

Reviewing files that changed from the base of the PR and between f59af65 and c6ba460.

📒 Files selected for processing (4)
  • CHANGELOG/v4.md
  • src/aelfrice/store.py
  • tests/test_fts5_query_escaping.py
  • tests/test_fts5_recall_1177.py

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.

@robotrocketscience robotrocketscience added the attn:review Needs review (PR open, awaiting reviewer) label Jul 31, 2026
@sourcery-ai

sourcery-ai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Reviewer's Guide

Switches the FTS5 search lane from implicitly AND-ing all query tokens to OR-ing the rarest few tokens by corpus document frequency, using per-connection TEMP vocab/probe tables, with robust fallbacks and regression tests to ensure recall, correctness, and safety of query escaping.

Sequence diagram for the updated rarest-token FTS5 MATCH construction

sequenceDiagram
    actor User
    participant MemoryStore
    participant SQLite

    User->>MemoryStore: search_beliefs(query)
    activate MemoryStore
    MemoryStore->>MemoryStore: _fts5_match_expression(query)
    alt [no tokens]
        MemoryStore-->>MemoryStore: return ""
    else [tokens present]
        alt [len(tokens) <= _FTS5_RAREST_TERMS]
            MemoryStore->>MemoryStore: _escape_fts5_query_disjunctive(query)
            MemoryStore-->>MemoryStore: MATCH expression (OR all tokens)
        else [len(tokens) > _FTS5_RAREST_TERMS]
            MemoryStore->>MemoryStore: _fts5_rarest_tokens(tokens, keep)
            MemoryStore->>MemoryStore: _ensure_fts5_query_probe()
            alt [probe available]
                MemoryStore->>SQLite: CREATE TEMP fts5_vocab_1177 / fts5_probe_1177 / fts5_probe_terms_1177
                MemoryStore->>SQLite: INSERT tokens INTO temp.fts5_probe_1177
                MemoryStore->>SQLite: SELECT term, doc FROM temp.fts5_probe_terms_1177
                MemoryStore->>SQLite: SELECT doc FROM temp.fts5_vocab_1177 WHERE term = ?
                MemoryStore-->>MemoryStore: rarest tokens by df
                MemoryStore-->>MemoryStore: MATCH expression (OR rarest tokens)
            else [probe unavailable or no tokens resolved]
                MemoryStore->>MemoryStore: _escape_fts5_query_disjunctive(query)
                MemoryStore-->>MemoryStore: MATCH expression (OR all tokens)
            end
        end
    end
    MemoryStore->>SQLite: SELECT ... FROM beliefs_fts WHERE beliefs_fts MATCH ?
    MemoryStore-->>User: list[Belief]
    deactivate MemoryStore
Loading

File-Level Changes

Change Details Files
Introduce reusable FTS5 token quoting and a disjunctive query builder, keeping the original conjunctive helper for strict callers.
  • Add _quote_fts5_token helper to centralize token quoting with correct FTS5 escaping semantics.
  • Refactor _escape_fts5_query to use _quote_fts5_token but keep its behavior as a conjunctive (implicit AND) builder for whitespace-separated tokens.
  • Add _escape_fts5_query_disjunctive to build an explicit OR expression over all tokens as a fallback when corpus statistics are unavailable.
src/aelfrice/store.py
Add a rarest-token-based MATCH expression builder backed by TEMP FTS5 vocab/probe tables, and wire it into all belief search entrypoints.
  • Introduce _FTS5_RAREST_TERMS constant (default 3) with detailed rationale documenting performance vs. recall tradeoff from production measurements.
  • Add _fts5_probe_state on MemoryStore to lazily track availability of TEMP vocab/probe tables per connection.
  • Implement _ensure_fts5_query_probe to create per-connection TEMP virtual tables fts5_vocab_1177, fts5_probe_1177, and fts5_probe_terms_1177, with graceful degradation when fts5vocab is unavailable.
  • Implement _fts5_rarest_tokens to map raw query tokens through FTS5's tokenizer, look up document frequencies via fts5_vocab_1177, and return the keep rarest original tokens (dropping non-indexing tokens and handling multi-term tokens).
  • Implement _fts5_match_expression to build the MATCH string used by search_beliefs / search_beliefs_scored / search_peer_beliefs: tokenize, short-circuit for small queries, otherwise trim to the rarest tokens using _fts5_rarest_tokens, falling back to the full OR expression when necessary.
  • Update search_beliefs, search_beliefs_scored, and search_peer_beliefs to use _fts5_match_expression instead of the old conjunctive _escape_fts5_query, and adjust their docstrings to describe disjunctive rarest-token behavior and peer behavior.
  • Ensure peer search builds the MATCH expression once from the local corpus and reuses it for all peers to avoid per-peer vocab scans.
src/aelfrice/store.py
Add comprehensive tests to pin the new recall behavior, tokenizer semantics, query construction, and fallbacks.
  • Create tests/test_fts5_recall_1177.py with a tmp-backed MemoryStore fixture and six core properties: non-zero recall for multi-word queries where no belief contains all tokens; trim ordering by document frequency; use of FTS5’s tokenizer (underscore/diacritic cases); emitting original tokens instead of stems; preserving escaping against FTS5 operators/punctuation; and preserving the conjunctive builder.
  • Add tests for blank-query short-circuiting, disjunctive fallback behavior, and behavior when the fts5vocab-based probe is unavailable (forcing the full OR path).
  • Access internal helpers (_fts5_rarest_tokens and _fts5_match_expression) in tests to assert ordering and expression shape, treating them as part of the contract for this behavior.
tests/test_fts5_recall_1177.py
Update an existing query-escaping test to reflect the new disjunctive contract while asserting ranking of full matches.
  • Rename and rewrite test_multi_word_query_implicit_and to test_multi_word_query_is_disjunctive_and_ranks_the_full_match_first, changing its expectations from a strict AND match set to disjunctive behavior where the belief containing all tokens ranks first but partial matches are also returned.
  • Maintain assertions on result set membership and ranking to ensure bm25 still promotes the belief containing all tokens, validating that partial matching is safe.
  • Update the test docstring to explain the prior implicit-AND behavior and the new disjunctive contract in the context of the recall cliff fixed by [R&D] Substrate alternates — log totality, index engine, pack construction, injection ledger #1177.
tests/test_fts5_query_escaping.py
Document the behavioral change and its measured impact in the v4 changelog.
  • Add a detailed v4 Fixed entry describing the FTS5 lane’s previous implicit-AND behavior and its zero-recall impact on real prompts.
  • Describe the new rarest-3-token OR behavior, its recall (zero-hit rate) and top-20 agreement vs. full OR, and the latency trade-off that motivated choosing 3 as the knee.
  • Document that rarity is based on corpus document frequency (not a stopword list), that TEMP vocab/probe tables avoid schema migrations and work on read-only DBs with graceful full-OR degradation, and that queries are resolved via FTS5’s tokenizer while expressions still carry original tokens due to non-idempotent stemming.
  • Clarify that _escape_fts5_query remains available for strict conjunctive callers and that the substrate swap aspect of [R&D] Substrate alternates — log totality, index engine, pack construction, injection ledger #1177 remains out of scope for this change.
CHANGELOG/v4.md

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

@github-actions

Copy link
Copy Markdown

PR-size soft cap

This PR is over the advisory size threshold:

  • 474 changed lines (limit: 200)
  • 4 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.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Toug:2026-07-31T02:58:09Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Reviewed against a build of the branch head. This is good work and I have no
blocking findings — two nits, both measured and both small enough that I would
not hold the merge for either.

What I verified independently

  • The mutation table reproduces. I ran three of them myself rather than
    taking the table on trust: reverting search_beliefs to the implicit AND
    fails 3 (test_multi_word_query_is_not_zero_recall,
    test_search_still_works_when_the_probe_is_unavailable, and the rewritten
    test_multi_word_query_is_disjunctive_and_ranks_the_full_match_first);
    dropping the df sort fails test_trim_ranks_by_document_frequency; emitting
    a resolved term instead of the original token fails
    test_ranking_uses_fts5_tokenizer_not_a_python_approximation. 28 pass
    unmutated.
  • The cliff is real and closed. On a purpose-built corpus, the retrieval budget zarf common text returns 0 hits conjunctively and 61 through
    the new path; the retrieval budget quokka goes 0 → 66.
  • Single-token callers are untouched. context_rebuilder searches
    per-token (search_beliefs(tok, …)), which takes the len(tokens) <= keep
    path and produces the same single quoted term as before. That was the caller
    I was most worried about when the semantics of a shared method change.
  • Full suite on the branch head: 6544 passed, 69 skipped, 71 xfailed
    matches the number in the body exactly. CI clean, no failures; discretion
    grep clean on added lines.

Two things I want to call out as good because they are the kind of thing that
normally ships as a silent defect: resolving tokens through FTS5's own
tokenizer rather than the Python one (62.9% → 99.7%), and carrying original
tokens rather than stems because porter is not idempotent. Both were found by
measurement, and the second is checked across the whole vocabulary rather than
spot-checked.

Nit 1 — the trim is order-dependent when document frequencies tie

ranked.sort(key=lambda pair: pair[0]) sorts on df alone. Python's sort is
stable, so ties break on the token's position in the query. The candidate set
is therefore a function of the token sequence, not the token set — two
phrasings of the same question can retrieve differently, and the agreement
metric cannot see it because it only evaluates one phrasing.

I initially thought this was significant. It is not, and I want to be explicit
that my first probe overstated it: a synthetic fixture with 60 identical-df
documents made ties look ubiquitous. On real traffic they are rare. Reversing
the token order of 500 distinct logged user prompts:

rate
candidate set changes under reversal 3 / 500 = 0.6%
a duplicate token consumes a rarest slot 3 / 500 = 0.6%

Sorting on (df, token) instead of df would make the result a function of
the token set for free, and deduping tokens would recover the wasted slot.
Both are one-liners. At 0.6% I would not block on either — your call whether
they are worth a follow-up commit or a comment noting the tie-break is
positional by design.

Nit 2 — len(tokens) <= keep bypasses the trim entirely

A 3-token query goes to the full-OR builder including its commonest term, so
the retrieval budget ORs "the". Cost is driven by the posting-list length
of the terms, not by how many there are, so a short query containing a very
common word pays roughly what the full OR pays on that term — the case the
trim exists to avoid. Your p50 is measured over real prompts, which are mostly
longer, so this does not show up there. Bounded at 3 terms and almost
certainly not worth code; worth a sentence in the docstring so the next reader
does not assume short queries are also trimmed.

On the scope discipline

The disposition was "fix the recall cliff on the existing lane, price the
trade, do not carry the substrate swap along." That is exactly what landed,
and folding it into the #1158 sub-item rather than opening a third issue for
the same surface is the right call.

I also want to flag agreement with the paragraph limiting the claim: on the 49
user queries and 332 harness blocks where the conjunctive lane returned
nothing, every hit is new and unverified. This PR establishes that the lane
stops being silent, not that the new hits rank well. Stating that explicitly
rather than letting 91.3% imply more than it does is the right way to report
it.

Approved (posted as a comment — the review API rejects it as a self-approval
since sessions share one account). Adding ready-to-merge.

@robotrocketscience robotrocketscience added the ready-to-merge Trigger merge-train: FF main to this PR's head label Jul 31, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Toug:2026-07-31T03:21:50Z]

@github-actions
github-actions Bot merged commit c6ba460 into main Jul 31, 2026
35 of 42 checks passed
@github-actions github-actions Bot removed the ready-to-merge Trigger merge-train: FF main to this PR's head label Jul 31, 2026
@github-actions

Copy link
Copy Markdown

merge-train: merged c6ba460main via FF push.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Kulili:2026-07-31T03:45:15Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Kulili:2026-07-31T03:45:29Z]

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

Labels

author-Setr PR coordination mutex

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant