Repository navigation
fix: snap compress range endpoints onto hidden orphan calls (#396) - #397
Conversation
When a compress range endpoint lands on a failed compress call that was hidden by orphan-hiding, resolveAnchorIndex rejected the whole range as 'cannot be anchored' even though the rest was visible and valid. The hide pass now records the padded refs of hidden orphan call/result messages in state.hiddenOrphanRefs; boundary resolution snaps such an endpoint to the nearest adjacent visible message (start -> next, end -> prev) and emits a 'snapped' warning, mirroring the existing block-anchor path. A dangling ref NOT in hiddenOrphanRefs still throws the existing consumed error (preserves #387 drift semantics); when no neighbor exists in the required direction the error now names the real cause (hidden orphan with no adjacent visible message). Adds optional CompressionState.hiddenOrphanRefs (absent on old persisted states = empty). No version bump. Fixes #396
📦 Built Package ArtifactBranch: Option A — Install from npm PR tag (recommended)In your adapter project: npm install acp-kernel@pr-397Each push to this PR publishes a new version under the Option B — Download artifact
tar xzf acp-kernel-pr397.tgz
npm install ./packageThis comment is automatically updated on each push. |
[bot] 🏷 Reviewed PR #397 ( ① Original-issue direction — correct. I traced the failure path in ② Regression audit — existing behaviors touched, one by one:
③ Code review. Verified: ref-width consistency (
④ Tests — real teeth, not ceremony. All 6 new tests assert concrete outcomes: exact snap indices (direction-sensitive — a wrong-direction implementation fails), ⑤ Diff cleanliness. 5 files, +394/-2, every line serves the fix; test file is new; no generated/lockfile noise; Open item: the optional 2-line carry-over in ③. Otherwise nothing blocks merge — over to you per §7.3. 修复了「压缩范围端点落在被孤儿隐藏窗口藏掉的失败 compress 调用上时整段范围被误报 cannot be anchored」的问题(端点吸附到相邻可见消息并给出 snapped 提示,#387 悬空引用报错保持不变);代码、回归面、测试与 diff 均审过无阻塞项,仅剩一处可选的两行加固(state-merge/sync 不携带新派生字段,可自愈),可以合并(按 §7.3 由人工执行)。 |
Summary
Fixes #396 — when a
compressrange endpoint lands on a failed compress call that was hidden by orphan-hiding, the whole range was rejected ascannot be anchored, wasting a compress attempt and consuming bcp'sMAX_COMPRESS_ATTEMPTSquota, even though the rest of the range was visible and valid.Root cause (and why not a blanket snap)
In
resolveAnchorIndex(src/boundaries.ts), a message endpoint that is present inmessageRefs.byRefbut absent from the visibleindexByMessageIdfalls through to aBoundaryNotFoundError("consumed"). That single code path serves two different cases that need opposite handling:hideConsumedCompressCalls's orphan window (KEEP_LAST_ORPHANED=2). Snapping to the nearest visible neighbor is safe and correct.From
(visible messages + state)alone these two are indistinguishable: both appear as "ref in byRef/byRaw, rawId not in the visible index, no block covers it." The only distinguishing signal is which refs the hide pass removed, which is dropped betweenprocessTurnandapplyCompression(the host strips orphans from the carried conversation). So the hide pass now records them in state.Change
CompressionState.hiddenOrphanRefs?: string[](padded refs of orphan compress call/result messages currently hidden by the orphan window). Optional ⇒ old persisted states lack it ⇒ treated as empty (no migration).hideConsumedCompressCallsadditionally returns the padded refs of the orphan call/result messages it hides (skips non-orphans whosetoolCallIdbelongs to a block, skipsBLOCKED_REF, dedups).hideCompressCallsNodewriteshiddenOrphanRefsinto state each turn;cloneStatepreserves it (the clone is field-by-field, so it had to be added explicitly orapplyCompression's clone would silently drop it).resolveAnchorIndex, after the existing active-block anchor check: if the endpoint ref ∈hiddenOrphanRefs, snap to the nearest adjacent visible message (start → next higher ref, end → previous lower ref; protected/BLOCKEDand synthetic messages are auto-skipped because they are absent frombyRaw) and return asnappedhint like the block-anchor path. If no neighbor exists in the required direction, throw aconsumederror that names the real cause ("hidden orphan compress call with no adjacent visible message"). If the ref is not inhiddenOrphanRefs, fall through to the unchanged existing throw (preserves fix: protected-zone rejection carries acp_status guidance; leanPack ref-stability wording (#385) #387 byte-for-byte).No pipeline-node ordering change, no wire-format change, no new dependency.
Behavior change (flagged per owner policy)
cannot be anchored … likely consumed by an existing block(misattributed cause)snappedwarningcannot be anchored … likely consumed by an existing blockcannot be anchored … hidden orphan compress call with no adjacent visible message … run acp_status(accurate cause)Review focus (§7.5)
Tests
New
tests/orphan-endpoint-snap.test.ts(6 tests, all pass):["m00001","m00002"]).hiddenOrphanRefsstill throwsconsumed(guards fix: protected-zone rejection carries acp_status guidance; leanPack ref-stability wording (#385) #387).applyCompression: rangem00005..m00013with m00005 a hidden orphan →errorsempty,blocksCreated1, warnings includesnapped.Full suite: 1047 pass / 0 fail. typecheck + build green.
Formatting note
All files touched by this PR conform to Prettier except
src/hide-consumed.ts, which uses 4-space indentation repo-wide (a pre-existing inconsistency — the baseline version already failsprettier --check). Reformatting it would be unrelated whole-file churn, so it was left matching its existing style. The full-treeformat:checkalso surfaces other pre-existing failures (json/config/some test files under prettier 3.9.6 vs the devDep floor^3.3.0) unrelated to this change.中文摘要:修复了 compress 区间端点落在「被 orphan-hiding 隐藏的失败 compress 调用」上时整条区间被误判为 cannot be anchored 的问题——隐藏路径现在把被隐藏孤儿的 ref 记进 state,边界解析把这种端点吸附到最近的相邻可见消息(并给出 snapped 警告);#387 的漂移引用语义保持不变。改动仅新增一个可选 state 字段,属 §7.3 状态结构变更,需人工合并。