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,327 @@
1
+ # Feishu Markdown Rendering And Reply Context
2
+
3
+ ## Status
4
+
5
+ - Target project: `work-ally`
6
+ - Audience: implementation engineer
7
+ - Scope: Feishu channel only
8
+ - Status: implemented in current branch
9
+ - Goal: define two concrete features for the next implementation pass
10
+
11
+ ## Summary
12
+
13
+ This document defines two Feishu-facing features for `work-ally`:
14
+
15
+ 1. Markdown-first outbound rendering for AI replies
16
+ 2. Reply-context extraction for inbound Feishu replies
17
+
18
+ Both features are motivated by a gap between the current `work-ally` implementation and the newer Feishu integration patterns already proven in OpenClaw.
19
+
20
+ The key conclusion is:
21
+
22
+ - Feishu output should no longer default to plain text for normal model replies.
23
+ - When a user replies to a previous Feishu message, `work-ally` should fetch the replied message and pass its plain-text body into the Codex prompt context.
24
+
25
+ ## Background
26
+
27
+ Historically we treated Feishu bot messages as if they could not reliably render Markdown, so `work-ally` built a downgrade layer that converts model output into plain text and uses `interactive` cards only for a few special cases.
28
+
29
+ After inspecting OpenClaw, that assumption is no longer sufficient.
30
+
31
+ OpenClaw demonstrates that Feishu-side Markdown rendering is practical by using Feishu-supported rich message types instead of raw text messages:
32
+
33
+ - `post` message payloads with `tag: "md"`
34
+ - `interactive` card payloads with `tag: "markdown"`
35
+
36
+ OpenClaw also demonstrates that reply context is recoverable by fetching the replied message via the Feishu message API and extracting plain text from the original message body.
37
+
38
+ ## Current `work-ally` State
39
+
40
+ ### Outbound rendering
41
+
42
+ Current rendering is centered in `bridge/src/channels/feishu/formatter.ts`.
43
+
44
+ Today the implementation behaves like this:
45
+
46
+ - plain replies are normalized and sent as `text`
47
+ - headings, fenced code, and inline code are downgraded to readable plain text
48
+ - tables and system notices are routed to `interactive` cards
49
+
50
+ Key references:
51
+
52
+ - `work-ally/bridge/src/channels/feishu/formatter.ts`
53
+ - `work-ally/bridge/src/channels/feishu/adapter.ts`
54
+ - `work-ally/tests/unit/channel/formatter.test.mjs`
55
+ - `work-ally/tests/unit/channel/feishu-adapter-outbound.test.mjs`
56
+
57
+ This means the current system already has a unified rendering entrypoint, but the default output target is still plain text rather than Feishu Markdown-capable rich messages.
58
+
59
+ ### Inbound reply handling
60
+
61
+ Current inbound normalization only extracts the current message body and does not elevate Feishu reply metadata into the normalized message contract.
62
+
63
+ Key references:
64
+
65
+ - `work-ally/bridge/src/channels/feishu/normalize.ts`
66
+ - `work-ally/bridge/src/types.ts`
67
+ - `work-ally/bridge/src/receiver.ts`
68
+
69
+ Today `work-ally` does not:
70
+
71
+ - preserve `parent_id`
72
+ - preserve `root_id`
73
+ - fetch the replied message body
74
+ - append reply-body context into the prompt sent to Codex
75
+
76
+ ## OpenClaw Reference Findings
77
+
78
+ ### A. Markdown rendering
79
+
80
+ OpenClaw uses two outbound forms.
81
+
82
+ #### 1. `post` rich-text message with Markdown node
83
+
84
+ Reference:
85
+
86
+ - `.cache/openclaw/extensions/feishu/src/send.ts`
87
+
88
+ Relevant logic:
89
+
90
+ - `buildFeishuPostMessagePayload(...)` builds `msg_type: "post"`
91
+ - payload contains `zh_cn.content` with `{ tag: "md", text: messageText }`
92
+
93
+ This is the important correction to the old assumption: OpenClaw is not relying on plain `text` messages for Markdown rendering.
94
+
95
+ #### 2. `interactive` card with Markdown element
96
+
97
+ Reference:
98
+
99
+ - `.cache/openclaw/extensions/feishu/src/send.ts`
100
+ - `.cache/openclaw/extensions/feishu/src/outbound.ts`
101
+ - `.cache/openclaw/extensions/feishu/src/reply-dispatcher.ts`
102
+ - `.cache/openclaw/extensions/feishu/src/streaming-card.ts`
103
+
104
+ Relevant logic:
105
+
106
+ - `buildMarkdownCard(...)` builds card schema with `tag: "markdown"`
107
+ - outbound routing chooses card rendering for explicit card mode or markdown-heavy content such as tables/code blocks
108
+ - streaming replies also use card-based markdown updates
109
+
110
+ For `work-ally`, streaming is not required for this feature set. The main takeaway is that `interactive` cards are a valid, proven Feishu Markdown carrier.
111
+
112
+ ### B. Reply-context extraction
113
+
114
+ Reference:
115
+
116
+ - `.cache/openclaw/extensions/feishu/src/send.ts`
117
+ - `.cache/openclaw/extensions/feishu/src/post.ts`
118
+ - `.cache/openclaw/extensions/feishu/src/bot.ts`
119
+
120
+ Relevant logic:
121
+
122
+ - `getMessageFeishu(...)` fetches message details through `client.im.message.get`
123
+ - `parseQuotedMessageContent(...)` extracts plain text from `text`, `post`, and `interactive`
124
+ - inbound bot logic reads `parent_id`, fetches the quoted message, and injects the extracted body into prompt context as `ReplyToBody`
125
+
126
+ This is the exact pattern we want to reuse conceptually in `work-ally`.
127
+
128
+ ## Feature 1: Markdown-First Feishu Rendering
129
+
130
+ ## Problem
131
+
132
+ Codex model replies are already Markdown-shaped. The current `work-ally` implementation strips or downgrades much of that structure before delivery, which reduces readability and wastes Feishu’s richer message capabilities.
133
+
134
+ ## Goal
135
+
136
+ Treat model output as Markdown by default and render it in Feishu through rich message types instead of downgrading normal replies to plain text.
137
+
138
+ ## Desired UX
139
+
140
+ - headings should remain visually structured
141
+ - lists should stay lists
142
+ - links should remain links
143
+ - code blocks should render as code blocks when practical
144
+ - tables should render as tables or as the best supported rich equivalent
145
+
146
+ ## Recommended design
147
+
148
+ Keep one outbound rendering entrypoint, but make it Markdown-first.
149
+
150
+ Suggested model:
151
+
152
+ 1. Keep a single renderer entry such as `renderFeishuMessages(markdown)`.
153
+ 2. Stop treating plain `text` as the default reply container for normal model output.
154
+ 3. Internally choose the Feishu carrier based on content shape:
155
+ - normal Markdown: use `post` with `tag: "md"`
156
+ - tables, approval/system cards, or content requiring structured blocks: use `interactive`
157
+ 4. Keep the adapter thin. It should send already-rendered payloads, not interpret Markdown itself.
158
+
159
+ ## Important implementation note
160
+
161
+ Do not collapse everything into one single Feishu carrier type.
162
+
163
+ We want one rendering pipeline, not one message type.
164
+
165
+ The correct simplification is:
166
+
167
+ - one Markdown-first rendering contract
168
+ - two internal outbound carriers when needed
169
+
170
+ This preserves simplicity without forcing all traffic through `interactive` cards.
171
+
172
+ ## Suggested contract changes
173
+
174
+ `FeishuRenderedMessage` should likely evolve from:
175
+
176
+ - `text`
177
+ - `interactive`
178
+
179
+ to something closer to:
180
+
181
+ - `post`
182
+ - `interactive`
183
+ - optional retained `text` only for true low-format cases if still needed
184
+
185
+ ## Acceptance criteria
186
+
187
+ - a normal multi-paragraph Markdown reply is no longer downgraded to plain `text`
188
+ - headings and links survive as Feishu-rendered rich content
189
+ - fenced code blocks render through a Feishu rich-message path instead of plain-text degradation when supported by the chosen carrier
190
+ - markdown tables no longer require text fallback as the primary path
191
+ - approval/system messages continue to work
192
+ - long replies still chunk safely
193
+
194
+ ## Feature 2: Reply-Context Support For Feishu Replies
195
+
196
+ ## Problem
197
+
198
+ When a user replies in Feishu to a previous AI message, `work-ally` currently does not carry the replied message body into the prompt context. The model therefore loses an important local reference and may not understand which prior point the user is asking about.
199
+
200
+ ## Goal
201
+
202
+ When an inbound Feishu message is a reply to an earlier message, fetch the replied message, extract its plain-text content, and include that content in the prompt sent to Codex.
203
+
204
+ ## Non-goal
205
+
206
+ Do not reconstruct the original rich structure.
207
+
208
+ We only need:
209
+
210
+ - replied message id
211
+ - optional topic/root id
212
+ - extracted plain-text body
213
+
214
+ This is enough to support the user experience of “I am asking about that earlier reply.”
215
+
216
+ ## Recommended design
217
+
218
+ ### Step 1. Extend normalized inbound contract
219
+
220
+ Add Feishu-relevant reply fields to `InboundMessage`, for example:
221
+
222
+ - `replyToMessageId?: string | null`
223
+ - `rootMessageId?: string | null`
224
+ - `replyToText?: string | null`
225
+
226
+ Exact field names can vary, but reply metadata must become part of the normalized bridge contract rather than remaining buried in raw channel payloads.
227
+
228
+ ### Step 2. Preserve reply metadata during normalization
229
+
230
+ In `bridge/src/channels/feishu/normalize.ts`, read reply metadata from the Feishu event, especially:
231
+
232
+ - `parent_id`
233
+ - `root_id`
234
+
235
+ These should be lifted into the normalized message object.
236
+
237
+ ### Step 3. Add Feishu-side message fetch helper
238
+
239
+ Add channel-local helper logic in the Feishu adapter layer to fetch a message by message id using the Feishu SDK.
240
+
241
+ This helper should extract plain text from at least:
242
+
243
+ - `text`
244
+ - `post`
245
+ - `interactive`
246
+
247
+ The extraction goal is readability, not exact reconstruction.
248
+
249
+ ### Step 4. Enrich prompt context before runtime dispatch
250
+
251
+ Before sending the final prompt to Codex, append reply context in a stable, explicit way, for example:
252
+
253
+ ```text
254
+ User is replying to a previous Feishu message.
255
+
256
+ Replied message body:
257
+ <plain text extracted from parent message>
258
+
259
+ Current user message:
260
+ <current inbound text>
261
+ ```
262
+
263
+ The exact envelope can vary, but the prompt should make the relationship explicit.
264
+
265
+ ## Acceptance criteria
266
+
267
+ - if a user replies to an earlier Feishu message, the normalized inbound message includes the reply target id
268
+ - `work-ally` fetches the replied message body before runtime dispatch when possible
269
+ - if the replied message is `text`, `post`, or `interactive`, the fetched body is converted to readable plain text
270
+ - the prompt sent to Codex includes the replied message body in a stable format
271
+ - if fetch fails, the current user message still proceeds normally without breaking the turn
272
+
273
+ ## Recommended Implementation Order
274
+
275
+ 1. Land reply metadata in normalized inbound contracts.
276
+ 2. Land message-fetch helper plus text extraction.
277
+ 3. Plumb reply-body context into runtime prompt construction.
278
+ 4. Replace Markdown downgrade-first rendering with Markdown-first rich rendering.
279
+ 5. Update adapter and unit tests.
280
+
281
+ Reply-context support is lower risk and directly improves conversation quality, so it is a good first implementation step.
282
+
283
+ ## Testing Guidance
284
+
285
+ ### Unit tests to add or revise
286
+
287
+ - `work-ally/tests/unit/channel/formatter.test.mjs`
288
+ - assert normal Markdown now renders to rich outbound payloads rather than downgraded plain text
289
+ - assert table/code/link cases route to the expected Feishu carrier
290
+
291
+ - `work-ally/tests/unit/channel/feishu-adapter-outbound.test.mjs`
292
+ - assert adapter sends `post` or `interactive` for normal final replies under the new renderer
293
+ - keep approval/system-card coverage intact
294
+
295
+ - new Feishu reply parsing tests
296
+ - normalize `parent_id` and `root_id`
297
+ - verify replied-message fetch is triggered
298
+ - verify fetched `text/post/interactive` content becomes readable plain text
299
+
300
+ - receiver or prompt-construction tests
301
+ - verify reply-body context is appended to the final runtime input
302
+
303
+ ### Manual verification checklist
304
+
305
+ - ask for a normal Markdown answer with headings and links
306
+ - ask for a code-heavy answer with fenced code blocks
307
+ - ask for a table-heavy answer
308
+ - in Feishu, reply to an earlier AI answer with “你说的第二点再展开一下”
309
+ - verify the model correctly interprets which prior message is being referenced
310
+
311
+ ## Risks And Constraints
312
+
313
+ - Feishu rich message capabilities differ by carrier type, so the renderer must choose the carrier intentionally.
314
+ - Reply-body fetch must be best-effort. Failure to fetch must not fail the turn.
315
+ - Quoted-message support should stay content-first. Do not add structure-preservation scope unless later demanded by product needs.
316
+ - Chunking logic must be revalidated after the outbound rendering change.
317
+
318
+ ## Final Recommendation
319
+
320
+ Implement both features, with these guiding rules:
321
+
322
+ - use one Markdown-first rendering pipeline for all model output
323
+ - allow that pipeline to choose between Feishu rich carriers internally
324
+ - support Feishu reply context by fetching the parent message and extracting plain text only
325
+ - do not add streaming or edit support as part of this scope
326
+
327
+ That gives `work-ally` a cleaner delivery path and a better multi-turn remote conversation experience without turning the Feishu bridge into a second agent system.
@@ -0,0 +1,21 @@
1
+ # Completed Topics
2
+
3
+ 这里收已经完成、已落地、已不应再按“待实现 spec”理解的专题文档。
4
+
5
+ 放进这个目录,表示至少满足下面两条:
6
+
7
+ 1. 用户路径或 shipped model 已经成立
8
+ 2. 当前继续讨论时,应默认把它当成已交付基线,而不是待做方案
9
+
10
+ 这个目录不是历史归档仓库。
11
+
12
+ 它的作用是避免两种错误:
13
+
14
+ - 已做完的功能还继续被当成 planning
15
+ - 新 feature 讨论时重复推翻已落地能力
16
+
17
+ 当前这里主要放:
18
+
19
+ - 已完成专题 spec
20
+ - 已落地功能的专题回写
21
+ - 产品负责人确认已收口的专题说明
@@ -0,0 +1,205 @@
1
+ # Approval Autonomy And Safe Defaults
2
+
3
+ ## Status
4
+
5
+ - Target project: `work-ally`
6
+ - Audience: product owner / implementation engineer
7
+ - Scope: runtime approval boundary, self-validation flow, human-in-the-loop contract
8
+ - Status: shipped baseline / 2026-03-15
9
+ - Goal: stop routing low-risk local self-validation through repeated user approvals while keeping real-risk actions under explicit human control
10
+
11
+ ## Summary
12
+
13
+ 当前审批机制的根问题不是“卡片文案不够好”,而是**审批边界定错了**。
14
+
15
+ 对 `work-ally` 这种由 assistant 兼任产品和开发的长期协作场景,很多动作本质上属于:
16
+
17
+ - 本地阅读
18
+ - 本地测试
19
+ - 当前仓库内改动
20
+ - 当前仓库内 commit
21
+ - assistant desk 根目录内的记忆/文档维护
22
+
23
+ 这些动作如果每一步都要求飞书用户手动同意,会把用户拖进 assistant 的自测过程里,造成高频、低价值、打断式参与。
24
+
25
+ 这条 spec 的核心判断是:
26
+
27
+ > 默认不该让用户审批 assistant 自己的低风险本地验证流程;用户只应被拉入真正需要人类拍板的边界动作。
28
+
29
+ ## Background
30
+
31
+ 近期真实对话中已经反复暴露出一个协作问题:
32
+
33
+ - assistant 在修复 bug、跑聚焦测试、提交代码时,频繁向飞书用户发起审批
34
+ - 用户被迫反复点“同意”,哪怕动作只是当前仓库内的自测和 commit
35
+ - 这让产品负责人兼开发者的工作流变成“每一步都要等用户当临时操作员”
36
+
37
+ 用户已经明确指出:
38
+
39
+ 1. 这类审批体验非常蠢
40
+ 2. assistant 需要先反思哪些动作根本不该找用户审批
41
+ 3. 如果确实存在不能自主决策的边界,也应把协作合同说清楚,而不是默认把所有动作都抛给用户
42
+
43
+ ## Problem Statement
44
+
45
+ 当前问题有三层。
46
+
47
+ ### 1. 低风险动作被错误归类为人工审批项
48
+
49
+ 例如:
50
+
51
+ - 查看日志
52
+ - 运行本地测试
53
+ - `git add`
54
+ - 当前仓库内 `git commit`
55
+ - assistant desk 根目录内的稳定文档更新
56
+
57
+ 这些动作虽然技术上属于 runtime approval,但从产品语义上看,并不都应该要求飞书用户逐条批准。
58
+
59
+ ### 2. 审批边界站在 runtime 视角,而不是站在协作关系视角
60
+
61
+ runtime 看到的是:
62
+
63
+ - 命令执行
64
+ - 文件修改
65
+
66
+ 但用户真正关心的是:
67
+
68
+ - 你是不是在做正常自测
69
+ - 你是不是在改当前项目
70
+ - 你是不是要碰项目外、外部系统、破坏性动作或发布动作
71
+
72
+ 如果产品不把这层翻译清楚,审批就会持续过度触发。
73
+
74
+ ### 3. 用户被卷入 assistant 的内部验证过程
75
+
76
+ 用户应该只在需要人类拍板时参与,而不是在 assistant 自己的修复闭环里扮演按钮操作员。
77
+
78
+ ## Product Decision
79
+
80
+ 建立一套更贴近协作现实的审批边界:
81
+
82
+ ### A. assistant 可自主决策的动作
83
+
84
+ 默认应允许 assistant 自主完成:
85
+
86
+ - 当前项目仓库内的读操作
87
+ - 当前项目仓库内的测试、lint、局部验证
88
+ - 当前项目仓库内的非破坏性代码改动
89
+ - 当前项目仓库内的 `git add` / `git commit`
90
+ - assistant desk 根目录内的 `SOUL.md` / `NOW.md` / `MEMORY.md` / `MISTAKES.md` / `journal/` / `conversations/` 等维护动作
91
+ - 为排查当前问题所需的本地日志读取与本地状态检查
92
+
93
+ ### B. 仍需用户参与拍板的动作
94
+
95
+ 默认仍应要求显式人工边界的动作:
96
+
97
+ - `git push`
98
+ - 发布、部署、重启线上入口或影响真实对话可用性的动作
99
+ - 项目外路径写入
100
+ - 可能破坏数据的动作
101
+ - 外网写操作或外部系统副作用
102
+ - 大范围删除 / 重置 / 覆盖
103
+ - 需要用户亲自参与的真人验收、重启、环境变更
104
+
105
+ ### C. 产品目标
106
+
107
+ 用户不再审批 assistant 的“自测过程”,只审批 assistant 的“风险边界”。
108
+
109
+ ## Goals
110
+
111
+ 1. 减少飞书里低价值审批打断
112
+ 2. 让 assistant 可以独立完成修复、自测、提交的基本闭环
113
+ 3. 保留真正高风险动作的人类拍板边界
114
+ 4. 把“哪些可自决、哪些必须拉人”变成稳定产品合同,而不是临场猜测
115
+
116
+ ## Non-goals
117
+
118
+ 本次不做:
119
+
120
+ - 完整权限系统重构
121
+ - 一次性解决所有 runtime 原生 approval 类型
122
+ - 自动放开外部副作用写操作
123
+ - 取消所有人工审批
124
+
125
+ ## Scope
126
+
127
+ V1 先收口三件事:
128
+
129
+ 1. 明确低风险本地动作的产品白名单
130
+ 2. 为这类动作提供默认放行或更低摩擦的授权路径
131
+ 3. 明确需要用户配合的高风险边界
132
+
133
+ ## Proposed Model
134
+
135
+ ### 1. Local Self-Validation Lane
136
+
137
+ 引入稳定产品判断:
138
+
139
+ - 当前项目目录
140
+ - 当前 assistant desk
141
+
142
+ 这两个根内的低风险动作,属于 assistant 的自驱工作范围。
143
+
144
+ 应优先支持:
145
+
146
+ - 无需逐条飞书审批
147
+ - 默认静默自动放行,但在内部状态与 archive 中保持可审计记录
148
+
149
+ ### 2. Human-Gated Lane
150
+
151
+ 以下动作进入人工拍板:
152
+
153
+ - 发布 / 推送 / 对外副作用
154
+ - 跨项目或跨目录写入
155
+ - 破坏性命令
156
+ - 高成本或不可逆操作
157
+ - 需要用户现实参与的动作(如重启、真人验证)
158
+
159
+ ### 3. Cooperation Contract
160
+
161
+ 如果某动作 assistant 不能自主完成,应明确告诉用户:
162
+
163
+ - 为什么这步必须你来
164
+ - 你需要做什么
165
+ - 做完后 assistant 会继续什么
166
+
167
+ 而不是简单丢一张 runtime 审批卡让用户自己猜。
168
+
169
+ ## Implementation Direction
170
+
171
+ 优先顺序:
172
+
173
+ 1. 先把产品白名单和高风险边界写清楚
174
+ 2. 再决定用哪种工程策略实现
175
+
176
+ 可选实现方向包括:
177
+
178
+ - 调整 runtime approval policy / trusted boundary
179
+ - 为当前项目与 assistant desk 生成更合理的默认授权配置
180
+ - 针对 `git add` / `git commit` / 本地测试等命令做有限范围的默认放行
181
+ - 在 bridge 层把审批分级,低风险动作走 assistant 自治 lane,高风险动作才出卡
182
+
183
+ V1 不要求一次定死实现方案,但必须先把产品边界拍板。
184
+
185
+ ## Acceptance Criteria
186
+
187
+ 1. assistant 在当前项目根与 assistant desk 根内做本地修复、自测、改动、提交时,不再频繁要求用户逐条审批
188
+ 2. 这类低风险动作默认静默自动放行,不再额外刷“已自动放行”的系统消息
189
+ 3. 用户只在高风险、外部副作用或明确需要人类拍板的动作上被拉入
190
+ 4. 文档中明确列出:哪些动作 assistant 可直接做,哪些动作必须找用户配合
191
+ 5. 用户参与动作时,提示语义从“技术审批”转向“协作边界说明”
192
+
193
+ ## Open Questions
194
+
195
+ 1. `git commit` 是否默认应进入 assistant 自治白名单
196
+ 2. 当前 repo + assistant desk 的 trusted / writable / approval 策略,是否应在 setup 时固化生成
197
+ 3. 是否需要为“产品负责人兼开发”提供单独的默认审批 profile
198
+
199
+ ## Product Judgment
200
+
201
+ 这不是“优化审批卡文案”的小修。
202
+
203
+ 这是 `work-ally` 的一个核心产品判断:
204
+
205
+ > assistant 既然承担产品和开发职责,就必须拥有完成低风险本地闭环的默认操作权;否则它就不是负责人,只是一个随时等人点同意的半自动工具。