You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ref capacity exhausted: the ref map's 5-digit ceiling permanently bricks long sessions (restart does not help) — widening is implemented in PR #496 #483
The throw escapes processTurn, so the failure is not a degraded turn — the host's whole
agent turn fails and, as established below, the session is permanently unusable.
Environment
Host: DeepSeek Harness (dsh) via the port billion-context-dsh, which reuses the kernel verbatim.
Kernel versions checked: 0.0.29 (the host's pinned build at the time), 0.0.63 (that port's pin),
0.0.98 and 0.0.99 (npm latest) — every one still ships MAX_INDEX = 99999, REF_PATTERN = /^m0*(\d{1,5})$/ and the same ref capacity exhausted throw
from allocateFreeRef, with the cursor still starting at highestUsedIndex(...) + 1.
The kernel's own sync.ts comment documents the additive intent: "Refs are additive (assignRefs never removes them from messageRefs)" — only tokenSnapshot is pruned.
Why the wall, not a leak (corrected)
An earlier revision of this issue claimed a restart clears it temporarily. That was wrong, and the
correction was verified line-by-line against the kernel and the DSH port's source:
The port hands the kernel the whole log, not the visible view: allLogMessages(session) = eventsToCoreMessages(session.events) — its doc comment says "ALL message-type events in log order — the visible surface PLUS everything shadowed by
compression … T2/T3 distillation requires the full log". Five call sites feed it to kernel.processTurn (nudge, compress tool, status tool, /acp-prune, index).
assign-refs is pipeline node fix: release workflow grep lookbehind error #1 (base = [assignRefsNode, syncBlocksNode, pruneNode, …]), and its
throw is uncaught, so it escapes processTurn before any compression decision can run.
Therefore a fresh process re-allocates refs for the entire log on its first turn (139,159
message-level events in the failing session) and throws again immediately; /acp-prune and the compress tool traverse the same path and cannot dig out either.
Net effect for this host shape: a log past ~100k message-level events bricks that session under ACP,
with no host-side recovery path.
Data point that bounds the fix
quantity
value
message-level events in the log (assistant/user/tool-call/tool-result)
139,159
of those, already shadowed by summary ranges
89,208
current visible surface
≈ 49,951
The live view needs ~50k refs; the cumulative map is what crosses 99,999. With MAX_INDEX = 9_999_999
the same session has ≈72× headroom over its entire lifetime.
Status (2026-10-02)
The hybrid widening (≤99999 byte-identical 5-digit padding; natural width above; MAX_INDEX = 9_999_999)
is implemented in PR fix(refs): widen ref capacity to 9,999,999 — hybrid widths (#483) #496 — 6 files, +98/−14, including one surface the original two-file estimate
missed (src/ccr.ts placeholder grammar), 1217/1217 tests plus typecheck/prettier and CI green.
Reviews are complete; the remaining step is the human merge required by §7.3, then a release.
acp-kernel@0.0.99 (the currently published latest) does not contain the widening.
Adapters must widen their mirrors in the same cycle; for the DSH port that is src/tools.tsMN_RE = /^m0*(\d{1,5})(?:#.*)?$/i + index <= 99999 → {1,7} / <= 9999999.
Short answer: Yes — widening is the right fix, and it turns out to be much smaller than it looks. I recommend the hybrid variant you floated in point 2 (keep 5 digits ≤ 99999, widen only above it, MAX_INDEX = 9_999_999). I'd reject non-fatal overflow (point 3) as the primary fix — reasoning below. Needs owner sign-off on the release slot before implementation, because it touches a kernel-owned wire contract.
Cursor restart: src/compress.ts:842 and src/rebuild.ts:36 both start from highestUsedIndex(state.messageRefs) + 1.
Additive map: src/sync.ts:50 — "Refs are additive (assignRefs never removes them from messageRefs)". Confirmed.
Ordering: assign-refs is pipeline node 1 (before recommend/nudge), so exhaustion fires before any compression decision can run — consistent with your observation that the session can't compress its way out.
Layer judgment: your live-view measurement (≈50k visible) is correct and is not the problem layer. Root cause = cumulative map + fixed 5-digit ceiling. Widening removes the ceiling while preserving docs: codify id-immutability invariant in AGENTS.md #192's id-immutability invariant (no number ever re-issued).
Uniform padding (m0000001) re-renders every visible tag +2 chars on upgrade. For a session with ~50k visible messages that's ≈100k chars ≈ ~25k extra tokens per request, paid by every session including ones far from the cap. Hybrid costs zero bytes until a session crosses 99999.
Backward compat falls out for free: refToIndex's leading-zero-tolerant pattern accepts both widths, so existing summary text citing 5-digit tags keeps resolving; and indexToRef's padStart(5) is a no-op for indices ≥ 100000, so the very same function already emits m100000-form once the bound is lifted.
Why not non-fatal overflow (point 3)
A ref-less message can't be cited in a compress range (boundary resolution is ref-keyed, src/boundaries.ts:20-28), can't enter a block's effectiveMessageIds, so prune never removes it → it accumulates in the visible view forever → guaranteed context overflow later, silently, with no diagnosable error. It also loses CCR retrieval and decompress range-mode for those messages. It converts a loud, diagnosable hard stop into a slow silent death. If a safety valve is wanted there, it's a separate design (id-based coverage), not a fallback for this fix.
Implementation surface (hybrid, 7 digits)
Kernel — surprisingly small, because the padding design was already forward-compatible:
src/boundaries.ts:20 — MESSAGE_REF_PATTERN → \d{1,7}; src/boundaries.ts:28 — bound 99999 → 9999999. (formatPaddedRef at :361 is already a no-op for wide indices.)
src/parse-compress-input.ts — checked: the line-form patterns already accept \d{1,7}, and its padStart(5) is a no-op for wide refs. No change needed.
Regression tests: allocation across the 99999→100000 boundary; mixed-width round-trip (m00001 ≡ m1 → 1; m99999/m100000 adjacency); boundary resolution of wide refs; byte-stability of all sub-cap fixtures.
Host side (your audit list, confirmed):
billion-context-dshsrc/tools.ts: MN_RE = /^m0*(\d{1,5})(?:#.*)?$/i + index <= 99999 → widen to {1,7} / <= 9999999. With hybrid, an un-updated host keeps working fully until a session crosses the cap (kernel-side parsing accepts both widths either way), but adapter mirror updates should still ship in the same cycle.
Capacity math: the failing session consumed 139k ids over months; 10M gives ≈72× headroom versus that session's entire lifetime.
Contract disclosure (why this is human-gated, not auto-merge)
This changes the rendered ref-tag shape — a §7.3 kernel-owned wire contract — so it stays human-gated and follows cross-repo sequencing: kernel releases first, adapters bump pins after. Behavior deltas: nil below the old cap (byte-identical output everywhere under 99999); above it, new tags are 6–7 digits; the exhaustion error string changes from m99999 to m9999999. No state-schema change (the cursor is recomputed from highestUsedIndex each turn, no persisted high-water mark).
Requesting
Owner approval of the hybrid approach + a release slot (latest published is v0.0.98). Once approved I'll open the PR with regression tests and hand you a testable build/pin for the 139k-message session (decompress / acp_status / compress against old and new tags).
Correction: restarting does not clear it — the ceiling is a hard wall, not a leak you can outrun
My opening comment said the failure is recoverable by restarting the hosting process ("kernel state is
in-memory, rebuilds from the current view, ~50k refs"). That was wrong, and I want to correct it here
because it changes how urgent this is.
That estimate used the model-visible message count. But the failing host (the DeepSeek Harness port billion-context-dsh) does not hand the kernel a pruned view: every call site passes allLogMessages(session) — its own doc comment says "ALL message-type events in log order — the visible
surface PLUS everything shadowed by compression … T2/T3 distillation requires the full log, not just the
visible surface":
// billion-context-dsh/src/messages.tsexportfunctionallLogMessages(session){returneventsToCoreMessages(session.events)}// every caller (nudge, compress tool, status tool, prune path):constcoreMessages=allLogMessages(session)constturn=env.kernel.processTurn({messages: coreMessages, state, config, tokenCount })
assign-refs is the FIRST node of the pipeline, so refs are allocated for every message in the whole
log, not for the view that survives prune. Consequences, now verified on the real session:
A fresh process re-allocates refs for all 139,159 message-level events on its first turn → it throws ref capacity exhausted immediately. Restart does not help, at all.
Compression cannot dig out either: /acp-prune and the compress tool go through the same allLogMessages → processTurn path, and assign-refs runs before prune.
So for this host the ceiling is a hard per-session wall at ~100k log messages: the session becomes
permanently unusable, with no host-side recovery path, for both the user and the agent.
That is a materially stronger failure mode than "the map grows monotonically and eventually stalls", and
I would suggest it raises the priority of the REF_WIDTH widening. If widening is not imminent, the
second-best host-side option now looks like: let the host pass the pruned view for ref assignment (or let assignRefs skip overflow messages instead of throwing), so at least the session survives with degraded
drill-down. Data for calibrating that: 139,159 message-level events in the log, 89,208 of them already
shadowed by summaries, i.e. only ≈49,951 are in the current visible view.
Correction accepted — and I've now verified the mechanism against current master. You're right that this is materially worse than "eventually stalls": for a host that passes the full log, the session is permanently bricked, and restart makes it fail immediately rather than later.
Kernel-side confirmation of your corrected model
The kernel has no notion of visibility. assignRefsNode (src/compress.ts:821-849) allocates a ref for every message the host hands it, excluding only protected/media messages (which get BLOCKED_REF, consuming no number) and ephemeral retrieval injections. Nothing filters or prunes before allocation — what the host passes is what gets numbered.
With fresh state the cursor starts at 1: nextIndex: highestUsedIndex(io.state.messageRefs) + 1 (src/compress.ts:842, same in src/rebuild.ts:36). So a 139,159-event full-log input allocates m00001…m99999 and throws ref capacity exhausted on the next message — on the first turn after restart, exactly as you now describe.
assign-refs is pipeline node fix: release workflow grep lookbehind error #1, ahead of prune and ahead of every compression decision (src/compress.ts:788-799), and the throw is uncaught — it escapes processTurn. So both consequences follow directly: restart cannot help, and /acp-prune / compress cannot dig out because they traverse the same path.
Severity update agreed: hard per-session wall at ~100k log messages, no host-side recovery path for this host shape. Priority raised accordingly. This does not change what the fix is — only how urgent it is.
Your two stopgap options, assessed
(a) Host passes the pruned/visible view instead of the full log. Feasible as degraded operation, with three costs: (i) it contradicts the stated reason for the full log (T2/T3 distillation needs the whole log); (ii) after a restart, shadowed messages get no refs, so mNNNNN tags already cited inside existing summaries dangle — decompress range-mode and search degrade across the historical span; (iii) it changes what the kernel's own prune/recommend/distill nodes see — a host-side semantic shift, not a fix. Acceptable as a temporary mitigation if giving up drill-down into shadowed history is tolerable.
(b) Kernel skips overflow messages instead of throwing. I would not make this silent. Ref-less messages can't be cited in compress ranges (boundary resolution is ref-keyed), can't enter block coverage, and are never pruned — they accumulate forever. Concretely, on a fresh restart with full-log input the ref-less set is log positions ≥ 100,000 ≈ the last ~39k events; at your observed ~36% visible density that's ~14k visible messages — i.e., exactly the newest work loses compressibility when it is needed most, and the failure mode becomes a slow, silent context-window fill instead of a loud, diagnosable stop. If a valve like this is ever wanted, it must be loud (persistent status/nudge warning carrying the unref count) and tracked as its own issue — not bundled with the widening.
Position
Hybrid widening (≤99999 byte-identical 5-digit; natural width above; MAX_INDEX = 9_999_999; patterns \d{1,5}→\d{1,7}) remains the correct and only durable fix; surface unchanged from my previous comment — effectively 2 lines each in src/refs.ts and src/boundaries.ts plus regression tests. Given the corrected severity (permanent brick, immediate on restart), I'm asking the owner again for approval + release slot — happy to land the PR immediately on sign-off, since latest published is v0.0.98.
PR up: #496 — hybrid widening exactly as discussed, plus one surface the 2-file estimate missed: the CCR placeholder grammar (src/ccr.ts) hardcodes m\d{5} and must accept canonical widened shapes or stored-placeholder round-trips break past m100000. Canonical-only grammar (5-digit padded | natural ≥100000, never leading-zero) keeps the #1456 prose firewall intact — the existing negative test caught the naive {1,7} version during implementation. 1217/1217, typecheck/prettier clean. Needs review #2 (ework's issue analysis = review #1) + human merge per §7.3.
Verified on head 83b8609 vs current master (merge-base = master tip, so the diff is exactly the PR's 6 files). Method: full line-by-line read of every hunk; byte-level comparison of the old/new PLACEHOLDER_LINE2_RE capture semantics; exhaustive inventory of every digit-bounded regex in src/; local typecheck + targeted tests (tests/refs.test.ts, tests/ccr.test.ts, tests/compress.test.ts, tests/parse-compress-input.test.ts) = 185/185; CI on the head SHA is green (both workflows).
Key subtlety checked: the old LINE2 regex captures the bare ref (quote sits outside the group — verified against git show FETCH_HEAD:src/ccr.ts:192), and the new construction '"(' + CANONICAL_REF + ')"' keeps the quote outside too. Consumer logic (ref !== hintRef) is untouched, so capture semantics are identical — no hidden behavior shift there.
changed the title [-]ref capacity exhausted: the monotonic ref map reaches m99999 and kills the host turn — is the REF_WIDTH widening (#191's stated proper fix) still planned?[/-][+]ref capacity exhausted: the ref map's 5-digit ceiling permanently bricks long sessions (restart does not help) — widening is implemented in PR #496[/+]on Oct 2, 2026
Activity
ranxianglei commentedon Sep 30, 2026
[bot] 🏷
Short answer: Yes — widening is the right fix, and it turns out to be much smaller than it looks. I recommend the hybrid variant you floated in point 2 (keep 5 digits ≤ 99999, widen only above it,
MAX_INDEX = 9_999_999). I'd reject non-fatal overflow (point 3) as the primary fix — reasoning below. Needs owner sign-off on the release slot before implementation, because it touches a kernel-owned wire contract.What I verified on current master
src/refs.ts:3-6—REF_WIDTH = 5,MAX_INDEX = 99999,REF_PATTERN = /^m0*(\d{1,5})$/;src/refs.ts:98-100throws exactlyref capacity exhausted: cannot allocate beyond m99999, and the throw escapesprocessTurn.src/compress.ts:842andsrc/rebuild.ts:36both start fromhighestUsedIndex(state.messageRefs) + 1.src/sync.ts:50— "Refs are additive (assignRefs never removes them from messageRefs)". Confirmed.Why hybrid over uniform 7-digit padding
m0000001) re-renders every visible tag +2 chars on upgrade. For a session with ~50k visible messages that's ≈100k chars ≈ ~25k extra tokens per request, paid by every session including ones far from the cap. Hybrid costs zero bytes until a session crosses 99999.refToIndex's leading-zero-tolerant pattern accepts both widths, so existing summary text citing 5-digit tags keeps resolving; andindexToRef'spadStart(5)is a no-op for indices ≥ 100000, so the very same function already emitsm100000-form once the bound is lifted.Why not non-fatal overflow (point 3)
A ref-less message can't be cited in a compress range (boundary resolution is ref-keyed,
src/boundaries.ts:20-28), can't enter a block'seffectiveMessageIds, so prune never removes it → it accumulates in the visible view forever → guaranteed context overflow later, silently, with no diagnosable error. It also loses CCR retrieval and decompress range-mode for those messages. It converts a loud, diagnosable hard stop into a slow silent death. If a safety valve is wanted there, it's a separate design (id-based coverage), not a fallback for this fix.Implementation surface (hybrid, 7 digits)
Kernel — surprisingly small, because the padding design was already forward-compatible:
src/refs.ts:5—MAX_INDEX→9_999_999;src/refs.ts:6— pattern\d{1,5}→\d{1,7}.indexToRefitself needs no change.src/boundaries.ts:20—MESSAGE_REF_PATTERN→\d{1,7};src/boundaries.ts:28— bound99999→9999999. (formatPaddedRefat :361 is already a no-op for wide indices.)src/parse-compress-input.ts— checked: the line-form patterns already accept\d{1,7}, and itspadStart(5)is a no-op for wide refs. No change needed.m00001≡m1→ 1;m99999/m100000adjacency); boundary resolution of wide refs; byte-stability of all sub-cap fixtures.Host side (your audit list, confirmed):
billion-context-dshsrc/tools.ts:MN_RE = /^m0*(\d{1,5})(?:#.*)?$/i+index <= 99999→ widen to{1,7}/<= 9999999. With hybrid, an un-updated host keeps working fully until a session crosses the cap (kernel-side parsing accepts both widths either way), but adapter mirror updates should still ship in the same cycle.Capacity math: the failing session consumed 139k ids over months; 10M gives ≈72× headroom versus that session's entire lifetime.
Contract disclosure (why this is human-gated, not auto-merge)
This changes the rendered ref-tag shape — a §7.3 kernel-owned wire contract — so it stays human-gated and follows cross-repo sequencing: kernel releases first, adapters bump pins after. Behavior deltas: nil below the old cap (byte-identical output everywhere under 99999); above it, new tags are 6–7 digits; the exhaustion error string changes from
m99999tom9999999. No state-schema change (the cursor is recomputed fromhighestUsedIndexeach turn, no persisted high-water mark).Requesting
Owner approval of the hybrid approach + a release slot (latest published is v0.0.98). Once approved I'll open the PR with regression tests and hand you a testable build/pin for the 139k-message session (decompress /
acp_status/compressagainst old and new tags).中文摘要:bug 已在当前 master 逐条核实(refs.ts 五位上限 + 只增不删的 ref map,累计超 99999 即在 assign-refs 节点抛错、杀死整个 turn);推荐「混合加宽」——≤99999 保持 5 位字节不变、超出后自然变宽到 7 位(MAX_INDEX=9999999),旧标签全部继续可解析,低于上限的会话零变化;否决「溢出消息无 ref 化」备选(无 ref 消息永远无法进入压缩范围/被剪除,只会变成无声的慢性上下文死亡)。内核侧实际改动仅 refs.ts 与 boundaries.ts 各两行加回归测试。因触碰 wire 契约(§7.3)需 owner 拍板版本后开工,随后按「先发内核、再升适配器」顺序落地。
LDyty commentedon Sep 30, 2026
Correction: restarting does not clear it — the ceiling is a hard wall, not a leak you can outrun
My opening comment said the failure is recoverable by restarting the hosting process ("kernel state is
in-memory, rebuilds from the current view, ~50k refs"). That was wrong, and I want to correct it here
because it changes how urgent this is.
That estimate used the model-visible message count. But the failing host (the DeepSeek Harness port
billion-context-dsh) does not hand the kernel a pruned view: every call site passesallLogMessages(session)— its own doc comment says "ALL message-type events in log order — the visiblesurface PLUS everything shadowed by compression … T2/T3 distillation requires the full log, not just the
visible surface":
assign-refsis the FIRST node of the pipeline, so refs are allocated for every message in the wholelog, not for the view that survives
prune. Consequences, now verified on the real session:ref capacity exhaustedimmediately. Restart does not help, at all./acp-pruneand thecompresstool go through the sameallLogMessages→processTurnpath, andassign-refsruns beforeprune.permanently unusable, with no host-side recovery path, for both the user and the agent.
That is a materially stronger failure mode than "the map grows monotonically and eventually stalls", and
I would suggest it raises the priority of the
REF_WIDTHwidening. If widening is not imminent, thesecond-best host-side option now looks like: let the host pass the pruned view for ref assignment (or let
assignRefsskip overflow messages instead of throwing), so at least the session survives with degradeddrill-down. Data for calibrating that: 139,159 message-level events in the log, 89,208 of them already
shadowed by summaries, i.e. only ≈49,951 are in the current visible view.
ranxianglei commentedon Sep 30, 2026
[bot] 🏷
Correction accepted — and I've now verified the mechanism against current master. You're right that this is materially worse than "eventually stalls": for a host that passes the full log, the session is permanently bricked, and restart makes it fail immediately rather than later.
Kernel-side confirmation of your corrected model
assignRefsNode(src/compress.ts:821-849) allocates a ref for every message the host hands it, excluding only protected/media messages (which getBLOCKED_REF, consuming no number) and ephemeral retrieval injections. Nothing filters or prunes before allocation — what the host passes is what gets numbered.nextIndex: highestUsedIndex(io.state.messageRefs) + 1(src/compress.ts:842, same insrc/rebuild.ts:36). So a 139,159-event full-log input allocatesm00001…m99999and throwsref capacity exhaustedon the next message — on the first turn after restart, exactly as you now describe.src/compress.ts:788-799), and the throw is uncaught — it escapesprocessTurn. So both consequences follow directly: restart cannot help, and/acp-prune/compresscannot dig out because they traverse the same path.Severity update agreed: hard per-session wall at ~100k log messages, no host-side recovery path for this host shape. Priority raised accordingly. This does not change what the fix is — only how urgent it is.
Your two stopgap options, assessed
(a) Host passes the pruned/visible view instead of the full log. Feasible as degraded operation, with three costs: (i) it contradicts the stated reason for the full log (T2/T3 distillation needs the whole log); (ii) after a restart, shadowed messages get no refs, so
mNNNNNtags already cited inside existing summaries dangle — decompress range-mode and search degrade across the historical span; (iii) it changes what the kernel's own prune/recommend/distill nodes see — a host-side semantic shift, not a fix. Acceptable as a temporary mitigation if giving up drill-down into shadowed history is tolerable.(b) Kernel skips overflow messages instead of throwing. I would not make this silent. Ref-less messages can't be cited in compress ranges (boundary resolution is ref-keyed), can't enter block coverage, and are never pruned — they accumulate forever. Concretely, on a fresh restart with full-log input the ref-less set is log positions ≥ 100,000 ≈ the last ~39k events; at your observed ~36% visible density that's ~14k visible messages — i.e., exactly the newest work loses compressibility when it is needed most, and the failure mode becomes a slow, silent context-window fill instead of a loud, diagnosable stop. If a valve like this is ever wanted, it must be loud (persistent status/nudge warning carrying the unref count) and tracked as its own issue — not bundled with the widening.
Position
Hybrid widening (≤99999 byte-identical 5-digit; natural width above;
MAX_INDEX = 9_999_999; patterns\d{1,5}→\d{1,7}) remains the correct and only durable fix; surface unchanged from my previous comment — effectively 2 lines each insrc/refs.tsandsrc/boundaries.tsplus regression tests. Given the corrected severity (permanent brick, immediate on restart), I'm asking the owner again for approval + release slot — happy to land the PR immediately on sign-off, since latest published is v0.0.98.中文摘要:已按内核代码逐条核实你的更正——内核没有「可见性」概念,assign-refs 是管线第一节点、对宿主传入的每条消息(全量日志)从 m00001 起分配且不捕获异常,因此重启后第一轮即抛错、压缩也无法自救;该会话是永久性硬墙而非渐进停滞,同意提升优先级。修复方案不变(混合加宽至 7 位,改动面仅 refs.ts/boundaries.ts 各两行+回归测试),两个临时方案中:宿主改传剪枝视图可行但牺牲历史钻取且与 T2/T3 蒸馏需求冲突;内核静默跳过溢出消息不可取(最新可见消息恰好失去可压缩性,故障变成无声的慢性上下文填满),若要做必须带持续告警并单独立项。仍请 owner 拍板批准并给出版本窗口。
fix(refs): widen ref capacity to 9,999,999 — hybrid widths (#483)
ranxianglei commentedon Oct 2, 2026
PR up: #496 — hybrid widening exactly as discussed, plus one surface the 2-file estimate missed: the CCR placeholder grammar (src/ccr.ts) hardcodes
m\d{5}and must accept canonical widened shapes or stored-placeholder round-trips break past m100000. Canonical-only grammar (5-digit padded | natural ≥100000, never leading-zero) keeps the #1456 prose firewall intact — the existing negative test caught the naive {1,7} version during implementation. 1217/1217, typecheck/prettier clean. Needs review #2 (ework's issue analysis = review #1) + human merge per §7.3.ranxianglei commentedon Oct 2, 2026
[bot] 🏷
Review #2 — APPROVED (human merge required, §7.3 contract #1/#2)
Verified on head
83b8609vs current master (merge-base = master tip, so the diff is exactly the PR's 6 files). Method: full line-by-line read of every hunk; byte-level comparison of the old/newPLACEHOLDER_LINE2_REcapture semantics; exhaustive inventory of every digit-bounded regex insrc/; local typecheck + targeted tests (tests/refs.test.ts,tests/ccr.test.ts,tests/compress.test.ts,tests/parse-compress-input.test.ts) = 185/185; CI on the head SHA is green (both workflows).Key subtlety checked: the old LINE2 regex captures the bare ref (quote sits outside the group — verified against
git show FETCH_HEAD:src/ccr.ts:192), and the new construction'"(' + CANONICAL_REF + ')"'keeps the quote outside too. Consumer logic (ref !== hintRef) is untouched, so capture semantics are identical — no hidden behavior shift there.结构化七问
assignRefs在管线第一节点抛错且不被捕获,整个会话被永久砖化(重启即死、压缩无法自救)。REF_WIDTH=5从"固定宽度"重定义为"填充下限"(padStart 对 ≥10^5 天然 no-op),上限提至 9,999,999;解析侧放宽为\d{1,7}(前导零容忍保留);CCR 占位符语法采用仅规范形(m\d{5}|[1-9]\d{5,6},永不带前导零)——这是原 2 文件估算漏掉的第三处,选规范形而非朴素{1,7}是为了保住 #1456 散文防火墙("m004234" 类近例仍被拒绝,既有负向测试在实现期确实抓到了朴素版)。Diff 整洁度
6 文件(3 src + 3 tests)、单提交、无锁文件/生成物连带、注释均为带 issue 编号的承重不变量说明(与仓库既有风格一致)、分支命名合规、
Fixes #483在位。干净。两条非阻塞备注
\d{4,})" —— 我在本仓src/里穷举了所有定长数字正则(boundaries{1,7}/块引用{1,9}、parse-compress-input 早已{1,7}、ccr 规范形、refs{1,7}),不存在名为 tag-echo-filter 或用\d{4,}的内核代码。内核侧确实已全部宽度无关(结论成立),但那句话指的应是宿主侧镜像,建议改为明确指向 dsh 等宿主,避免后人按图索骥落空。MN_RE = /^m0*(\d{1,5})(?:#.*)?$/i+index <= 99999需同步放宽;未升级的宿主在会话越过上限前完全可用(内核侧两种宽度都认)。跨仓提交按惯例走 owner 批准流程。判定
代码审查通过,测试有牙(边界跨越分配含占用扫描路径、新上限拒绝、双宽度往返、CCR 规范形正负双向均被钉住),CI 全绿。可合——但按 §7.3 由人工合并,我不执行 merge。 合并后按内核先发顺序通知各适配器升 pin。
中文摘要:Review #2 通过。逐行审完 6 个文件的全部改动,重点核对了 CCR 第二行正则新旧捕获组语义(引号均在组外,捕获语义不变)、穷举了 src/ 内全部定长数字正则确认无遗漏面、本地定向测试 185/185 + typecheck/prettier 干净、CI 双工作流全绿。七问逐项作答:低于 99,999 字节级零变化、无回归、缓存不受影响、设计方向即 #191 正解、无新配置。两条非阻塞备注:PR 体中 "tag-echo-filter uses \d{4,}" 一句在本仓无对应代码(应指宿主侧,建议改措辞);dsh 等适配器镜像需同周期放宽。等待人工合并,合并后内核先发、适配器随后升 pin。
[-]ref capacity exhausted: the monotonic ref map reaches m99999 and kills the host turn — is the REF_WIDTH widening (#191's stated proper fix) still planned?[/-][+]ref capacity exhausted: the ref map's 5-digit ceiling permanently bricks long sessions (restart does not help) — widening is implemented in PR #496[/+]Merge pull request #496 from ranxianglei/2026-10-01_ref-width-widen-483