Skip to content

fix(prompts): mark stale summary line-numbers/snippets AND carried user directives as historical - #492

Open
ranxianglei wants to merge 2 commits into
masterfrom
2026-10-01_stale-map-contract
Open

ranxianglei wants to merge 2 commits into
masterfrom
2026-10-01_stale-map-contract

Conversation

@ranxianglei

@ranxianglei ranxianglei commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Fixes billion-context-pi#592 (sub-defects 1 + 2, write-side).

Same root cause for both: a compression summary loses temporal/structural metadata, so a later model treats history as present.

Sub-1 — stale code artifacts used as facts

Line numbers & code snippets recorded in a summary are POINT-IN-TIME snapshots; they drift once the file is edited afterwards. Rule added under KEEP VERBATIM telling the model to treat them as navigation hints only and re-read/grep the file before building an edit's oldText (or checking current content against a summary snippet).

Sub-2 — stale user directives re-executed as current

Two rules added to howToCompressRules:

  • USER DIRECTIVES: imperative / nudge / "do X" user messages are the highest-risk carry-forward. Every carried user message keeps its message ref plus a [HISTORICAL] prefix; an imperative one must ALSO be rewritten in past tense ("was asked to X") so no command tone survives. Never render a carried directive in present/imperative voice — that reads as a live order.
  • OPEN OBJECTIVES: the Open objectives: line is now emitted UNCONDITIONALLY ((none) when nothing remains open) so a weak model cannot silently skip the field and its absence is never ambiguous.

Scope

  • Touches ONLY HOW_TO_COMPRESS_RULES (src/compression-rules.ts) + regenerates the 3 byte-stable prompt fixtures. No wire format, no pipeline node, no config/default change.
  • Lean pack intentionally untouched (it carries its own condensed contract in the pi slot).
  • A consumer-side "summaries are HISTORICAL" soft rule already exists but small models ignore it in practice; these structural write-side markers are the durable fix.

Verification (local, targeted — full suite on CI)

  • tsc --noEmit clean
  • node --import tsx --test tests/surface-config.test.ts tests/packs.test.ts tests/nudge-text.test.ts → 63 pass / 0 fail (includes the byte-stable fixture comparison and the existing open-objectives guardrail tests)
  • prettier --check src/compression-rules.ts clean

The HOW TO COMPRESS KEEP VERBATIM rules told summarizers to record file
line numbers and key code lines verbatim, with no staleness caveat. A later
model treats those as directly-editable facts, but they drift once the file
changes after the block is written - the mechanism behind edit oldText drift
-> rejected batches -> replay dead-loops (billion-context-pi#592).

Add a single dual-sided rule under KEEP VERBATIM: line numbers/snippets are
POINT-IN-TIME snapshots as of block creation; treat them as navigation hints,
not editable ground truth; re-read/grep before building an edit oldText from
them. Re-pin the three byte-stable prompt fixtures.

Refs ranxianglei/billion-context-pi#592
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-10-01_stale-map-contract (96d111f)

Option A — Install from npm PR tag (recommended)

In your adapter project:

npm install acp-kernel@pr-492

Each push to this PR publishes a new version under the pr-492 npm tag.

Option B — Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf acp-kernel-pr492.tgz
npm install ./package

This comment is automatically updated on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 Reviewed PR #492 (branch 2026-10-01_stale-map-contract, head f33bc8d). Verified against the branch, not redone. Conclusion below.

What I verified

  • Current base: HEAD = origin/master + exactly 1 commit — no stale-base risk.
  • Diff matches description: +4/-0 — one bullet under KEEP VERBATIM in src/compression-rules.ts plus re-pin of the three byte-stable default-prompt fixtures (prompt-{function,text,hybrid}-default.txt). The new line is byte-identical across all four files.
  • CI on head is green: test (24), test (22), pr-validation, build-artifact — all success.
  • Independently reproduced the load-bearing checks on this checkout: tsc --noEmit clean; tests/surface-config.test.ts (the byte-stable fixture comparison) 17/17 pass — so the re-pinned fixtures genuinely match the rendered output rather than being hand-typed; prettier --check src/compression-rules.ts clean.

One material finding — the fix does not reach the lean-pack Tier-1 surface

LEAN_HOW_TO_COMPRESS (src/packs.ts:217) is a separate, hardcoded Tier-1 how-to-compress contract — its own template literal, not derived from HOW_TO_COMPRESS_RULES at runtime (confirmed). Its KEEP VERBATIM section also records line numbers verbatim (src/packs.ts:224 "File paths with line numbers…") but does not carry the new point-in-time / re-read-before-edit caveat. It is wired into the Pi adapter via adapters.pi.promptSections.howToCompress (src/packs.ts:277).

That matters because the motivating incident bcp#592 lives in billion-context-pi — precisely the host the lean pack serves. If the Pi host runs the lean pack, this PR fixes the default contract but leaves the incident's actual surface uncovered.

Your scoping note covers T2/T3 correctly (they drop line numbers by design — verified: TIER2_DISTILL_RULES' DROP list explicitly drops exact line numbers/diffs). But it says nothing about the lean pack, which is also Tier-1. That reads as an oversight rather than a documented decision. Under the mechanism-first policy, the stale-line-number mechanism spans both Tier-1 surfaces, so the comprehensive fix would cover both.

Two options — your call (deciding input: does the Pi host actually select lean or default?):

  • (A) Expand this PR — add the same caveat to LEAN_HOW_TO_COMPRESS. Small, keeps the fix whole.
  • (B) Keep scope — explicitly state why the lean pack is out of scope here and open a follow-up for it.

I have not edited anything (CI green, no request to change); flagging for your decision since this is load-bearing prompt surface.

Structured seven-question review

  1. 是什么问题: Line numbers / code snippets recorded in a summary are point-in-time snapshots that drift once the file is edited; a later model reads them as editable ground truth, builds an edit oldText from a stale number → mismatch → batch rejected → replay dead-loop (bcp#592).
  2. 什么原因导致的: The write-side KEEP VERBATIM rule told summarizers to record line numbers verbatim but carried no staleness caveat, so nothing told a later reader the number may already be wrong. Root cause is a missing consumer-side caution inside the write-side contract — not a defect in any compression pipeline path.
  3. 怎么修复的: An additive write-side caveat under KEEP VERBATIM in HOW_TO_COMPRESS_RULES: label recorded line numbers/snippets as point-in-time navigation hints and require re-read/grep before building an edit or checking current content against one. Purely additive; removes or contradicts no existing rule.
  4. 是否有回归问题: None in the touched paths. Existing rules preserved (additive only); the nudge path that embeds these rules uses substring assertions and still holds; T2/T3 correctly untouched. One incompleteness, not a regression: the lean-pack Tier-1 surface is left without the caveat (finding above).
  5. 是否破坏缓存: By design and unavoidably — the standing default system-prompt text gains one line, so any host that prefix-caches the system prompt takes a one-time miss on upgrade until re-established. Inherent to any standing-prompt-text change; the re-pinned fixtures are the disclosed output-format delta. No retry / incremental-cursor / dedup-key logic affected.
  6. 是否改了设计方向: No. Same write-side contract, same load-bearing-vs-surface split, same acknowledgeRisk override gate. No architecture / boundary / data-flow shift.
  7. 是否引入了新的配置: No new env / config / default. Prompt text only.

Verdict

Correctly self-labelled as not an auto-merge candidate — it touches §7.3 load-bearing prompt surface (#7), which stays human-gated. Direction is sound, implementation is clean and minimal, CI is green. Recommend holding merge until you settle the lean-pack question (option A, or documented exclusion + follow-up B). Once that's resolved, it's ready to merge.

…ectives line unconditional

Sub-defect 2 of billion-context-pi#592 (same root cause as sub-1: summary loses temporal/structural metadata -> model treats history as present). In howToCompressRules: (a) imperative/nudge user messages carried into a summary must keep their ref plus a [HISTORICAL] prefix and be rewritten in past tense so no command tone survives; (b) the Open objectives line is now emitted unconditionally ('(none)' fallback) so a weak model cannot silently skip the field.
@ranxianglei ranxianglei changed the title fix(prompts): mark summary line numbers/snippets as point-in-time (stale before edit) fix(prompts): mark stale summary line-numbers/snippets AND carried user directives as historical Oct 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant