Skip to content

fix(memory): warn on hidden owned-private matches in list/search - #405

Merged
coryzibell merged 6 commits into
mainfrom
fix/list-search-private-hint
Jul 8, 2026
Merged

fix(memory): warn on hidden owned-private matches in list/search#405
coryzibell merged 6 commits into
mainfrom
fix/list-search-private-hint

Conversation

@coryzibell

@coryzibell coryzibell commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Closes #400

Summary

Implements option (a) from #400, per the Savorist's decision: list and search run public-only by default and silently drop the caller's own private matches (an ~85% undercount versus wake, which includes them). Rather than change the default visibility, we now emit a best-effort hint on stderr when the caller's own private matches are being hidden.

  • Zero change to stdout, --json output, or exit codes — the hint is stderr only.
  • Default visibility is unchanged (public-only stays the default).
  • Option (b) — changing the default to include private — was explicitly rejected.
  • The hint fires only when the context is public-only and an agent is set. --include-private and --mine both resolve to include_private=true and silence the hint.

Also documents the raw-vs-decayed --min-resonance divergence in the CLI help text: wake filters on raw stored resonance, while list/search filter on decayed effective resonance. This is a documentation-only clarification pending #404's explicit --resonance-basis flag.

Files affected

  • src/store.rs — new trait method owned_private_matching.
  • src/surreal_db/queries.rs — implementation; reuses build_resonance_filter / build_category_filter with an owner-scoped bound param (deliberately does not use build_visibility_filter, which would also admit public rows).
  • src/surreal_db/trait_impl.rs — delegation.
  • src/wake_ritual.rsMockStore stub.
  • src/helpers.rshidden_private_hint (pure, returns Option<String>) + warn_hidden_private (stderr wrapper).
  • src/handlers/memory.rsList/Search arms call the hint after stdout printing.
  • src/cli.rs--min-resonance help text (raw-vs-decayed note).
  • CHANGELOG.md — new entry.

Test results

  • cargo build — clean
  • cargo clippy --all-targets — clean
  • cargo test1174 lib passed + integration suites / 0 failed (16 new tests: 5 store-level, 11 hint-level; plus new integration suite tests/hidden_private_hint_cli.rs)
  • E2E verified: hint appears on stderr only; --include-private silences it.

Review fixes

Addressing the review before merge:

  • W1 — semantic-mode hint suppression. The hidden-private hint is now gated off under --semantic: semantic ranking does not run the owner-scoped count path, so the hint could misreport. Docstrings re-scoped to text search accordingly.
  • S1 — hydration-cost comment. Added a comment documenting the hydration cost of the count query so future readers understand why it stays best-effort.
  • S2 — stderr/stdout CLI integration test. New integration test asserts the hint lands on stderr and never contaminates stdout (tests/hidden_private_hint_cli.rs).
  • N2 — MockStore unreachable comment. Clarifying comment on the MockStore stub explaining why the branch is unreachable in the mock.

Bonus fix

src/surreal_db/connection.rsapply_schema now retries transient "read or write conflict" errors from concurrent embedded-SurrealDB schema init, using bounded jittered backoff. All schema statements are IF NOT EXISTS, so retries are idempotent and the happy path is untouched. This was a pre-existing flake surfaced by the new integration test running schema init concurrently.

Note

The count query is best-effort — any error is swallowed and never affects the exit code or stdout.

5Rtd1YLmJx9Gc6fxchTaCoNxLEptgCZUNoyar1n5rSXaAmeb16a9aQCamrktv8KZ5rnNmYyYaRN7kXMWagU4d2etTiBD8Pae9Vv8tFmE94nZxbZ53XpGqgoWBKpdvjgwLgFxkLHmw95XvdrMeoTk6REq3rxciC5baXtJ2RwyWfxgWjqTH78bEjXEuDoU6RrRmP6PZaQVVFCcRwe6s3TWEFN9dV8WAfcmAGrTrK1GpQHdN6Da3rSHVKxQJiseq9hqGMiz1vErkvnWkzxsaKp32ddG4RxiK36VTYNbHG9WMxFAQ4a3tFor9UmpKo8uYFkMN4o4QuiC2pCvQ51XaputFSTtZqT65RoZkWiLQWrWkNvAQMQ53CoXLVg5CN2UvYMSHN7XZJCtrqAyoNA6Yu9TuVDDeoV2tMdVHewwQ9uFLh6FxF4BtVJS5TXPWYDAqN2pcvzBrDajN4k6iRWZ5RBcjvYRn4LPoxeXTpsdiV5187eDsSUsZu7JU7iUgEeHxMWZHnawdAFnCAXKpiCkDGUoVUL8z214g6yKWsKDERhkXX3U1L6pHzQhWrtEhuPWKsrM4TvPPpiFGEBxqYvKQNX2EtFWuCGuzpCbDUFV96rK3vFX2xTGZsY73f5w7JLkzT29pd2zqxmNoF8PjWMkstNjLkZ17EZP4Xik7ydrtjix9RhFtmEuXw4QYSzEFxUA5Qh3Pb7joe8e85cZHTEMJ5dgeAUZQ6vkrg2BofZLEGcDfx9rqZZcNftPUJxnfAiSKAvfjnqoScEg4XWx5szkdGYysWqAfpbrchrBXQGwWsLVwT832v2u47a8L9wGnnQNZkrW7xMa3mb6v7g4pMZ2v24CZ3p81nmZW2MKruJjQgoKHddE5nYvHR4vdaPJACZTrYs6pw3r9xyKC9NqiSu1s5B33WEeUVZnQUpziRTmZgSthaQ2e8Q5FkYiQBAYU3Xdf93opAnvG8XUUxwjumwfwKPG72H9Qz5GquveeB8BmTmr5YXqbkKX5CSYyonPDHBLkCDjvVKpRZZ9K8Z1MutxsuH4fuWtEx9Jy67yBVS9WLc1Qxz7EVswDkUgQRPrXZmgUmSifQKwekLozuh28WD11KsvyieoKYmMNEwRE7peeYvTcjH45McBTxRuQyHoUWHJYiwwWFcaDHYczzvhbCz59TwLxgiUWrYNQSY8wrA3h4peJvNW5dPssWNLYA9XdeBgifEYv5nGmBBiVXgGEjXkVTR8GKV2ytvCTFG1usR76oRzxDN2VpjXFrEEEPwfpU7UkQto9V8fRtggKjWowNqY8vuMiiABbhZzoxrQr3eXop6McFkSGWwkYzYyp6HRddGaDyF2323G

[crc64:cuneiform|gzip:base58flickr]
VKRNKM2Q00009PMMMH30401104B00000EGNUB8V002V017IT00SPQ2SI68Q6UV1VB3ATRCQTOF38BCK5JGMDRH6OUTAI4DLF2P70GMRDR3NMI6UA5H0TBL3ECE61RSKBIVMIBT65NT59UT6104BMSJRECUHU6G2T4I087NKGK9F3BI70LJJBGGBT2R6VCL1A88K6I69JG5J0C7KVUTUORH2DH8L5TKUEIRJDBH2E1K8KO90TK1381IME8HAOFPTLQCKTJVPD3IETSF91GS3DICPQQRM1K1BCAE45T5UR61AFU000000CLE4TRU6DO4FM000RK0DV040011FPMS0R3H37VC1000000025IMG=

[xxhash3-128:barcode|lzma:base32hex]
@coryzibell

Copy link
Copy Markdown
Owner Author

Code Review (Verdictia Stern)

Verdict: REQUEST CHANGES — C 0 · W 1 · S 2 · N 2. Privacy core is clean; one warning blocks.

Critical

None. owned_private_matching_async (src/surreal_db/queries.rs:598-607) is strictly scoped visibility = 'private' AND owner = $current_agent with bound params; category names validated; deliberately avoids build_visibility_filter (would admit public rows). Owner-scoping proven by test_owned_private_matching_scopes_to_caller_only. No injection, no cross-agent leak.

Warnings

W1 — Semantic-search parity gap; docstrings overclaim (src/handlers/memory.rs:571, src/surreal_db/queries.rs:592, src/store.rs:194, src/helpers.rs:191)
search --semantic uses vector similarity, but the hint count always uses the BM25 @@ predicate and is called unconditionally. Under --semantic:

The store.rs:194 / helpers.rs:191 docs claim "the same BM25 @@ match search uses" — only true for the non-semantic branch. No test covers --semantic + hint. Fix: gate the hint off when semantic is true (or count with the semantic predicate), and scope the docstring claim to text search.

Suggestions

S1 — Hint runs a second full query with row hydration on every default list/search when an agent is set (queries.rs:618-624). Hydration is necessary for the in-memory tag/field filters, but it doubles DB work on the common path. Document as a known cost.
S2 — stdout/JSON invariance is argued structurally but not integration-tested. One stderr-vs-stdout capture test (especially on the --semantic path) would pin W1's regression surface.

Nitpicks

N1 — PR body says "4 store-level, 12 hint-level" tests; actual split is 5 + 11 (total 16 correct).
N2MockStore::owned_private_matching unreachable!() (src/wake_ritual.rs:818) is genuinely unreachable from wake paths and matches sibling stubs. Acceptable.

Cleared

Semantic neutrality (stderr-only, errors swallowed, hint after all stdout printing); filter parity for text search including limit-stripping; full trigger matrix; default visibility unchanged (decree-compliant — option (b) not implemented); CHANGELOG creation satisfies the repo's own documented convention, not scope creep; second commit is pure rustfmt.

c80b344761746520233430302070726976010df04c68696e74206f666620756e646572202d2d73656d616e7469633b2073636f706520646f63737472696e677320746f2074657874207365617263680a0a5665726469637469612073656e74205052015b1435206261636b052cb068652062656e63682028573120626c6f636b6572202b2053312f53322f4e32292e205468650a68696464656e2d1191f0466e7564676520636f756e7473206d61746368657320776974682074686520424d3235204040207072656469636174652c20776869636820646f65730a6e6f742061677265652077013158766563746f722073696d696c6172697479202d2d20736f0deb04277305c71df3242720697420626f74680a250d6c2d20616e64206f7665722d7265706f727465642072656c617469766501e03877686174202d2d696e636c7564652d11c13943380a61637475616c6c792073686f777301ef0c7265616405c900732d673820666c6167207468726f7567682068250c005f0d4b005f259d182f0a7761726e5f361b00059b1c73757070726573730551052c30656e746972656c79207768656e01ce346973207365742e0a0a57313a206721f12146052c56f50140616464206120756e69742074657374207401e50869742021603c204e4f540a202020206669726520696e19c5206d6f6465206576656e09792c61206c69746572616c20404029c424206578697374732e205345550874686505494473746f72652e7273202f2068656c70657273010d3a73020868652045770c286e6f6e559f3029206272616e63682e0a53313a01320c756d656e01af186520616363657021ad5866756c6c2d726f7720687964726174696f6e20636f737401ba106f776e65643568006d416708696e670594f04c2d2d206170706c795f656e7472795f66696c74657273206e65656473206669656c6473206120434f554e542063616e6e6f742070726f6a6563742e0a53323a206e657720434c4920696e7465670d77254b0028010604732f36c90125e4385f636c692e7273292070726f76696e4110292025a908726561650544737464657272206f6e6c792c206e657665720113506f7574202f202d2d6a736f6e2e0a4e323a20636f6d250c20776879204d6f636b532165083a3a6f52f300412204756e05631461626c6520280573046973250b016b6020696e766f6b65642066726f6d204c6973742f53656172636811848877616b65292e0a0a416c736f2068617264656e20656d62656464656420736368656d612551086963612183b03a207265747279205375727265616c44422773207472616e7369656e742c0a73656c662d6465636c617265642d052b05980027653a186f7220777269748120186e666c69637427891b206a6974746572656420817c3c6f66660a28626f756e6465642c20494641b7182045584953545301ee286964656d706f74656e742961921c69732077617320618153002d45a6218110666c616b6545fd100a636f6e6301ac206e742066726573682d45b5042069612304696e41cb32eb0104737501a01c6578706f7365643b051f006e4912000a41000427730d46044442094314746970706564616314696e746f206125437c206661696c7572652e204861707079207061746820756e61666665637465642e

[blake2s:base45|snappy:hex]
@coryzibell

Copy link
Copy Markdown
Owner Author

Re-review (Verdictia Stern) — @ 57d668a

# Finding Status
W1 Semantic-mode hint suppression RESOLVED — guard is the first thing the pure function does; Search passes the real destructured flag, List hardcodes false; docstrings re-scoped; hint_absent_under_semantic_mode flips only the flag on the same fixture, proving suppression is flag-driven
S1 Hydration-cost comment RESOLVED — comment-only change, honest justification (COUNT can't project field-presence predicates; would over-count)
S2 CLI integration test RESOLVED — drives real CARGO_BIN_EXE_mx against isolated MX_SURREAL_ROOT; proves stderr-only hint (list + search), hint-free valid --json stdout, --include-private suppression. Semantic suppression pinned at unit layer (ONNX model — correct call for a hermetic test)
N1 PR-body test split RESOLVED
N2 MockStore unreachable!() comment RESOLVED

Bonus fix (apply_schema retry): ACCEPTED. Bounded (12 attempts, ~4s worst case); narrow (all_retryable — one non-retryable error → fail-fast with the full error map); idempotency verified against the actual schema (IF NOT EXISTS/OVERWRITE/keyed UPSERT/guarded UPDATE, explicitly designed for replay); backoff math overflow-safe (shift capped, saturating mul, 320ms ceiling). No smuggling — all 8 touched files map 1:1 to findings + bonus + CHANGELOG.

New finding: N (nit) — jitter derives from SystemTime::now().subsec_nanos() % 40; on coarse-resolution clocks, lockstep processes could draw near-identical jitter. Fine on Linux, self-limiting via bounded attempts. Optional hardening: fold per-process entropy (PID) into the seed.

Overall: APPROVE

the smallest nod — the nut is good. Down the good chute.

92Funo>VXtDKrVo/xz>H=1TMQK]UrrlYn=r4NXwh<IcswdDC7?Dp=pW}j7=I[y6}BC{V>c}U7i}>*Rv-e}]S7>:6Ys0UM[YIs{b-{HN=D#MA]RC3UB$ls?wB/+R+?3j!u{r<CgV.mo$>witP-#Irq$}0KduS{$t6)gW1xZKBus)k$JF%$T<&qZLYpu#Hi-Sx13)owRLsBKAm-9^$}ANX:^]=2P=E7!td<[H5$F^H76g#H0L9L7k-ylNRqTF6.m2+M)8)Tht6tKj[H<>HRYQZRZ2i<TCB^^eKrY(r+ycG9rmyz!05AMlEoE1RL9lcd%X{t]=niK)Nk)AQ=63){LD0()l

[xxhash64:base16|zstd:z85]
…000111010010001011001011101100001101011001010011110101111110001110110100000001110110010010001000100111011100100110111110010110111101000001001110100111111010001100100010101101000100110011110011110111100011000010100001111010000101010110111000100000011111001001010111110100110110010101101100101001011000101010011110011

丯丄ꃃ並㐛ꁣ㓌ꂢ丙乯㒍ꁖꁙ㑮㓐갆갚㑤㒑걖ꂋ㑰㓉겗㒘㑴㒔갆갚㑮㓐곂东걥㓍곶ꂘ㑮㒍걒ꁘ걡㓍겗仈乤㒥ꁦ㑜걧㒕곦仙㐬亁ꀶ仚乥㒵값ꁚ㑮㒥ꁂ东걥㓑ꀧ걂갊㐙곶ꀛ乯㓝ꀲ且乒亀갳㐌丵亸갅㑜乤㒅ꁆ㑜ꀠ㒑곶仜ꀠ㓑곲丛㑡㓑갶갈乴㒡걒东ꁨ㒥ꀇ丙㑤亁갦㑚乡㓙겖ꃜ갺丨곒丛㑥㒵곷亞㐮㓑ꂗ与갠㓁ꁖ些乩㒌곖ꃛ걬㓤갇㒚㑳㒥갦걛乩㓑ꂒ丙乥㒙갗㑛乴亀겲业乩㒑걁ꁬ且㓍ꁆ㐙㑲㓈乛㓢上亀갂객ꁩ㓑겂下㐭㓍걖ꁘ㑮㓑겖仈乳㓕ꀇ东걥㓍ꀶ걛ꁮ交곂东걢ꁜ丏专ꀠ㒹곷㐙㐠㒽곢丝乨㒔亢丈乳㒡갗亙㑤亁걦걛乴㒕ꀢ丙걬㒅걷介丠㒅곦㐈乲㒅ꁲꁝ걳亵걆㑘ꁡ㓥걖㐈乢㒅ꀶ걜ꀠ㒽곢丝ꁡ㒭걒下㐭㒵겖ꂋ㑲㒕ꀶꃛ갡仱ꀂꂂ갭亁갗亘ꁨ㒥ꁆ㑘ꁴ㓕ꀦ㑋건㓥ꀃ겈乡㓁ꀆꀞ㑟㓌걔㑁丠㓈갔下中㒽곢ꁘꁯ㒹걦ꀚ㑣㓐亘각乩㒸곖㒅丠㒅ꁗ㐛ꀭ且ꀰ乓걄㑉乔乑乍㐔곦ꁙ为亁ꀇ㑘걬㒥갲ꁉ㐨丑걆㑌㐝仴仡㑒갡㓘ꁆꁙ㑭㒽ꀧ걈乳㒕갷㐚㑯㒸乔곉乥㒝걖ꂙ㑲㒅ꁆ㑙乁丰ꀇ介ꁯ㓕ꁂꃛ乬㒴곶ꁞ中㐄갥㐋걭㒐갇㒚㑡亁걆ꃘꁳ亽갧㑚㑬㒐곧仚丮

[sha3-384:binary|snappy:base1024]
d6fooyyyyyyyyy89qikc15s5eygm543mxn374tz1zinos3yfye1dmrff7zg4y33epgefu88b3bf57xohmgme5et7druex38dxq85wc35zn9eq184bp4r3onrue4r3dd84q4g1yoknc41d5zbgxknnkonqei7f6cpbqpcy6deti8zb9mob6ruofkfpc1qzf9mpy5ffqsc5g15r3zc7t5qb18ft4wdmf8f8bybsiubepau9qcyksmai1jjyndqdt9nyzee8frxni1o1j1pddibo8fr15ofxhdxfbcnhz5xpai4xb4bezbf5hgb7wiztrk8fxtin6z1nrei81z7kh5yep6zwogjza88jirkitx3adbz3djjk84xge11jse53et3nx516r7j44p9xduy3kinmb5u73tnjpd4kwxyffer55sok5godes6ypmyfjpxjs11huhon3yixfmcn3cdgeaono1dza3375n6syn7sf5tbke9wxjuqq4d8nemd76x1ojtby49irdnw4cgbdq6fn9yyuen5ccs6qy3oimpwbex1tramtmukwke667oerjpytbnb7p6eoazsh3qew4jxab149uy4tay9pqfw5rwc6xhzi68h9m4zk6zasb6f3zg7x53jmptbzakgfb173wsiug3ccwtkodjhtsscia7udk51osya3moqcn7g5mmkoi4gpfej558jtjyseo3hzeydcisocoztsm3o56qrr6n4ftxkiz6fuzu3gxdowbjaqf3dooaekbxf6736u3g6py44twnan3prw6g6hx7j756pysbnwad5i5j7bh43gni4m1w7uimwh1knjkoarh71yhwezt9rfi8w3tryfftpmsg36okd5qwd4dcwqoznyj751sxgfido7epq3h6dhs37b9sq97mq1bdggbwi6k4hwrpa5t4ycesauet8dp6tkf73o7oiin9zkjjju6q8raysdz3jwak6osu68w1ei3ry6zw6gs9mor3sx3jumpgj33ettg6otams96h9936n9isa3rgiir15ndxe7kgq457netku831ts4ehomzfkjzw9jxpqgaid5m69mu8k96ynu1jwfr6rnyyyy

[xxhash3-64:base62|gzip:base32_zbase]
@coryzibell
coryzibell merged commit 1a309c0 into main Jul 8, 2026
9 checks passed
@coryzibell
coryzibell deleted the fix/list-search-private-hint branch July 8, 2026 12:31
gabaum10 pushed a commit that referenced this pull request Jul 8, 2026
- Add exclude_tag_prefixes: Vec::new() to the hidden_private_hint
  KnowledgeFilter literal and exclude_tags: None to the
  base_filter() test helper -- the latent E0046 compile break under
  --all-targets that Cory flagged as condition (3) once
  feat/exclude-tags-search-list is rebased onto main's #405
  (hidden_private_hint).
- Add hint_respects_exclude_tags_filter: a regression test proving
  hidden_private_hint's count composes correctly with --exclude-tags
  through the shared apply_entry_filters chokepoint (condition 4).
- Regenerate docs/out/llm/mx-docs.md via docs/build.sh from the
  rebased docs/src/memory.typ instead of trusting git's line-merge,
  which had drifted from source during the rebase.
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.

list/search silently omit the caller's own private entries — default-contract mismatch with wake

1 participant