mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-23 23:55:50 +00:00
* fix(memory): improve recall delivery and multilingual fallback
* fix(memory): bound heuristic recall scoring
* test(memory): pin initial recall budget with fake timers
Rewrite the slow-recall test to assert with fake timers that the main
request is still held 1 ms inside the 100 ms initial budget and proceeds
without memory at expiry, so budget changes can no longer pass unnoticed.
* test(memory): pin recall budget and scoring contracts
Address review findings with mutation-verified pins:
- settle-early: bounded wait ends when recall settles, not at full budget
- Cron and ToolResult consume points stay zero-wait
- post-wait replacement guard refuses stale handles
- type boost flips the winner (tie-break no longer masks its removal)
- hiragana-only coverage for the CJK tokenizer
- design doc: RFC #7040 sets no numeric overhead target; fix attribution
* fix(memory): preserve recall field weighting
* fix(memory): recall relevant topics beyond scan cap (#8803)
* fix(memory): bound recall candidates after full scan
* test(memory): pin bounded selector inputs
* fix(memory): preserve bounded recall candidates
* fix(memory): preserve lexical recall candidates
* fix(memory): prioritize lexical model candidates
* fix(core): preserve UTF-16 manifest boundaries
* fix(memory): address recall review feedback
* test(memory): measure recall rollout gate against the pre-change scorer
RFC #7040 gates the multilingual precision change on evidence that English
Recall@5 and no-result precision do not regress. Add a labeled 45-case
corpus and an evaluation harness that scores both the shipped deterministic
selector and a frozen copy of the pre-change scorer over it, so the gate is
reproducible rather than asserted.
* fix(memory): deliver a deterministic fast recall result on the initial turn
The initial-turn budget is 100 ms, but recall awaits the model selector,
which is a network side query with a 30 s ceiling. The budget therefore
expires on the common path and delivery falls through to the ToolResult
point — which a tool-free turn never reaches, so the result is discarded as
no_safe_delivery_point. That is the case memory matters most for.
Publish the deterministic candidates that selectModelCandidateDocuments
already computes, before blocking on the selector, and inject them when the
budget expires. The refined result still lands at ToolResult, with documents
the fast phase already delivered filtered out.
phase telemetry now carries both stages: phase is the delivery stage, strategy
is the selection method, and they are orthogonal.
* docs(memory): record the fast-path decision and phase/strategy split
* test(memory): report the mixed-language slice in the rollout gate
* docs(memory): align recall docs on the deterministic fast path
memory-system.md documented recall selection but never documented delivery, so
the delivery telemetry from #7393 was undocumented and the fast path had no
home in the canonical reference. Add a delivery section and the delivery event
table, and correct two docs that still described the single-path behaviour.
* fix(memory): use Array<T> for the fast-path test doc lists
@typescript-eslint/array-type forbids T[] for non-simple types.
* docs(memory): clarify recall delivery telemetry
* fix(memory): report already-delivered recall count
* docs(memory): align recall delivery claims
* fix(memory): rank ties by recency and record fast-delivered discards
Three review follow-ups on the recall reliability change.
Tie-break: `selectRelevantAutoMemoryDocuments` broke score ties with
`type.localeCompare`, which orders feedback < project < reference < user.
That was tolerable while the result was five documents wide; the fast path
takes only MAX_FAST_RECALL_DOCS = 2, so a tied user-typed document was
dropped every time — the exact memory a tool-free turn exists to surface.
Ties now fall to recency, then to input order, which keeps the
project-before-user precedence the concatenation already establishes.
Corpus: the case labeled `semantic-no-lexical` had no relevant documents,
so it was a no-result case wearing the wrong label and nothing measured the
cost of "no lexical match, no score". Relabel it and add three genuine
answerable-but-lexically-disjoint cases. Both scorers return nothing for
them, so the slice sits outside the quality floor and is asserted
separately: the fast path closes the timing gap, not the matching gap.
Tool-free delivery is 92.3%, not 100%, and the residual is that slice.
Telemetry: a tool-free turn logs its terminal event from the discard path,
which did not apply the fast-phase exclusion. A turn whose every selected
document had already been fast-delivered was recorded as
`no_safe_delivery_point`, inflating the "memory never reached the model"
bucket with turns that got it. Apply the same rule the ToolResult consume
point uses; a partial overlap still reports the cancellation reason.
* docs(memory): state the candidate-cap trade and the per-turn document count
Two review follow-ups, documentation only. No behaviour change.
"Removes the 200-document cap" oversold the candidate change. What it does
is swap a per-scope, query-blind recency truncation for a global,
query-aware one, and the effect is not a uniform widening: at or under 200
documents nothing was excluded by count under either design, but the new
25,000-byte manifest budget is a ceiling the old path lacked; between 200
and 400 with neither scope over 200 the old path sent every document and
the new one sends at most 200, so fewer reach the model; only a scope over
200 is the case the change is actually for. Record all three, plus the fact
that the manifest budget packs rather than prefixes.
MAX_RELEVANT_DOCS = 5 bounds one prompt, not one turn. A fast delivery of
two plus a refined delivery of five disjoint documents puts seven in front
of the model; dedupe removes repeats, not the sum. This follows from
dropping combined fast/refined budget accounting, which was a deliberate
choice, but the number was never written down next to the constant that
reads like a hard cap.
* fix(memory): end the initial recall wait on the fast result, widen tokenization
The 100 ms initial budget was a fixed cost, and the evidence for it measured
the wrong thing. Deterministic *scoring* is microseconds, but the fast result
is only published once recall has enumerated, read, and parsed the memory
tree — and this branch removed the 200-document cap for recall, so that scan
grows with the tree. recall-scan-latency.test.ts adds that measurement
against a real temporary tree: ~29 ms at 200 topics, ~70 ms at 500, ~130 ms
at 1000.
So for any tree small enough to scan in time — the ordinary case — the fast
result was in hand tens of milliseconds before the budget expired, and the
rest of the budget was spent waiting on a model selector this design already
assumes will miss it. The wait now ends on whichever comes first: recall
settling, the fast result being published, cancellation, or the ceiling. The
preference order is unchanged, because the code after the wait still prefers
a settled recall. Past roughly a thousand topics the scan alone exceeds the
ceiling and the turn pays the full budget for nothing; that is recorded as a
known limitation rather than fixed, since the fix is a persistent catalog.
Tokenization kept only [a-z0-9]{3,} runs, so Cyrillic, Greek, Arabic, and
accented Latin produced no tokens at all and the deterministic path was
unconditionally silent for them. Keep whole runs of non-CJK letters, marks,
and digits instead. CJK is excluded per character rather than by alternation
order: \p{L} also matches Han, so a Latin-initial run would otherwise swallow
the CJK after it and turn abc漢字 into one token. Scripts without word
separators outside the CJK set still collapse to one run, which is recorded
rather than claimed as segmentation.
Two smaller follow-ups. The active-tool alias set is now derived once per
recall instead of once per scanned document, which mattered little under the
old 200-document cap and more without it. And the eval prints the Recall@5 a
query-blind random scorer would score on this corpus (20%), with a test
holding that floor at or below 25%, because a small corpus flatters every
design and the headline was unreadable without it.
* docs(memory): correct the initial-turn preference claim, pin it with a test
Local end-to-end verification on #8716 found the claim added in
|
||
|---|---|---|
| .. | ||
| assets | ||
| design | ||
| developers | ||
| e2e-tests | ||
| images | ||
| plans | ||
| superpowers | ||
| users | ||
| verification/abort-controller-refactor | ||
| _meta.ts | ||
| index.md | ||