test,chore: clear the static and coverage lanes for the cache stack

Static: the cache package.json files array matches the workspace
constraint shape, the unused dsh-storage-json devDependency is dropped
(tests run on the memory backend), and docs/module-graph.md is
regenerated for the new package edge.

Coverage: two unreachable branches deleted rather than tested —
coldSnapshot's floor-0 tail reuse (a baseSeq-0 restore never throws and
an unrelated record still carries a usable watermark) and flushSoft's
non-mandatory clean-skip (throttle triggers only fire dirty). New tests
close the real gaps: write() on a never-dirty session and the non-JSON
unit-state rejection, plugin disposal clearing armed interval timers,
cachedSnapshot's all-version-mismatched and cwd-identity arms, the
zero-units empty-log cut, the coordinator seek-hook ladder (suffix /
not-found / plain failure / abort-reason relay), and the superseded-
retirement race proving forget()'s exact-entry guard.
This commit is contained in:
imccyu
2026-07-28 12:51:03 +08:00
parent ee79b7a73a
commit 019dd7d894
6 changed files with 205 additions and 13 deletions
@@ -186,9 +186,10 @@ export class SessionProjectionCache extends Service {
restored = this.ctx.sessionProjections.restore(cached, tail.events, floor)
} catch {
// The recoverable restore failures: an unrelated record, or a row
// overreaching the stored log end (or predating the floor). All
// resolve identically — discard the cache and refold the full log.
const whole = floor === 0 && related ? tail : await persistence.readFrom(id, 0, signal)
// overreaching the stored log end (or predating the floor). Both imply
// floor > 0 (baseSeq-0 restores never throw and an unrelated record
// still carried a usable watermark), so the full log is a fresh read.
const whole = await persistence.readFrom(id, 0, signal)
restored = this.ctx.sessionProjections.restore({}, whole.events, 0)
}
await this.putSoft(id, identityOf(tail.meta), restored.checkpoint, 'cold-read write-back')
@@ -237,11 +238,12 @@ export class SessionProjectionCache extends Service {
}, 'sessionProjectionCache.timers')
}
/** One fail-soft durable checkpoint: skip when clean, log on failure. */
/**
* One fail-soft durable checkpoint. Every caller has work by construction:
* the throttle triggers only fire dirty (markClean clears the timer with
* the counter) and the two mandatory points write unconditionally.
*/
private async flushSoft(session: Session, trigger: string): Promise<void> {
const state = this.dirty.get(session)
const mandatory = trigger === 'turn/end' || trigger === 'detach'
if (!mandatory && (state === undefined || state.pending === 0)) return
try {
await this.write(session)
} catch (error) {