feat: expire stale candidates and stop charging for a discarded field - #301
Merged
Conversation
Owner
Author
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Bundle ReportBundle size has no change ✅ Affected Assets, Files, and Routes:view changes for bundle: engram-frontend-client-array-pushAssets Changed:
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #301 +/- ##
==========================================
- Coverage 91.03% 91.03% -0.01%
==========================================
Files 221 222 +1
Lines 23099 23195 +96
==========================================
+ Hits 21029 21116 +87
- Misses 2070 2079 +9 ☔ View full report in Codecov by Harness. |
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.
Two small, independently-safe changes carved out of a larger attempt that did not survive review (see "What was dropped").
Stop paying for a field we throw away
parse_extraction_outputrejected any extraction output whose top-level keys were not exactly{memories, no_signal_observation_ids}— and then, ~18 lines later, recomputedno_signal_observation_idsfrom the chunk manifest, discarding whatever the model sent:So omitting that key bought a
provider_output_malformed→ PROVIDER_TRANSIENT → paid retry, for a value that never reaches anything. Unknown keys are still rejected; a missingmemoriesis still rejected.One existing test used exactly this omission as its definition of "malformed", so it now uses a genuinely missing key instead — noted because it is a behaviour change, not a test tweak.
Candidate review TTL
candidate_ttl.pywas a no-op: it ran the reconciler and returnedrejected=0unconditionally. Nothing ever expired, which is why 5,836 candidates have accumulated inproposed.It now expires stale proposed candidates with an audit trail (
MemoryAutoRejected, reasonreview_ttl_expired, TTL days, timestamps), skipping anything with an unresolved conflict or active decision work. Rows are preserved —PROPOSED → REJECTED, never deleted.Two safety properties, both tested:
execute()with no arguments previews.ENGRAM_CANDIDATE_TTL_DRY_RUNis a kill switch, not a default. When set, the sweep can only preview, whatever the caller passes. An operator can freeze it without finding every call site.Operator path:
engram_expire_stale_candidatespreviews and prints what it would expire;--applyperforms it.What was dropped, and why
A sibling attempt precomputed the judge's feasible verdict set to avoid paying for impossible decisions. Two reviewers found it made things worse, and I verified both:
judge_policy_deniedwas reclassified to INVALID_INPUT, andwork_execution.py:883makes INVALID_INPUT terminal on the first failure, bypassing the streak. An honest answer would have killed the candidate permanently on attempt 1.merge_evidenceroutes to_execute_candidate_revision— it writes a new version of the target memory using the candidate's text. Handing the model a menu of only-identity options and ordering it to copy one verbatim would coerce a false merge that overwrites stored memories.Both are data-integrity failures, not cost bugs. The underlying fix is to invert the contract — the model answers only per-target relations and code derives the outcome — which is a separate, properly-designed slice.
Verification
docker compose -p engram-curation run --rm app pytest -q engram→ 3514 passed.ruff check engram→ clean.🤖 Generated with Claude Code