Skip to content

fix(memory): SSE-liveness debt — wall-clock guard contradicts streaming principle; periodic prepare path still long-holds locks #324

Closed

Description

@LeXwDeX

现象(欠账)

memory 写路径修复(fix/memory-write-path)让链路跑通,但遗留一项"已声明、未兑现"的本金欠账:

  1. SSE 原则未贯彻:model.ts 采用 RESPONSE_TIMEOUT = 5 分钟墙钟作为"最后兜底",与用户确认的原则冲突——"模型持续流式输出时不应判超时"。真正的逐 chunk 存活判定(streaming 消费 + 连接/首 chunk 超时 + chunk 间 idle 超时,任何 chunk——含 reasoning delta——到达即重置)尚未实现(设计门 H3)。
  2. 锁纪律仅覆盖 checkpoint 路径:周期 prepare 路径仍在 fence+lock 下内联跑 maintain(长持锁);search 仍跨 match 持锁(为保留同查询合并的刻意取舍,需在规格中显式声明而非隐式保留)。

证据

  • packages/opencode/src/memory/model.ts:RESPONSE_TIMEOUT = Duration.minutes(5),注释自认 "This is not an activity budget"。
  • packages/opencode/src/memory/memory.ts:kickMaintenance/backgroundMaintain 仅被 checkpointUnsafe 调用;prepareUnsafe 的 due 分支仍在 fence+lock 下运行 maintain。
  • 本地代理 SSE 探测:一次 17.2s 生成期间收到 2051 个 chunk(连续 reasoning_content delta)——chunk 级存活判定在该上游可行。

修法选项

  • memory 模型调用改为流式消费,实现 connect/首 chunk 超时 + chunk 间 idle 看门狗(任何 chunk 到达即重置);退役墙钟兜底,仅保留"连接建立零响应"作为非响应判据。
  • 将后台 maintain + 锁纪律扩展到周期 prepare 路径,并同步调整其钉住的"周期维护同步"测试语义。
  • 保留 search 的同查询合并行为(coalesces concurrent identical queries 测试不变)。

验收

  • 长时间流式 maintain 调用在 chunk 持续到达期间永不被杀;仅"零 chunk / 静默超窗"的连接被中止。
  • 发版后 live 验收:/compact → ~/.local/share/opencode/memory/projects/<hash> 话题文件落盘 → memory_search 可召回。
  • test/memory/ 全套绿(含 fix/memory-write-path 已加的两个回归)。

关联

  • 前序修复:fix/memory-write-path(json 词保证 + reasoning 预算 + 墙钟移除 + checkpoint 异步维护)。

Activity

  1. LeXwDeX commented on Aug 18, 2026

    @LeXwDeX
    OwnerAuthor

    状态更新(随 graphagent-v1.0.25 发布):

  2. LeXwDeX commented on Aug 18, 2026

    @LeXwDeX
    OwnerAuthor

    PR #333 偿付 debt 2 的 prepare 路径:周期维护在 fence 之后后台运行(镜像 checkpoint 形状),due-only 轮次不再读 store/拿锁;search 的同查询合并按验收要求保留,取舍已在代码注释与 CONTEXT.md 显式声明(MEM-02)。回归测试 keeps the fence and project lock free while background maintenance streams 钉住新纪律。debt 1 此前已由 PR #329 偿付(逐 chunk 存活,无墙钟),本 PR 复核确认。

  3. LeXwDeX commented on Aug 18, 2026

    @LeXwDeX
    OwnerAuthor

    PR #333 二轮(/code-review 修订,2f0b6ffeb):

    • due-only 轮次恢复 identity 存活门——bookkeeping + kickMaintenance 包回 fence.withLiveIdentity,已退役 identity 不再触发维护模型调用(并恢复 clearSession);shouldMatch 分支的 kick 同样加 fence 门,与 checkpointUnsafe 注释声明对齐。
    • 消除 prepareUnsafe 唯一的 else,改早返回;kick 参数对象在分支间复用单绑定。
    • 修正注释/CONTEXT.md 中"synchronous render half"不实措辞:fence 下是有界 matcher 模型调用(与 search 同形),非同步纯内存。
    • CONTEXT.md 补声明:prepare/checkpoint 的有界 matcher 跨 fence 持锁是有意取舍(match 结果喂 lock 下的原子 read-match-write)——偿付审计 MEM-01"含首轮 match"的声明缺口。
    • 测试:删冗余 hookRuns(fixture state.maintenance 已计数同一事件);裸 Effect.timeout 换 awaitWithTimeout(失败带消息)。
      验证:typecheck ✅;test/memory + memory-search 96 pass ✅;httpapi-memory-wiring + session/system 10 pass ✅。
  4. LeXwDeX commented on Aug 18, 2026

    @LeXwDeX
    OwnerAuthor

    PR #333 三轮(cfb42e89c)——二轮 /code-review 发现的 C1 阻塞项与配套修正:

    • C1(阻塞):checkpointUnsafe 的 kick 在 render fence 释放后无存活门,退役 identity 可烧穿两次维护模型调用,且与 CONTEXT.md:54 新不变量及自身注释矛盾。修法:存活门移入 kickMaintenance 内部(fence.withLiveIdentity 包住原子占槽+fork,返回 Option),三处调用点统一为 Option.isNone(...) → clearSession,规则单一定义。
    • 顺带消解二轮其余发现:due-only 分支去 fence 包裹(记账是进程本地写,门由 kick 自带,消除嵌套非可重入锁风险);MaintenanceInput 命名类型收敛 Data Clump;searchUnsafe 注释压至 5 行指向 CONTEXT.md;修正误导性 "(see checkpointUnsafe)" 交叉引用;测试 join fork 出的 prepare fiber、恢复 helper 默认超时。
    • 三轮复审:Spec 轴零发现;Standards 轴仅剩一处钉死 "2 seconds" 偏离默认超时,已修。
    • 退役竞态完备化:kick 门拒 → 不占槽/不调模型/清 session;fork 后才退役 → 提交仍被 applyUpdate 自身 fence 拒。
      验证:typecheck ✅;memory + memory-search 96 pass ✅;消费方 10 pass ✅;新免锁测试连跑 3 次稳定。
  5. LeXwDeX commented on Aug 18, 2026

    @LeXwDeX
    OwnerAuthor

    第四轮双轴复审(cfb42e89c)结论——代码 ship-ready:

    • Standards 轴:APPROVE。上轮遗留的钉死 "2 seconds" 已改为 helper 默认;旧项(无 else、单一存活门、MaintenanceInput、注释、join fiber)全部复核未回归;无新违反。(仅两条无害小注:maintenance 对象在 !due && shouldMatch 时多构建一次,可忽略;Option.isNone(...) clearSession 三次出现是锁纪律惯用法非重复。)
    • Spec 轴:六项 C1 子检查全部成立;超时改动语义中性。
    • 文档修正:PR fix(memory): extend background-maintenance lock discipline to the periodic prepare path (issue #324) #333 正文 "顺带消解 MEM-03" 措辞已改为部分收敛——瞬态不一致半被 CONTEXT.md 声明的设计语义吸收,"失败仅 logWarning、不向用户暴露" 一半仍开放。
    • 留待 follow-up(不阻塞本 PR):(1) MEM-03 剩余半——后台维护失败的暴露通道;(2) 既有可观测性缺口——backgroundMaintain 丢弃 applyUpdate 的 Option(退役拒提交静默结束)。
      测试复核:memory 44 pass、memory 全套 96 pass、消费方 15 pass。
  6. added a commit that references this issue on Aug 18, 2026
  7. LeXwDeX commented on Aug 18, 2026

    @LeXwDeX
    OwnerAuthor

    完成:PR #333 已合并到 dev(merge commit 1d087ff)。

    交付内容(dev 分支生效):

    • prepare 周期维护移出 fence/lock,镜像 checkpoint 形状(MEM-01)
    • search 锁取舍显式声明(MEM-02)
    • 存活门收归 kickMaintenance 单一定义,prepare/checkpoint 三处同形
    • 测试钉死语义按约定调整为 polling + 新增免锁回归

    遗留 follow-up(不属本 issue 范围):MEM-03 剩余半(维护失败的暴露通道);backgroundMaintain 丢弃 applyUpdate Option 的静默退役提交(可观测性)。见 #issuecomment-5323787153。

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