Repository navigation
Replies: 2 comments
|
The four properties hold on the published artifact, and one of them has a counterpart in an earlier thread. Versions. I read One line is strict inside its domain and silent outside itYour first and fourth properties are two readings of a single comparison. In That is The domain is the frame list, and the frame list is supplied by the object under check. I do not read that as a design accident so much as a recurring shape: when a completeness check's domain is derived from the data it is checking, strictness inside the domain carries no information about coverage of it. What that means for the two proposals in this family
That does not make the gap here worse; it makes it quieter. Your second caveat is the reason: an unfinished tail is legitimate (98 of 470 in your store), so shape cannot carry a completeness verdict — and if the torn case stops raising its hand either, then a recorded total is no longer one option of two but the only signal left. The same field also says why this cannot be closed by tightening a check. In A boundary to state with the cache witness, since it is the cheap one
What bounds it is what the cache is. The same package describes a stored record that fails schema as disposable derived data, moves it aside under So the witness holds while a checkpoint predating the damage is still there to compare against, and it is gone once a cold read has rebuilt from the log itself — where both sides then agree, because both now come from the same shorter file. If the event that shortened the log also cost the cache its record, the witness and the thing witnessed disappear together. That is not an argument against using it; it is the boundary that travels with it, and it is cheap to state now and expensive to discover later. Authorship note: code locations were read by me against the published tarballs of the versions named above; drafting assisted by AI; verification and publication by me. I have no engineering background — if any technical claim reads wrong, please call it out; I will re-verify against the toolchain and correct. Reported by the OfferKuai Team — Founder: Zhaofeng (Yaming). Website: https://www.offerkuai.com/ | Contact: contact@offerkuai.com 中文版四条性质在已发布产物上都成立,而其中一条在一个更早的帖里有一个对应物。 版本。持久化一侧我读的是 同一行:域内零容忍,域外无声你的第 1 条与第 4 条,是同一处比较的两种读法。 这在 rc.2 上是 域就是那份帧列表,而帧列表由被检查的对象自己提供。 我不把这读成设计上的偶然,而读成一种会重复出现的形状:当完整性检查的"域"由它要检查的数据自己推导时,域内的严格程度对"域覆盖了多少"不提供任何信息。 这对本族的两个提案意味着什么
这不会让这里的缺口更严重,只会让它更安静。理由正是你的第二条 caveat:未完成的尾部是合法的(你的店里 470 个里有 98 个),所以形状承担不了完整性判定 —— 而若撕裂那一类也不再举手,那么**"显式记录总数"就不再是两个选项之一,而是仅剩的信号**。 同一个字段也说明了为什么这件事没法靠收紧检查来解决。 给缓存见证划一条随行的边界(因为它是便宜的那个)你引的 约束它的是这个缓存本身是什么。 同一个包把"不通过 schema 的已存记录"描述为可丢弃的派生数据,走 所以:只要"损坏之前那个检查点"还在,见证就成立;一旦发生过一次从日志自身出发的冷读重建,它就没了 —— 因为那时两侧会一致,两侧都来自同一个更短的文件。若缩短日志的那个事件同时也让缓存丢了那条记录,见证与被见证的东西会一起消失。 这不是反对用它,这是必须随它一起说出口的那条边界 —— 现在说很便宜,日后发现很贵。 声明:上列代码位点由我本人对照上述版本的已发布产物重新通读;文稿撰写由 AI 辅助;核验与发布由我本人负责。我没有工程背景 —— 若任何技术表述有误,请直接指出,我会对照工具链重新核实并更正。我们是插件作者、不是维护者,本文不陈述任何官方政策。 本报告由 OfferKuai(Offer快)团队提交 —— 创始人:Zhaofeng(Yaming)。官网:https://www.offerkuai.com/ | 联系:contact@offerkuai.com |
|
One caveat for the witness you propose, from a store where the naive comparison already produces a false violation — plus the reason we do not assert on it ourselves. The comparison has to name which log it is comparing. A session directory can hold several generation files at once ( And the relation is one-directional in practice, not just in principle. Across the 6 canonical logs in that store that have a cache record: 5 have Why we do not report on it: the log is the store that gets rewritten, and legitimate rewrites shorten it. Our repair's committed-gap path truncates to the last |
Uh oh!
There was an error while loading. Please reload this page.
Versions: DSH
0.1.7-rc.2, Nodev22.23.2, Windows Server 2022. Package:@deepseek-ai/dsh-session-persistence-jsonl@0.1.7-rc.2(lib/index.js, SHA2560845707017ACC2B4A8A75EAB2244FA3FD88587B8094AB32014321DCE7CA1E31B).Summary. A session log is a concatenation of independent Zstandard frames. If a log loses whole trailing frames, the reader returns a shorter but perfectly valid session: every frame checksum validates, the surviving records parse, and their
seqnumbers are contiguous from 0. Nothing surfaces an error, so a truncated transcript is indistinguishable from a session that ended earlier. Damage inside a frame is caught; loss of complete frames at the tail is not.Why it happens. Four properties combine:
scanZstdFrames(index.js:1300) walks frames by index with no declared total, so the frame list simply ends earlier. A structurally valid prefix is not distinguishable from a complete file.compressZstdFramepasses onlyCHECKSUM_OPTIONS(index.js:1291, 1369); the observed frame header is descriptor0x04→contentSizeFlag=0,singleSegment=0, so the reader's header parse (index.js:1318-1324) has no per-frame length to verify against.type, version, id, createdAt, cwd, parentSession, isSeeded, origin, delegationDepth, agentPreset— no event count, byte offset, or total. (inheritedEventCountexists only for seeded/forked logs and describes the inherited prefix, not the file.)seq 0..Ncontiguous. The contiguity checks (assertContiguous,dsh-session-persistence/lib/index.js:229) validate appends against the cursor derived from the same shorter read, so appends resume seamlessly after the gap.In-process case is already handled — the gap is the restart case.
JsonlSessionHandle.observedLength(index.js:36, 102-105) already refuses a shrunken log:That covers truncation while the same process holds the handle. Because
observedLengthis a per-handle field starting at 0, a new process accepts the short log silently — and readers outside DSH never had the guard at all.Reproduction. No DSH code or fixture is needed. Against any
session*.jsonl.zstdunder the session store, cut the file at a frame boundary and compare. Measured on a real 221-frame v4 log (435,248 bytes):seqreadZstdPrefix)Drop-last-frame, showing that nothing complains:
Expected: both report
contiguous: true; the second just has fewer records and a lowerlastSeq. No exception is raised anywhere in the read path.Suggested fix, if you consider this worth closing. The cheapest option needs no format change and no writer change. The projection cache (
<root>/session_projcache/sessions/<id>.json) already persists a per-key{ver, seq, val}fold watermark, documented as able to lag the log but never lead it (dsh-session-projection-cache/lib/index.js:203-204). Socache.seq > log.lastSeqis already a witness sitting on disk. On a 470-log store,cache.seqequals the current generation's lastseqin 270/270 sessions where both exist (0 lagging, 0 leading), so the invariant is currently exact and a violation would be meaningful. Comparing them on open would surface the loss without touching the log format.Alternative, if you would rather the file be self-describing: emit a periodic
session/checkpointevent carrying the currentseqand byte offset (or running event count), or declare a total when the log is sealed.Severity, stated plainly. This is a detection gap, not a data-loss bug we can reproduce in normal operation. The write path fsyncs whole frames and rolls a partial append back to the previous size, so reaching this state requires an event that destroys already-durable data (filesystem damage, a partial restore, truncation by another tool). Across a 470-log store every log decodes cleanly; we have found no damaged log. Two further caveats worth having on the record:
turn/end(86session/end-seed, 3session/title, 3tool/call, 2 eachmodel/selection,approval/policy,step/start), and V4 keeps incomplete tails open by design (dsh-session-format-v3-to-v4: "incomplete tails retain their open transactions"). Resuming a short log also appendssession/end-seed(dsh-session/lib/index.js:1312), so it can come back looking deliberately closed. The signal has to be an explicit recorded total, not an inferred shape.All reactions