Skip to content

[design] bg-session 足迹不死是同一个根:统一用 claude agents --json 作 liveness 真相源 #12

Description

@NorveraFlorent

[design] bg-session 足迹不死是同一个根:统一用 claude agents --json 作 liveness 真相源

提给 forge-launcher;跨引 forge-hub #36、本仓 #8/#11
不是新 bug 报告,是把已存在的几条 issue 归并到同一个根,并提一个能同时止血两边的杠杆。
(命令名、版本事实、ghost 过滤实现状态均已对 claude v2.1.154 + forge-hub HEAD 代码核过。)

TL;DR

#8(launcher 看 bg session "仍在运行")、#11(launcher 清不掉僵死 daemon job)、forge-hub #36(/instances 无限累积 ghost)——表面三个独立症状,根是同一个:Claude Code 的 bg-fleet daemon 通过 auto-adopt + claim-spare 把一个 bg session 的"足迹"留住/复活,而没有任何一方对这个足迹做原子拆除。Hub 和 launcher 各自维护一份"谁还活着"的账本(Hub 的 instance-identities.json / launcher 的 kill(pid,0) 存活判定),两份账本都会被 daemon 的复活机制弄脏。

提议:liveness 的真相源统一走 claude agents --json(官方 bg-fleet 视图),别各自维护会过期的本地账本。

一根三枝

一个 bg session 的足迹 = {bg-spare 进程 · 一套 MCP server · hub-channel.ts→Hub 注册 · ~/.claude/jobs/<id>/state.json · ~/.claude/sessions/<pid>.json}

session"关掉"时 daemon 不整体拆,反而能 re-adopt / claim-spare 拉回(#8 实证:daemon.log bg adopt: adopted=N、反复 claimed-spare ... (fleet))。于是同一个不死足迹,三个外显:

现有 issue 留下的足迹片段 现修法(各自打补丁)
Hub 一直亮 / ghost 累积 hub #36 hub-channel 还连 → instance-identities.json 永不剔(645 条,85% 无 tag) Hub GC + ?include= 过滤(未实现,见下)
"session 还活着" 关不掉 launcher #8 PID 文件 + fleet 成员 → --resume 撞 "running as bg agent",kill 又复活 launcher 读官方 bg 视图
僵死 job 清不掉 launcher #11 jobs/<id>/state:failed 死锁 launcher 扫 jobs/ + 一键送葬

注:另有一类"长命 session 的 MCP server 持 stale 码"现象(2026-05-30 实测,一个对账工具因此差点误删数据),但它的根是 Python import-cache 不 reload + 消费侧缺破坏性护栏,与 bg-fleet 的 adopt/claim-spare 无因果(fleet 只是把 session 拉长做了放大)。故不并入本 issue,另行处理(reconcile 类工具加"孤儿占比异常即中止"护栏 + 改码后 /mcp 重连的纪律)。

为什么"单一 oracle"是去重的正解

Hub(#36)和 launcher(#8)现在各修各的:Hub 自己 GC identities、launcher 自己 kill(pid,0)。但两份账本都会被 daemon 的 auto-adopt/claim-spare 复活弄脏。与其各自维护,不如共用官方权威视图:

版本事实(必须诚实):#8 写作版本(2.1.143)报告 fleet routine 的 ~/.claude/sessions/*.json kind 仍是 "interactive",故"本地 sentinel 走不通、必须走 oracle"。但 v2.1.154 实测 kind 已是 "bg",claude agents --json 也报 "kind":"background"。这反而部分削弱了"必须走 oracle"的论证 —— 一个本地 kind=="bg" sentinel 现在可能就够,不一定要 shell out 到 claude agents请作者按当前版本复核:若 kind=bg sentinel 稳定,那是更轻的路;claude agents --json 作为更权威、跨"PID 文件/sentinel 都可能滞后"的兜底。两条都比"各维护本地账本"强。

另:终止 bg session 的官方命令入口待作者确认 —— #8 用户故事提到 claude agents kill <id>,但 v2.1.154 的 claude agents 是零位置参数命令,无 kill 子命令(claude agents kill 报错)。清理动作的官方入口需作者查当前版本(可能改了拼法或走别的命令)。本 issue 不把 claude agents kill 当既成接口

一个读码确证的观察:#36 的过滤从未落地

forge-hub #37(CLOSED)与 #36(OPEN)是同一 issue 的两份(正文逐字相同);#37 被 close 但 fix 没进代码,#36 还开着。读 HEAD 确认:

  • routes/instances.ts::handleInstances() 无任何 query 参数解析,永远 buildInstanceList(true)instance-manager.ts:117 listKnownInstances() 全量 union saved identities。#36 提议的 ?include=live|tagged 在代码里根本不存在
  • 客户端 fh 的 peers/status 调 hubGet("/instances")(无 query),拿回后客户端切分并主动打印 known 段(已知但不在线: ...)。
  • 双重确定:服务端没 filter + 客户端故意显示 known。2026-05-30 实测 fh hub peers27 个 forge-<pid> ghost("上次出现 13h/19h前")。

→ 即 #36 的提议未实现,ghost 仍全量返回。这是三枝里最实、可直接落码的一枝。

不在本 issue 范围

  • 不重复 #36 已提的 instance-identities.json GC / ?include= 具体实现。
  • 不替作者定 launcher 要不要暴露 bg 这一维(background agent session 未与交互 session 区分 — 用户视角"仍在运行"歧义 #8 已把决定权留给作者)。本 issue 只主张:若要修,统一以 claude agents --json(或当前版本的 kind=bg sentinel)作 liveness 真相,比各维护本地账本更省、更不被 daemon 复活打脸。
  • MCP stale 码那枝(import-cache + reconcile 护栏)不属本 issue,见上注。

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