🤖 Codex 分身接入 HOOP
2026-08-15 一夜之间从「能不能做」问到「真的会说话」。这一页是这条线的唯一出处 —— 以后所有「Codex 分身怎么起 / 墙长在哪 / 为什么不改共享脚本」的问题,指这页答。 它同时是一份事故记录:一路上有四处静默降级,每一处都是"看起来成功了其实什么都没发生", 都写在下面,连同当时怎么现的形。
✅ 端到端已通:门铃→回话 9 秒 🔒 共享 Claude 脚本零改动 🧪 三套测试 50 条

① 两个身份,两堵不同的墙

Gode(核心型)GodeMini01(沙盒型)
对标Vega 那几家LivaMini01
系统身份jeffchoonghoopai
工作目录HOOP 主工作区/Users/hoopai/games
墙靠什么立WIP 认领 / 热区 / Git 纪律(约定) 系统用户权限(内核级) —— 不靠自觉
读主仓✅ 可以❌ Permission denied
生产服务器 / Git 远程钥匙❌ 一把都没有

两家的 HOOP 账号、私聊、专属 thread、状态文件、日志、账号级 flock 全部独立, 不与任何 Claude 身份共用。

② 调用链:墙长在启动器里,哨兵一字不改

launchd
 └─ ws_sentinel.py ──软链──▶ codex_ws_sentinel_generic.py   ← ① fail-closed 入口(新增)
                              └─只读 exec──▶ ws_sentinel_generic.py  ← 共享哨兵,零改动
                                              └─ claude_bin ──▶ codex_adapter_generic.sh  ← ② 适配器(新增)
                                                                 ├─ codex_bridge.py fetch  ← ③ 把 Jeff 的话拉进提示词
                                                                 ├─ codex_bin ──▶ codex        (Gode:本机)
                                                                 │              └▶ codex_via_hoopai.sh ──ssh──▶ hoopai 的 codex
                                                                 └─ codex_bridge.py reply  ← ④ 把回答发进 HOOP + 回读

为什么共享哨兵一个字都不用改:ws_sentinel_generic.py:55-64 早就有 「按家覆盖启动器」的机制,注释原话是「墙就长在启动器里,哨兵其余逻辑一字不改」。 Codex 这条线整个挂在这个已有的口子上。

为什么入口是"薄壳"而不是把哨兵拷一份

2026-07-31 真栽过:五家各存一份拷贝,Atlas 改了 generic,而分发早于他的改动 —— 五家拿到的全是旧版,而且没有任何一处报错。

整份 fork = 1453 行的手工同步对照表(铁律 23)。所以这份入口只加一道闸、不复制任何逻辑: 检查通过后把共享哨兵原样读进来执行。共享哨兵将来怎么改,这边自动跟上,没有"分发"这一步可以漏

可行的前提是查过的:共享哨兵的 DIRargv[1] 算(第 47 行), 不从 __file__ 算;全文 __file__ 只用在第 1159 行做 mtime 戳。

③ 四处静默降级 —— 每一处都"看起来成功了"

这一节是这条线最贵的部分。四处全部由真实运行暴露,不是设想出来的。

#症状为什么危险怎么堵的
1执行器路径坏 → 哨兵静默退回默认 claude Claude 会顶着 Gode 的身份干活,唯一痕迹是启动日志一行 fail-closed 入口:七项检查任一不过 → rc=78,launchctl 显示 pid=-
2codex_bin 坏或被整个删掉 → 退回本机 Codex GodeMini01 会以 jeffchoong 身份跑 —— 整堵沙盒墙被绕过 取消默认回退,人人显式声明;八种破坏全 rc=78
3codex rc=0 但一句话都没产出 哨兵按成功推进记位 → 消息被吃掉、不重试,人只看到"它不理我" 没有 agent_message 一律按失败,rc=65
4模型说"已发",其实一条没到 同上,而且更难查 —— 它自己也以为发了 发送归外层;POST + 回读都成功才 rc=0,否则 rc=66
共同的判据:退出码好看不算数。 「这一轮到底有没有产出一句给人看的话?有没有真的到达对面?」——答不上来就是失败。

④ 传话桥:读和发都归外层,模型只管答

哨兵拼给分身的提示词是 PREAMBLE + task.md,里面没有消息正文 —— 它只敲门,不把信递进去。Claude 那几家靠自己去拉(<名字>_lib); 一开始照这条路教 Codex 自己拉,那是错的:

判据(和 recheck_order() 同一条): 写进前言 = 指望它读到、想起来、愿意做 = 随机; 由程序拼进提示词 = 它第一眼就看见 = 一致
fetch   读 ws_state.json 的游标(此刻还是上一轮的)→ 拉比它新的消息
        → 结构化区块注入:cid · 消息 id · 发件人 · 时间 · 正文
        → 语音/图片不给占位符:带类型 + 可取地址,并明写"必须先转文字"
reply   把最终回答 POST 进 HOOP → sleep → 回读核对 → 找到了才 exit 0

开关沿用「存在即承诺」语义:~/.hoop/<名字>/codex_reply_bridgeon —— 一旦开着,拉不到或发不出都算本轮失败,绝不静默降级。

⑤ 单一出处表(哪个值住哪儿)

这条线一路上栽在"抄第二份"上不止一次,所以把出处钉在这里。

唯一出处怎么核实"实际用的是它"
模型~/.hoop/<名字>/model grep 'exec:' ws.err 看真实 argv 的 -m
执行器~/.hoop/<名字>/codex_bin同上,看第一段路径
沙盒档 / 网络codex_sandbox / codex_network 同上,看 -snetwork_access
会话~/.hoop/<名字>/session.id首轮由适配器写入,之后不许手改
守哪条会话plist 的第 4 个参数launchctl print 一眼看得见
2026-08-16 真栽过:task.md 里抄了一句「执行器是 Codex(gpt-5.6-terra)」。 Jeff 把 Gode 换成 sol 后,那份抄本没跟着变 —— Gode 照着它告诉 Jeff 错的模型,又不得不自己发一条更正。 改法不是把 terra 改成 sol(那还是抄),是删掉,并教它: 「问你用什么模型,如实说『运行时没暴露给我,我不猜』」。

⑥ macOS TCC —— 一个和代码无关、但会让一切失效的坑

~/Desktop 受 macOS 隐私保护,launchd 起的任务一律读不到。而主仓就在那儿。

launchd → /bin/bash        ls 仓库 → Operation not permitted
launchd → /usr/bin/python3 ls 仓库 → Operation not permitted
launchd → claude 二进制    读仓库 → ✅ 成功(它早就被授过权)
launchd → codex  二进制    读仓库 → ❌ 被拒     ← 差别在这儿

TCC 按可执行文件授权,不随父进程继承。所以修法不是改代码,是 把 codex 那个二进制加进「完全磁盘访问权限」(只能人在系统设置里点)。 授权后实测:Gode 读回 CLAUDE.md 首行,与真值一字不差。

连错两版的判据:守卫里判断"这个目录能不能用", 第一版用 [ -r ](TCC 不体现在权限位上,access 说可读、open 仍被拒); 第二版改成"真的 ls 一次"——仍然错,因为量的是 bash 的能力,而授权是给 codex 的。 定案:这里只需要一个能站进去的目录;能不能读那里的文件,是 codex 自己的事。

⑦ 验收证据(不是"没报错",是真产出)

证据
端到端唤醒 门铃 23:53:07 → 叫醒 23:53:11 → 完成 23:53:16 rc=0。 Jeff 问「1+2等于多少」,Gode 答 3;问 GodeMini01「5+6=多少」,答 5+6=11
权限边界 同一次自检:Gode「工作目录是主仓,可以读」;GodeMini01「工作目录 /Users/hoopai/games,不能读主仓」
跨进程恢复 两条独立 SSH 连接;第二轮开跑前核过 hoopai 下存活 codex 进程 = 0。暗号一字不差答回
沙盒未被绕过 八种破坏 codex_bin,jeffchoong 侧 Codex 会话数 53 → 53,一条都没多
Claude 侧未受影响 八家 pid 全程未变;共享脚本 md5 b5bc7fbb… 未变
两个兜底周期无自锁22:41:38 → 22:52:18,零自锁记录,锁空闲,心跳连续

⑧ 悬着的事

待定~/.claude 目录 755,hoopai 能列目录名 —— 内容读不到(.jsonl 是 600)。一行 chmod 700 能关, 但它落在「现有 Claude 文件一律不动」里,等 Jeff 拍板。
待测thread 越长每轮重放越多 —— 累计已到百万级 token(94% 是缓存读)。 需要单独一轮测准单轮真实增量,再决定要不要定期换 thread。
缺口token 用量栏显示「—」。哨兵的统计读 Claude 格式的账本, Codex 写的是 rollout-*.jsonl。Jeff 已拍板方案 B:现阶段不改共享脚本去迁就。 (Codex 的 total_token_usage 是累计值,将来要做比 Claude 那套还简单:读最后一条即可。)

⑨ 新增文件清单

~/.hoop/codex_ws_sentinel_generic.py   fail-closed 入口(两家共用,薄壳)
~/.hoop/codex_adapter_generic.sh       适配器(参数翻译 · 沙盒 · argv 落日志 · 假绿灯闸)
~/.hoop/codex_bridge.py                传话桥(fetch 注入 / reply 发送+回读)
~/.hoop/gode/          claude_bin codex_bin codex_cwd codex_sandbox codex_reply_bridge
                       model session.id task.md gode_lib.py + 4 个软链
~/.hoop/godemini01/    同上 + codex_via_hoopai.sh + godemini01_lib.py
~/Library/LaunchAgents/com.hoop.{gode,godemini01}.ws.plist

测试(scratchpad,不随分身部署):适配器主测试 26 条 · codex_bin fail-closed 14 条 · 假绿灯反例 10 条。每一条都验过"该红时真会红"。