Repository navigation
fix(code-intelligence): a failed embedding cache write no longer discards the embedding (#13437) - #13495
Merged
Conversation
…ards the embedding (#13437) _generate_embedding wrapped the embedding generation AND the cache write in one try/except Exception: embedding = await generate_embedding_with_fallback(...) if embedding: await self._embedding_cache.put(...) # same try return embedding So a failing EmbeddingCache.put threw away a perfectly good vector and fell through to the SHA-256 pseudo-embedding. Every pattern written while the cache misbehaved was indexed under a vector that encodes nothing about the code, silently degrading code_patterns similarity search -- the failure is invisible because a well-shaped vector is still returned. The cache is an optimisation. Its failure must never change the value returned, so generation and the write are now guarded separately. A cache-write failure logs and returns the real embedding; the hash fallback is reached only when generation itself fails or yields nothing. This is the third defect of this shape in this one function. #12374 and #12407 were both a broad except swallowing something and silently substituting the pseudo-embedding; the existing test file was written for #12407 and covers generation, not the write. Three new tests close that gap and also pin that the fallback stays reachable -- this narrows the except, it does not remove it. Extracted _hash_pseudo_embedding while here: the function had grown to 43 code lines against the repo's 30-line rule (31 before this change). It is now 34 and the helper is 5, and the fallback's docstring can state plainly that it encodes nothing semantic. Mutation-checked: restoring the combined try fails test_returns_the_real_embedding_when_the_cache_write_fails. 10 passed. Also reformatted a malformed "# nosec B110 - prose" annotation: bandit parses everything after the test id as further ids and warns per word, which made the no-severity-floor bandit run non-silent for anyone touching this file.
Contributor
✅ SSOT Configuration Compliance: Passing🎉 No hardcoded values detected that have SSOT config equivalents! |
This was referenced Aug 4, 2026
Open
mrveiss
added a commit
that referenced
this pull request
Aug 4, 2026
* chore: claim worktree * fix(lint): keep prose out of the nosec test-id list (#13521) Bandit parses everything after the suppression token as a whitespace-separated list of test IDs, so a trailing explanation becomes a list of bogus IDs and it warns once per word. One line in context_window_manager.py produced three warnings; slash_command_handler.py produced twenty. That matters because code-quality.yml runs bandit with no severity floor, so its output is expected to be silent, and scripts/pr-preflight.sh treats a non-empty bandit run as a failure. It was a papercut that recurred: it fired in three separate PRs on one day -- #13438, #13434 and #13495 -- each fixed a single file at a time by whoever happened to be editing it. 390 annotations across 191 files are rewritten from }, # nosec B105 - config dict; token_estimation is not a credential to }, # nosec B105 # config dict; token_estimation is not a credential The second hash ends bandit's parse, so the explanation survives and the annotation is silent. Verified rather than assumed before applying: the old form emits 6 warnings on a one-line file, this form and the preceding-comment form both emit 0, and both still suppress. Four separator variants were present (-, en dash, em dash, colon); all are handled. One prose comment in api/database_mcp.py also had to be reworded -- it discussed the token in running text, which bandit cannot distinguish from a real annotation. scripts/check_nosec_format.py enforces the format, wired into code-quality.yml beside the other repo-invariant checks. Its own tests assemble the token at runtime rather than spelling it out, because bandit scans test files too and literal fixtures would have made the checker's tests the last violation. Also worth recording: the old form is not as narrowly scoped as its authors intended. "# nosec B105 - explanation" discards the unrecognised extras, so it reads as scoped while behaving as a bare "# nosec B105". Verification: malformed annotations remaining 0 bandit "Test in comment" warnings 0 across autobot-backend, autobot_shared, autobot-slm-backend, scripts, tools suppressions still effective identical issue counts on a 5-file sample, base vs branch 194 rewritten files parsed with ast 0 syntax errors 430 annotations, per-file id multisets 0 files changed their id set a2a + utils + api + repo_tests + scripts 2414 passed checker tests 13 passed Two things the rewrite exposed, both handled: The longer annotation pushed three lines past 120 characters, black reflowed them, and the suppression landed on the closing-bracket line -- orphaned from the expression it protected. That is the #9489 hazard database_mcp.py already documents. Those three now put the prose on its own line above the statement, where black cannot move it. Verified: orphan count is 38 on base and 38 on the branch, zero files made worse. Investigating those three showed 38 suppressions were ALREADY orphaned on base and suppressing nothing. Filed as #13528 rather than fixed here -- clearing the 390 warnings is what made them visible. Also narrows pr-preflight.sh's bandit gate: "nosec encountered, but no failed test" is bandit reporting an unnecessary suppression in a file you happened to touch, not a defect in your change. Blocking on it would stop anyone editing a file that carries a stale suppression.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #13437
Thinking Path
_generate_embeddingwrapped generation and the cache write in onetry/except Exception:A failing
EmbeddingCache.puttherefore discarded a perfectly good vector and fell through to the SHA-256 pseudo-embedding. What makes it costly is that it fails invisibly — a well-shaped 768-dimension vector still comes back, so nothing errors. Every pattern written while the cache misbehaved was indexed under a vector encoding nothing about the code, quietly degradingcode_patternssimilarity search.The cache is an optimisation. Its failure must never change the value returned.
This is the third defect of this shape in this one function. #12374 and #12407 were both a broad
exceptswallowing something and silently substituting the pseudo-embedding. The existing test file was written for #12407 — it covers generation thoroughly and the write not at all, which is exactly the gap this fell through.What Changed
pattern_analysis/analyzer.py_hash_pseudo_embeddingextractedpattern_analysis/embedding_test.pyA cache-write failure now logs and returns the real embedding. The hash fallback is reached only when generation itself fails or yields nothing — two of the new tests pin that, because this narrows the
except, it does not remove it.Function length. The change pushed
_generate_embeddingto 43 code lines against the repo's 30-line rule (it was 31 before). Extracting_hash_pseudo_embeddingbrings it to 34, with a 5-line helper — and lets the fallback's docstring say plainly that it encodes nothing semantic, which is the property that makes silently returning it harmful.Verification
Mutation-checked. Restoring the combined
tryfails the new test:The two fallback tests pass under that mutation as well as after the fix, which is correct — they guard a path the change deliberately preserves.
Model Used
Claude Opus 5 (1M context)