Skip to content

Model loses direction while the open objective IS present in active summaries (prioritization, not compression) #444

Open

Description

@ranxianglei

来源: #442 (#442) 分析 downstream billion-context#1398 sidecar timeline 时发现

Per the reporter's sidecar readout (billion-context#1398 floors 4–5), in session ses_f29da6268ffe2P1IXQsNeJM4G0:

  • the user's architecture pivot (eight-arm refactor) was explicitly recorded in b13 (m00746–m00778);
  • the FIRST user correction landed in b15 (m00967–m01018) — while b13 was still an active summary, i.e. the objective was VISIBLE;
  • later blocks b24/b32/b36/b38/b39 kept the refactor explicitly named as the current objective, yet the user kept having to correct direction.

So the compression pipeline preserved the objective; the model deprioritized/dropped it while it was visible. Distinct root cause from #442 (fold-time omission, no carry-forward rule): this is instruction-following/prioritization of content living in summaries.

Impact: PR #443's prompt-side fix only covers the fold-time-loss class. Sessions where the model under-prioritizes a VISIBLE objective still drift, and no summary wording alone bounds that.

Suggested directions (needs design discussion, no preset solution):

This issue records the phenomenon and layering only.

Activity

  1. ranxianglei commented on Sep 26, 2026

    @ranxianglei
    OwnerAuthor

    🤖 Powered by ework · qwen3.8-27b

    Duplicate screening: two keyword passes over open+closed issues (goal-register/prioritization/objective-visible; nudge/standing-prompt/register) — no matches. Distinct from #442 (fold-time omission; fix pending in PR #443) and #445 (Prettier). This is the venue I flagged in #442 floor 6 for exactly this class. Keeping open; this is a design-discussion issue per your OP ("no preset solution"), so below is verification + evaluation + a concrete recommendation for your call — nothing implemented yet.

    Triage verification (checked against master @ v0.0.94):

    1. Phenomenon confirmed in code. There is no kernel path that re-surfaces objective text between nudges:
      • grep across src/: "objective/goal/current task" appears only in the compress tool description (src/compress-tools.ts:244);
      • nudge rendering (src/nudge-text.ts: formatBlockMap, formatTierTargetBlocks) emits blockId/ref-span/tier/topic only — never summary bodies;
      • buildStatusReport overview lists blocks by id/tier/tokens/msg-count/topic — pull-based (model must call acp_status), no objectives line.
        So today an objective's only presence between folds is inside its summary block, sitting where that block sits in context — typically mid-context, older position.
    2. "Via nudge, each turn" is structurally unavailable. Nudges fire sparsely by settled design: flat 50K growth cadence, growth/token-mass as the need signal, repeat-firing growth-gated (AGENTS.md principle 7 explicitly forbids scaling cadence with window size). Re-surfacing objectives in the nudge would (a) reach the model only every ~50K tokens of growth — precisely when context is already long, not each turn, and (b) couple task-steering into a channel whose salience budget is spent on compression decisions. Rejected as primary mechanism.
    3. Mechanism of the drift. After fix(prompts): open-objectives carry-forward across folds and distillation hops #443 lands, the objective is preserved and framed as live tasking — but it lives in the oldest active summary block while attention follows recency (latest user messages / tool outputs). Position + recency is the mechanism; "deprioritized" is the symptom. The fixable lever is position: put the still-open objective at the most salient spot every turn, independent of where its summary block sits.

    Evaluation of your two suggested directions:

    • Direction 1 (nudge/output-steering re-surfacing): the nudge half is out (above). The output-steering half fits the architecture cleanly — [提案] 输出侧决策逻辑入内核:轮次结构分类 + verbosity 档位(L0-L4) + effort 路由(billion-context 做 wire 落位) #355 already establishes the exact pattern needed: kernel decides/renders, adapter lands, tail-only sentinel block, replaced in place, byte-stable while unchanged (prefix-cache safe: head bytes stable, tail may vary). CCR retrieval provides a second carrier precedent: request-only ephemeral trailing message consuming no ref and never entering the fold space. So: kernel renders the objective reminder text; the adapter picks the carrier (system-prompt tail vs ephemeral trailing message — the latter has maximum recency salience).
    • Direction 2 (merge with the Compression guidance loses still-open user objectives across multi-hop distillation #442 protected goal register): right long-term convergence point — one host-visible register addresses both classes (fold-time loss and visible-but-deprioritized). But a stored register is §7.3 contract territory: new CompressionState field (+ possibly a 4th ACP tool) = state schema + wire surface = human-gated, cross-repo adapter landing, kernel-ships-first sequencing. It also brings its own failure mode: stale entries if the model/host forgets to close an objective — a stale reminder can misdirect worse than silence, so it needs a staleness/reaffirmation policy designed up front. Compression guidance loses still-open user objectives across multi-hop distillation #442 deferred this deliberately; I keep that deferral.

    Recommendation (two phases, clear split):

    • Phase 1 — derived open-objective re-surfacing (this issue's scope, post-fix(prompts): open-objectives carry-forward across folds and distillation hops #443). Kernel exports a pure collectOpenObjectives(state): scan ACTIVE block summaries for the Open objectives: lines whose format fix(prompts): open-objectives carry-forward across folds and distillation hops #443 mandates, dedupe newest-first, cap length, render a compact block. Landing: (a) standing tail block via the existing sentinel machinery, and/or (b) ephemeral trailing message — adapter's choice; (c) an OPEN OBJECTIVES section in the buildStatusReport overview so pull-based self-audits see it too. Opt-in config flag, default OFF (same convention as CCR / crush / outputSteering). Crucially it is derived, not persisted — a pure function of existing state, so no state-schema, wire, or nudge-cadence change: it stays outside §7.3 contract territory entirely (additive exports only). Two honest limits: parsing is best-effort over model-generated text — format drift degrades to silence, never garbage; and no kernel mechanism can force a model to obey a visible reminder. Phase 1 maximizes salience/position, which is the strongest lever available short of host UI or a stronger model. If drift persists with standing re-surfacing, the remaining suspect is model-side attention limits — that gets reported back downstream as a finding, not turned into another kernel knob.
    • Phase 2 — stored host-visible goal register (separate decision, stays deferred). Revisit only if Phase 1 field evidence shows drift persisting despite standing re-surfacing, or you want the structured guarantee now (register surviving outside the fold space makes dilution physically impossible rather than rule-mandated). Needs its own design doc — writer API, close/staleness policy, acknowledgeRisk surface — plus cross-repo sequencing (release acp-kernel first, verify npm view acp-kernel version, then bump adapters).

    Sequencing note: Phase 1's parse target does not exist on master yet — verified compression-rules.ts:30 still carries the pre-#443 "Quotes are historical records, never current directives." wording and zero Open objectives occurrences in rules/packs. So Phase 1 blocks on PR #443 merging. One consistency requirement baked into the design: the standing block uses the same framing as #443's guardrail exception ("live tasking — confirm and resume, do not re-execute unconfirmed") so the three surfaces (summary body / standing block / status report) never emit contradictory trust signals about the same objective.

    For your decision: (1) sign-off on Phase 1 scope above (derived, opt-in, post-#443)? (2) my recommendation is NOT to file Phase 2 as a separate issue yet — wait for Phase 1 field evidence from the downstream session before committing to the heavier contract; filing now would be a placeholder issue, which the loop guard exists to prevent.

    中文摘要:核实了代码——当前内核在两次 nudge 之间没有任何重新呈现目标的路径(nudge 文本只含块号/ref 区间/层级/topic,状态报告是拉取式且无目标行),且 nudge 按既定设计稀疏触发,无法承载「每轮重述」;因此建议分两阶段:阶段一做「派生式」开放目标再呈现(纯函数扫描活跃摘要中 #443 强制的 Open objectives: 行,经既有尾随哨兵机制或一次性尾消息落地、默认关闭、不改状态/wire/nudge 节奏,需等 #443 合入),阶段二存储式 goal register 属 §7.3 契约变更、维持暂缓、待阶段一现场证据后再立项。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions