docs(research): opencode 会话忙闲信号实测 — 修正此前「无状态端点」的假阴性结论 - #471
Conversation
/event 是 SSE 推送流, session.status 事件带 per-session 的 busy/idle 状态。 此前判定为「无可用状态端点」是探测方法造成的假阴性:只探了主动查询类 端点, 且抓流时读取超时 2 秒仅收到 89 字节即断开。 同时记录该信号的边界: 它只能约束程序自身, 约束不了人。已复现当第二条 消息真正落在第一条生成窗口内时两条会被合并为同一条回复。
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3f9a6dfa6c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # opencode 会话忙闲信号 — 实测记录 | ||
|
|
||
| 日期:2026-07-29 | ||
| 环境:独立服务器(4 核 / 14G,专用于该实验),opencode-ai 1.17.13,免费模型 `opencode/deepseek-v4-flash-free` |
There was a problem hiding this comment.
Validate the signal against the supported OpenCode pin
When this record is used to choose the M2 co-presence topology, the experiment only establishes behavior for opencode-ai@1.17.13, while agent-network/src/opencode-pin.ts:22 fixes the supported runtime at 1.18.1 and docs/rfcs/RFC-029-opencode-runtime-integration.md:13 explicitly treats 1.17.13 results as historical. Endpoint availability, event schema, and concurrent-turn behavior therefore remain unverified for the version that will actually ship; rerun the probe on 1.18.1 or scope these conclusions as historical rather than using them for the current design decision.
Useful? React with 👍 / 👎.
| {"type":"session.status","properties":{"sessionID":"ses_…","status":{"type":"idle"}}} | ||
| ``` | ||
|
|
||
| 单次生成期间共捕获 224 条事件,类型分布(节选): |
There was a problem hiding this comment.
Preserve the required Docker probe evidence
For the M0 topology decision, the 224-event count and message-merging result exist only as prose: this commit adds no isolated Dockerfile/run script, raw SSE and timestamp dump, or docs/tests/report-testN.txt. This also bypasses the explicit M0 gate in docs/rfcs/RFC-029-opencode-runtime-integration.md:641 requiring a Docker-isolated probe with raw HTTP dumps, so reviewers cannot verify either the event schema or that the two messages actually overlapped; rerun this as an independent Docker suite and retain its raw evidence and report.
AGENTS.md reference: AGENTS.md:L7-L9
Useful? React with 👍 / 👎.
| - 外部程序**可以**订阅 `/event`、按 `sessionID` 跟踪忙闲,从而在会话忙时不发送自己的消息; | ||
| - 这使"轻量方案(订阅事件 + 自我串行)"重新可行,无需自建终端仲裁层。 |
There was a problem hiding this comment.
Establish session state when the SSE listener connects
When the daemon starts or reconnects while a session is already busy or idle, this subscription-only design has no demonstrated initial state: the sole procedure at lines 57–59 opens /event before starting generation and therefore validates only future busy→idle transitions. Assuming idle on connection can inject a message into an existing turn, while treating an unknown state as busy can leave an already-idle session queued indefinitely if the stream does not replay a snapshot; probe startup and reconnect during both states and define a snapshot/resynchronization path before concluding that event subscription can replace the arbitration layer.
AGENTS.md reference: AGENTS.md:L5-L6
Useful? React with 👍 / 👎.
| ## 对共存设计的影响 | ||
|
|
||
| - 外部程序**可以**订阅 `/event`、按 `sessionID` 跟踪忙闲,从而在会话忙时不发送自己的消息; | ||
| - 这使"轻量方案(订阅事件 + 自我串行)"重新可行,无需自建终端仲裁层。 |
There was a problem hiding this comment.
Apply the RFC fallback after reproducing an unarbitrated race
In the actual co-presence scenario where a human turn overlaps an anet turn, the experiment at line 48 demonstrates that OpenCode merges the requests rather than arbitrating them, yet this line concludes that the no-arbiter option is sufficient. That result matches the explicit Option (ii) fallback condition in docs/rfcs/RFC-029-opencode-runtime-integration.md:546—a concurrent race with no arbitration—and an SSE status notification cannot enforce the human lease required at line 500. Either retain the arbiter fallback or formally change the accepted co-presence semantics to allow merged turns; the observed signal alone does not justify the stated topology decision.
Useful? React with 👍 / 👎.
为什么提这个
opencode 人机共存的拓扑决策一直卡在一个前提上:外部能否知道某个会话正在生成中。
此前探测得出的结论是"没有可用的状态端点",据此倾向选择成本较高的方案(自建一层终端仲裁,估 14-20h)。
实测发现这个结论是假阴性,信号一直都在。
结论
/event是一条 SSE 推送流,session.status事件带 per-session 的busy/idle状态。单次生成期间捕获 224 条事件,busy→idle 转换完整。之前判成"没有"的原因有两条,都属探测方法问题:
/event的那次读取超时 2 秒、仅收到 89 字节即断开,没读到流内容。信号是推送式的,而探测方式是拉取式的,形态不匹配。
同时记录了边界(避免下一个人过度乐观)
该信号只能约束程序自身,约束不了人。同环境下已复现:当第二条消息真正落在第一条的生成窗口内时,两条会被合并为同一条回复(相同 message id、同时返回)。
所以订阅事件的真实价值是"我们自己不制造冲突 + 检测到忙时可提示",而不是"冲突问题解决了"。
复现要点
文档里写了完整复现步骤。其中一条值得强调:验证合并现象时必须先用时间戳证明第二条消息确实落在第一条的生成窗口内——若第一条已结束才发第二条,会得到不同的 message id,从而得出"不会合并"的错误结论(我第一次跑就踩了这个坑)。
纯文档,无代码改动。