MatchGuardContract
What a guard may and may not do, beyond what the tests check.
These were implicit until M38 held this suite to a guard written in a style none of kmemo's own eleven uses: stateful, case-sensitive, and on its own tokenizer. A suite extracted from existing implementations encodes their shared assumptions without stating them, and the assumptions only become visible when something that does not share them is measured against it. Each of these is a rule that was real and unwritten.
A guard may not be asynchronous, and no test says so because the type does. MatchGuard.evaluate is not suspend, so a guard that wanted to consult a service could not be written at all. If your check needs a network call it belongs in a dev.kmemo.Verifier, which is the seam built for exactly that and which runs on far fewer candidates.
A guard may hold state, as long as remembering changes no verdict. Nothing here is required to be a pure function, and "guards are pure" would be the wrong rule: what is required is that the verdict depends only on the two prompts it was handed. Memoization satisfies that. A counter that grows stricter over time, or a cooling-off period after a rejection, does not, and the guard is deterministic and a fresh instance agrees with the one before it are what catch it.
A guard must be thread-safe, and this suite does not check it. MatchGuard requires it and the eleven shipped guards get it for free by being stateless, so nothing here ever exercised it. A guard that keeps state on a plain map passes every test in this suite and corrupts under a real cache's concurrency. That gap is recorded rather than papered over, because a test that ran a guard from two threads and passed would prove less than this sentence does.
A guard owes nothing to any tokenizer. kmemo's guards share Text.contentTokens because it was there, not because the contract asks for it. A case-sensitive, punctuation-preserving guard on its own splitter passes this suite unmodified: the corpus properties are about verdicts rather than about how a verdict was reached.
Functions
Not an assertion: the number the guard arrives with. Printed to the build log and written to reportPath as JSON, in the same shape kmemo publishes for its own guards.