fix(compress): normalize double-escaped summaries at ingest; flag unverifiable user quotes (closes #309) - #310
Conversation
…erifiable user quotes (closes #309)
📦 Built Extension ArtifactBranch: Option A — Install from npm PR tag (recommended)pi install npm:billion-context-pi@pr-310Each push to this PR publishes a new version under the Option B — Download artifact
tar xzf billion-context-pi-pr310.tgz
pi install ./packageThis comment is automatically updated on each push. |
ranxianglei
left a comment
There was a problem hiding this comment.
审查结论(来自 #309 提交者):LGTM,可合并
验证:全测试通过(477/0)、构建成功、与 #311 无冲突。
对照 issue 规格逐项确认:① 入库前规范化——sanitizeSummary 挂在 applyCompression 之前(正确的 ingest 点),只解码 \uXXXX(含代理对),\n/\ 保持字面,阈值 20 严格大于语义(合法转义示例存活),debug 事件 summary-unescaped✓;② verbatim 无 ref 检测——只 log 不改写(summary-unverifiable-quote,含中文"原话"模式)✓;③ system-prompt 三合一规则:TASK AS OF BLOCK CREATION 时态、verbatim 必带 mNNNNN、禁止从工具输出复制 \uXXXX✓。decodeUnicodeEscapes 对孤立高位代理不崩溃也有测试。
后续可选项(不阻塞):
- findUnverifiableUserQuote 只要摘要里任意位置出现一个 mNNNNN 就放行——"一个真 ref + 一句伪造引用"的混合摘要会漏检。日志用途下保守方向可接受,将来可收紧为引用就近匹配。
- 只管新写入;存量已损坏的 sidecar 块不会追溯修复(可考虑 /acp repair 子命令;01a071dc 会话已死,不急)。
ranxianglei
left a comment
There was a problem hiding this comment.
审查结论(来自 #309 提交者):LGTM,可合并
验证:全测试通过(477/0)、构建成功、与 #311 无冲突。
对照 issue 规格逐项确认:① 入库前规范化——sanitizeSummary 挂在 applyCompression 之前(正确的 ingest 点),只解码 \uXXXX(含代理对),\n、\ 保持字面,阈值 20 严格大于语义(合法转义示例存活),debug 事件 summary-unescaped✓;② verbatim 无 ref 检测——只 log 不改写(summary-unverifiable-quote,含中文"原话"模式)✓;③ system-prompt 三合一规则:TASK AS OF BLOCK CREATION 时态、verbatim 必带 mNNNNN、禁止从工具输出复制 \uXXXX✓。decodeUnicodeEscapes 对孤立高位代理不崩溃也有测试。
后续可选项(不阻塞):
- findUnverifiableUserQuote 只要摘要里任意位置出现一个 mNNNNN 就放行——"一个真 ref + 一句伪造引用"的混合摘要会漏检。日志用途下保守方向可接受,将来可收紧为引用就近匹配。
- 只管新写入;存量已损坏的 sidecar 块不会追溯修复(可考虑 /acp repair 子命令;01a071dc 会话已死,不急)。
|
协调通知(来自 #309 提交者):写侧保真规则已按 owner 指示上移内核——acp-kernel#205 把这些规则合并进调优文本本身( 本 PR 的处理建议:保持现状合入(适配器层 SUMMARY FIDELITY RULES 与内核文本短期重复,无害,保证覆盖连续);内核发版后另开小 PR:①bump acp-kernel 到新精确版本并重建(AGENTS.md 规定)②删除本 PR 的 system-prompt.ts 新增节,由内核单一来源提供。 |
✅ 协调建议收到,已核实并同意"保持现状合入"。 验证结果
已采取动作
🔒 I can't merge PRs — system rules forbid it. Please merge yourself: #310 |
|
Follow-up amendment (owner-directed, supersedes the coordination note above): acp-kernel v0.0.55 is published — the write-side fidelity rules now live in the kernel (
Verification note: earlier "3 failing tests" reports during review were node_modules contamination from incremental |
e5a8789 to
2067261
Compare
|
Correction to my earlier comment: the "node_modules contamination / no kernel regression" conclusion was wrong — the Verified with instrumented runs on real 0.0.55:
Not kernel bugs — these 3 tests assert pre-#194/#198 semantics and need adaptation in the kernel-bump PR (bump to 0.0.55 + remove the adapter prompt section + adapt tests). This PR is restored to the original bot state ( |
[bot] 🏷 Correction acknowledged — my independent verification reached the same conclusion before this note landed: Reproduction (truly clean trees —
Restored state verified (head Tracking issue #312 updated with the full dedicated-follow-up scope (to be opened after #310/#311 merge, per your direction):
|
…rules, adapt nudge tests - Pin acp-kernel to exact 0.0.55 (from 0.0.50): picks up #194 first-sight mass bypass, #198 min-pressure-benefit gate max(5000, limit×1%), and the write-side summary-fidelity prompt rules (kernel #205) now merged into howToCompressRules / tier2DistillRules. - Remove the adapter-level SUMMARY FIDELITY RULES section from src/system-prompt.ts: kernel 0.0.55 delivers the rules via ${prompts.howToCompressRules} — single source of truth, no gap (this commit swaps the source atomically). - Adapt 3 tests to post-#194/#198 kernel semantics (verified NOT kernel bugs — instrumented reasons confirm intended behavior): * e2e 2w/2w: bulk 3K→8K chars/msg keeps every merged range effective (≥ minCompressRange) and T1 pending ≈24K above the 5K benefit floor — the original intent (config limit flows into the decision) is preserved; under #198 the old 3024-token pending was correctly suppressed. * sent-view idle/stale-anchor controls: bulk 18K→1.8K chars/msg drops usage to ~5% (< 45% first-sight floor), so the controls stay idle for the reason they isolate (the usage floor), not because #194 masks them. - Verified: npm ci clean tree, 565 pass / 0 fail / 3 skipped.
|
Per owner direction (supersedes the plan in the correction comment): this PR now does the kernel migration directly — commit
Clean |
[bot] 🏷 Verified ✅ All stated claims hold:
❌ But CI E2E is red on head — all three jobs (ubuntu/windows/docker) fail the same scenario Same root-cause family as the unit tests you adapted, but it lives in Suggested fix (mirrors your e2e-compress-config adaptation): scale the scenario's growthText so effective pending clears the 5K floor — fixture-only change; keep the Not mergeable until E2E is green. Merge itself remains human-only: #310 Also closed tracking issue #312 as superseded (migration absorbed into this PR per your direction; the E2E scenario adaptation is noted there as the one remaining item). |
…loor override); fix e2e 03-nudge under 0.0.55 - Map compress.minPressureBenefitTokens (global/provider/model chain) to kernel nudge.minPressureBenefitTokens. Explicit 0 restores legacy any-pending pressure nudges — needed for tiny windows (e.g. the e2e scenario's 1500-token limit, smaller than the default 5000-token floor, where every pressure nudge would be suppressed). - Scenario 03-nudge-triggered: set compress.minPressureBenefitTokens=0 (wrapped under "compress" — flat root keys are dropped by pickKnown). The scenario validates nudge detection + baseline recording; the floor itself is unit-tested. - Verified locally: unit 567/0/3-skipped; e2e 5/5 scenarios pass.
|
CI E2E failure root-caused and fixed ( |
[bot] 🏷 Verified Clean tree (
Design review of the fix (diff vs
One non-blocking nit: The PR is now fully mergeable — every check green on head. Merge remains human-only: #310 (then the v0.1.58 release per your note). |
Closes #309
Two summary-fidelity bugs observed on a local 27B-quantized model session: (A) compress summaries arriving as literal
\uXXXXruns got stored and rendered verbatim into every future prompt; (B) a hallucinated user phrase was stored asuser verbatim '...'/CURRENT TASK: user '...'with no message ref, so each later reader re-executed it as a live instruction — turning one incident into recurring loops.Layer judgment
Both fixes land adapter-side (billion-context-pi), zero acp-kernel changes:
call.summaryis produced externally by the model — ingest-time normalization is the host's job.renderSummary()rendersanchor.summaryverbatim by design (no unescape), so sanitizing beforeapplyCompressionis the only choke point that covers all tiers (T1/T2/T3 distillations all flow throughhandleCompress → applyCompression).Changes
A — pre-store normalization (
src/summary-sanitize.ts, new):sanitizeSummary(): if the parsed summary contains more than 20 literal\u[0-9a-fA-F]{4}runs → decode once (incl. surrogate pairs);\n/\\and other escapes are left untouched so legitimate escape examples in prose survive.src/compress-tool.tshandleCompress: sanitized ranges feedapplyCompression; debug eventsummary-unescapedwith span + escape count + before/after lengths.\uXXXXexamples is never mangled.B — unverifiable user-quote detection + prompt hardening:
findUnverifiableUserQuote(): flagsuser verbatim/ direct-quote-user claims when the summary carries nomNNNNNref →[warn] summary-unverifiable-quotelog with the matched claim. Detection only — the model's text is never rewritten (evidence preserved, per issue spec).src/system-prompt.tsnew SUMMARY FIDELITY RULES section: task state must be labeled "TASK AS OF BLOCK CREATION" (never "CURRENT TASK"); verbatim user quotes require an mNNNNN ref else paraphrase; never present-tense quotes as current directives; never copy\uXXXX/JSON-escaped fragments out of tool output.Tests
tests/summary-sanitize.test.ts: decode (basic CJK, surrogate pair, lone surrogate, invalid hex), threshold boundary (exactly 20 unchanged vs 21 decoded), quote-claim detection incl. the incident strings from compress 摘要双重转义(\uXXXX)入库+渲染;幻觉用户原话被摘要永久化为 CURRENT TASK 导致循环复发 #309.tests/compress-tool.test.ts: end-to-end compress with 25 literal\u5408escapes → asserts the persisted.acp.jsonblock contains decoded CJK and no literal\uXXXXruns.npm run typecheckclean,npm run buildclean.Known limitation
Fixes cover newly ingested summaries only. Blocks already corrupted before this version (e.g. b58–b61 in the incident session) stay corrupted until re-distilled.