fix(qa): require cache probe marker

This commit is contained in:
Vincent Koc
2026-05-03 16:50:32 -07:00
parent a94897d99c
commit de82d17de2
3 changed files with 17 additions and 4 deletions

View File

@@ -35,6 +35,7 @@ Docs: https://docs.openclaw.ai
- Channels/WhatsApp: allow `@whiskeysockets/libsignal-node` in `onlyBuiltDependencies` so pnpm v9+ `blockExoticSubdeps` no longer rejects the baileys git-tarball subdep and silences all inbound agent replies. Fixes #76539. Thanks @ottodeng and @vincentkoc.
- Gateway/systemd: preserve operator-added secrets in the Gateway env file across re-stage while clearing OpenClaw-managed keys (such as `OPENCLAW_GATEWAY_TOKEN`) so a fresh staging value is never shadowed by a stale env-file copy; operator secrets are also retained when the state-dir `.env` is empty. Fixes #76860. Thanks @hclsys.
- QA/cache: require the full `CACHE-OK <suffix>` marker before live cache probes stop retrying, so suffix-only prose cannot hide a broken probe response. Thanks @vincentkoc.
- Slack/Matrix: avoid creating blank progress-draft messages when `streaming.progress.label=false` and progress tool lines are disabled. Thanks @vincentkoc.
- QA/Matrix: keep the mock OpenAI tool-progress provider aligned with exact-marker Matrix prompts so the hardened live preview scenario still forces a deterministic read before final delivery. Thanks @vincentkoc.
- OpenAI/Google Meet: wait for realtime voice `session.updated` before treating the bridge as connected, so Meet joins do not return with audio queued behind an unconfigured realtime session. Thanks @vincentkoc.

View File

@@ -99,6 +99,13 @@ describe("live cache regression runner", () => {
text: "",
}),
).toBe(false);
expect(
__testing.shouldRetryCacheProbeText({
attempt: 1,
suffix: "openai-stable-hit-a",
text: "I saw openai-stable-hit-a.",
}),
).toBe(true);
expect(
__testing.shouldRetryCacheProbeText({
attempt: 1,

View File

@@ -136,7 +136,11 @@ function shouldRetryCacheProbeText(params: {
}): boolean {
const responseTextLower = normalizeLowercaseStringOrEmpty(params.text);
const suffixLower = normalizeLowercaseStringOrEmpty(params.suffix);
return !responseTextLower.includes(suffixLower) && params.attempt <= LIVE_CACHE_RESPONSE_RETRIES;
const markerLower = `cache-ok ${suffixLower}`;
return (
(!responseTextLower.includes(markerLower) || !responseTextLower.includes(suffixLower)) &&
params.attempt <= LIVE_CACHE_RESPONSE_RETRIES
);
}
async function runToolOnlyTurn(params: {
@@ -244,9 +248,10 @@ async function completeCacheProbe(params: {
}
const responseTextLower = normalizeLowercaseStringOrEmpty(text);
const suffixLower = normalizeLowercaseStringOrEmpty(params.suffix);
const markerLower = `cache-ok ${suffixLower}`;
assert(
responseTextLower.includes(suffixLower),
`expected response to contain ${params.suffix}, got ${JSON.stringify(text)}`,
responseTextLower.includes(markerLower),
`expected response to contain CACHE-OK ${params.suffix}, got ${JSON.stringify(text)}`,
);
const usage = normalizeCacheUsage(response.usage);
return {
@@ -256,7 +261,7 @@ async function completeCacheProbe(params: {
hitRate: computeCacheHitRate(usage),
};
}
throw new Error(`expected response to contain ${params.suffix}`);
throw new Error(`expected response to contain CACHE-OK ${params.suffix}`);
}
async function runRepeatedLane(params: {