work-ally 0.2.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/AGENTS.md +110 -0
  2. package/DASHBOARD.md +160 -0
  3. package/PRODUCT.md +113 -0
  4. package/README.md +403 -0
  5. package/ally.sh +171 -0
  6. package/bridge/src/approval-rules.ts +360 -0
  7. package/bridge/src/channel-delivery.ts +207 -0
  8. package/bridge/src/channel-types.ts +22 -0
  9. package/bridge/src/channels/fake/adapter.ts +31 -0
  10. package/bridge/src/channels/feishu/adapter.ts +411 -0
  11. package/bridge/src/channels/feishu/approvals.ts +6 -0
  12. package/bridge/src/channels/feishu/formatter.ts +276 -0
  13. package/bridge/src/channels/feishu/normalize.ts +368 -0
  14. package/bridge/src/codex-config.ts +52 -0
  15. package/bridge/src/config.ts +240 -0
  16. package/bridge/src/fake-runtime-client.ts +505 -0
  17. package/bridge/src/handoff-service.ts +494 -0
  18. package/bridge/src/logger.ts +194 -0
  19. package/bridge/src/memory-digest.ts +186 -0
  20. package/bridge/src/receiver-approval-autonomy.ts +158 -0
  21. package/bridge/src/receiver-control-core.ts +140 -0
  22. package/bridge/src/receiver-control-work-session.ts +218 -0
  23. package/bridge/src/receiver-control.ts +83 -0
  24. package/bridge/src/receiver-delivery.ts +136 -0
  25. package/bridge/src/receiver-helpers.ts +96 -0
  26. package/bridge/src/receiver-human-gate.ts +333 -0
  27. package/bridge/src/receiver-inbound-preflight.ts +162 -0
  28. package/bridge/src/receiver-recovery.ts +236 -0
  29. package/bridge/src/receiver-runtime-callbacks.ts +367 -0
  30. package/bridge/src/receiver-runtime-policy.ts +132 -0
  31. package/bridge/src/receiver-runtime-state.ts +124 -0
  32. package/bridge/src/receiver-support-actions.ts +189 -0
  33. package/bridge/src/receiver-thread-start.ts +57 -0
  34. package/bridge/src/receiver-turn-coordination.ts +94 -0
  35. package/bridge/src/receiver-turn-execution.ts +257 -0
  36. package/bridge/src/receiver-turn-failure.ts +143 -0
  37. package/bridge/src/receiver-turn-result.ts +185 -0
  38. package/bridge/src/receiver-turn-steer.ts +70 -0
  39. package/bridge/src/receiver-work-session.ts +76 -0
  40. package/bridge/src/receiver.ts +329 -0
  41. package/bridge/src/router.ts +62 -0
  42. package/bridge/src/runtime-client-agent-messages.ts +150 -0
  43. package/bridge/src/runtime-client-message-dispatch.ts +176 -0
  44. package/bridge/src/runtime-client-protocol.ts +411 -0
  45. package/bridge/src/runtime-client-request-ops.ts +56 -0
  46. package/bridge/src/runtime-client-run-turn.ts +158 -0
  47. package/bridge/src/runtime-client-thread-ops.ts +270 -0
  48. package/bridge/src/runtime-client-transport.ts +309 -0
  49. package/bridge/src/runtime-client-turn-poll.ts +224 -0
  50. package/bridge/src/runtime-client-turn-read.ts +185 -0
  51. package/bridge/src/runtime-client-turn-state.ts +105 -0
  52. package/bridge/src/runtime-client.ts +344 -0
  53. package/bridge/src/runtime-user-input.ts +403 -0
  54. package/bridge/src/scheduler.ts +239 -0
  55. package/bridge/src/server-handoff-command.ts +364 -0
  56. package/bridge/src/server-main.ts +80 -0
  57. package/bridge/src/server-routine-command.ts +60 -0
  58. package/bridge/src/server-routine-execution.ts +222 -0
  59. package/bridge/src/server-runtime-app-support.ts +107 -0
  60. package/bridge/src/server-runtime-app.ts +238 -0
  61. package/bridge/src/server-thread-sync-command.ts +63 -0
  62. package/bridge/src/server.ts +17 -0
  63. package/bridge/src/session-store-delivery.ts +220 -0
  64. package/bridge/src/session-store-human-gate.ts +380 -0
  65. package/bridge/src/session-store-inbound-acceptance.ts +66 -0
  66. package/bridge/src/session-store-meta.ts +134 -0
  67. package/bridge/src/session-store-turn-ledger.ts +272 -0
  68. package/bridge/src/session-store.ts +380 -0
  69. package/bridge/src/system-notify.ts +220 -0
  70. package/bridge/src/thread-sync.ts +200 -0
  71. package/bridge/src/translator.ts +494 -0
  72. package/bridge/src/types.ts +289 -0
  73. package/bridge/src/utils.ts +104 -0
  74. package/bridge/src/work-session-store.ts +471 -0
  75. package/docs/.gitkeep +0 -0
  76. package/docs/architecture/codex-feishu-bridge-proposal.md +2742 -0
  77. package/docs/completed/FEATURE-feishu-markdown-and-reply-support.md +327 -0
  78. package/docs/completed/README.md +21 -0
  79. package/docs/completed/SPEC-approval-autonomy-and-safe-defaults.md +205 -0
  80. package/docs/completed/SPEC-approval-batch-and-strict-reply-shortcuts.md +153 -0
  81. package/docs/completed/SPEC-conversation-noise-reduction-and-busy-input-gate.md +538 -0
  82. package/docs/completed/SPEC-engineering-sop-skillization.md +190 -0
  83. package/docs/completed/SPEC-faithful-bridge-core-thinning-v2.md +376 -0
  84. package/docs/completed/SPEC-faithful-bridge-core-thinning.md +1071 -0
  85. package/docs/completed/SPEC-group-chat-sender-identity.md +301 -0
  86. package/docs/completed/SPEC-middleware-exception-visibility.md +227 -0
  87. package/docs/completed/SPEC-nightly-memory-digest-visibility.md +121 -0
  88. package/docs/completed/SPEC-project-group-chat-human-centered-conversation-mapping.md +326 -0
  89. package/docs/completed/SPEC-remove-cli-persona-bootstrap.md +201 -0
  90. package/docs/developer-workflow.md +49 -0
  91. package/docs/implementation/SPEC-codex-same-machine-session-handoff-implementation.md +239 -0
  92. package/docs/implementation/test-coverage-map.md +363 -0
  93. package/docs/implementation/work-ally-implementation-guide.md +790 -0
  94. package/docs/issues/README.md +10 -0
  95. package/docs/issues/pending/ANALYSIS-ally-premature-recovery-notice-and-task-state-semantics-2026-03-18.md +295 -0
  96. package/docs/issues/resolved/ANALYSIS-approval-waiting-visible-but-approval-artifact-missing-2026-03-16.md +466 -0
  97. package/docs/issues/resolved/ANALYSIS-blocking-state-visible-without-user-actionable-artifact-2026-03-16.md +261 -0
  98. package/docs/issues/resolved/ANALYSIS-codex-app-server-transport-disconnect-semantics-2026-03-14.md +606 -0
  99. package/docs/issues/resolved/ANALYSIS-premature-terminalization-on-fresh-thread-poll-and-object-error-leak-2026-03-16.md +348 -0
  100. package/docs/issues/resolved/ANALYSIS-runtime-turn-delivery-and-recovery-2026-03-14.md +603 -0
  101. package/docs/issues/resolved/ANALYSIS-self-test-gap-approval-waiting-visible-but-approval-artifact-missing-2026-03-16.md +166 -0
  102. package/docs/issues/resolved/ANALYSIS-self-test-gap-blocking-state-visible-without-user-actionable-artifact-2026-03-16.md +186 -0
  103. package/docs/issues/resolved/ANALYSIS-self-test-gap-premature-terminalization-on-fresh-thread-poll-and-object-error-leak-2026-03-16.md +166 -0
  104. package/docs/issues/resolved/REPORT-ally-runtime-turn-delivery-3b42fb8-2026-03-15.md +373 -0
  105. package/docs/manual-acceptance.md +127 -0
  106. package/docs/ops-runbook.md +44 -0
  107. package/docs/planning/FEATURE-memory-system.md +748 -0
  108. package/docs/planning/SPEC-active-turn-steer-and-context-compaction-visibility.md +269 -0
  109. package/docs/planning/SPEC-approval-rules-inheritance-and-local-validation-lane.md +450 -0
  110. package/docs/planning/SPEC-assistant-persona-bootstrap.md +199 -0
  111. package/docs/planning/SPEC-assistant-rename.md +610 -0
  112. package/docs/planning/SPEC-bridge-app-server-protocol-alignment.md +667 -0
  113. package/docs/planning/SPEC-claude-runtime-host-for-work-ally.md +434 -0
  114. package/docs/planning/SPEC-cli-feishu-codex-session-unification.md +236 -0
  115. package/docs/planning/SPEC-codex-same-machine-session-handoff.md +873 -0
  116. package/docs/planning/SPEC-feishu-reaction-shortcuts.md +282 -0
  117. package/docs/planning/SPEC-local-stable-release-boundary.md +166 -0
  118. package/docs/planning/SPEC-managed-thread-entry-and-surface-mobility.md +862 -0
  119. package/docs/planning/SPEC-minimal-bridge-semantics-and-user-visible-surface.md +362 -0
  120. package/docs/planning/SPEC-npm-alpha-distribution-and-install-first-release.md +222 -0
  121. package/docs/planning/SPEC-remove-websocket-runtime-transport.md +364 -0
  122. package/docs/planning/SPEC-runtime-abstraction-phase-1.md +424 -0
  123. package/docs/planning/SPEC-runtime-connection-and-turn-recovery-semantics.md +274 -0
  124. package/docs/planning/SPEC-session-presence-and-state-visibility.md +397 -0
  125. package/docs/planning/SPEC-skill-first-capability-packaging.md +338 -0
  126. package/docs/planning/SPEC-stable-archive-contract.md +456 -0
  127. package/docs/planning/SPEC-supervised-start-boundary.md +127 -0
  128. package/docs/planning/SPEC-user-barrier-reduction-and-activation.md +832 -0
  129. package/docs/planning/ally-next.md +1278 -0
  130. package/docs/planning/assistant-workbench-spec.md +725 -0
  131. package/docs/planning/product-workbench.md +283 -0
  132. package/docs/product-onboarding.md +227 -0
  133. package/docs/product-spec-standard.md +528 -0
  134. package/docs/troubleshooting.md +45 -0
  135. package/docs/user-quickstart.md +46 -0
  136. package/internal/dispatch.sh +95 -0
  137. package/internal/lib/common.sh +1450 -0
  138. package/internal/modules/assistant/manage.sh +1312 -0
  139. package/internal/modules/bootstrap/setup.sh +144 -0
  140. package/internal/modules/config/init-env.sh +10 -0
  141. package/internal/modules/global/manage.sh +154 -0
  142. package/internal/modules/handoff/manage.sh +54 -0
  143. package/internal/modules/mcp/manage.sh +83 -0
  144. package/internal/modules/ops/logs.sh +76 -0
  145. package/internal/modules/routines/manage.sh +55 -0
  146. package/internal/modules/runtime/assistant-autosave.sh +26 -0
  147. package/internal/modules/runtime/restart.sh +6 -0
  148. package/internal/modules/runtime/start.sh +283 -0
  149. package/internal/modules/runtime/status.sh +194 -0
  150. package/internal/modules/runtime/stop.sh +55 -0
  151. package/internal/modules/runtime/supervisor.sh +216 -0
  152. package/internal/modules/runtime/update.sh +26 -0
  153. package/package.json +41 -0
  154. package/runtime/config/.gitkeep +0 -0
  155. package/runtime/host/.gitkeep +0 -0
  156. package/runtime/host/healthcheck-codex-app-server.ts +22 -0
  157. package/runtime/host/ping-pong-codex-app-server.ts +66 -0
  158. package/runtime/host/probe-codex-app-server.ts +115 -0
  159. package/skills/archive-reader/SKILL.md +9 -0
  160. package/skills/feishu-production-debug/SKILL.md +37 -0
  161. package/skills/feishu-production-debug/references/feishu-debug-order.md +49 -0
  162. package/skills/feishu-production-debug/references/platform-permission-baseline.md +23 -0
  163. package/skills/issue-to-spec-triage/SKILL.md +44 -0
  164. package/skills/issue-to-spec-triage/references/triage-rules.md +66 -0
  165. package/skills/memory-digest/SKILL.md +9 -0
  166. package/skills/post-implementation-closure/SKILL.md +39 -0
  167. package/skills/post-implementation-closure/references/closure-checklist.md +45 -0
  168. package/skills/post-implementation-closure/references/doc-drift-map.md +49 -0
  169. package/skills/product-spec/SKILL.md +244 -0
  170. package/templates/env.example +5 -0
  171. package/templates/routines/nightly-memory-digest.yaml +10 -0
  172. package/templates/workspace/AGENTS.md +26 -0
@@ -0,0 +1,373 @@
1
+ # 报告:Ally 在 `3b42fb8` release 下的 turn 终态误判、用户语义失真与证据链
2
+
3
+ 更新时间:2026-03-15
4
+ 面向版本:`work-ally` release 对应 commit `3b42fb8`
5
+ 排查对象:`Ally` 助理
6
+ 助理办公桌:`/Users/allenfeng/.work-ally/assistants/Ally`
7
+ 工作仓库:`/Users/allenfeng/Development/Repositories/work-ally`
8
+
9
+ ## 1. 这份报告回答什么
10
+
11
+ 这份报告只回答当前这次真实问题,不讨论泛泛的架构理想。
12
+
13
+ 要回答的是:
14
+
15
+ 1. 这次问题面向哪一个版本。
16
+ 2. 查的是什么问题。
17
+ 3. 出问题的是哪个助理。
18
+ 4. 这次到底是谁出的问题。
19
+ 5. 证据是什么。
20
+ 6. 为什么中间层会把对应 turn 判成 failed。
21
+ 7. 这种“判失败 + 提示连接中断”的设计是不是有问题。
22
+
23
+ ## 2. 先给结论
24
+
25
+ ### 2.1 版本结论
26
+
27
+ 本次排查面向的版本是:
28
+
29
+ - release 对应 commit:`3b42fb8`
30
+ - commit 标题:`Stabilize auto-approved turn final-state handling`
31
+
32
+ ### 2.2 对象结论
33
+
34
+ 本次问题面向的助理是:
35
+
36
+ - 助理名称:`Ally`
37
+ - 办公桌路径:`/Users/allenfeng/.work-ally/assistants/Ally`
38
+
39
+ ### 2.3 事故结论
40
+
41
+ 这次你在飞书里看到的:
42
+
43
+ > `【系统消息】 Codex App Server 连接中断,当前任务状态未确认。若未自动恢复,请重新发送上一条消息。`
44
+
45
+ **在这次具体事件里,不代表 Codex App Server 真的断了,也不代表 Codex 那边的会话真的停掉了。**
46
+
47
+ 更准确的事实是:
48
+
49
+ 1. bridge 在 `3b42fb8` 版本里对 turn 终态采用了一个有界轮询确认模型。
50
+ 2. 这一轮在 20 秒窗口内没有读到 turn 的 final state。
51
+ 3. `runtime-client` 于是直接把 turn 判成 failed,错误文本是 `runtime turn final state unavailable`。
52
+ 4. `receiver` 又把这类错误统一翻译成“Codex App Server 连接中断,当前任务状态未确认”。
53
+ 5. 但 Codex 侧真实会话并没有停,后面继续执行,最终还给出了 commentary、final answer 和 `task_complete`。
54
+ 6. 这些后续真实结果没有再被中间层补捞并转发回飞书。
55
+
56
+ 所以,这次事故的根因不是“上游没干完”,而是:
57
+
58
+ > **中间层把“本轮终态在本地确认窗口内未拿到”错误地升级成了“turn failed”,再进一步错误地翻译成了“连接中断”。**
59
+
60
+ ## 3. 我查了什么问题
61
+
62
+ 本次排查聚焦的用户问题是:
63
+
64
+ - 2026-03-15 UTC+8 17:00 左右,`Ally` 在飞书会话里先给出正常工作中进度,随后提示“Codex App Server 连接中断,当前任务状态未确认”。
65
+ - 用户想确认:
66
+ - 这是否意味着 Codex 那边也停工了。
67
+ - 恢复后系统到底做了什么。
68
+ - 中间层有没有继续读取 Codex 的后续输出。
69
+ - 中间层有没有把后续真实结果忠实转发回来。
70
+
71
+ 同时,本次报告继续回答一个更上位的问题:
72
+
73
+ > 为什么中间层要把 turn 判成 failed?这种设计是否已经偏离了“老老实实转发”的产品职责?
74
+
75
+ ## 4. 哪个助理出的问题
76
+
77
+ 这次问题发生在:
78
+
79
+ - 助理:`Ally`
80
+ - 会话:`feishu--oc_1890ff7f9132a7d90b09765f27ff5bc8`
81
+ - 对应用户消息:`继续`
82
+ - 对应 turn:`019cf0b9-0a0a-7e40-9c29-ad5483945bd1`
83
+
84
+ 相关运行资产路径:
85
+
86
+ - 会话视图:`/Users/allenfeng/.work-ally/assistants/Ally/conversations/2026-03-15--feishu--oc_1890ff7f9132a7d90b09765f27ff5bc8.md`
87
+ - archive 原材料:`/Users/allenfeng/.work-ally/assistants/Ally/.system/archive/2026/03/15/feishu--oc_1890ff7f9132a7d90b09765f27ff5bc8.ndjson`
88
+ - bridge 日志:`/Users/allenfeng/.work-ally/assistants/Ally/.system/logs/bridge-2026-03-15.log`
89
+ - runtime session 台账:`/Users/allenfeng/.work-ally/assistants/Ally/.system/runtime/sessions/feishu--oc_1890ff7f9132a7d90b09765f27ff5bc8/`
90
+ - Codex rollout 原始轨迹:`/Users/allenfeng/.work-ally/assistants/Ally/.system/codex-home/sessions/2026/03/14/rollout-2026-03-14T18-04-33-019cebce-0481-7be3-98ce-38432074d5bd.jsonl`
91
+
92
+ ## 5. 证据链
93
+
94
+ ### 5.1 用户消息和中间层已接收
95
+
96
+ archive 记录表明:
97
+
98
+ - `2026-03-15T08:59:43.771Z` 收到用户消息 `继续`
99
+ - `2026-03-15T08:59:44.304Z` 发出 ack reaction
100
+ - `2026-03-15T08:59:46.235Z` 发出 `已收到,开始处理。`
101
+
102
+ 这说明消息已经进入中间层,不存在“飞书没收到”的问题。
103
+
104
+ ### 5.2 Codex 的 commentary 已经真实产出且已被转发一次
105
+
106
+ archive 记录表明:
107
+
108
+ - `2026-03-15T08:59:59.714Z` 记到 system event:
109
+ - `我现在直接把文档落地,不再只停在判断层。先做两件事:迁移三份已完成 spec 到 docs/completed,再把桌面端 spec 按“全局单例桌面 App”模型重写,同时把产品账本里的相关引用和描述一起对齐。`
110
+ - `2026-03-15T09:00:00.317Z` 这条 commentary 已经作为 `⏳ 工作中` 被真正转发到飞书。
111
+
112
+ 这说明当时并不是完全卡死,Codex 的中间进度已经通过 bridge 回来了。
113
+
114
+ ### 5.3 bridge 在 20 秒轮询窗口后先把 turn 判失败
115
+
116
+ `bridge-2026-03-15.log` 显示:
117
+
118
+ - `2026-03-15T08:59:44.522Z` turn 启动,turn id 是 `019cf0b9-0a0a-7e40-9c29-ad5483945bd1`
119
+ - `2026-03-15T08:59:44Z` 到 `2026-03-15T09:00:25Z` 之间,bridge 反复发送 `thread/read`
120
+ - `2026-03-15T09:00:25.412Z` 触发 `runtime.turn_final_state_poll_timeout`
121
+ - 同时记录:
122
+ - `status: "active"`
123
+ - `activeFlags: []`
124
+ - 随后 `2026-03-15T09:00:25.412Z` 直接执行 `receiver.turn_failed`
125
+
126
+ 这里最关键的一点是:
127
+
128
+ > 在这个时间窗口里,没有对应的 `runtime.connect_closed` 日志。
129
+
130
+ 也就是说,这次 **不是 bridge 先观察到真实 websocket 断开,再告诉用户“连接中断”**;而是 **它先因为终态轮询超时,把 turn 判成 failed**。
131
+
132
+ ### 5.4 用户看到的“连接中断”是中间层翻译后的兜底文案,不是底层真实事件
133
+
134
+ 同一条 archive 记录表明:
135
+
136
+ - `2026-03-15T09:00:25.414Z` system event 里记录的原始错误是:
137
+ - `runtime turn final state unavailable: 019cf0b9-0a0a-7e40-9c29-ad5483945bd1`
138
+ - `2026-03-15T09:00:25.966Z` 发给用户的 error reply 却变成:
139
+ - `【系统消息】 Codex App Server 连接中断,当前任务状态未确认。若未自动恢复,请重新发送上一条消息。`
140
+
141
+ 这说明“连接中断”是 **receiver 侧的统一用户文案归一化结果**,不是这次事故现场观察到的底层事实。
142
+
143
+ ### 5.5 Codex 会话没有停,后面还继续执行并最终完成
144
+
145
+ Codex rollout 原始轨迹显示:
146
+
147
+ - `2026-03-15T09:00:54.885Z` 还有新的 commentary:
148
+ - “迁档已经开始落地了,桌面端那份旧 spec 我也已经整体重写成‘全局单例桌面 App’的版本……”
149
+ - `2026-03-15T09:01:04.011Z` 还有新的 commentary:
150
+ - “我已经先把三份已完成 spec 真实迁走了,也把桌面端主 spec 重写成了‘全局单例桌面 App’版本……”
151
+ - `2026-03-15T09:01:14.562Z` Codex 产出了 final answer
152
+ - `2026-03-15T09:01:14.563Z` 记录了 `task_complete`
153
+
154
+ 所以这次对 Codex 侧的真实描述应该是:
155
+
156
+ > **Codex 会话继续运行,而且完成了。**
157
+
158
+ ### 5.6 中间层没有把这些迟到但真实存在的结果补发回来
159
+
160
+ 飞书 archive 和 conversation view 里没有看到:
161
+
162
+ - `09:00:54` 的 commentary
163
+ - `09:01:04` 的 commentary
164
+ - `09:01:14` 的 final answer
165
+
166
+ 用户只看到了:
167
+
168
+ 1. 已收到,开始处理
169
+ 2. 一条“⏳ 工作中” commentary
170
+ 3. 然后直接看到“连接中断,当前任务状态未确认”
171
+
172
+ 这说明:
173
+
174
+ > **中间层在把 turn 提前判失败之后,没有继续把 Codex 迟到到达的真实终态结果重新对账并补发。**
175
+
176
+ ## 6. 为什么会把对应 turn 判成 failed
177
+
178
+ 面向 `3b42fb8` 版本的源码,可以明确看到这次误判不是偶然,而是产品当前设计的直接结果。
179
+
180
+ ### 6.1 `runtime-client` 的终态确认模型是有界轮询 + 超时即 reject
181
+
182
+ `3b42fb8` 版本的 `bridge/src/runtime-client.ts` 中,`pollTurnFinalState()` 的逻辑是:
183
+
184
+ 1. 对同一个 turn 持续做 `thread/read(includeTurns=true)`。
185
+ 2. 如果读到了该 turn 的终态,就 resolve。
186
+ 3. 如果在 `TURN_FINAL_POLL_TIMEOUT_MS` 窗口内一直读不到终态,且 thread 也没进入等待人工输入的状态,就直接 reject。
187
+
188
+ 这个 reject 分两种文案:
189
+
190
+ - 如果 thread 已 idle:`runtime turn reconciliation failed: idle thread without completed turn event (...)`
191
+ - 否则:`runtime turn final state unavailable: ...`
192
+
193
+ 而默认超时值是:
194
+
195
+ - `TURN_FINAL_POLL_TIMEOUT_MS = 20000`
196
+
197
+ 也就是 20 秒。
198
+
199
+ ### 6.2 这次 turn 不是“真的失败”,而是“20 秒内没等到终态”
200
+
201
+ 这次 turn 在 `09:00:25` 被判失败时:
202
+
203
+ - bridge 自己记录的状态还是 `active`
204
+ - 没有记录真实 `connect_closed`
205
+ - 之后 49 秒左右,Codex 自己给出了 `task_complete`
206
+
207
+ 因此,这次的失败语义并不是“上游执行失败”,而是:
208
+
209
+ > **中间层在自己的确认窗口内没有看到终态,于是把“未确认”升级成了“失败”。**
210
+
211
+ ### 6.3 `receiver` 又把这类失败包装成了“连接中断”
212
+
213
+ `3b42fb8` 版本的 `bridge/src/receiver.ts` 中,`normalizeErrorReplyText()` 明确把下面两类错误统一改写为基础设施失败文案:
214
+
215
+ - `runtime turn final state unavailable`
216
+ - `runtime turn reconciliation failed`
217
+
218
+ 对应的用户文案来自 `bridge/src/translator.ts`:
219
+
220
+ > `Codex App Server 连接中断,当前任务状态未确认。若未自动恢复,请重新发送上一条消息。`
221
+
222
+ 这就造成了语义错位:
223
+
224
+ - 底层真实错误:`终态未在本地确认窗口内拿到`
225
+ - 用户看到的文案:`连接中断`
226
+
227
+ ## 7. 这种设计是不是有问题
228
+
229
+ 结论:**有问题,而且是产品语义层面的结构性问题。**
230
+
231
+ ### 7.1 中间层把“未确认”过早升级成“失败”
232
+
233
+ 作为中间层,最重要的职责不是尽快结束一个本地 Promise,而是:
234
+
235
+ - 不丢用户消息
236
+ - 不丢上游结果
237
+ - 不把不确定性伪装成确定性
238
+
239
+ 但当前设计在 20 秒后直接把 turn 归类为 failed,这本质上是在用本地等待窗口替代事实真相。
240
+
241
+ ### 7.2 更严重的问题不是判 failed,而是错误对用户说“连接中断”
242
+
243
+ 如果用户看到的是:
244
+
245
+ - “系统暂未确认上一轮是否已完成,我会继续补捞结果”
246
+
247
+ 这还是诚实的。
248
+
249
+ 但这次真实情况是:
250
+
251
+ - 没观察到真实断连
252
+ - 上游实际上继续执行并完成了
253
+ - 用户却被告知“连接中断”
254
+
255
+ 这会把用户逼到一个错误判断里:
256
+
257
+ - 以为 Codex 没干了
258
+ - 以为必须立刻重发
259
+ - 以为之前那轮一定已经死了
260
+
261
+ ### 7.3 这已经偏离了“老老实实转发”的产品职责
262
+
263
+ 用户真正需要的是:
264
+
265
+ > 只要 Codex 那边后来真的产出了状态、reply、final answer,中间层就应该尽量把它捞回来并转发,而不是先告诉用户“连接中断”,让用户自己猜。
266
+
267
+ 从这个角度看,当前设计存在三个偏航:
268
+
269
+ 1. 把“本地确认窗口结束”当成“这一轮失败”。
270
+ 2. 把“终态未确认”误说成“连接中断”。
271
+ 3. 在判失败后没有继续对账并补发迟到到达的真实结果。
272
+
273
+ ## 8. 对“长连接是否真的有必要”的判断
274
+
275
+ ### 8.1 对最终交付正确性来说,长连接不应该是前提
276
+
277
+ 如果产品目标是:
278
+
279
+ - 稳定性优先于即时性
280
+ - 中间层只需要忠实转发 Codex 状态与结果
281
+
282
+ 那么 **最终结果交付** 不应该建立在长连接的连续观察上。
283
+
284
+ 更合理的主模型应该是:
285
+
286
+ > **pull-primary,push-assisted**
287
+
288
+ 也就是:
289
+
290
+ - 最终 reply、turn 终态、是否完成,以主动 `thread/read` / turn 对账为准。
291
+ - push 或长连接只负责更好的实时体验,以及审批、等待用户输入这类在线交互承接。
292
+
293
+ ### 8.2 纯 pull 是可以认真评估的
294
+
295
+ 如果你当前最看重的是:
296
+
297
+ - 不要再有“中断”“未确认”这种误导性系统提示
298
+ - 即时性可以退一点
299
+ - 只要保证最终状态和结果稳定可见
300
+
301
+ 那么“定时主动拉取”的方向是合理的,至少在用户可见交付层是合理的。
302
+
303
+ 它的优点是:
304
+
305
+ 1. 把正确性建立在可重试读取上,而不是实时事件流上。
306
+ 2. 不再要求桥始终在线地盯住每一个瞬时事件。
307
+ 3. 可以把“迟到结果”自然收进正常路径,而不是当成异常分支。
308
+
309
+ ### 8.3 但即使保留长连接,也不应该再让它承担正确性职责
310
+
311
+ 这里真正的问题不是“是否一定要 websocket”。
312
+
313
+ 真正的问题是:
314
+
315
+ > **现在的系统把用户语义正确性,压在了实时连接和有界确认窗口上。**
316
+
317
+ 这才是根因。
318
+
319
+ 所以,即便继续保留长连接:
320
+
321
+ - 它也应该只是体验增强层
322
+ - 不应该再是“用户最终能否拿到结果”的真相来源
323
+
324
+ ## 9. 对当前产品的直接结论
325
+
326
+ ### 9.1 为什么要判 turn failed?
327
+
328
+ 在当前实现里,判 failed 是为了:
329
+
330
+ - 让 `runTurn()` 尽快收口
331
+ - 防止一个 turn 永久卡在 in-progress
332
+ - 给用户一个明确状态
333
+
334
+ 但这套设计的问题是:
335
+
336
+ - 它把“状态明确”建立在“过早宣告失败”上
337
+ - 而不是建立在“持续对账直到拿到事实真相”上
338
+
339
+ ### 9.2 这种设计是否应该改
340
+
341
+ 我的判断是:**应该改,而且是根模型要改。**
342
+
343
+ 至少应该改到下面这个层级:
344
+
345
+ 1. **无真实 `connect_closed` 证据时,不得对用户说“连接中断”。**
346
+ 2. **`runtime turn final state unavailable` 不应直接等价为 failed。**
347
+ 3. **turn 在超出本地确认窗口后,应进入 `final_state_pending` / `awaiting_reconcile` 之类的中间态,而不是 failed。**
348
+ 4. **只要 Codex 后来给出了 final answer,中间层就必须继续尝试补捞并补发。**
349
+ 5. **用户可见的主文案应从“你重发吧”改成“我先继续确认;若稍后仍拿不到,再请你决定是否重发”。**
350
+
351
+ ### 9.3 如果要进一步往你说的方向收口
352
+
353
+ 那下一步最合理的产品方向就是:
354
+
355
+ - 用户可见交付链路改成 **主动取为主**
356
+ - 实时 push 退化成 commentary / approval / waiting 状态增强
357
+ - turn 不再因为本地等待窗口结束而直接 failed
358
+ - “重发上一条消息”从默认动作降级为最后兜底动作
359
+
360
+ ## 10. 最终结论
361
+
362
+ 对这次 `Ally` / `3b42fb8` release 的事故,可以压缩成一句话:
363
+
364
+ > 不是 Codex 会话先停了,而是中间层先把“终态暂未确认”误判成 failed,再错误翻译成“连接中断”,导致用户看不到 Codex 随后真实完成的结果。
365
+
366
+ 如果再压缩成产品判断,就是:
367
+
368
+ > 当前设计确实有问题。它没有做到“老老实实转发事实”,而是在本地确认窗口结束后,过早替用户下了结论。
369
+
370
+ 这也是为什么你的 pull-first 直觉是对的:
371
+
372
+ > **对于最终结果交付,稳定事实读取应该高于实时连接观察。**
373
+
@@ -0,0 +1,127 @@
1
+ # 人工验收清单
2
+
3
+ ## 1. 一键初始化
4
+
5
+ - [x] `ally setup <name> --workspace <path>` 成功
6
+ - [x] 自动创建 assistant 办公桌
7
+ - [x] 自动生成 `~/.work-ally/assistants/<name>/.system/config.env`
8
+ - [x] 自动初始化 assistant 办公桌 Git 仓库
9
+ - [x] 自动生成默认 `SOUL.md`
10
+ - [x] 已有 assistant 再执行 `ally assistant ensure` 时,不会静默改写现有 `SOUL.md`
11
+ - [x] 同一项目允许绑定多个 assistant,不会因为新建 assistant 静默覆盖旧绑定
12
+ - [x] 同一项目绑定到 3 个 assistant 后,隐式 `ally status` 会明确报错要求带 `--assistant`,显式 `--assistant <name>` 仍能分别启动/查看/停止
13
+
14
+ ## 2. 最小配置路径
15
+
16
+ - [x] 默认配置只保留最小必要项
17
+ - [x] 用户不需要手工创建 assistant 配置文件
18
+ - [x] 用户可直接 `ally start --assistant <name>`
19
+ - [x] 如果 Feishu 机器人还不能发消息,`ally start` 会直接失败,不假装启动成功
20
+
21
+ ## 3. Assistant 独占性
22
+
23
+ - [x] 同一个 assistant 在项目 A 中启动成功
24
+ - [x] 在任意目录显式执行 `ally start --assistant <name>` 时,会落到该 assistant 绑定的项目上下文,而不是当前 cwd
25
+ - [x] 当同一项目绑定多个 assistant 时,隐式命令不会误选,必须显式带 `--assistant <name>`
26
+
27
+ ## 4. 资产归属
28
+
29
+ - [x] `conversations/` 落在 assistant 办公桌
30
+ - [x] `journal/` 落在 assistant 办公桌
31
+ - [x] `SOUL.md` / `MEMORY.md` / `MISTAKES.md` / `NOW.md` 落在 assistant 办公桌
32
+ - [x] `.system/archive/` / `.system/routines/` 落在 assistant 办公桌
33
+ - [x] 项目目录默认不承载 `.work-ally/` 运行态
34
+
35
+ ## 5. 办公桌 Git
36
+
37
+ - [x] assistant 初始化后已有 Git 仓库
38
+ - [x] 启动时自动 checkpoint
39
+ - [x] 关闭时自动 checkpoint
40
+ - [x] 未配置远端时跳过 push
41
+ - [x] 配置远端后会尝试 push
42
+
43
+ ## 6. Feishu 回复体验
44
+
45
+ - [x] 普通最终回复默认不是纯文本降级,而是 Markdown-rich 渲染
46
+ - [x] 表格类回复继续走可读的 card 路径
47
+ - [x] 在 Feishu 里回复 assistant 某条历史消息时,reply target 会被识别
48
+ - [x] 若 reply target 可读取,其正文会进入本轮 prompt 上下文
49
+ - [x] 若 reply target 拉取失败,本轮消息仍能正常继续
50
+ - [x] Feishu 飞书平台最小权限配置正确:开启“读取用户发给机器人的单聊消息”、开启“接收群聊中@机器人消息事件”、不启用“获取群组中所有消息(敏感权限)”
51
+ - [x] Feishu 群聊里,未显式 `@assistant` 的普通消息默认忽略
52
+ - [x] Feishu 群聊里,显式 `@assistant` 时会正常触发;reply assistant 或群里裸控制命令不作为支持路径
53
+ - [x] 同一 assistant 的私聊会话与群聊会话会进入两条独立 thread,并能各自正确回投到原 Feishu 会话
54
+
55
+ ## 7. 状态可见性
56
+
57
+ - [x] 用户发消息后,正常路径默认只保留 reaction ack,不再额外补 `已收到,开始处理。` 这类通用系统状态噪音
58
+ - [x] 长耗时任务中,用户能看到 `正在处理...` 或后续工作中心跳
59
+ - [x] 若 runtime 已明确进入 `waitingOnApproval` / `waitingOnUserInput`,即使等待时间超过 pull-primary 终态超时窗口,也不会误报 `runtime turn final state unavailable`
60
+ - [x] 审批出现时,除审批卡外还能看到 `当前已暂停,等待你审批后继续。`
61
+ - [x] 等待审批时,下一条消息若严格等于 `OK` / `同意` / `NO` / `拒绝`,会直接作为审批处理;其他文本不会透传给 Codex
62
+ - [x] 同一轮出现多项待审批时,前台默认合并成一张审批卡,用户一次回复即可整批通过或整批拒绝
63
+ - [x] 补充信息出现时,除提问文案外还能看到 `当前已暂停,等待你回复后继续。`
64
+ - [x] 若 runtime 只暴露 `waitingOnApproval` / `waitingOnUserInput` 标记、但前台还没有真正可操作的审批卡或提问内容,bridge 默认保持静默,不再额外解释“artifact 还没同步到前台”
65
+ - [x] waiting_user 建立后,不再继续发送通用 thinking heartbeat
66
+ - [x] 正常长任务默认不再补 `正在处理...` / `正在思考中,请稍候…` 这类通用状态噪音
67
+ - [x] assistant 存在 active turn 时,普通自然语言 follow-up 默认优先沿协议主路径进入当前 turn,而不是被 bridge 自创 busy gate 先拦住
68
+ - [x] 只有审批 / 补充信息这类已经对用户可见的阻塞请求,后续自然语言才会在桥层被拦下并明确提示先处理当前阻塞
69
+ - [x] assistant 忙时发送 `/new` 不会隐式抢占当前轮,而是提示先 `/stop`
70
+ - [x] 如果本轮实际上已结束、但没有可见正文可发,会明确补一条 `本轮已结束,当前没有更多动作;如需继续,请直接发下一条消息。`
71
+ - [x] 如果同一条旧消息因渠道重投再次到达,但会话已经被更新消息推进,不会突然把旧任务重新做一遍
72
+ - [x] 如果旧 turn 已被新消息抢占,旧 turn 后续迟到的进度或结果不会再突然回到用户面前
73
+ - [x] 即使用户消息刚到 bridge 时 ack 发送失败,inbound ledger 里仍能看到该消息已被 bridge 接住
74
+ - [x] 一旦 `turn/start` 成功,inbound ledger 会落成 `threadId / turnId / acceptedAt`,不再只能靠日志猜“有没有真正进 Codex”
75
+ - [x] 即使在线 `turn/completed` 事件丢失,只要 `thread/read(includeTurns=true)` 已经有终态,最终回复仍能正常回来
76
+ - [x] bridge↔runtime 连续 20 轮 ping-pong 往返后,不残留 stale active turn,最终回复顺序稳定
77
+ - [x] runtime recovery notice 发出后,对应 turn ledger 仍保持真实终态(例如 `failed`),不再额外伪造 `recovery_required` 执行态
78
+ - [x] recovery 成功补发最终结果后,对应 turn ledger 会落成 `deliveryStatus=delivered` 或 `delivery_unavailable`
79
+ - [x] 旧 turn 被新消息推进后,对应 turn ledger 会落成 `deliveryStatus=suppressed`,并记录 `suppressedByMessageId` / `suppressionReason` 这类 suppression provenance
80
+ - [x] `ally status` 显示 assistant 名
81
+ - [x] `ally status` 显示 assistant codex home
82
+ - [x] `ally status` 显示 assistant autosave
83
+ - [x] `ally status` 显示 assistant lock status
84
+ - [x] `ally status` 显示 channel lock status
85
+ - [x] 远程 `/status` 显示 assistant mode 信息
86
+
87
+ ## 8. 无人值守恢复
88
+
89
+ - [x] `ally start` 后 `supervisor` 存活
90
+ - [x] bridge 异常退出后可自动恢复
91
+ - [x] `ally status` 能看到 `supervisor`
92
+ - [x] `ally logs supervisor` 可查看托管日志
93
+ - [x] 在受管 AI 执行环境中直接 `ally start` 会被明确拒绝,不再伪装成后台已成功托管
94
+ - [x] `WORK_ALLY_FOREGROUND=1 ally start` 仍可用于前台联调
95
+
96
+ ## 9. Nightly Digest 可见性
97
+
98
+ - [x] `nightly-memory-digest` 默认按 assistant 本地时区 23:00 调度
99
+ - [x] digest 完成后,如存在可用 Feishu 投递目标,会主动推送本次提炼摘要
100
+ - [x] 当次无新增稳定记忆时,系统消息会明确说明,而不是静默结束
101
+ - [x] 最近一次运行结果仍可在 `.system/runtime/routines-state.json` 与 `.system/runs/latest.json` 追溯
102
+
103
+ ## 10. 审批自治边界
104
+
105
+ - [x] 当前项目目录内的低风险本地验证动作(例如跑测试、`git add`、`git commit`)默认自动放行
106
+ - [x] assistant 办公桌内的低风险文件改动默认自动放行,不再逐条打断用户
107
+ - [x] shell 包裹的本地自测 / 提交命令,只要仍落在项目根内且不含高风险片段,默认自动放行
108
+ - [x] 自动放行后的低风险本地动作默认对用户静默,不再额外刷“已自动放行”系统说明;审计仍保留在内部事件与状态里
109
+ - [x] 需要人工拍板时,审批卡会明确说明“为什么这一步超出默认自决边界”
110
+ - [x] 非 allowlisted 的 MCP 工具确认仍保持人工确认,不被本地默认放行误吞
111
+
112
+ ## 11. 中间层异常透明化
113
+
114
+ - [x] 若上一轮已经产出结果,但当时未能稳定送达,用户下一次发消息时 bridge 会先自动补发上一轮结果;只有补发仍失败时才退回系统说明
115
+ - [x] runtime transport 生命周期默认不再直接透传给用户;用户侧只保留必要的事实型说明,不暴露实现层的断开/恢复术语
116
+ - [x] runtime final-state timeout 若随后可补读到终态结果,会优先自动补回结果,而不是直接要求用户重发
117
+ - [x] runtime recovery attempt、stale cleanup、artifact pending 这类内部过程默认不再直接暴露给用户
118
+ - [x] runtime recovery required / infrastructure failure 仍会明确提示事实型说明,例如“系统暂时还没确认这一轮是否已完成 / 请重发上一条消息”
119
+ - [x] 出站链路异常不会把 turn 误判成成功送达,同时 turn ledger 可追溯 delivery unavailable
120
+
121
+ ## 12. 稳定发布边界
122
+
123
+ - [x] 默认 `ally` 入口走 npm 安装形态(install-first)
124
+ - [x] 在源码仓库里持续修改实现时,不会自动影响默认稳定入口
125
+ - [x] 执行 npm 安装/升级后,`ally` 入口行为与发布版本一致
126
+ - [x] `ally status` 能看见 install channel 与 implementation/install root 信息
127
+
@@ -0,0 +1,44 @@
1
+ # 运维手册
2
+
3
+ ## 日常命令
4
+
5
+ ```bash
6
+ ally status --assistant <name>
7
+ ally logs timeline --assistant <name>
8
+ ally logs supervisor --assistant <name>
9
+ ally restart --assistant <name>
10
+ ally stop --assistant <name>
11
+ ally assistant rename <old> <new>
12
+ ally global list
13
+ ```
14
+
15
+ ## 安装发布口径
16
+
17
+ 当前默认 `ally` 入口走 npm 安装形态(install-first)。
18
+
19
+ 运维判断分两层:
20
+
21
+ - 源码是否已修好
22
+ - 目标机器是否已执行 npm 安装/升级
23
+
24
+ 如果用户反馈“代码改了但行为没变”,先确认:
25
+
26
+ ```bash
27
+ npm i -g work-ally@alpha
28
+ ```
29
+
30
+ 然后让用户回传:
31
+
32
+ ```bash
33
+ ally status --assistant <name>
34
+ ```
35
+
36
+ 重点看 `install channel`、`implementation`、`install root`。
37
+
38
+ ## 运行边界
39
+
40
+ 后台由 `supervisor` 托管 `bridge` 与 `assistant-autosave`。
41
+
42
+ - Codex runtime 为 assistant 私有 stdio child process
43
+ - 不需要用户关心本地端口
44
+ - runtime child 异常后由 bridge 在后续请求中拉起