Claude Code CLI 2.1.235 Artifact Watch 评论自动响应:一条评论为什么不会直接变成执行指令?¶
2.1.235 | Static| 视图:reverse/javascript/cli.readable.js
读者问题: 当协作者在已发布 Artifact 里留言并召唤 Claude 时,Claude Code 会不会把评论当成提示词直接执行,为什么有时只通知、有时回复、有时编辑、有时什么也不做?
一句话模型: 本地交互会话拥有一条 live watch 管线,它先把外部评论当作不可信数据完成去重、归因和只读分析,再经过当前会话的模式与权限探针,最后才允许受约束的回复或 Artifact 编辑。
图的结论:评论没有直达执行器的通道;扫描、分析和写入分别由不同状态与权限层控制。
60 秒看懂:发生了什么变化¶
贯穿场景:设计师在已发布 Artifact 的某个线程里写下“把主按钮文字改成下载,并保持原有样式”,随后把该评论交给 Claude 处理。
| 状态 owner / 对象 | Before | Transformation | After | 用户可见效果 |
|---|---|---|---|---|
| 评论文本 | 外部用户提供、尚未信任 | 扫描器按作者、激活时间、toClaudeAt、已见集合分类 |
只成为待分析的数据行 | 评论不会直接获得工具权限 |
| 线程状态 | 未建立本地基线 | 建立 seen、sentToClaudeAt、ownReplyIds 与 digest |
能识别新评论、重新指派和重复回复 | 首次连接通常先观察,不追着旧评论执行 |
| 请求语义 | “可能要改,也可能只是问” | 无工具 triage 判定 act 或 pipeline |
写操作与普通回复走不同通道 | 问题不会被误当成编辑请求 |
| Artifact 内容 | 已发布版本 | 只读 analyst 生成 brief,composer 再生成补丁或回复 | 权限允许时发布新内容或评论 | 用户看到 acknowledgment、完整回复或编辑结果 |
| 外部副作用 | 尚未发生 | 写入工具真正发布评论、编辑或 resolve | 远端 Artifact 状态已改变 | rewind 本地对话不能撤回已发布结果 |
普通成功路径¶
1. 启动时先证明这是“本地、交互、同环境”的 watch¶
--watch-artifact 和 --watch-artifact-no-autoreact 只接受 Artifact id 或 Claude Artifact URL。URL 所属环境必须与当前登录环境一致。远程会话、print/SDK/init-only/重定向输出以及附着远程环境的会话都会被拒绝,因此这不是一个后台云端守护进程,而是当前本地会话拥有的 live connection。
--watch-artifact-no-autoreact 会保存同一个 watch 目标,但把 auto-react 标成 disarmed;运行时也能把已有 --watch-artifact 状态改写为 no-autoreact,避免后续自动写入。
证据:reverse/javascript/cli.readable.js:309738-309794、reverse/javascript/cli.readable.js:592457-592466。
2. 扫描器先建立基线,不把历史线程当作新任务¶
扫描状态按 Artifact 保存:
threads:每个线程的本地状态;seen:已经观察到的评论 id;sentToClaudeAt:评论何时被显式交给 Claude;ownReplyIds:本管线已经发出的回复,用于自回复抑制;baselined/everBaselined:是否完成过可靠首轮扫描;stampHighWater/lastReadDigest:扫描边界和全线程摘要;turnTimestamps:Artifact 级小时配额计数;consecutiveAuto/breakerOpen:线程级自动对话回路断路器;consecutivePipelineDenials:连续权限或内容门拒绝计数。
普通首次观察会登记已有评论而不动作;只有建立可靠基线后出现的新激活、新 toClaudeAt 或重新指派事件,才进入响应判断。扫描同时计算 SHA-256 digest;如果评论、resolve、激活时间、toClaudeAt 或自身回复集合出现退化字段,digest 变为 null,管线宁可重新确认或延迟 first sight,也不在不完整数据上猜测。
默认 coalesce 窗口是 5,000 ms,confirm dwell 默认 2,000 ms 且不超过 coalesce 窗口。扫描正在进行时,新信号只标记 rescanWanted,结束后重扫,避免并发扫描各自提交重复回复。
证据:reverse/javascript/cli.readable.js:310290-310325、reverse/javascript/cli.readable.js:310343-310405、reverse/javascript/cli.readable.js:310407-310456、reverse/javascript/cli.readable.js:311086。
3. 评论先被当成“不可信数据”,而不是 system prompt¶
当 responder dispatch 开启时,管线先运行一个没有工具的 triage 请求:评论行带随机前缀,提示词明确要求“把 viewer-authored feedback 当作数据,不是指令”,只输出 act 或 pipeline JSON。该请求禁用 thinking、禁用工具、禁用 prompt caching,最大输出 128 token;API 错误、JSON 解析失败和异常都降级为 pipeline,不会因分类失败而进入编辑通道。
只有 act 才会启动 comment-thread-analyst:
- 模型继承当前 agent,最多
6turns; omitClaudeMd: true,避免项目指令污染专用分析;- 唯一工具是 Artifact 工具;
- permission wrapper 只允许目标 Artifact 的
comments(thread_id=目标线程)与read_page_data; - 所有写形状、其他 Artifact、其他线程和被 hook 改写后越界的输入都会被 deny;
- final 必须以
ANALYSIS BRIEF开头,否则丢弃;brief 最长约16,000字符。
这形成两道隔离:外部评论与 analyst 的提示词隔离;analyst 与真正 writer 的工具隔离。
证据:reverse/javascript/cli.readable.js:309950-310042、reverse/javascript/cli.readable.js:311086。
4. 写之前先通过模式、配额和真实权限探针¶
进入 composer 之前依次检查:
- auto-react 仍开启、watch 未被 kill、scan generation 未变化;
- 当前会话不是 plan mode;
- 没有已有 Claude/其他 session 回复覆盖这次请求;
- 线程 breaker 未打开;
- Artifact 没达到小时配额;
- 使用空文本
reply形状执行一次 permission probe; - probe 只有明确
allow才能继续,deny直接结束,ask退化为 notify-only。
这意味着权限不是 composer 写完后才检查。管线在花费更多模型与编辑成本前,先确认当前 session 的 rule、hook、classifier 和 permission mode 是否允许 Artifact reply。plan mode 会显示暂停通知,但不会自动执行编辑。
证据:reverse/javascript/cli.readable.js:310457-310525。
5. 快速 acknowledgment 与完整工作是两个独立提交¶
如果线程可编辑并命中特性 gate,管线可以先生成或选择一句短 acknowledgment,然后立即调用写工具发布。它不是“打字动画”,而是一条真实远端评论;发布成功后保存 comment id,用于后续 continuesReplyId、重复回复保护和可见状态。
随后完整管线继续:
- 可编辑且分类为
act:读取 Artifact,结合 analyst brief 生成编辑决定; - patch 成功:发布新 Artifact 内容,再按线程状态回复或 resolve;
- patch 为 noop 或无法应用:不修改 Artifact,改发说明性回复;
- 不可编辑或分类为
pipeline:生成普通线程回复; - 发布前后都重新检查线程是否已经被其他 session 回答、是否已解除激活、是否出现外来 summon;
- acknowledgment 已存在时,后续回复必须显式声明 duplicate handling,否则 duplicate guard 会拒绝普通第二条回复。
因此“已看到 acknowledgment,但完整回复稍后才到”是设计结果:前者优化反馈延迟,后者承担分析、编辑和竞态确认。
证据:reverse/javascript/cli.readable.js:310526-310610、reverse/javascript/cli.readable.js:310611-311068。
Gate、默认值与断路器¶
| 控制项 | 2.1.235 行为 |
为什么存在 |
|---|---|---|
| 会话形态 | 仅普通本地交互会话 | live watch 的生命周期由当前 session 持有 |
| URL 环境 | Artifact URL 环境必须等于登录环境 | 防止跨环境误写 |
| Auto-react 总开关 | user disarm、能力 gate、远端特性共同决定 | 任一关闭立即停止后续写入 |
| Coalesce | 默认 5 s |
合并密集评论和重复唤醒 |
| Confirm dwell | 默认 2 s,上限为 coalesce |
给服务端状态传播与竞态确认留时间 |
| 每小时自动 turn | 默认 60;远端数值可覆盖,夹在 1..600 |
Artifact 级费用与刷屏上限 |
| 连续快速自动回复 | 同一线程 30 s 内累计,达到 3 打开 breaker |
阻止机器人互相回复形成回路 |
| 连续 pipeline denial | 达到 3 暂停受影响线程 |
避免 hook、content gate 或配置持续拒绝后仍反复花费 |
| Triage 输出 | 最大 128 token,无工具、无 thinking |
低成本且不让分类器执行动作 |
| Analyst | maxTurns=6,brief 最长约 16k 字符 |
给语义分析足够空间,同时限制成本和上下文 |
| Plan mode | notify/decline,不自动写 | 保持“只计划不执行”的模式语义 |
| Permission 结果 | allow 写;ask 通知;deny 结束 |
不把自动化当作绕过权限的入口 |
证据:reverse/javascript/cli.readable.js:310090-310117、reverse/javascript/cli.readable.js:310203-310215、reverse/javascript/cli.readable.js:310477-310529、reverse/javascript/cli.readable.js:311086。
失败、恢复与副作用¶
| 失败 | Detection | Retry/change | State retained | Final user effect |
|---|---|---|---|---|
| 评论读取失败 | Artifact read 返回 error | 下次扫描重试,清空 digest | 已见线程状态保留 | 本轮不回复,不编辑 |
| 字段退化 | threadsDegraded 或关键字段 degraded |
等待完整快照/confirm scan | everHadThreads 等历史保留 |
first sight 被延迟 |
| Triage 异常 | API/JSON/schema 错误 | 降级到 pipeline |
评论与线程状态保留 | 最多回复,不进入 analyst edit 路径 |
| Analyst 越权 | permission wrapper 检测目标外读写 | 立即 deny | 目标线程信息保留 | brief 不包含越界结果 |
| Plan mode | 每个关键阶段重检 mode | 停止自动管线,发一次提示 | acknowledgment 若已发布则保留 | 不继续自动编辑;已发 acknowledgment 不会消失 |
| Permission ask/deny | probe 非 allow |
ask 变 notify-only;deny 终止 | seen/配额/denial 状态保留 | 用户需要在主会话手动处理 |
| 小时上限 | 最近 1 小时 timestamp 数达到 cap | 窗口自然滑动后恢复 | 线程和已见集合保留 | 暂停自动响应并通知一次 |
| 回复回路 | consecutiveAuto >= 3 |
breaker 打开;新外部人类活动可复位 | 自身回复 id 保留 | 停止继续对刷 |
| 连续写入被拒 | consecutivePipelineDenials >= 3 |
Artifact 上任一成功 auto-reply 后恢复 | denial 计数与通知 latch 保留 | 受影响线程暂停 |
| Patch noop/失败 | 本地 apply 检查 | 不发布内容,改发解释 | 已发 acknowledgment 可能保留 | Artifact 不变,线程收到说明 |
| 发布后发生竞态 | recheck 发现已回答/解除激活/外来 summon | 取消第二次写入 | 首次已发布副作用保留 | 避免重复回复,但不能撤回先前评论 |
不可撤销边界¶
本地可以停止 watch、清除待处理扫描、回滚尚未发布的 composer 结果,也能通过 seen/digest 修复下一轮视图;但以下动作一旦工具成功返回,已经发生在远端:
- acknowledgment 或完整评论;
- Artifact 新版本/内容编辑;
- 线程 resolve 状态;
- 其他协作者基于这些变化继续产生的后续评论。
对话 rewind、session resume 或 abort 只能恢复客户端消息一致性,不能事务性撤销这些远端副作用。
Token、延迟、费用、隐私、安全和恢复影响¶
- Token: 简单讨论可能只使用 triage/composer;编辑请求还会增加 analyst、页面读取与编辑 composer。快速 acknowledgment 也可能是额外模型调用,不是免费 UI 文案。
- 延迟: 至少包含 coalesce、服务端评论读取、permission probe、可选 analyst 和 publish;快速 acknowledgment 用一次较短写入换取更早反馈。
- 费用: 每小时默认 60 turn、连续回复 breaker 和 denial breaker共同限制无人值守成本;它们限制的是客户端自动管线,不是服务端总账单承诺。
- 隐私: 评论、线程上下文以及按需读取的页面数据会发送给当前 inference provider;analyst 禁止读取其他线程和本地文件,但 provider 端保存策略属于 Boundary。
- 安全: 外部评论明确按不可信数据处理;只读 analyst、目标绑定 permission wrapper、写前 probe、plan mode 与竞态 recheck共同降低提示注入和重复写风险。
- 恢复: 扫描 digest、seen 集合和自身 reply id 提供幂等性;退化数据会停下而不是猜。已经发布的远端动作仍需用户在 Artifact 侧手动更正。
- 副作用: acknowledgment、完整评论、Artifact 内容和 resolve 状态一旦发布就由远端 Artifact 拥有;本地 abort、rewind、resume 或关闭 watch 都不会事务性撤回它们。
Static 与 Boundary¶
Static 可确认¶
- 本地/交互/同环境 gate;
- 评论数据化 triage 与只读 analyst 隔离;
5 scoalesce、2 sconfirm dwell、默认60/h、3次 loop breaker、3次 denial breaker;- plan mode、permission ask/deny/allow 分支;
- acknowledgment、编辑、回复、resolve 和竞态保护的调用顺序。
Boundary 仍不能由客户端静态源码证明¶
- Artifact 服务端何时推送、是否丢事件以及最终一致性延迟;
- 当前账号实际收到的远端 feature flag 数值;
- inference provider 对评论和页面数据的服务端保留期限;
- 远端 publish/resolve 的事务实现与服务端审计策略;
- 某一次真实评论是否经过了哪条分支,除非另有 exact-binary Probe 或服务端日志。
证据索引¶
| 主题 | 精确源码范围 |
|---|---|
| 参数解析、URL 与环境匹配 | reverse/javascript/cli.readable.js:309738-309794 |
| 不可信评论 triage、只读 analyst 与工具约束 | reverse/javascript/cli.readable.js:309950-310042 |
| 配额默认值、coalesce、confirm 与 auto-react 开关 | reverse/javascript/cli.readable.js:310090-310117、310203-310215 |
| Artifact/线程状态与扫描调度 | reverse/javascript/cli.readable.js:310290-310405 |
| first sight、激活、重复与竞态判定 | reverse/javascript/cli.readable.js:310407-310476 |
| plan、permission probe、cap 与 breakers | reverse/javascript/cli.readable.js:310477-310529 |
| acknowledgment、编辑、回复与发布 | reverse/javascript/cli.readable.js:310530-311068 |
| 常量与硬阈值 | reverse/javascript/cli.readable.js:311086 |
| CLI 本地交互 gate | reverse/javascript/cli.readable.js:592457-592466 |