Skip to content

fix(memory): archive stale memories only on contradiction - #38

Open
Verdenroz wants to merge 3 commits into
mainfrom
fix/reverify-contradiction
Open

Verdenroz wants to merge 3 commits into
mainfrom
fix/reverify-contradiction

Conversation

@Verdenroz

Copy link
Copy Markdown
Owner

Description

Reverify asked only whether the current code supports a stale memory, and a code chunk cannot support a claim like "Valgrind dies with SIGILL on this machine". In a dry run on the live index it reaffirmed 0 of 5, which as shipped meant marking true memories failed and archiving them after 14 days. Reverify now also asks whether the code contradicts the claim. Only a contradiction lets gc archive early, and a memory that comes back inconclusive is never archived on age. At the calibrated default the contradiction check flags nothing yet, so in practice this is the safe half of the design until a better prompt or model separates the two.

Changes

  • Added a contradiction question to ClaimScorer in verify.rs, with its own chat prompt and SystemOne question. config.prompt still applies only to the support question.
  • Added [verify] contradiction_threshold, default 0.8.
  • Changed reverify to score support first, then contradiction. Support reaffirms, contradiction stamps contradicted_at, and anything else stamps only reverified_at. The report gained a contradicted count.
  • Added schema v8: a nullable contradicted_at column. The migration clears reverified_at, since earlier checks asked only the support question.
  • Split the gc stale rule. A stale memory contradicted since it went stale is archived at once. One never checked since it went stale is archived after gc_stale_days. One checked and found inconclusive is kept.
  • Restored the gc_stale_days default to 30.
  • Added eval/contradiction-pairs.jsonl: 22 presumed-true and 6 hand-edited contradicted pairs from this repo's own memories.
  • Updated the Staleness, GC and reverify sections of docs/memory.md.

Breaking Changes

Schema moves from v7 to v8 and migrates on Store::open. Back up index.db before deploying.

Testing

Calibration with qwen3.6:35b-a3b over 91 presumed-true pairs (77 live stale memories with indexed evidence, 14 good pairs from verify-pairs.jsonl) and 27 hand-edited contradicted claims:

Threshold False contradictions (of 91) Contradictions caught (of 27)
0.5 2 8
0.7 1 3
0.8 0 0
0.9 0 0

The highest-scoring true memory and the highest-scoring edited claim both scored 0.76. The committed file holds only the rows built from this repo's content.

Checks run:

Command Result
cargo test -p scry-core 119 passed
cargo test -p scry-server 12 passed
cargo test -p scry 16 passed
cargo clippy --workspace --all-targets -- -D warnings clean
cargo fmt --all --check clean

- Unprovable from code is not wrong; reverify needs a separate
  signal for claims the evidence contradicts
- A claim code cannot prove is not wrong; age alone no longer
  archives a memory that was checked and found inconclusive
- v8 clears reverified_at: earlier checks asked only the support
  question
- 0.8 is the lowest grid value with no false contradiction on the
  presumed-true set; lower values contradicted real memories
@soothfast-bot

soothfast-bot Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

soothfast gate

scry-core

ok    scry_core::bench_chunk_rust instructions 77453608.0 -> 77453608.0 (+0.0%)
ok    scry_core::bench_chunk_rust callgrind_ir 77453608.0 -> 77453608.0 (+0.0%)
SOFT  scry_core::bench_chunk_rust walltime_median_ns 5603205.0 -> 7280515.0 (+29.9%) (counters flat on unchanged code — environment, not regression)
ok    scry_core::bench_chunk_rust allocs 3628 -> 3628 (allowed <= 3809)
ok    scry_core::bench_chunk_rust alloc_bytes 793931 -> 793931 (allowed <= 833627)
ok    scry_core::bench_dense_search instructions 83037285.0 -> 83042209.0 (+0.0%)
ok    scry_core::bench_dense_search callgrind_ir 83037285.0 -> 83042209.0 (+0.0%)
SOFT  scry_core::bench_dense_search walltime_median_ns 7428144.0 -> 9266875.0 (+24.8%) (counters flat on unchanged code — environment, not regression)
ok    scry_core::bench_dense_search allocs 11 -> 11 (allowed <= 11)
ok    scry_core::bench_dense_search alloc_bytes 16274 -> 16274 (allowed <= 17087)
ok    scry_core::bench_expand_symbols instructions 26893.0 -> 26931.0 (+0.1%)
ok    scry_core::bench_expand_symbols callgrind_ir 26893.0 -> 26931.0 (+0.1%)
SOFT  scry_core::bench_expand_symbols walltime_median_ns 1725.2 -> 2472.3 (+43.3%) (counters flat on unchanged code — environment, not regression)
ok    scry_core::bench_expand_symbols allocs 55 -> 55 (allowed <= 57)
ok    scry_core::bench_expand_symbols alloc_bytes 2366 -> 2366 (allowed <= 2484)
ok    scry_core::bench_hash_bytes instructions 1048705.0 -> 1048705.0 (+0.0%)
ok    scry_core::bench_hash_bytes callgrind_ir 1048705.0 -> 1048705.0 (+0.0%)
SOFT  scry_core::bench_hash_bytes walltime_median_ns 71722.2 -> 82274.8 (+14.7%) (counters flat on unchanged code — environment, not regression)
ok    scry_core::bench_hash_bytes allocs 0 -> 0 (allowed <= 0)
ok    scry_core::bench_hash_bytes alloc_bytes 0 -> 0 (allowed <= 0)
ok    scry_core::bench_lexical_search instructions 150829568.0 -> 150739223.0 (-0.1%)
ok    scry_core::bench_lexical_search callgrind_ir 150829568.0 -> 150739223.0 (-0.1%)
SOFT  scry_core::bench_lexical_search walltime_median_ns 12650193.0 -> 16068538.0 (+27.0%) (counters flat on unchanged code — environment, not regression)
ok    scry_core::bench_lexical_search allocs 34 -> 34 (allowed <= 35)
ok    scry_core::bench_lexical_search alloc_bytes 2467 -> 2467 (allowed <= 2590)
ok    scry_core::bench_normalize_remote_url instructions 2664.0 -> 2664.0 (+0.0%)
ok    scry_core::bench_normalize_remote_url callgrind_ir 2664.0 -> 2664.0 (+0.0%)
SOFT  scry_core::bench_normalize_remote_url walltime_median_ns 159.9 -> 236.1 (+47.6%) (counters flat on unchanged code — environment, not regression)
ok    scry_core::bench_normalize_remote_url allocs 5 -> 5 (allowed <= 5)
ok    scry_core::bench_normalize_remote_url alloc_bytes 116 -> 116 (allowed <= 121)
ok    scry_core::bench_search_with_vector instructions 366367856.0 -> 366287406.0 (-0.0%)
ok    scry_core::bench_search_with_vector callgrind_ir 366367856.0 -> 366287406.0 (-0.0%)
SOFT  scry_core::bench_search_with_vector walltime_median_ns 32133177.0 -> 39700962.0 (+23.6%) (counters flat on unchanged code — environment, not regression)
ok    scry_core::bench_search_with_vector allocs 1286 -> 1286 (allowed <= 1350)
ok    scry_core::bench_search_with_vector alloc_bytes 169014 -> 169014 (allowed <= 177464)
ok    scry_core::bench_chunk_rust assert complexity: claimed O(n); growth drift x1.00 over sizes [64, 256, 1024] (limit x2.5)
ok    scry_core::bench_hash_bytes assert alloc: allocs 0 <= 0
ok    scry_core::bench_hash_bytes assert complexity: claimed O(n); growth drift x0.98 over sizes [4096, 65536, 1048576] (limit x2.5)
ok    scry_core::bench_normalize_remote_url assert alloc: allocs 5 <= 5
gate: passed (7 item(s))

@soothfast-bot

soothfast-bot Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

soothfast verdict

4 projects: 0 regressed, 0 errored, 4 pass

docs

pass | docs | 0s

baseline merge: 2 file(s) into head: 11 item(s)
baseline merge: not measured (reused-walltime): HEAD's run came from the run cache (binary 70ab9da711f650cc); its walltime is that run's: scry_core::bench_chunk_rust, scry_core::bench_dense_search, scry_core::bench_expand_symbols, scry_core::bench_hash_bytes, scry_core::bench_lexical_search, scry_core::bench_normalize_remote_url, scry_core::bench_search_with_vector
ok    docs/search.md:43 claim `scry_core::bench_hash_bytes.alloc.allocs <= 0` (actual 0.0)
ok    docs/search.md:47 claim `scry_core::bench_normalize_remote_url.alloc.allocs <= 5` (actual 5.0)
ok    docs/search.md:51 claim `scry_core::bench_dense_search.alloc.allocs <= 11` (actual 11.0)
docs check: passed (6 file(s))

python

pass | import | 9s

test_injected_memories     10,427.0630 (>1000.0)  11,163.7280 (309.70)   10,538.0905 (>1000.0)   92.9036 (83.65)    10,509.8725 (>1000.0)   82.9650 (829.65)       11;4       94.8939 (0.00)         94           1
test_load_transcript       27,116.1860 (>1000.0)  31,815.5610 (882.61)   27,878.6792 (>1000.0)  812.4604 (731.58)   27,730.4755 (>1000.0)  515.6860 (>1000.0)       2;2       35.8697 (0.00)         34           1
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Legend:
  Outliers: 1 Standard Deviation from Mean; 1.5 IQR (InterQuartile Range) from 1st Quartile and 3rd Quartile.
  OPS: Operations Per Second, computed as 1 / Mean
============================== 4 passed in 3.51s ===============================
============================= test session starts ==============================
platform linux -- Python 3.14.4, pytest-8.4.2, pluggy-1.7.0
benchmark: 5.3.1 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
rootdir: /home/runner/work/_temp/soothfast-base-42243676777-1/plugin/plugins/scry/hooks
plugins: benchmark-5.3.1, platformdirs-4.14.3, typeguard-4.4.4
collected 4 items

tests/bench_hooks.py ....                                                [100%]
Wrote benchmark data in: bench.json



------------------------------------------------------------------------------------------------ benchmark: 4 tests -----------------------------------------------------------------------------------------------
Name (time in us)                  Min                    Max                   Mean              StdDev                 Median                 IQR            Outliers           OPS            Rounds  Iterations
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_plan_memories              7.9550 (1.0)          41.3780 (1.0)           8.3456 (1.0)        1.0834 (1.0)           8.2050 (1.0)        0.1010 (1.0)      627;1404  119,823.6545 (1.0)       28840           1
test_render_capped            762.0930 (95.80)     1,170.5750 (28.29)       790.8915 (94.77)     25.0238 (23.10)       787.7955 (96.01)     14.5880 (144.44)      51;44    1,264.3959 (0.01)       1190           1
test_injected_memories     10,419.7000 (>1000.0)  10,848.0300 (262.17)   10,519.6761 (>1000.0)   73.7123 (68.04)    10,499.8800 (>1000.0)   56.9660 (564.02)       20;8       95.0600 (0.00)         94           1
test_load_transcript       27,233.1240 (>1000.0)  32,386.6940 (782.70)   27,746.8083 (>1000.0)  893.0972 (824.37)   27,512.6110 (>1000.0)  358.2380 (>1000.0)       1;5       36.0402 (0.00)         34           1
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Legend:
  Outliers: 1 Standard Deviation from Mean; 1.5 IQR (InterQuartile Range) from 1st Quartile and 3rd Quartile.
  OPS: Operations Per Second, computed as 1 / Mean
============================== 4 passed in 3.56s ===============================
gate: build=? noise_floor=0.00% thresholds: instructions +5% ir +5% walltime +10.0% alloc/size +5% polls/wakes +5%
ok    pytest::tests/bench_hooks.py::test_injected_memories walltime_median_ns 10499880.0 -> 10509872.5 (+0.1%)
ok    pytest::tests/bench_hooks.py::test_load_transcript walltime_median_ns 27512611.0 -> 27730475.5 (+0.8%)
ok    pytest::tests/bench_hooks.py::test_plan_memories walltime_median_ns 8205.0 -> 8326.0 (+1.5%)
ok    pytest::tests/bench_hooks.py::test_render_capped walltime_median_ns 787795.5 -> 805183.0 (+2.2%)
gate: passed (4 item(s))
imported 4 benchmark(s): /home/runner/work/_temp/soothfast-baseline/import-python.json

scry-core

pass | rust | 6s

SOFT  scry_core::bench_chunk_rust walltime_median_ns 5603205.0 -> 7280515.0 (+29.9%) (counters flat on unchanged code — environment, not regression)
ok    scry_core::bench_chunk_rust allocs 3628 -> 3628 (allowed <= 3809)
ok    scry_core::bench_chunk_rust alloc_bytes 793931 -> 793931 (allowed <= 833627)
ok    scry_core::bench_dense_search instructions 83037285.0 -> 83042209.0 (+0.0%)
ok    scry_core::bench_dense_search callgrind_ir 83037285.0 -> 83042209.0 (+0.0%)
SOFT  scry_core::bench_dense_search walltime_median_ns 7428144.0 -> 9266875.0 (+24.8%) (counters flat on unchanged code — environment, not regression)
ok    scry_core::bench_dense_search allocs 11 -> 11 (allowed <= 11)
ok    scry_core::bench_dense_search alloc_bytes 16274 -> 16274 (allowed <= 17087)
ok    scry_core::bench_expand_symbols instructions 26893.0 -> 26931.0 (+0.1%)
ok    scry_core::bench_expand_symbols callgrind_ir 26893.0 -> 26931.0 (+0.1%)
SOFT  scry_core::bench_expand_symbols walltime_median_ns 1725.2 -> 2472.3 (+43.3%) (counters flat on unchanged code — environment, not regression)
ok    scry_core::bench_expand_symbols allocs 55 -> 55 (allowed <= 57)
ok    scry_core::bench_expand_symbols alloc_bytes 2366 -> 2366 (allowed <= 2484)
ok    scry_core::bench_hash_bytes instructions 1048705.0 -> 1048705.0 (+0.0%)
ok    scry_core::bench_hash_bytes callgrind_ir 1048705.0 -> 1048705.0 (+0.0%)
SOFT  scry_core::bench_hash_bytes walltime_median_ns 71722.2 -> 82274.8 (+14.7%) (counters flat on unchanged code — environment, not regression)
ok    scry_core::bench_hash_bytes allocs 0 -> 0 (allowed <= 0)
ok    scry_core::bench_hash_bytes alloc_bytes 0 -> 0 (allowed <= 0)
ok    scry_core::bench_lexical_search instructions 150829568.0 -> 150739223.0 (-0.1%)
ok    scry_core::bench_lexical_search callgrind_ir 150829568.0 -> 150739223.0 (-0.1%)
SOFT  scry_core::bench_lexical_search walltime_median_ns 12650193.0 -> 16068538.0 (+27.0%) (counters flat on unchanged code — environment, not regression)
ok    scry_core::bench_lexical_search allocs 34 -> 34 (allowed <= 35)
ok    scry_core::bench_lexical_search alloc_bytes 2467 -> 2467 (allowed <= 2590)
ok    scry_core::bench_normalize_remote_url instructions 2664.0 -> 2664.0 (+0.0%)
ok    scry_core::bench_normalize_remote_url callgrind_ir 2664.0 -> 2664.0 (+0.0%)
SOFT  scry_core::bench_normalize_remote_url walltime_median_ns 159.9 -> 236.1 (+47.6%) (counters flat on unchanged code — environment, not regression)
ok    scry_core::bench_normalize_remote_url allocs 5 -> 5 (allowed <= 5)
ok    scry_core::bench_normalize_remote_url alloc_bytes 116 -> 116 (allowed <= 121)
ok    scry_core::bench_search_with_vector instructions 366367856.0 -> 366287406.0 (-0.0%)
ok    scry_core::bench_search_with_vector callgrind_ir 366367856.0 -> 366287406.0 (-0.0%)
SOFT  scry_core::bench_search_with_vector walltime_median_ns 32133177.0 -> 39700962.0 (+23.6%) (counters flat on unchanged code — environment, not regression)
ok    scry_core::bench_search_with_vector allocs 1286 -> 1286 (allowed <= 1350)
ok    scry_core::bench_search_with_vector alloc_bytes 169014 -> 169014 (allowed <= 177464)
ok    scry_core::bench_chunk_rust assert complexity: claimed O(n); growth drift x1.00 over sizes [64, 256, 1024] (limit x2.5)
ok    scry_core::bench_hash_bytes assert alloc: allocs 0 <= 0
ok    scry_core::bench_hash_bytes assert complexity: claimed O(n); growth drift x0.98 over sizes [4096, 65536, 1048576] (limit x2.5)
ok    scry_core::bench_normalize_remote_url assert alloc: allocs 5 <= 5
baseline saved: /home/runner/work/scry/scry/.soothfast/baselines/head.json
gate: baseline "head" lists 7 item(s) of scry-core as reused-walltime (walltime): HEAD's run came from the run cache (binary 70ab9da711f650cc); its walltime is that run's
gate: passed (7 item(s))

test-rust

pass | test | 21s

test: 3 of 3 packages selected, 6 binaries run, 0 skipped (cached), doc tests in 2 packages

This branch was previously deployed

1 inactive deployment
soothfast-bot — 8e716cd6 Deployed Nov 6, 2026 by Verdenroz via soothfast / verdict #33
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.

1 participant