---
id: fuzz-cache-opseq-decision
name: Decide on the optional cache_opseq target and ticket the max_entries=0 finding
status: completed
depends_on: []
scope: narrow
risk: low
impact: phase
level: implementation
---
## Description
Resolve plan checklist item 12: decide whether `KeyCache` op-sequence
model fuzzing (`cache_opseq`) is worth a target, and, either way, turn
the existing finding into a proper ticket. This is a decision task:
write down the judgment, do not silently skip.
Known state from `docs/plans/fuzzing.md` (Scope decisions table,
Optional row) and the spec:
- `KeyCache` (`src/cache.rs`) is internal state logic with no
untrusted bytes; the family precedent (alktty `session_opseq`)
model-fuzzes this shape anyway.
- One concrete bug is already identified: a `max_entries = 0`
configuration currently grows past the bound (the eviction
comparison admits growth where the bound should hold). Verify this
claim against `src/cache.rs` before writing the ticket: read the
insert/evict path and, if true, write a failing deterministic test
as the ticket's evidence. If the bound check has since been fixed,
note that and close the item as resolved-no-ticket.
- The `service.rs` full lifecycle opseq was already decided Skip
(overlaps the deterministic suite); `cache_opseq` is the same shape
on a smaller surface, which is what makes it a real candidate.
Expected outcome is one of:
1. **Implement it now** (same wave): write the shared module with a
reference model (plain `HashMap` + `Vec` mirror of `order`/
`entries`), drive `insert`/`get`/`clear` op sequences with bounded
keys, assert `order`/`entries` consistency, the `max_entries`
length bound (including the 0 case), and LRU ordering against the
model; add the target, seeds, replay test, README row; campaign
runs follow in a later session. Pick this only if the review of
the cache internals suggests TTL/LRU interleavings beyond the 0
bound are likely hiding more.
2. **Ticket it and move on**: file the `max_entries = 0` bound bug as
a ticket either in `docs/architecture/open-questions.md` or a new
`docs/` ticket per the repo's convention, fix the bound directly
with a regression test if it is a one-line comparison fix
(`>` vs `>=` or an explicit zero-guard), and record the
`cache_opseq` decision as deferred with rationale in the plan doc.
Option 2's fix is in scope regardless of which option is picked when
the fix is small: the bound violation is a real
(though config-reachability-limited, internal-only) bug.
Update `docs/plans/fuzzing.md`: the Scope decisions row and the
checklist item 12 both move to decided state, with session notes.
## Acceptance Criteria
- [x] The `max_entries = 0` growth claim is verified against
`src/cache.rs` by direct code reading and, if true, by a
deterministic failing test
- [x] If the bug is real: `max_entries` is honored (a `max_entries =
0` config inserts and evicts without growing `entries`),
regression test added, the fix documented in session notes
- [x] The `cache_opseq` go/defer decision is recorded in
`docs/plans/fuzzing.md` (Scope decisions table + checklist
item 12 + session notes) with rationale referencing the alktty
precedent
- [x] Option 1 only: target, seeds, replay test, and README row exist
and pass; Option 2 only: the ticket exists (or the fix is
completed) and the deferral is written down
- [x] `cargo test --all-features`, `cargo test`, `cargo clippy
--all-features --all-targets` pass from the repo root
(`secp256k1` feature untouched by the cache changes)
## References
- `docs/plans/fuzzing.md` (Scope decisions table, Optional row;
checklist item 12)
- `src/cache.rs` (the eviction path around `max_entries`)
- alktty `session_opseq` precedent (referenced in the plan)
## Notes
Option 2 picked (ticket/fix and defer). Code review of `src/cache.rs`
confirmed the claim: with `max_entries = 0`, `insert` always took the
`entries.len() >= max_entries` branch, but `evict_lru` found an empty
`order` and evicted nothing, so the cache permanently held one entry
against a zero-entry bound. A comparison toggle does not fix it (a `>`
swap would admit growth outright), so the fix is an explicit
zero-guard: `insert` returns early when `max_entries == 0`, the key is
dropped and zeroized. Regression tests added in `src/cache.rs`:
`test_cache_max_entries_zero_disables_cache` (deterministically failed
before the fix) and `test_cache_max_entries_one_holds_single_lru_entry`
(edge pin). No separate ticket file: the fix is completed, which the
acceptance criteria treat as satisfying the ticket requirement. The
deferral rationale (structural consistency of `order`/`entries`,
deterministic-suite coverage of the interleavings, low residual value
on this surface, alktty `session_opseq` named as the shape to revisit)
is recorded in `docs/plans/fuzzing.md` Scope decisions row, the
Targets section note, and session 4 notes; checklist item 12 checked.
The fuzz/shared replay and `secp256k1` feature are untouched.
## Summary
Verified the `max_entries = 0` bound bug by code reading, fixed it in
`src/cache.rs` with a zero-guard plus two regression tests, deferred
the `cache_opseq` fuzz target with written rationale in
`docs/plans/fuzzing.md` (Scope decisions row, Targets note, checklist
item 12, session 4 notes), and added a CHANGELOG 0.2.0 entry for the
fix. Verification: 114/107 tests (all-features/default) pass, clippy
clean, docs clean, shared fuzz replay 8/8 pass.