feat: support MCP elicitation and rebuild release
This commit is contained in:
47
.planning/2026-08-23-gitea-workflow/findings.md
Normal file
47
.planning/2026-08-23-gitea-workflow/findings.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Gitea Workflow 研究与决策
|
||||
|
||||
## 需求
|
||||
|
||||
- Gitea 实例级 Webhook 指向 cc-web;用户在 Issue/PR 普通评论中 `@ccweb-bot`。
|
||||
- 未登记仓库首次合法触发自动接入,clone 到可配置工作区根目录。
|
||||
- 同一仓库串行、不同仓库并行;任务队列、会话映射和回执状态必须持久化。
|
||||
- 所有后台会话固定使用 Codex App 与 yolo;Gitea 是唯一主交互入口。
|
||||
- Agent 通过官方 `gitea-mcp` 读取上下文、研究、修改和回帖;cc-web 负责状态与最终兜底。
|
||||
|
||||
## 本地代码发现
|
||||
|
||||
- `server.js` 已有持久会话创建、`handleMessage`、Codex App 线程、MCP 配置和 turn 完成路径。
|
||||
- `codexAppThreadConfig()` 已将运行时 MCP 组装成 `thread/start.config`;可扩展为工作流专属 `gitea` 配置。
|
||||
- `handleCodexAppTurnComplete()` 是最合适的 turn 完成监听点。
|
||||
- `handleInternalMcpApi()` 是 cc-web 内部 MCP API,不应暴露为 Gitea 公网 Webhook。
|
||||
- 项目无 Node SQLite 依赖;现有会话和配置以 JSON 文件持久化,工作流状态可采用原子 JSON 文件或新增独立存储模块。
|
||||
|
||||
## 官方 gitea-mcp
|
||||
|
||||
- 官方仓库:`https://gitea.com/gitea/gitea-mcp`。
|
||||
- 支持本机 stdio 与 HTTP;本任务选择 stdio。
|
||||
- 支持 `GITEA_HOST`、`GITEA_ACCESS_TOKEN`,可通过 `-t stdio -H <host>` 启动。
|
||||
- 工具覆盖 Issue、PR、仓库、文件、分支、提交、Release、Actions 等。
|
||||
- 支持 scope/tool 过滤,但用户要求 MVP 开放全部写权限;必须配套暂停、中止和审计。
|
||||
|
||||
## 外部方案
|
||||
|
||||
- Matea:最接近完整 Gitea Webhook Agent 网关,可参考其签名、去重、队列和状态机;社区规模仍小。
|
||||
- wshm:支持多 Forge 和后台同步,但不是 cc-web 会话桥接。
|
||||
- pi-dispatch:队列、预算和沙箱值得参考,但使用 pi Agent。
|
||||
- gitea-claude-agent:验证 `@机器人 → 多轮澄清 → PR` 交互,但依赖 Gitea Actions。
|
||||
|
||||
## 关键技术约束
|
||||
|
||||
- Webhook 必须在验签、delivery 去重、Bot 自评论过滤后才创建任务。
|
||||
- 每个 turn 生成唯一标识,正常最终评论由 MCP 发送;Webhook/API 查询确认真实回帖。
|
||||
- 任务完成但没有回帖时,只允许在同一会话发起一次“只补发回执、不重复修改”的隐藏 turn。
|
||||
- 回执仍失败才使用 Bot Token 走 Gitea REST 兜底。
|
||||
- 共享工作区有未提交修改时不 reset、不覆盖;当前会话可继续,其他会话排队。
|
||||
|
||||
## 已落地实现
|
||||
|
||||
- 主服务入口为 `lib/gitea-workflow-service.js`,底层组合 `domain/store/queue`;避免同时维护两套运行时状态。
|
||||
- `server.js` 只负责实例化配置、Webhook 路由、工作区 runner、Codex App turn 桥接和管理 API。
|
||||
- 管理配置中的 Secret/Token 保存到独立 0600 文件,GET 配置只返回“是否已配置”;保存后重启使运行时闭环读取新凭据。
|
||||
- 回帖核验只接受 `kind=final|fallback` 的隐藏标识,状态评论不会误判为最终回帖;查询异常也只进行一次“只补发回执”补偿。
|
||||
148
.planning/2026-08-23-gitea-workflow/integration-map.md
Normal file
148
.planning/2026-08-23-gitea-workflow/integration-map.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# Gitea Workflow × Codex App 集成地图
|
||||
|
||||
> 分析日期:2026-08-24
|
||||
>
|
||||
> 范围:只读分析 Codex App 的 `thread/start` MCP 配置、会话创建、消息处理、turn 完成、后台运行、重启恢复和现有回归入口。未修改 `server.js`、`lib/`、`public/`。
|
||||
|
||||
## 证据与方法
|
||||
|
||||
- codebase-memory 项目:`home-cc-web`,`list_projects` 返回根目录 `/home/cc-web`,节点 6670、边 15041;`index_status` 为 `ready`。
|
||||
- 使用 `get_architecture(aspects=["all"])` 确认 `server`、`codex-app-runtime`、`regression`、`codex` 等模块边界。
|
||||
- 使用 `search_graph`、`get_code_snippet`、`trace_path(mode="calls")` 定位函数和调用关系;再以 `rg -n`/`nl -ba` 交叉核对行号。
|
||||
- 图谱无法把回调赋值完整表示出来:`handleCodexAppNotification` 的调用者列表为空,但源码显示它在 `getCodexAppClient()` 作为 `onNotification` 回调传入客户端,再由 JSONL 客户端转发。因此下图的“回调边”以源码为准。
|
||||
|
||||
## 端到端运行链路
|
||||
|
||||
```text
|
||||
WebSocket message (server.js:7706-7766)
|
||||
-> handleMessage (server.js:9852-10190)
|
||||
-> handleCodexAppMessage (server.js:11888-11960)
|
||||
-> activeCodexAppTurns + persistCodexAppTurnState(immediate)
|
||||
-> startCodexAppTurn (server.js:11962-12022)
|
||||
-> getCodexAppClient (server.js:11782-11845)
|
||||
-> createCodexAppServerClient (lib/codex-app-server-client.js:6-223)
|
||||
initialize -> initialized -> postInitialize
|
||||
-> thread/start 或 thread/resume (threadParams)
|
||||
-> codexAppThreadConfig -> mcp_servers.* / web_search
|
||||
-> turn/start (collaborationMode)
|
||||
-> app-server stdout JSONL
|
||||
-> client.handleMessage (lib/codex-app-server-client.js:78-111)
|
||||
-> onNotification(handleCodexAppNotification)
|
||||
-> findCodexAppRouteByRuntime (server.js:11050-11066)
|
||||
-> codexAppRuntime.processCodexAppNotification (lib/codex-app-runtime.js:865-1040)
|
||||
-> persist state; turn/completed -> handleCodexAppTurnComplete
|
||||
```
|
||||
|
||||
### 1. 线程级 MCP 配置和环境
|
||||
|
||||
| 位置 | 行号 | 当前行为 | Gitea 扩展建议 |
|
||||
|---|---:|---|---|
|
||||
| `server.js` `buildCcwebMcpRuntimeConfig` | 2709-2753 | 生成内置 `ccweb` MCP;HTTP 模式把来源会话/跳数放入 URL,stdio 模式生成命令及 env。 | Gitea MCP 应作为同级线程配置注入,不要改 app-server 进程全局 env。 |
|
||||
| `server.js` `listRuntimeMcpServerConfigs` | 2755-2772 | 合并项目 `.codex/config.toml` MCP 与内置 `ccweb`,按规范化 server 名去重。 | 增加 workflow 专属配置的明确入口(建议由 `mcpContext`/工作流配置传入),并定义与项目同名时的优先级。 |
|
||||
| `server.js` `codexAppCcwebMcpEnv` | 11680-11691 | 输出 `CC_WEB_SOURCE_SESSION_ID`、`CC_WEB_CROSS_HOP_COUNT`、内部 token 等。 | Gitea 仓库/任务 ID 应随 `thread/start.config.mcp_servers.gitea.env` 下发;不要写入 `buildCodexAppClientSpec().env`。 |
|
||||
| `server.js` `codexAppThreadConfig` | 11693-11702 | 把 runtime 配置映射为 `config["mcp_servers.<server>"]`,并设置 `web_search`。 | 这是最直接的 `gitea-mcp` 注入扩展点;应保证每个线程拿到独立 host/token/workflow 上下文。 |
|
||||
| `server.js` `codexAppThreadParams` | 11857-11868 | 组装 cwd、model、权限和 `config`,用于 start/resume。 | Gitea 任务必须从这里同时覆盖 `thread/start` 和 `thread/resume`,避免重试/恢复丢 MCP。 |
|
||||
| `server.js` `buildCodexAppClientSpec` | 11735-11780 | 构造单例 app-server 进程的 command/args/env/signature;会剥离敏感环境变量。 | 不要把仓库 token 放此处;若新增全局配置,需纳入 signature 并评估活跃 turn 的复用策略。 |
|
||||
|
||||
当前 `codexAppCcwebMcpEnv` 已证明“来源上下文线程级下发”可行。Gitea 方案应沿用这一模式,例如 `mcp_servers.gitea` 的 `command/args/env` 或 streamable HTTP 配置,而不是 dynamic tools。
|
||||
|
||||
### 2. 会话创建、turn 启动和协作参数
|
||||
|
||||
| 位置 | 行号 | 当前行为 | Gitea 扩展建议 |
|
||||
|---|---:|---|---|
|
||||
| `server.js` WebSocket 分发 | 7706-7766 | `message` 进入 `handleMessage`;slash 先经 `handleSlashCommand`。 | Webhook 不应伪造 WebSocket;工作流服务可调用同一内部处理函数或抽取“创建/投递消息”服务层。 |
|
||||
| `server.js` `handleMessage` | 9852-10190 | 校验输入、创建/加载 JSON 会话、持久化用户消息;Codex App 分支调用 `handleCodexAppMessage`。`ws` 可为 null,`wsSend` 会安全忽略。 | 后台 Gitea 任务可复用会话创建逻辑,但应显式传 workflow 元数据、固定 agent=`codexapp`/yolo,并避免前端 slash 语义污染。 |
|
||||
| `server.js` `handleCodexAppMessage` | 11888-11960 | 建立 `activeCodexAppTurns` entry,记录 `mcpContext`/retry 信息,立即写 state,然后异步启动 turn。 | 在 entry/retry state 中保留不可变的 Gitea workflow/task/repository correlation;该信息不能只存在内存。 |
|
||||
| `server.js` `startCodexAppTurn` | 11962-12022 | 复用单例客户端;有 threadId 则 resume,否则 start;校验恢复线程 ID;保存 runtime thread ID;发送 turn/start。 | Gitea 首次任务走 start,后续评论/重试走同一 thread resume;将 gitea MCP 配置传入 `codexAppThreadParams`,并在 mismatch 时按任务失败处理。 |
|
||||
| `server.js` `codexAppCollaborationMode` / `codexAppTurnParams` | 11704-11712、11870-11886 | collaboration mode 总是存在,`plan` 映射 plan,否则 default;model/reasoning_effort/developer instructions 放 settings,顶层不重复 model/effort。 | 后台固定 yolo 会得到 mode=default;不要为 Gitea 任务再传顶层 model/effort,避免回到非原生协作路径。 |
|
||||
| `server.js` `codexAppPostInitialize` | 11714-11733 | initialize 后 best-effort 探测 goals 和 `collaborationMode/list`,失败只记日志。 | Gitea 集成不应依赖探测成功;可在能力缺失时记录降级并继续普通 turn。 |
|
||||
|
||||
### 3. JSON-RPC 客户端、通知、服务端请求
|
||||
|
||||
| 位置 | 行号 | 当前行为 | Gitea 扩展建议 |
|
||||
|---|---:|---|---|
|
||||
| `lib/codex-app-server-client.js` `handleMessage` | 78-111 | 以 `id` 区分 pending response/server request;无 `id` 的 method 交给 `onNotification`。 | 回执监听应基于 turn/thread ID,不要解析 UI 文本;保留未知 method 日志。 |
|
||||
| `lib/codex-app-server-client.js` `start` | 140-190 | 启动 stdio app-server,绑定 readline,initialize/initialized,再运行 `postInitialize`;退出 reject 所有 pending。 | gitea-mcp 子进程由 app-server 按线程配置管理;不要另起一套 app-server 连接。 |
|
||||
| `server.js` `getCodexAppClient` | 11782-11845 | 全局单例 `codexAppClient`;配置 signature 变化时若有其他活跃 turn 会复用旧客户端,否则失败残留并重启。 | 队列不能通过频繁改全局 config 切换仓库;每个任务使用线程 config,避免触发 singleton stale-config 分支。 |
|
||||
| `server.js` `handleCodexAppNotification` | 11105-11155 | 先处理 compact/Goal/MCP startup,再按 parent/child/磁盘恢复路由;runtime 返回 done 时进入完成处理。 | Gitea 回执监听应挂在此处或其下游的领域事件,而不是监听 WebSocket `done`;需识别 `turn/completed` 的 status/error。 |
|
||||
| `server.js` `handleCodexAppServerRequest` | 11572-11608 | 处理 approval、user input、dynamic tool;未知请求按保守策略拒绝。 | gitea-mcp 是 MCP server,不应新增 dynamic tool 旁路;若需要用户确认,明确映射到工作流 waiting_user。 |
|
||||
| `server.js` `handleCodexAppServerExit` / `handleCodexAppTurnFailure` | 11610-11637、12165-12170 | app-server 退出会使所有 active Codex App turn 失败;失败统一进入完成清理和重试判定。 | Gitea 任务需把此类失败映射为可恢复状态,防止一次 singleton 退出同时丢失多个仓库任务。 |
|
||||
|
||||
### 4. turn 完成、后台运行与持久化恢复
|
||||
|
||||
| 位置 | 行号 | 当前行为 | Gitea 扩展建议 |
|
||||
|---|---:|---|---|
|
||||
| `server.js` `handleCodexAppTurnComplete` | 12024-12163 | 去重并持久化 assistant/toolCalls;处理 transient retry;删除 active entry、清理 run dir;有 ws 发 `done`,无 ws 则广播 `background_done` 并调用通知。 | 在删除 entry/清理目录前写入 workflow completion/outbox;回执确认、补触发和 REST 兜底必须幂等。建议发领域事件,避免依赖 `sendNotification`。 |
|
||||
| `server.js` `handleCodexAppSteerMessage` | 12189-12419 | 运行中用 `turn/steer`;遇到 no-active-turn 会先收敛旧输出,再复用消息启动新 turn。 | Gitea 连续评论可转为 steer,但要用任务级 message/delivery ID 去重,避免 steer fallback 重复修改仓库。 |
|
||||
| `server.js` `handleCodexAppAbortSession` | 12421-12451 | 发送 `turn/interrupt`,超时后强制完成;无可用 client 直接以 interrupted 完成。 | 工作流取消/仓库停用可复用,但状态机需区分用户取消、运维中止、app-server 崩溃。 |
|
||||
| `server.js` `writeCodexAppTurnState` / `persistCodexAppTurnState` / `loadCodexAppTurnState` | 5039-5059、5061-5082、5093-5116 | state 原子写入、延迟 flush、大小保护;序列化当前 turn 文本/toolCalls/usage/error。 | 必须把 workflow/task/repository correlation 和回执状态纳入独立持久化(或扩展 state schema),不能只依赖 `entry.mcpContext`。 |
|
||||
| `server.js` `recoverCodexAppTurnState` | 5137-5226 | 重启后恢复 partial assistant/toolCalls,写 interrupted system message,清理 run dir;不自动继续 turn。 | `queued`/`waiting_user`/`running` 的 Gitea 状态恢复应在工作流队列层实现;running 只能标为 interrupted 后按策略最多重试一次。 |
|
||||
| `server.js` `recoverProcesses` + 启动调用 | 7398-7490、12950 | 启动扫描 `*-run`;Codex App state 优先走 `recoverCodexAppTurnState`,普通进程走 JSONL tail/replay。 | 工作流恢复入口应在此生命周期之后加载 durable queue,再决定是否重新投递;不要在 `recoverProcesses` 中直接执行 Gitea 网络副作用。 |
|
||||
| `server.js` `updateSessionRuntimeThreadIndex` / `saveSession` | 3443-3456、4497-4520 | 保存会话时维护 thread→session O(1) 索引;未知线程有负缓存,避免通知热路径反复扫盘。 | Gitea 任务必须持久化稳定 session/thread 映射;删除仓库/会话时同步清理映射,避免跨仓库通知串线。 |
|
||||
| `server.js` `findCodexAppRouteByRuntime` | 11050-11066 | parent active entry → 已知 child thread → 磁盘 adopt;child 先于 parent adoption。 | Gitea MCP 可能产生 child/collab activity,工作流回执只认 parent turn;需保留 child 事件但禁止把 child completion 当最终回执。 |
|
||||
|
||||
无 WebSocket 的后台路径是可行的:`handleMessage(null, ...)` 的 `wsSend` 为空安全,`handleCodexAppTurnComplete` 会走 `background_done`/通知分支。但该分支目前没有 Gitea 专属完成回调,必须增加可持久化的领域事件或 outbox。
|
||||
|
||||
## 建议的最小扩展边界
|
||||
|
||||
1. 新增独立 Gitea Workflow 服务/存储模块:Webhook 验签、delivery 去重、仓库/任务状态机、同仓库锁、全局并发上限、outbox/审计。不要把队列逻辑塞进 `handleMessage` 或 app-server 客户端。
|
||||
2. 为 Codex App 调用增加“线程级 MCP 配置”参数(沿用 `mcpContext` 传递),在 `listRuntimeMcpServerConfigs` → `codexAppThreadConfig` → `codexAppThreadParams` 形成单向注入;官方 `gitea-mcp` 使用 stdio 或明确的 streamable HTTP 配置,token 只放线程 env/安全存储。
|
||||
3. 将 workflow correlation(实例、owner/repo、issue/PR、评论 delivery、turn ID)写入独立任务记录,并在 turn state 中保存最小引用,保证进程重启和自动重试可恢复。
|
||||
4. 在 `handleCodexAppNotification`/`handleCodexAppTurnComplete` 旁增加内部领域事件适配器:先 durable commit,再确认 Gitea 评论;缺失回执只允许一次补触发,最后 REST 兜底。
|
||||
5. 后台任务通过现有 Codex App 单例运行,但由 Gitea 队列决定何时调用;不要修改 `buildCodexAppClientSpec` 的全局环境来切换仓库。
|
||||
|
||||
## 潜在冲突与风险
|
||||
|
||||
- **单例 app-server 与多仓库并发**:`getCodexAppClient` 只有一个 client,退出会影响全部 active turns;队列必须在 client 层之上做并发/重试隔离。
|
||||
- **线程配置与进程环境边界**:`buildCodexAppClientSpec` 的 env 是进程级,不能承载某个仓库的 Gitea token;否则并发任务会串凭据。
|
||||
- **恢复丢失 MCP 上下文**:`adoptCodexAppUnroutedTurn`(`server.js:10379-10423`)新建 entry 时 `mcpContext: {}`;`recoverCodexAppTurnState` 也只恢复输出,不恢复 workflow 上下文。必须把上下文放独立任务记录或 state schema。
|
||||
- **完成清理时序**:`handleCodexAppTurnComplete` 在 12087-12088 删除 active entry/清理 run dir,之后才通知后台客户端;回执 outbox 必须在清理前落盘。
|
||||
- **自动重试副作用**:`shouldRetryCodexTransientFailure` 路径会重新启动 turn;若模型已修改仓库或已发评论,需用任务/评论/turn 幂等键防止重复写入。
|
||||
- **通知路由与未知线程**:当前有 O(1) index、磁盘 adoption 和负缓存;Gitea 不能只依赖内存 active map,否则重启后通知会变成 unrouted。
|
||||
- **现有运行保护**:`handleMessage` 会拒绝 active turn/Goal/compaction;工作流必须先做同仓库串行和取消策略,不能靠重复调用绕过保护。
|
||||
- **协作模式形状**:启用 collaborationMode 时顶层不得重复 `model`/`effort`;gitea workflow 的 yolo 固定策略应保持 `mode=default` + settings。
|
||||
- **安全边界**:Webhook HMAC、Bot 自评论过滤、delivery 去重、token 脱敏、remote URL 不落 token,需要在 Gitea 层完成,不能依赖 Codex App MCP 工具自行保证。
|
||||
|
||||
## 必须补的测试
|
||||
|
||||
### Codex App 协议/单元回归
|
||||
|
||||
- `thread/start` 首次任务与 `thread/resume` 重试都包含 `mcp_servers.gitea`,且每个 session 的 host/token/workflow env 隔离;项目 MCP、ccweb MCP、gitea MCP 同时存在时不丢失/不误去重。
|
||||
- collaborationMode 仍把 model/reasoning_effort/developer instructions 放在 `settings`,顶层没有重复 model/effort;yolo 固定为 default。
|
||||
- `handleMessage(null, ...)` 能创建后台 Codex App 会话;无 ws 完成时仍持久化 assistant/toolCalls,并触发可断言的 workflow completion/outbox,而不是只依赖 push notification。
|
||||
- turn state 原子写入/重启恢复包含 workflow correlation;恢复后 parent thread、gitea MCP 配置和回执状态不丢失;未知线程 adoption 不得把 child 事件当 parent 最终完成。
|
||||
- app-server 退出、transient capacity/reconnect、thread resume mismatch、steer no-active-turn 均只产生一次工作流状态转移和一次回执动作。
|
||||
|
||||
### Gitea Workflow 业务回归
|
||||
|
||||
- Webhook HMAC 正确/错误、重复 delivery、Bot 自评论、非 Issue/PR 普通评论过滤。
|
||||
- 首次合法触发自动接入仓库、clone/fetch 失败恢复、同仓库串行、不同仓库并行及全局并发上限。
|
||||
- 评论→session 映射稳定(`gitea + owner/repo + type + number`),连续评论 steer/resume 不重复用户消息或仓库修改。
|
||||
- turn 完成后 MCP 回帖确认:正常一次;缺失时仅一次“只补回执”隐藏 turn;再次失败才 REST 兜底;重启/重复 webhook 不重复评论。
|
||||
- running 中断、waiting_user 保留、queued 恢复、仓库暂停/全局暂停/取消排队/中止 turn 的状态机和审计日志。
|
||||
- token 不出现在日志、错误消息、session JSON、git remote URL;Webhook 重放和跨仓库 thread/child 通知不能串任务。
|
||||
|
||||
### 现有可复用测试入口
|
||||
|
||||
| 入口 | 行号 | 已覆盖内容 |
|
||||
|---|---:|---|
|
||||
| `scripts/regression.js` `assertCodexAppRuntimeSubAgentActivityContract` | 2276-2500 | runtime 通知到工具/子代理卡片的归一化。 |
|
||||
| `scripts/regression.js` `assertCodexAppTransientReconnectContract` | 2501-2542 | reconnect 进度不结束 turn,终态错误结束 turn。 |
|
||||
| `scripts/regression.js` `assertCodexAppStaleRunningRecoveryContract` | 4038-4058 | stale steer 替换 turn 的持久化/flush 约束。 |
|
||||
| `scripts/regression.js` `runCodexAppRuntimeImageSteerRegression` | 4233-4365 | 后台/运行中 steer 真实 WebSocket 流程。 |
|
||||
| `scripts/regression.js` `runCodexAppStaleRunningRegression` | 4367-4558 | app-server 缺失终态通知后的恢复及消息去重。 |
|
||||
| `scripts/regression.js` `assertCodexAppChildToolRoutingContract` | 4867-5074 | child thread 与父卡片路由隔离。 |
|
||||
| `scripts/regression.js` `assertCodexAppUnroutedNotificationRoutingContract` | 5076-5113 | thread 索引、负缓存、磁盘 adoption。 |
|
||||
| `scripts/regression.js` `main --target` | 6032-6173 | 可增加 `gitea-workflow-codexapp` 独立目标,避免每次跑完整回归。 |
|
||||
| `scripts/regression.js` Codex App 集成段 | 7239-7622 | mock app-server 下的 session、MCP config、collaboration、retry、Goal、runtime warning、工具调用。 |
|
||||
| `scripts/mock-codex-app-server.js` `ensureThread`/`completeMcpToolTurn`/`startTurn`/`handleRequest` | 91-114、670-737、863-1023、1066-1250 | 可扩展为断言 gitea MCP 配置、thread/start/resume 和 turn/completed。 |
|
||||
| `package.json` `regression` script | 8 | 统一执行 `node scripts/regression.js`。 |
|
||||
|
||||
建议新增 mock 行为:在 `completeMcpToolTurn` 输出 gitea server 的 `type/command/args/env`(token 脱敏),在 `thread/start`/`thread/resume` 记录 config fingerprint;新增回归目标仅断言协议形状和状态机,不调用真实 Gitea。
|
||||
|
||||
## 当前验证结果
|
||||
|
||||
- `node scripts/regression.js --target codexapp-retry-runtime`:通过。
|
||||
- `node scripts/regression.js --target codexapp-unrouted-routing`:通过。
|
||||
- `node scripts/regression.js --target codexapp-stale-running`:通过。
|
||||
- 本任务没有修改 `server.js`、`lib/` 或 `public/`;仅新增本分析文件(以及临时计划 CSV,完成后删除)。校验时工作区已存在其他并行任务对 `server.js`/`lib` 的改动,本任务未触碰、未回退这些改动。
|
||||
134
.planning/2026-08-23-gitea-workflow/plan-review.md
Normal file
134
.planning/2026-08-23-gitea-workflow/plan-review.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Gitea Workflow 实施计划审查
|
||||
|
||||
审查日期:2026-08-24
|
||||
审查范围:`task_plan.md`、`findings.md`、`Gitea Workflow TO DO list.csv` 及用户目标“完整实现 Gitea Webhook → Codex App 持久会话 → 官方 gitea-mcp → Gitea 回执的可靠工作流,并提供管理页面、队列、恢复、测试和文档”。本审查只产生本文件,不修改业务代码或其它计划文件。
|
||||
|
||||
## 结论
|
||||
|
||||
当前计划在“阶段主题”层面与 CSV 基本一致,但还不能作为可直接执行和可验收的实施基线。建议先补齐 Phase 1 的协议、状态机、数据模型和安全设计,再进入实现;否则最容易出现“功能链路能跑一次,但重启、重复 Webhook、回执失败或并发时不可靠”的假完成。
|
||||
|
||||
建议结论:**有条件通过,暂不进入核心编码**。以下 P0 项未关闭前,不应把 CSV 中的核心实现项标记为 DONE。
|
||||
|
||||
## 计划与 CSV 一致性
|
||||
|
||||
| 计划范围 | 对应 CSV | 一致性 | 审查意见 |
|
||||
|---|---|---|---|
|
||||
| Phase 1 需求、协议、设计(`task_plan.md:13-18`) | 1、2 | 基本一致 | CSV 第 1 项为 `IN_PROGRESS`,与计划“进行中”一致;但“已完成边界汇总”和“设计文档尚在编写”的完成定义没有拆开。 |
|
||||
| Phase 2 状态、Webhook、工作区(`task_plan.md:20-25`) | 3、4 | 基本一致 | 计划把队列/恢复和工作区放在同一阶段,CSV 分成两项;缺少明确前置依赖、交付物和负责人。 |
|
||||
| Phase 3 Codex App、MCP、回执(`task_plan.md:27-32`) | 5 | 基本一致 | CSV 将线程注入、监听、补触发、REST 兜底合成一项,无法分别验收可靠性。 |
|
||||
| Phase 4 管理界面、测试(`task_plan.md:34-39`) | 6、7 | 基本一致 | 计划中的重启恢复、并发、自触发回归没有在 CSV 中单列,容易被“测试已补充”掩盖。 |
|
||||
| Phase 5 整合、验证、交付(`task_plan.md:41-45`) | 8、9、10 | 一致 | 但“解决冲突”“协议形状检查”“文档/审计”均没有明确输出文件、命令和通过阈值。 |
|
||||
|
||||
总体上,CSV 的 10 项与计划的 15 个子项不是一一映射关系。建议在计划和 CSV 中增加稳定的任务 ID(例如 P1-01、P2-02),并为每项写明 `交付物 / 依赖 / 验收命令或证据 / 负责人`;否则状态同步只能凭文字判断。
|
||||
|
||||
## P0 阻断项
|
||||
|
||||
### P0-1:Phase 1 没有可执行的退出条件
|
||||
|
||||
计划只写“核验协议”“完成 PRD、状态机、数据模型和安全设计”(`task_plan.md:16-17`),没有指定文档路径、协议版本、决策结果或评审证据。`findings.md` 目前只有研究结论,没有状态转移表、字段定义、接口契约或威胁模型。
|
||||
|
||||
进入 Phase 2 前至少应产出并评审:
|
||||
|
||||
- Webhook 请求/响应和签名头的版本化协议矩阵;官方 `gitea-mcp` 版本、启动参数、stdio 生命周期和工具能力清单。
|
||||
- 任务/会话/回执状态机(状态、事件、合法转移、重试上限、超时、取消、死信、人工恢复)。
|
||||
- 持久化数据模型、唯一键、索引、迁移/版本策略和崩溃恢复规则。
|
||||
- 线程、任务、Webhook delivery、评论和回执之间的关联契约。
|
||||
- 安全威胁模型与密钥生命周期设计。
|
||||
|
||||
### P0-2:持久化方案无法证明并发安全和崩溃一致性
|
||||
|
||||
`findings.md:17` 仅指出现有 JSON 持久化或“新增独立存储模块”,但没有做出选择。队列、去重、状态转移、会话映射和回执确认若继续散落写 JSON,会遇到并发覆盖、半写文件、重复消费和重启丢失;这与目标中的“持久化、可靠、可恢复”直接冲突。
|
||||
|
||||
计划必须明确单一事实源及原子性边界:至少要定义 inbox(Webhook 去重)、任务状态、会话映射、outbox(评论/补发意图)如何在一次事务或等价原子操作中落盘;并定义锁、文件替换、fsync、备份/恢复和 schema 迁移。若采用 JSON,需证明单写者/进程锁和崩溃恢复;若不能证明,应在计划中选择具备事务语义的存储。
|
||||
|
||||
### P0-3:状态机、幂等和回执可靠性没有闭环
|
||||
|
||||
`findings.md:34-39` 给出了“先验签/去重/过滤”“最多一次隐藏补触发”“REST 兜底”的原则,但没有定义可判定的幂等键、评论标记、确认窗口、重复运行保护或失败终态。尤其“任务完成但没有回帖”时,如何区分 MCP 调用已成功但查询延迟、调用超时、网络重试导致重复评论,当前不可执行。
|
||||
|
||||
需要补充:
|
||||
|
||||
- delivery、任务、turn、评论发送各自的唯一 ID 及跨表关联。
|
||||
- 评论内容中的稳定 run/attempt 标记或等价查询条件,保证 MCP、补触发和 REST 兜底不会重复发同一回执。
|
||||
- Gitea 查询的最终一致性轮询窗口、退避、最大次数和“未知”状态;未知不能直接当失败重跑。
|
||||
- 重试分类(网络/5xx、4xx 权限、超时、进程崩溃)、退避与死信/人工重放策略。
|
||||
- 重启时每个状态的恢复动作,特别是 `running`、`waiting_user`、`receipt_pending` 和 `cancel_requested`。
|
||||
|
||||
### P0-4:三方协议的真实形状和版本未锁定
|
||||
|
||||
`findings.md:19-25` 只记录官方仓库和大致参数,`task_plan.md:29-30` 也没有 app-server 方法、事件类型或 MCP 注入字段。没有固定版本和 mock 契约,无法验收“线程级注入”和“turn 状态监听”是否真的走原生协议。
|
||||
|
||||
应在 Phase 1 固化并纳入 mock/回归断言:
|
||||
|
||||
- Gitea Webhook 事件类型、评论字段路径、签名算法/头、delivery ID、超时和 HTTP 返回语义。
|
||||
- Codex App `initialize/initialized`、能力探测、`thread/start`、线程恢复、turn 事件和取消/中止的确切参数形状。
|
||||
- `thread/start.config.mcp_servers.*` 的线程级 env 注入、stdio 子进程启动/退出、超时和错误事件;不得退回动态工具或进程级来源上下文。
|
||||
- 官方 `gitea-mcp` 版本/校验和、`GITEA_HOST` 与 token 注入方式、工具列表和权限边界。
|
||||
|
||||
### P0-5:全写权限下的安全边界不足
|
||||
|
||||
计划确认“开放官方 gitea-mcp 全部写权限”(`task_plan.md:63`),但安全设计只写 HMAC 和 Token 分离(`task_plan.md:62`)。这不足以支撑公网 Webhook 和自动接入。至少要覆盖重放、密钥轮换、日志脱敏、仓库/主机 allowlist、请求体大小和限流、路径穿越、命令注入、工作区权限、MCP 子进程隔离,以及管理页面的认证/授权/CSRF。
|
||||
|
||||
同时要明确 Bot 自评论过滤不能只依赖用户名:应定义稳定的 bot identity、来源 delivery/run 标记和异常情况下的 fail-closed 行为,避免自触发风暴(`findings.md:34-36`)。
|
||||
|
||||
### P0-6:跨模块写冲突和整合策略未规划
|
||||
|
||||
Phase 5 写“整合子任务改动并解决冲突”(`task_plan.md:43`),但没有任务分区、文件所有权、接口冻结点或整合顺序。预期热点很可能包括 `server.js`、配置/持久化模块、Codex App turn 路径、MCP 配置、路由和前端状态;多个实现项若直接改同一入口,会产生不可审计的隐式冲突。
|
||||
|
||||
进入实现前应把工作拆成不重叠边界(例如 domain/state、webhook/queue、workspace、app-server adapter、API/UI、tests/docs),先冻结共享接口,再按依赖顺序合并;禁止以“最后人工解决冲突”作为验收条件。
|
||||
|
||||
## P1 重要遗漏与可执行性问题
|
||||
|
||||
### 1. 队列与恢复
|
||||
|
||||
Phase 2 只列“状态、队列、去重和恢复”(`task_plan.md:22`),缺少全局并发上限为 2 的调度算法、公平性、每仓库串行锁的租约/心跳、进程崩溃后的 stale runner 判定、优雅停机、取消排队与中止 turn 的区别、超时、死信和人工重放。应为每一类恢复场景写出输入、状态变化和预期结果,并加入 kill/restart 测试。
|
||||
|
||||
### 2. Webhook 与自动接入
|
||||
|
||||
`findings.md:5-7` 未定义配置校验、首次接入的授权边界、仓库 URL/owner/repo 的规范化、默认分支和私有仓库权限失败处理。需要明确先快速返回 HTTP,再异步入队还是同步处理;验签失败、重复 delivery、非评论事件、非目标评论、Bot 自评论分别返回什么,避免 Gitea 重试风暴。
|
||||
|
||||
### 3. 工作区与 Git 认证
|
||||
|
||||
`findings.md:40` 的“脏工作区不 reset、不覆盖”是原则,不是行为契约。要决定脏状态时任务是失败、暂停、继续当前会话还是创建隔离 worktree;定义分支/ref 切换、fetch 冲突、仓库删除、磁盘不足、凭据泄露(命令行/日志/remote config)和清理策略。持久 checkout 与“同仓库串行”还需明确锁的持有范围。
|
||||
|
||||
### 4. Codex App 会话与 waiting_user
|
||||
|
||||
计划确认会话键(`task_plan.md:59`)和固定 `codexapp + yolo`(`task_plan.md:58`),但没有说明首次建线程、重启后的线程恢复、线程不存在/过期、app-server 断线、turn 超时及取消后的映射。`waiting_user`(`task_plan.md:31`、`task_plan.md:64`)没有定义由哪类 Gitea 评论恢复、是否阻塞同仓库、超时/过期和最大连续轮数;“连续评论投递”也没有顺序和幂等规则。
|
||||
|
||||
### 5. 管理 API 与前端
|
||||
|
||||
Phase 4 只写“实现管理 API 和前端页面”(`task_plan.md:36`),没有端点契约、权限角色、敏感配置展示规则、暂停/停用/取消/中止的并发控制、审计记录格式、分页过滤、实时刷新和错误提示。管理页面不能成为绕过队列状态机的第二套写入口;所有操作应复用同一领域命令和审计链。
|
||||
|
||||
### 6. 测试与验收证据
|
||||
|
||||
CSV 第 7、9 项虽覆盖“测试、构建、协议验收”,但没有测试矩阵、通过阈值或运行命令。至少应覆盖:重复/乱序 Webhook、伪造/重放签名、Bot 自触发、首次接入、同仓库并发、不同仓库并发上限、脏工作区、clone/fetch 失败、app-server 断线、MCP 缺失/工具报错、turn 中止、waiting_user、MCP 成功但查询延迟、补触发成功/失败、REST 兜底、进程 kill 后恢复、密钥轮换和管理 API 未授权。
|
||||
|
||||
协议测试不能只断言“调用成功”,还要断言真实参数形状、线程级 env、没有重复顶层字段、调用的是 MCP 工具而非动态工具,并保存 mock 输入/输出作为审计证据。应明确单元/集成/E2E 的最小通过标准和 CI/本地命令。
|
||||
|
||||
### 7. 运维、可观测性与交付
|
||||
|
||||
用户目标包含队列、恢复和文档,但计划未列指标、结构化日志、关联 ID、告警、数据保留、备份恢复、升级/回滚、配置校验和运行手册。Phase 5 的“文档、审计和最终交付”(`task_plan.md:45`)应明确至少包括架构/状态机、部署配置、Webhook 配置、密钥轮换、故障处理、恢复/重放、权限说明和已知限制。
|
||||
|
||||
## 建议的执行顺序与退出条件
|
||||
|
||||
1. **先完成 Phase 1 冻结契约**:补齐上述协议矩阵、状态机、schema/迁移、领域命令、错误/重试表、安全威胁模型和 mock fixture;每项有文档路径和评审人。
|
||||
2. **再实现持久化领域核心**:先落地 inbox、任务、会话、outbox、审计的原子状态转移和恢复算法,再接 Webhook/工作区,避免入口先写出不可恢复的副作用。
|
||||
3. **接入 Codex App 与 gitea-mcp**:以固定版本和协议 mock 验证线程级配置、turn 事件、取消/重连;所有回执发送经过幂等 outbox。
|
||||
4. **最后接 API/UI 和故障测试**:API/UI 只发领域命令;并发、重启、自触发和回执异常测试必须在标记 Phase 4 完成前通过。
|
||||
5. **Phase 5 交付门槛**:构建、静态检查、全测试和协议形状断言均有可复现命令;文档、审计样例、恢复演练记录和回滚方案齐全。
|
||||
|
||||
推荐把每个阶段的状态改成“未开始/进行中/阻塞/完成”,并在完成条件中引用证据文件或测试名称,而不是只保留勾选框。
|
||||
|
||||
## 最小验收清单(对应用户目标)
|
||||
|
||||
- Webhook:合法评论可入队;伪造、重放、重复 delivery、非目标事件和 Bot 自评论均按契约处理。
|
||||
- 持久会话:会话键稳定;重启后任务、锁、turn 和 waiting_user 状态可恢复且不重复执行。
|
||||
- 官方 MCP:固定版本、线程级配置、凭据不落盘/不泄露;读写工具调用可被 mock 和审计验证。
|
||||
- 回执:成功评论可确认;未知状态不会盲目重跑;只允许一次补触发,之后 REST 兜底仍幂等。
|
||||
- 队列/并发:同仓库串行、不同仓库并行、全局上限 2 可证明;暂停、取消、终止和死信可操作。
|
||||
- 管理面:认证授权、审计、状态查询和运维操作与领域状态机一致,不暴露 token/secret。
|
||||
- 可靠性测试:覆盖崩溃恢复、并发、网络/权限/协议失败和自触发回归,并有可重复命令与结果。
|
||||
- 文档交付:架构、配置、部署、密钥、故障恢复、重放、回滚和已知限制齐全。
|
||||
|
||||
## 审查结论摘要
|
||||
|
||||
CSV 与计划没有明显的宏观漏项,但粒度、状态和验收证据不一致;当前最大的风险不是缺少页面或接口,而是持久化原子性、状态机幂等、三方协议形状和全写权限安全边界尚未冻结。补齐 P0 并将其转成带 ID/依赖/证据的任务后,计划才具备可靠实施条件。
|
||||
97
.planning/2026-08-23-gitea-workflow/progress.md
Normal file
97
.planning/2026-08-23-gitea-workflow/progress.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# Gitea Workflow 进度
|
||||
|
||||
## 2026-08-23
|
||||
|
||||
### 当前阶段:需求、协议与架构文档化
|
||||
|
||||
- 已完成用户需求 grilling 对齐。
|
||||
- 已确认 cc-web 本地会话、Codex App、MCP 注入和 turn 完成代码入口。
|
||||
- 已确认官方 gitea-mcp 的 stdio、环境变量和工具覆盖。
|
||||
- 已创建主实施计划,准备并行派发文档与协议核验子任务。
|
||||
|
||||
### 文件
|
||||
|
||||
- `.planning/2026-08-23-gitea-workflow/task_plan.md`
|
||||
- `.planning/2026-08-23-gitea-workflow/findings.md`
|
||||
- `.planning/2026-08-23-gitea-workflow/progress.md`
|
||||
|
||||
### 测试
|
||||
|
||||
| 测试 | 结果 |
|
||||
|---|---|
|
||||
| 当前 git status | 工作区初始干净 |
|
||||
| codebase-memory 索引 | ready |
|
||||
|
||||
## 2026-08-24
|
||||
|
||||
### 当前阶段:集成验收与交付
|
||||
|
||||
- 已落地 `lib/gitea-workflow-domain.js`、`store`、`queue`、`service`,统一由 `server.js` 挂接。
|
||||
- 已落地 Webhook HMAC/mention/delivery 去重、HTTPS 工作区 clone/fetch、临时 Git 凭据和脏目录阻塞。
|
||||
- 已落地 Codex App 线程级官方 `gitea-mcp`、turn 标记、回执查询、一次只补发回执和 REST 兜底。
|
||||
- 已落地配置、仓库、任务、审计、暂停/恢复/取消/中止管理 API 与前端页面。
|
||||
- 专项测试全部通过:Webhook 回归 9 场景、核心 7 项、工作区、Codex、管理页面。
|
||||
- `npm run regression` 已在 60 秒门限内完成并输出 `Regression checks passed.`;此前的长时观察记录已由最终回归结果覆盖。另已通过 `codexapp-unrouted-routing`、`composer-slash-routing`、`codexapp-stale-running` 目标回归。
|
||||
- 当前主机未安装 `gitea-mcp` 可执行文件,也未配置真实 Gitea host/token;真实出站 MCP/REST 回帖仍需部署后 smoke test。
|
||||
|
||||
### 2026-08-24 主任务终审补强
|
||||
|
||||
- 修正隐藏回执补发轮次的 turn 标识校验:允许初始预分配标识与历史补发标识集合,避免
|
||||
Agent 已回帖但因 Codex 实际 turnId 变化被误判为缺失。
|
||||
- 查询 Gitea 回帖状态为 `unknown` 时改为保守失败:不自动补发/REST 覆盖,先发送状态
|
||||
告警并标记 `failed_reply`,与 CODEX-INTEGRATION、PRD、ARCHITECTURE 统一。
|
||||
- 工作区成功/脏目录状态回写 Workflow Store;管理页补齐仓库工作区、队列数、活动任务、
|
||||
脏目录原因和审计日志镜像;配置接口校验 Gitea 地址并保留 0600 凭据文件。
|
||||
- Gitea 管理 JS/CSS 使用独立缓存版本,避免只修改管理资源时命中旧缓存。
|
||||
- 修正管理停用仓库后的规范化 Webhook 路径:`enqueueNormalizedTask` 现在返回明确
|
||||
`ok`,对已停用仓库拒绝入队并写入审计,避免通过独立 receiver 绕过停用控制。
|
||||
- 管理页保存的并发上限现会在重启后被 Workflow 服务读取;环境变量凭据只同步“已配置”
|
||||
标志,不把明文 Secret 写入管理状态。
|
||||
- 工作区脏目录现在记录 `dirtySessionKey`:同一 Issue/PR 会话后续 turn 可安全沿用现场,
|
||||
跳过 fetch/checkout;其他讨论仍进入 `blocked_workspace`,且每轮结束会重新探测并在清洁后
|
||||
自动清除脏标记。
|
||||
- Webhook 对完整 `ccweb-gitea` 隐藏标识做前置过滤,即使 Gitea 事件缺少作者字段也不会
|
||||
触发自循环。
|
||||
- 队列的工作区阻塞、失败、取消和中止终态现在统一发送 Gitea 状态评论;REST 兜底成功
|
||||
会单独标记“REST 兜底回帖”,避免用户只看到初始排队消息。
|
||||
- 管理配置更新和控制操作均强制要求 `reason`,并在接口层返回 `reason_required`,保证审计
|
||||
记录不会出现无原因的人工运维动作。
|
||||
|
||||
### 终审验证
|
||||
|
||||
| 测试 | 结果 |
|
||||
|---|---|
|
||||
| Gitea 专项 5 脚本 | 全部通过 |
|
||||
| `node --check` + `git diff --check` | 通过 |
|
||||
| `timeout 60s npm run regression` | `Regression checks passed.` |
|
||||
| 隔离服务启动/HTTP 冒烟 | 根页面 200;未认证管理 API 401;未配置 Secret 的 Webhook 503 |
|
||||
|
||||
### 主任务验收补强
|
||||
|
||||
- 复核子任务实际代码、管理页面/API 挂接、文档和持久化边界;未发现重复路由或
|
||||
覆盖核心模块的问题。
|
||||
- 修正 `server.js` 启动时环境配置同步:只有实际提供的环境字段才覆盖管理镜像,
|
||||
避免空的 `CC_WEB_GITEA_WORKSPACE_ROOT` 或并发字段覆盖页面已保存配置。
|
||||
- 重新通过五个 Gitea 专项脚本、`node --check`、`git diff --check` 和
|
||||
`timeout 60s npm run regression`。
|
||||
- 隔离实例 HTTP 冒烟再次通过:根页面 200、未认证管理 API 401、未配置 Secret
|
||||
的 Webhook 503;未触碰当前 PM2 进程。
|
||||
- 追加运行时收敛保护:初始/补发 Codex turn 启动被拒绝时立即清理 waiter 并进入
|
||||
回帖失败/REST 兜底路径;任务持久化 `turnState/turnStartedAt/turnUpdatedAt`,
|
||||
管理页可看到更准确的 Turn 状态。
|
||||
- 工作区校验新增 Gitea host 白名单,阻止 Webhook 携带跨实例 clone URL;队列恢复
|
||||
和入队按评论创建时间(缺失时按接收/创建时间)排序,满足同讨论串顺序处理。
|
||||
- 上述补强后核心、Webhook、管理专项测试与语法/差异检查均再次通过。
|
||||
- 启动配置镜像现在会读取 Secret 文件实际内容后再标记“已配置”,不存在或空文件
|
||||
不会造成管理页虚报凭据可用;隔离 HTTP 冒烟仍通过。
|
||||
- 对“每个 @ 必须有回执”再补强:停用仓库或 Webhook 异步入队异常会发送带任务标识的
|
||||
Gitea 状态回帖,而不是只写本地日志。
|
||||
- 澄清交互采用独立 `waiting_user` 隐藏回执标识;Agent 提问不会被误判为最终回帖
|
||||
缺失,也不会触发重复修改型 reply retry。未配置 Bot Token 时 Webhook 直接 503,
|
||||
防止接受无法回帖的任务。
|
||||
- 最终专项脚本、语法检查、差异检查和完整 regression 均通过。
|
||||
- 复核“文档与集成测试”子任务回传:7 个 fixture 均可解析,独立脚本未加载
|
||||
`server.js`,9/9 场景覆盖验签、过滤、恢复、回帖补偿、MCP 和安全边界;未发现
|
||||
该子任务修改 `server.js` 的证据。
|
||||
- 主任务随后补上状态评论持久化幂等键(`taskId:status`),使 ARCHITECTURE/PRD
|
||||
中的评论幂等契约与运行实现一致;专项测试继续通过。
|
||||
340
.planning/2026-08-23-gitea-workflow/protocol-research.md
Normal file
340
.planning/2026-08-23-gitea-workflow/protocol-research.md
Normal file
@@ -0,0 +1,340 @@
|
||||
# Gitea Webhook 与官方 gitea-mcp 协议研究
|
||||
|
||||
> 研究性质:只读核验;未修改 `server.js`、`lib/`、`public/`。
|
||||
>
|
||||
> 核验时间:2026-08-24(Asia/Shanghai)。
|
||||
>
|
||||
> 版本口径:Gitea 文档采用 versioned 1.24 文档;Gitea 源码采用 GitHub
|
||||
> `go-gitea/gitea` 提交 `4852091e851060d2233fcbf789727b1c5dba4bfc`
|
||||
> (2026-08-23);`gitea-mcp` 当前 `main` 为
|
||||
> `815a0e26aab26c2df0963f7fca172a6f5b4a2e41`(2026-08-23),最新正式发行版
|
||||
> 为 `v1.6.0`,其提交为 `290d06b40b9fd225e53ec9fa4be1431dc787d0ff`
|
||||
> (2026-07-30)。当前 `main` 已切换到官方 MCP Go SDK,和 v1.6.0 的依赖/实现
|
||||
> 不完全相同;部署时应固定一个已验证的二进制或提交,不要隐式跟随 `main`。
|
||||
|
||||
## 1. 官方来源
|
||||
|
||||
### Gitea
|
||||
|
||||
- Webhook 文档(1.24):
|
||||
<https://gitea.com/gitea/docs/raw/branch/main/versioned_docs/version-1.24/usage/webhooks.md>
|
||||
(渲染页:<https://docs.gitea.com/1.24/usage/webhooks/>)。
|
||||
- API 使用说明(1.24):
|
||||
<https://gitea.com/gitea/docs/raw/branch/main/versioned_docs/version-1.24/development/api-usage.md>。
|
||||
- API 1.24.7 “Add a comment to an issue”:
|
||||
<https://docs.gitea.com/api/1.24/operations/issue-create-comment/>。
|
||||
- Webhook 发送逻辑:
|
||||
<https://github.com/go-gitea/gitea/blob/4852091e851060d2233fcbf789727b1c5dba4bfc/services/webhook/deliver.go>
|
||||
- Webhook payload 定义:
|
||||
<https://github.com/go-gitea/gitea/blob/4852091e851060d2233fcbf789727b1c5dba4bfc/modules/structs/hook.go>
|
||||
- Issue/PR/Comment 结构:
|
||||
<https://github.com/go-gitea/gitea/blob/4852091e851060d2233fcbf789727b1c5dba4bfc/modules/structs/issue.go>、
|
||||
<https://github.com/go-gitea/gitea/blob/4852091e851060d2233fcbf789727b1c5dba4bfc/modules/structs/pull.go>、
|
||||
<https://github.com/go-gitea/gitea/blob/4852091e851060d2233fcbf789727b1c5dba4bfc/modules/structs/issue_comment.go>。
|
||||
- 官方集成测试(普通 Issue/PR 评论):
|
||||
<https://github.com/go-gitea/gitea/blob/4852091e851060d2233fcbf789727b1c5dba4bfc/tests/integration/repo_webhook_test.go>
|
||||
(`Test_WebhookIssueComment`、`Test_WebhookPullRequestComment`)。
|
||||
|
||||
### gitea-mcp
|
||||
|
||||
- 仓库:<https://gitea.com/gitea/gitea-mcp>。
|
||||
- 当前 README(启动、工具表、HTTP 说明):
|
||||
<https://gitea.com/gitea/gitea-mcp/src/branch/main/README.md>。
|
||||
- 当前启动参数与环境变量源码:
|
||||
<https://gitea.com/gitea/gitea-mcp/src/branch/main/cmd/cmd.go>。
|
||||
- 当前 MCP/HTTP 运行与限制:
|
||||
<https://gitea.com/gitea/gitea-mcp/src/branch/main/operation/operation.go>。
|
||||
- 当前 Gitea API 客户端、超时、重定向和认证:
|
||||
<https://gitea.com/gitea/gitea-mcp/src/branch/main/pkg/gitea/gitea.go>、
|
||||
<https://gitea.com/gitea/gitea-mcp/src/branch/main/pkg/gitea/rest.go>。
|
||||
- 最新发行版:<https://gitea.com/gitea/gitea-mcp/releases/tag/v1.6.0>。
|
||||
|
||||
## 2. Gitea Webhook 协议
|
||||
|
||||
### 2.1 请求、事件头和签名
|
||||
|
||||
Gitea Webhook 通常向目标 URL 发送 JSON `POST`。官方文档的示例明确要求
|
||||
`POST`、`Content-Type: application/json`,并给出以下头:
|
||||
|
||||
```text
|
||||
X-Gitea-Delivery: <UUID>
|
||||
X-Gitea-Event: <event>
|
||||
X-Gitea-Signature: <hex SHA-256 HMAC>
|
||||
```
|
||||
|
||||
当前 Gitea 源码 `services/webhook/deliver.go` 还发送:
|
||||
|
||||
```text
|
||||
X-Gitea-Event-Type: <internal event type>
|
||||
X-Gitea-Hook-Installation-Target-Type: <system|repository|organization|user|default>
|
||||
X-Gogs-* # 兼容 Gogs
|
||||
X-Hub-Signature: sha1=<hex>
|
||||
X-Hub-Signature-256: sha256=<hex>
|
||||
X-GitHub-* # 兼容 GitHub
|
||||
```
|
||||
|
||||
事件名有两个层次,不能混淆:
|
||||
|
||||
- Issue 普通评论:`X-Gitea-Event: issue_comment`,内部类型也是
|
||||
`issue_comment`。
|
||||
- PR 普通评论:`X-Gitea-Event: issue_comment`,但当前源码的
|
||||
`X-Gitea-Event-Type` 为 `pull_request_comment`。
|
||||
|
||||
因此接收端应优先使用 `X-Gitea-Event` 判断大类,再结合 payload 的
|
||||
`is_pull`/`pull_request` 或 `X-Gitea-Event-Type` 区分 Issue 与 PR;不要只依赖
|
||||
某个新版本才出现的扩展头。
|
||||
|
||||
配置 Secret 后,当前发送端对**原始请求 body 字节**计算:
|
||||
|
||||
```text
|
||||
hex(HMAC-SHA256(secret, body))
|
||||
```
|
||||
|
||||
`X-Gitea-Signature` 是不带 `sha256=` 前缀的小写十六进制串;源码同时生成
|
||||
GitHub 兼容的 `sha256=<hex>` 形式。接收端必须在 JSON 解析或重新格式化之前读取
|
||||
原始 body,并使用常量时间比较(如 Node `timingSafeEqual`)。文档中的 PHP 示例
|
||||
先 `trim` 后计算,只是示例代码;对本项目应以实际收到的 body 字节为准,不能依赖
|
||||
JSON 等价性或 `trim` 后再验签。
|
||||
|
||||
Webhook payload 中历史 `secret` 字段自 Gitea 1.13 起已弃用,文档标注将移除;
|
||||
不能把 payload 的 `secret` 当作认证依据。应使用独立配置的 HMAC Secret。
|
||||
|
||||
**实现约束:** Webhook 路由应限制 body 大小和 `application/json`,先验签、再解析,
|
||||
验签失败返回 4xx;合法请求在持久化 delivery 与任务后尽快返回 2xx,不能把整个
|
||||
Codex turn 放在 HTTP 请求内同步执行。
|
||||
|
||||
### 2.2 Delivery UUID 与去重
|
||||
|
||||
`X-Gitea-Delivery` 是 Gitea 为每次发送生成的 UUID。官方文档介绍了 Webhook
|
||||
设置页的 Recent Deliveries,但没有规定接收方的去重窗口、重试次数、重试退避、
|
||||
投递顺序或“同一 delivery 是否只会出现一次”。Gitea 源码的 `HookTask.UUID` 在
|
||||
发送端数据库中唯一,但那是 Gitea 自己的出站任务记录,不是对外接收方的幂等协议。
|
||||
|
||||
**本项目必须自行实现:**
|
||||
|
||||
1. 在验签之后、入队之前,以 `(Gitea 实例标识, X-Gitea-Delivery)` 建立持久唯一键。
|
||||
2. 采用原子“插入/已存在”语义;重复请求直接返回成功,不重复 clone、启动 turn 或
|
||||
回帖。
|
||||
3. delivery 记录应包含原始 headers/body 摘要、接收时间、解析结果和最终任务状态,
|
||||
便于审计与恢复。
|
||||
4. 由于官方未承诺顺序,队列按仓库串行化;不要按 delivery UUID 推断先后。
|
||||
|
||||
### 2.3 Issue/PR 普通评论 payload
|
||||
|
||||
当前 Gitea `modules/structs/hook.go` 定义的 `IssueCommentPayload` 为:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "created|edited|deleted",
|
||||
"issue": { "number": 123, "title": "...", "body": "...", "user": {}, "repository": {} },
|
||||
"pull_request": { "number": 123, "title": "...", "user": {}, "base": {}, "head": {} },
|
||||
"comment": {
|
||||
"id": 456,
|
||||
"html_url": "...",
|
||||
"pull_request_url": "...",
|
||||
"issue_url": "...",
|
||||
"user": {},
|
||||
"body": "评论正文",
|
||||
"assets": [],
|
||||
"created_at": "...",
|
||||
"updated_at": "..."
|
||||
},
|
||||
"changes": { "body": { "from": "旧正文" } },
|
||||
"repository": { "id": 1, "name": "repo", "full_name": "owner/repo", "clone_url": "..." },
|
||||
"sender": { "id": 2, "login": "user" },
|
||||
"is_pull": true
|
||||
}
|
||||
```
|
||||
|
||||
字段语义:
|
||||
|
||||
- `action=created` 是新增普通评论;`edited`/`deleted` 是评论编辑/删除。
|
||||
- Issue 评论:`is_pull=false`,通常没有 `pull_request` 对象。
|
||||
- PR 普通评论:`is_pull=true`,当前官方测试确认 `pull_request_comment` 事件的
|
||||
payload 类型仍是 `IssueCommentPayload`,并带 `pull_request` 对象;评论入口仍是
|
||||
PR 所对应的 Issue number。
|
||||
- `comment.body` 是 Markdown 文本;`comment.id` 是回查/审计评论的稳定标识。
|
||||
- `changes.body.from` 只对编辑动作有意义。
|
||||
- `repository.full_name` 是最适合用作工作流仓库键的字段,但仍应做格式校验并拒绝
|
||||
路径穿越/不合法 owner 或 repo。
|
||||
|
||||
**本项目触发过滤建议:** MVP 只消费 `action=created`,并在 `comment.body` 中按
|
||||
明确规则识别 `@ccweb-bot`;编辑/删除不应重新触发任务。还必须在入队前过滤
|
||||
`sender` 为本项目 Bot 的自评论,否则“最终回帖 → Webhook → 新任务”会形成回路。
|
||||
Bot 判断应使用稳定用户 ID/登录名配置,而不是只匹配正文。
|
||||
|
||||
## 3. Gitea REST/API 回执协议
|
||||
|
||||
### 3.1 普通评论写入
|
||||
|
||||
官方 API 1.24 文档的新增评论接口为:
|
||||
|
||||
```text
|
||||
POST /api/v1/repos/{owner}/{repo}/issues/{index}/comments
|
||||
Authorization: token <personal-access-token>
|
||||
Content-Type: application/json
|
||||
|
||||
{"body":"评论正文"}
|
||||
```
|
||||
|
||||
返回的是 Comment 对象。PR 普通评论同样使用该 Issue 评论接口(`index` 为 PR
|
||||
编号);PR review/diff inline comment 是另一套 review API,不能用来代替普通状态
|
||||
回帖。`gitea-mcp` 的 `issue_write` 中 `method=add_comment` 正是这个普通评论路径,
|
||||
而 `pull_request_review_write` 的 `reply_comment` 针对 review comment 线程。
|
||||
|
||||
Gitea API 文档说明支持 `Authorization: token ...`,OAuth token 也接受
|
||||
`Authorization: bearer ...`;URL query token 虽受支持,但本项目禁止把 token 放在
|
||||
URL、日志或评论中。Bot Token 与 Webhook HMAC Secret 必须分离。
|
||||
|
||||
### 3.2 分页、响应上限和错误
|
||||
|
||||
- Gitea 默认 `[api].MAX_RESPONSE_ITEMS=50`;可由实例配置改变。
|
||||
- API 使用 `page` 与 `limit` 分页,并在有更多页时返回 `Link`;还可返回
|
||||
`x-total-count`。
|
||||
- `gitea-mcp` 对外参数多使用 `page`/`per_page`,SDK/API 请求会被 Gitea 的
|
||||
`MAX_RESPONSE_ITEMS` 静默截断;不能把 `per_page` 当作无上限。
|
||||
- Gitea 文档没有给出适用于所有实例的固定速率限制数字。反向代理、云服务或实例
|
||||
配置可能另行限流;实现应对 429/5xx 做有上限的退避,并记录 `Retry-After`,不要
|
||||
假设一定存在该头。
|
||||
- `gitea-mcp` 源码的 REST helper 使用 60 秒 HTTP client timeout;没有看到自动
|
||||
重试逻辑。工具调用失败通常编码为 MCP `tools/call` 结果的 `isError=true`,而
|
||||
malformed request/server fault 才是 JSON-RPC error。Codex App 监听时必须同时处理
|
||||
这两类失败。
|
||||
|
||||
**实现影响:** 正常回执优先由 MCP `issue_write/add_comment` 发送;cc-web 必须通过
|
||||
API 查询或 Webhook 回流确认 comment ID/正文确实出现。若 turn 完成但没有真实回帖,
|
||||
只允许同一会话发起一次“只补发回执、不重复修改”的 turn;仍失败时才用上面的 REST
|
||||
endpoint 兜底,并把 HTTP 状态、响应摘要和是否产生 comment ID 写入审计。
|
||||
|
||||
## 4. 官方 gitea-mcp(stdio)
|
||||
|
||||
### 4.1 启动与配置
|
||||
|
||||
官方 README 给出的 stdio 形状是:
|
||||
|
||||
```bash
|
||||
gitea-mcp -t stdio -H https://gitea.example.com
|
||||
```
|
||||
|
||||
通过环境传 Token(推荐,避免出现在进程列表):
|
||||
|
||||
```text
|
||||
GITEA_HOST=https://gitea.example.com
|
||||
GITEA_ACCESS_TOKEN=<token>
|
||||
```
|
||||
|
||||
当前 `cmd/cmd.go` 支持的关键参数/环境变量:
|
||||
|
||||
| 用途 | 参数 | 环境变量/默认值 |
|
||||
|---|---|---|
|
||||
| 传输 | `-t`, `--transport` | `MCP_MODE`;默认 `stdio` |
|
||||
| Gitea 地址 | `-H`, `--host` | `GITEA_HOST`;空时 `https://gitea.com` |
|
||||
| Token | `-T`, `--token` | `GITEA_ACCESS_TOKEN`;也支持 `GITEA_ACCESS_TOKEN_FILE` |
|
||||
| 只读 | `-r`, `--read-only` | `GITEA_READONLY=true` |
|
||||
| 工具白名单 | `-O`, `--tools` | `GITEA_TOOLS` |
|
||||
| Scope 白名单 | `-S`, `--scope` | `GITEA_SCOPES` |
|
||||
| 调试/TLS | `-d`, `-k` | `GITEA_DEBUG=true`、`GITEA_INSECURE=true` |
|
||||
| HTTP 备用传输 | `-b`, `-p` | 默认 bind 全接口、port `8080` |
|
||||
| 附件内联上限 | `--max-inline-attachment-bytes` | `GITEA_MAX_INLINE_ATTACHMENT_BYTES`;默认 5 MiB |
|
||||
|
||||
参数优先级以源码为准:显式 token 参数优先于环境;没有显式 token 时读取
|
||||
`GITEA_ACCESS_TOKEN`,再尝试 `GITEA_ACCESS_TOKEN_FILE`。项目应使用固定版本的本地
|
||||
二进制作为 stdio command,并将 `GITEA_HOST`/`GITEA_ACCESS_TOKEN` 放在线程级 MCP
|
||||
进程环境,不要放到全局长驻 app-server 环境或命令行 `-T`。当前 `main` 的 `go.mod`
|
||||
要求 Go 1.26.0/toolchain 1.26.6;因此不建议在本项目运行时用未锁定的
|
||||
`go run ...@latest`(本机旧 Go 或上游变更都会导致不可复现)。
|
||||
|
||||
stdio 模式日志写入 `$HOME/.gitea-mcp/gitea-mcp.log`(目录 0700、滚动保留);源码
|
||||
只在 HTTP 模式把日志复制到 stdout。不要把任何调试输出写入 stdio 的协议 stdout,
|
||||
否则会污染 MCP JSON-RPC 流。
|
||||
|
||||
建议的线程级配置形状(概念示例):
|
||||
|
||||
```json
|
||||
{
|
||||
"command": "/opt/cc-web/bin/gitea-mcp-v1.6.0",
|
||||
"args": ["-t", "stdio", "-H", "https://gitea.example.com"],
|
||||
"env": { "GITEA_ACCESS_TOKEN": "<线程专用 Bot Token>" }
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 工具和写操作
|
||||
|
||||
当前 README 工具表共 54 个工具;未指定 `-r`、`-S`、`-O` 时全部暴露。`-r` 会隐藏
|
||||
所有 Write 工具;`-S` 与 `-O` 是并集筛选。动作型工具通过 `method` 参数再选择
|
||||
具体操作。
|
||||
|
||||
主要只读工具包括:用户/组织(`get_me`、`get_user_orgs`)、搜索(`search_*`)、
|
||||
通知读取、标签/里程碑/wiki/计时/包读取、Issue 列表/读取/附件读取、PR 列表/读取、
|
||||
Actions 配置/运行读取、仓库列表/tree、文件/目录读取、分支/标签/提交/Release 读取、
|
||||
版本查询。
|
||||
|
||||
Write 工具及其源码声明的实际操作如下:
|
||||
|
||||
| 工具 | 写操作 |
|
||||
|---|---|
|
||||
| `notification_write` | `mark_read`、`mark_all_read` |
|
||||
| `label_write` | repo/org label 的 create、edit、delete |
|
||||
| `milestone_write` | create、update/edit、delete |
|
||||
| `wiki_write` | create、update、delete 页面 |
|
||||
| `timetracking_write` | start/stop/delete stopwatch、add/delete time |
|
||||
| `package_write` | 删除 package version(不可逆) |
|
||||
| `issue_write` | create、update、`add_comment`、`edit_comment`、add/remove/replace/clear labels |
|
||||
| `pull_request_write` | create、update、close、reopen、merge、update_branch、add/remove reviewers |
|
||||
| `pull_request_review_write` | create/submit/delete/dismiss review、`reply_comment`、resolve/unresolve thread |
|
||||
| `actions_config_write` | Actions secret/variable 的 upsert/create/update/delete(repo/org) |
|
||||
| `actions_run_write` | dispatch、cancel、rerun workflow run |
|
||||
| `create_repo`/`fork_repo` | 创建/派生仓库 |
|
||||
| `create_or_update_file`/`delete_file` | 创建、更新、删除仓库文件 |
|
||||
| `create_branch`/`delete_branch` | 创建、删除分支 |
|
||||
| `create_tag`/`delete_tag` | 创建、删除 tag |
|
||||
| `create_release`/`delete_release` | 创建、删除 Release |
|
||||
|
||||
这远超“回帖”所需权限。当前产品决策明确要求 MVP 开放官方 gitea-mcp 全部写权限,
|
||||
因此必须同时具备全局暂停、仓库停用、排队取消、turn 中止和完整审计;不能把“工具
|
||||
调用成功”当作安全授权。生产环境仍应为 Bot Token 配置最小可用仓库/组织权限,并在
|
||||
管理面明确显示高风险工具可用。
|
||||
|
||||
### 4.3 HTTP 备用模式的限制(不是本 MVP 主路径)
|
||||
|
||||
当前 `main` 的 HTTP transport 使用 `/mcp`,只接受 POST,启用 Origin 校验,且始终
|
||||
无状态:不使用 `Mcp-Session-Id`、独立 SSE 或 `Last-Event-ID` 恢复。反向代理必须原样
|
||||
转发 `Mcp-Protocol-Version`、`Mcp-Method`、`Mcp-Name`。HTTP 请求可用
|
||||
`Authorization: Bearer <token>` 或 `Authorization: token <token>`,这是逐请求的
|
||||
Gitea credential passthrough,不是 MCP OAuth。HTTP MCP 默认请求体上限由源码提高到
|
||||
32 MiB。若未来不用 stdio 而改 HTTP,必须额外处理这些协议与反代约束。
|
||||
|
||||
## 5. 对 cc-web 工作流的落地影响
|
||||
|
||||
1. **Webhook 接收顺序固定为:** 限制请求 → 读取原始 body → HMAC 验签 → 校验
|
||||
`X-Gitea-Delivery`/事件头 → JSON 解析 → Bot 自评论过滤 → `action=created` 与
|
||||
`@ccweb-bot` 过滤 → 持久化唯一 delivery → 快速 2xx → 入队。
|
||||
2. **会话键:** 使用 `gitea + repository.full_name + issue/PR 类型 + number`;PR
|
||||
普通评论仍使用 `issue_write/add_comment`,不是 review reply。
|
||||
3. **回执确认:** MCP 最终输出不等于 Gitea 已写入;必须查询评论列表/回流 Webhook
|
||||
确认正文和 comment ID。缺失时最多一次隐藏补发 turn,最后 REST 兜底。
|
||||
4. **凭据隔离:** HMAC Secret 只用于入站验签,Bot Token 只用于 MCP/REST/Git;Token
|
||||
通过线程级 env 注入,禁止写入 remote URL、任务正文、日志和 URL query。
|
||||
5. **并发和恢复:** delivery 去重、任务状态、仓库锁和回执审计必须持久化;同仓库
|
||||
串行、不同仓库按全局上限并行。进程重启后 queued 恢复,running 按既定最多一次重试,
|
||||
waiting_user 保留。
|
||||
6. **API 稳定性:** 对 429、5xx、网络超时做有限退避;尊重 Gitea 的分页上限和
|
||||
`MAX_RESPONSE_ITEMS`;MCP 的 `isError=true` 与 JSON-RPC error 均视为失败路径。
|
||||
|
||||
## 6. 不确定项与需集成验收的内容
|
||||
|
||||
- 官方文档没有承诺 Webhook 的重试次数、退避策略、投递顺序或接收端去重语义;需在
|
||||
实际 Gitea 版本通过失败投递/Recent Deliveries 做黑盒验收。
|
||||
- `X-Gitea-Event-Type`、部分 payload 字段和旧版本兼容头可能随 Gitea 版本变化;接收
|
||||
端必须允许缺失扩展头,并以稳定字段/大类事件降级。
|
||||
- API 速率限制取决于 Gitea 版本、实例配置、云服务或反向代理;当前官方资料未提供
|
||||
可直接套用的全局数值。
|
||||
- Token 的精确 scope/仓库权限由目标 Gitea 实例决定;需要用目标 Bot Token 实测
|
||||
`issue_write/add_comment`、读取评论、读取 PR/文件和必要的仓库操作。
|
||||
- 当前 `gitea-mcp` `main` 要求 Go 1.26,最新发行版与 `main` 的 MCP SDK/HTTP 实现
|
||||
不同;必须在 CI/部署机锁定并做 stdio `initialize`、`tools/list`、真实
|
||||
`issue_write` 和错误返回回归。
|
||||
- Gitea Webhook 文档示例 PHP 使用 `trim` 后验签,与当前发送源码“对原始 body 签名”
|
||||
的实现细节存在示例层面的歧义;本项目应以原始字节验签,并用目标实例实际 delivery
|
||||
样本校验。
|
||||
|
||||
71
.planning/2026-08-23-gitea-workflow/task_plan.md
Normal file
71
.planning/2026-08-23-gitea-workflow/task_plan.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Gitea Workflow 持久化实施计划
|
||||
|
||||
## 目标
|
||||
|
||||
在 cc-web 内完整落地 Gitea Webhook → 持久 Codex App 会话 → 官方 gitea-mcp → Gitea 回执的工作流,并提供可恢复的队列、工作区和管理界面。
|
||||
|
||||
## 当前阶段
|
||||
|
||||
Phase 5:集成验收与交付
|
||||
|
||||
## 阶段
|
||||
|
||||
### Phase 1:需求与协议确认
|
||||
|
||||
- [x] 汇总用户已确认的产品边界
|
||||
- [x] 核验官方 Gitea Webhook 与 gitea-mcp 协议
|
||||
- [x] 完成 PRD、状态机、数据模型和安全设计
|
||||
- 状态:已完成
|
||||
|
||||
### Phase 2:核心工作流实现
|
||||
|
||||
- [x] 实现配置、状态、队列、去重和恢复
|
||||
- [x] 实现 Webhook 验签、事件过滤和自动接入
|
||||
- [x] 实现 clone/fetch、仓库锁和工作区状态
|
||||
- 状态:已完成
|
||||
|
||||
### Phase 3:Codex App 与回执集成
|
||||
|
||||
- [x] 注入线程级 gitea-mcp 配置
|
||||
- [x] 实现 turn 状态监听、回执确认、补触发和 REST 兜底
|
||||
- [x] 实现 waiting_user 与连续评论投递
|
||||
- 状态:已完成
|
||||
|
||||
### Phase 4:管理界面与测试
|
||||
|
||||
- [x] 实现工作流管理 API 和前端页面
|
||||
- [x] 补充单元、回归、协议 mock 和安全测试
|
||||
- [x] 完成重启恢复、并发和自触发回归
|
||||
- 状态:已完成
|
||||
|
||||
### Phase 5:集成验收与交付
|
||||
|
||||
- [x] 整合子任务改动并解决冲突
|
||||
- [x] 运行测试、构建和真实协议形状检查
|
||||
- [x] 完成文档、审计和最终交付
|
||||
- 状态:已完成
|
||||
|
||||
## 已确认决策
|
||||
|
||||
| 决策 | 结论 |
|
||||
|---|---|
|
||||
| 触发 | Issue/PR 普通评论中的 `@ccweb-bot` |
|
||||
| 实例 | MVP 单个 Gitea 实例,一个全局 Bot |
|
||||
| 未登记仓库 | 首次合法触发自动接入并 clone |
|
||||
| 工作区 | 全局根目录下每仓库持久 checkout |
|
||||
| Git 认证 | HTTPS + Bot Token,临时凭据,不写 remote URL |
|
||||
| 并发 | 同仓库串行,不同仓库并行,全局上限默认 2 |
|
||||
| Agent | 固定 `codexapp` + `yolo` |
|
||||
| 会话键 | `gitea + owner/repo + type + number` |
|
||||
| 交互 | Gitea 主交互,cc-web 镜像和运维 |
|
||||
| 回复 | 状态评论 + MCP 最终回复;缺失时补触发一次,最后 REST 兜底 |
|
||||
| 安全 | HMAC Webhook Secret;Bot Token 与 Secret 分离 |
|
||||
| MCP 权限 | 用户明确要求开放官方 gitea-mcp 全部写权限 |
|
||||
| 恢复 | queued 恢复,waiting_user 保留,running 中断并最多重试一次 |
|
||||
| 运维 | 全局暂停、仓库停用、取消排队、中止 turn、审计日志 |
|
||||
|
||||
## 错误记录
|
||||
|
||||
| 错误 | 尝试 | 处理 |
|
||||
|---|---:|---|
|
||||
| 暂无 | 1 | - |
|
||||
15
.planning/2026-08-24-gitea-codex-turn/findings.md
Normal file
15
.planning/2026-08-24-gitea-codex-turn/findings.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Gitea Codex 工作流实现发现
|
||||
|
||||
- `server.js` 的 `codexAppThreadConfig(session, options)` 最终将每项配置写为 `mcp_servers.<server>`,所以独立适配器应返回与 app-server config 兼容的 `{type:'stdio', command, args, env}`。
|
||||
- `startCodexAppTurn()` 在 `thread/start`/`thread/resume` 后记录 `entry.threadId`,在 `turn/start` 返回后记录 `entry.turnId`;`handleCodexAppTurnComplete()` 是完成监听点。
|
||||
- 运行时事件可能从 `params.turnId`、`params.turn.id`、`params.item.turnId` 取 turnId;关联标记必须同时支持明文状态字段和 HTML 注释隐藏标识。
|
||||
- 官方 gitea-mcp 采用 stdio,启动参数为 `-t stdio -H <host>`,token 通过线程级 env 注入;不应放到长驻 app-server 全局环境。
|
||||
- gitea-mcp 最终评论需要查询确认;查询需匹配资源、Bot 作者和 taskId/turnId/resourceKey 隐藏标识,未知状态不能直接重复执行。
|
||||
- waiting_user 新评论应复用原 `sessionKey/threadId`,但每条评论创建新 task/turn;补触发必须是同一 thread 的隐藏轮次,且最多一次。
|
||||
|
||||
## 实现接口
|
||||
|
||||
- `buildGiteaThreadConfig({cwd, host, accessToken})` 返回 `config['mcp_servers.gitea']`,内部为 `type=stdio`、`gitea-mcp -t stdio -H host` 和线程级 `GITEA_HOST/GITEA_ACCESS_TOKEN`。
|
||||
- `buildWorkflowMarker`/`parseWorkflowMarker` 使用 `<!-- ccweb-gitea v="1" ... -->`,缺少 taskId、turnId 或 resourceKey 时返回 null。
|
||||
- `createGiteaWorkflowCodex` 通过 `verifyReply/listComments/startTurn/sendRestReply` 依赖注入实现外部副作用;`unknown` 查询直接进入保守 `failed_reply`,不会自动补触发。
|
||||
- `server.js` 的 `codexAppThreadConfig` 同时兼容 `{host, accessToken}` 和已构建的 stdio config;`startCodexAppTurn`、`handleCodexAppTurnComplete` 提供 turn 生命周期薄挂接,既有 waiter 在完成时执行回执确认/补偿。
|
||||
22
.planning/2026-08-24-gitea-codex-turn/progress.md
Normal file
22
.planning/2026-08-24-gitea-codex-turn/progress.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Gitea Codex 工作流实施进度
|
||||
|
||||
## 日志
|
||||
|
||||
- 2026-08-24:读取现有 Gitea Workflow 研究、PRD/ARCHITECTURE 和 Codex App 代码入口;确认本次只改独立模块、最小 server.js 挂接点、协议 mock 与单测。
|
||||
- 2026-08-24:完成代码入口定位;计划审查要求补齐隐藏标识、Bot 身份、回执三态、同线程单次补触发和 REST 终止条件,已纳入验收约束。
|
||||
- 2026-08-24:新增 `lib/gitea-workflow-codex.js`,实现线程级 stdio gitea-mcp 配置、版本化隐藏标记、turnId/threadId 提取、Bot 自评论过滤、waiting_user 连续评论、回执 confirmed/missing/unknown 和单次 reply_retry/REST 兜底。
|
||||
- 2026-08-24:`server.js` 增加独立模块 require、gitea MCP config 合并、turn started/completed 薄挂接,并让既有 Gitea waiter 在 turn 完成时 settle;查询未知状态不再自动重跑。
|
||||
- 2026-08-24:补充 server waiter 对预分配 workflow marker turnId 与实际 Codex turnId 的兼容匹配;首轮旧 marker 可确认,reply_retry 使用实际新 turnId。
|
||||
- 2026-08-24:新增 `scripts/gitea-workflow-codex-unit.js` 协议 mock/单测,覆盖配置形状、隐藏标记、Bot id/login、waiting_user 事件与同线程连续评论、单次补触发、REST 次数和 unknown 保守终态。
|
||||
|
||||
## 验证
|
||||
|
||||
| 命令 | 结果 |
|
||||
|---|---|
|
||||
| `node --check`(Codex 适配器、server.js、相关 Gitea 模块) | 通过 |
|
||||
| `node scripts/gitea-workflow-codex-unit.js` | 通过 |
|
||||
| `node scripts/gitea-webhook-regression.js` | 通过,9 场景 |
|
||||
| `node scripts/gitea-webhook-workspace-unit.js` | 通过 |
|
||||
| `node scripts/gitea-workflow-management-unit.js` | 通过 |
|
||||
| `node scripts/gitea-workflow-core-unit.js` | 通过,6 项 |
|
||||
| `timeout 60s npm run regression` | 未通过:既有 MCP 创建会话回归在等待 `background_done` 处超时(scripts/regression.js:6717),未出现本适配器异常;需后续单独修复该既有回归 |
|
||||
39
.planning/2026-08-24-gitea-codex-turn/task_plan.md
Normal file
39
.planning/2026-08-24-gitea-codex-turn/task_plan.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Gitea Codex 工作流会话与回执实施计划
|
||||
|
||||
## 目标
|
||||
|
||||
新增独立的 Codex App/Gitea 工作流适配模块,覆盖线程级官方 gitea-mcp 注入、turnId 关联、waiting_user 连续评论、回执确认、一次补触发和 REST 兜底;以最小挂接点接入 server.js,并用协议 mock 与单测验收。
|
||||
|
||||
## 步骤
|
||||
|
||||
- [x] 定位现有 Codex App 线程、turn、MCP 配置和通知路由挂接点
|
||||
- [x] 冻结独立模块 API 与隐藏 turnId/Bot 自评论标记协议
|
||||
- [x] 实现线程级 gitea-mcp 配置、waiting_user 评论串联和回执状态机
|
||||
- [x] 增加 server.js 最小挂接示例与可复制的接入说明
|
||||
- [x] 编写协议 mock 和模块单元测试,覆盖补触发/REST 兜底
|
||||
- [x] 运行语法检查、单测和现有回归并记录结果
|
||||
|
||||
## 完成记录
|
||||
|
||||
- 线程级 `mcp_servers.gitea`、隐藏标识、Bot 过滤、waiting_user 连续评论、一次补发和
|
||||
REST 兜底已接入 `server.js`。
|
||||
- 回帖校验允许历史补发标识集合;查询 `unknown` 时保守失败,不自动重复写操作。
|
||||
- `node scripts/gitea-workflow-codex-unit.js`、Gitea 专项回归和全量 `npm run regression`
|
||||
均通过。
|
||||
|
||||
## 验收约束
|
||||
|
||||
- 隐藏标识采用版本化 HTML 注释,编码后的 payload 必须包含 `taskId`、`turnId` 和 `resourceKey`,解析失败时不得误判为本轮回执。
|
||||
- Bot 自评论优先按稳定 user id 判定;未配置 id 时才回退到大小写无关 login;带有效 ccweb 隐藏标识的评论始终不触发新任务。
|
||||
- 回执确认严格区分 `confirmed`、`missing` 和 `unknown`;查询异常为 `unknown`,禁止据此重复执行代码修改。
|
||||
- 正常回执未确认时最多创建一个 `reply_retry`,且必须复用原 `threadId`;补触发提示词只允许补发回执,不允许修改代码。
|
||||
- 补触发后仍非 `confirmed` 才允许 REST 兜底;REST 成功或最终失败后不得再次补触发。
|
||||
- gitea-mcp 只能出现在 `thread/start.config.mcp_servers.gitea`,host/token 经该线程的 args/env 注入,禁止写入 app-server 进程全局环境。
|
||||
- `server.js` 仅增加 require、配置合并、turn started/completed/user-input 信号等薄挂接;工作流逻辑必须留在独立模块。
|
||||
- 单测必须断言线程配置形状、标记往返/Bot 过滤、waiting_user 同线程投递、查询异常、单次补触发和 REST 兜底次数。
|
||||
|
||||
## 错误记录
|
||||
|
||||
| 错误 | 尝试 | 处理 |
|
||||
|---|---:|---|
|
||||
| planning-with-files catchup 脚本路径不存在 | 1 | 记录后改为直接读取现有规划文件并继续 |
|
||||
13
.planning/2026-08-24-gitea-mcp-failure/findings.md
Normal file
13
.planning/2026-08-24-gitea-mcp-failure/findings.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Gitea MCP 失败链路发现
|
||||
|
||||
- 当前宿主机 `command -v gitea-mcp` 无输出;server.js 已有命令存在性检查,
|
||||
缺失时抛出稳定错误码 `gitea_mcp_not_found`。
|
||||
- runner 在命令检查后创建/复用 Codex App 会话,再通过
|
||||
`thread/start.config.mcp_servers.gitea` 注入 stdio MCP;当前没有独立的 MCP
|
||||
initialize 预检。
|
||||
- Codex App `thread/start` 请求超时为 60 秒;如果 MCP 子进程存在但启动后立即
|
||||
退出,任务可能等待较久或只在 app-server 事件出现后才失败。
|
||||
- 队列异常终态会发送一次 `giteabot: 任务失败:...`;正常流程只发送起始的
|
||||
`giteabot: 已收到`,最终正文由 MCP/REST 兜底负责。
|
||||
- 当前工作区存在大量其他代理未提交改动,本轮只改动与 MCP 失败链路直接相关的
|
||||
文件和独立测试/文档,禁止 reset 或覆盖无关变更。
|
||||
9
.planning/2026-08-24-gitea-mcp-failure/progress.md
Normal file
9
.planning/2026-08-24-gitea-mcp-failure/progress.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Gitea MCP 失败链路进度
|
||||
|
||||
## 记录
|
||||
|
||||
- 2026-08-24:建立本轮持久化计划;确认服务已由 PM2 `ccweb` 在线运行,未执行重启。
|
||||
- 2026-08-24:确认缺失命令已有快速失败,但命令存在且 stdio 握手失败仍缺少预检。
|
||||
- 2026-08-24:计划审查指出必须单独验收失败回执只发送一次、三类故障分流、线程级注入保持不变和不自动安装;已补入计划。
|
||||
- 2026-08-24:新增 `lib/gitea-mcp-probe.js`,在启动 Codex App 前发送 MCP `initialize`,对启动失败、握手错误和超时返回稳定错误码。
|
||||
- 2026-08-24:新增 `fixtures/gitea-workflow/mock-gitea-mcp.js` 与 `scripts/gitea-mcp-probe-unit.js`,预检成功、错误响应、进程退出、超时和命令不存在场景通过。
|
||||
27
.planning/2026-08-24-gitea-mcp-failure/task_plan.md
Normal file
27
.planning/2026-08-24-gitea-mcp-failure/task_plan.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Gitea MCP 失败链路收尾计划
|
||||
|
||||
## 目标
|
||||
|
||||
让 Gitea Workflow 在官方 `gitea-mcp` 缺失、无法启动或 stdio 握手失败时,
|
||||
在有限时间内结束任务并发送一次可见失败回执;正常 MCP 链路保持线程级注入,
|
||||
不引入 Docker 依赖、不修改用户未授权的部署配置。
|
||||
|
||||
## 步骤
|
||||
|
||||
- [in_progress] 核对当前 Gitea runner、Codex App 启动和回执结算链路
|
||||
- [pending] 增加 gitea-mcp stdio 预检与有限超时错误分类
|
||||
- [pending] 增加针对缺失、启动失败、握手失败的独立回归断言,并验证每类只发一次 giteabot 失败回执
|
||||
- [pending] 同步中文文档和运行配置说明,明确线程级注入与不自动安装约束
|
||||
- [pending] 运行静态检查、单测、Webhook 回归并核对运行状态
|
||||
|
||||
## 错误记录
|
||||
|
||||
| 错误 | 尝试 | 处理 |
|
||||
|---|---:|---|
|
||||
| 全局技能路径不存在 | 1 | 改用项目内 `.codex/skills/planning-with-files` 与用户目录 `todo-list-csv` |
|
||||
|
||||
## 决策
|
||||
|
||||
- 不自动安装外部二进制;安装属于外部系统变更,需用户明确确认。
|
||||
- 预检只验证命令可执行和 MCP initialize 响应,不记录 Token 或完整 stderr。
|
||||
- Gitea Workflow 任务失败回执仍统一使用 `giteabot:` 前缀。
|
||||
21
.planning/2026-08-24-gitea-webhook-docs/findings.md
Normal file
21
.planning/2026-08-24-gitea-webhook-docs/findings.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# 发现记录
|
||||
|
||||
## 现状
|
||||
|
||||
- `docs/gitea-workflow/PRD.md` 与 `ARCHITECTURE.md` 已存在,已约定 MVP 为单
|
||||
Gitea 实例、`ccweb-bot`、同仓库串行、全局并发 2、线程级 `gitea-mcp`。
|
||||
- 当前仓库没有 Gitea 业务实现入口;本轮脚本必须测试协议/状态机纯函数,不应
|
||||
假设或改写 `server.js`。
|
||||
- `package.json` 只有 `npm run regression`;新增脚本应提供独立 `node` 入口,
|
||||
便于实现接入阶段先运行夹具回归。
|
||||
|
||||
## 交付设计
|
||||
|
||||
- `DEPLOYMENT.md`:配置分层、Webhook 反向代理、凭据、目录权限、启动顺序、
|
||||
健康检查、暂停/恢复、备份恢复、故障处置、日志脱敏和容量基线。
|
||||
- `TESTING.md`:测试金字塔、fixtures 协议、场景矩阵、故障注入、恢复/重试
|
||||
断言、MCP `thread/start.config.mcp_servers.gitea` 契约与安全测试。
|
||||
- `scripts/gitea-webhook-regression.js`:零依赖 Node 脚本,加载 fixtures,
|
||||
对原始 body 做 HMAC-SHA256 常量时间校验,验证事件过滤、delivery 去重、
|
||||
队列恢复/并发互斥、回执补发/REST 兜底、线程级 MCP 配置、路径边界以及敏感
|
||||
信息不泄漏。
|
||||
10
.planning/2026-08-24-gitea-webhook-docs/progress.md
Normal file
10
.planning/2026-08-24-gitea-webhook-docs/progress.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# 进度日志
|
||||
|
||||
- 2026-08-24:启用 `todo-list-csv`,建立六步计划和独立清单。
|
||||
- 2026-08-24:确认 PRD/ARCHITECTURE 已由其他工作流提供,本轮仅新增部署/测试
|
||||
文档、独立脚本与 fixtures,避免修改业务主逻辑和现有目标文档。
|
||||
- 2026-08-24:计划审查第一次发现交付范围和测试入口未明确;已修正并复审通过。
|
||||
- 2026-08-24:新增 `DEPLOYMENT.md`、`TESTING.md`、7 个 JSON fixture 文件和独立
|
||||
`scripts/gitea-webhook-regression.js`;脚本覆盖 9 个离线场景。
|
||||
- 2026-08-24:`node --check`、`timeout 60s node scripts/gitea-webhook-regression.js`
|
||||
和 `git diff --check` 全部通过。
|
||||
30
.planning/2026-08-24-gitea-webhook-docs/task_plan.md
Normal file
30
.planning/2026-08-24-gitea-webhook-docs/task_plan.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Gitea Webhook Agent 文档与回归计划
|
||||
|
||||
## 目标
|
||||
|
||||
补齐 Gitea Workflow 的部署运维和测试设计文档,并新增不依赖业务实现的
|
||||
Webhook 协议回归脚本与 fixtures,覆盖验签、事件过滤、队列恢复、回执重试
|
||||
兜底、MCP 线程配置和安全边界;不修改 `server.js` 主逻辑,也不覆盖其他代理
|
||||
正在进行的 Gitea 文档或实现改动。
|
||||
|
||||
## 步骤
|
||||
|
||||
1. 盘点并复核 `docs/gitea-workflow/PRD.md`、`ARCHITECTURE.md`,确认需求覆盖和
|
||||
与其他代理的修改边界
|
||||
2. 补齐/校验 PRD 与架构交付基线,并设计部署运维文档固定安全与恢复契约
|
||||
3. 设计测试文档并建立需求到回归场景覆盖矩阵
|
||||
4. 新增独立 webhook 回归脚本与 JSON fixtures,固定直接验收入口
|
||||
5. 运行定向回归、脚本语法检查和差异范围审查
|
||||
6. 完成文档一致性复核并清理临时清单
|
||||
|
||||
## 约束
|
||||
|
||||
- 现有 `PRD.md`、`ARCHITECTURE.md` 属于其他代理的工作区:先做只读一致性复核;
|
||||
只有发现本需求明确缺口且确认不覆盖其改动时才做最小追加,否则在新增文档中
|
||||
记录已覆盖项和缺口。
|
||||
- 本轮新增/修改目标为 `docs/gitea-workflow/DEPLOYMENT.md`、`TESTING.md`、
|
||||
`scripts/gitea-webhook-regression.js` 及 `fixtures/gitea-workflow/*.json`,
|
||||
并在新增文档中明确引用 PRD/架构路径。
|
||||
- 独立验收入口固定为:`timeout 60s node scripts/gitea-webhook-regression.js`;
|
||||
脚本输出每个场景的 PASS/FAIL 与最终汇总,不依赖服务启动或网络。
|
||||
- 不修改 `server.js` 主逻辑、`lib/`、`public/`、依赖和其他代理的目标文档。
|
||||
24
.planning/2026-08-24-gitea-workflow-management/task_plan.md
Normal file
24
.planning/2026-08-24-gitea-workflow-management/task_plan.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Gitea Workflow 管理 API 与页面实施计划
|
||||
|
||||
## 目标
|
||||
|
||||
在不覆盖 Gitea Workflow 核心领域模块的前提下,为 cc-web 增加可挂接的管理 API、
|
||||
前端管理页面和最小回归测试。页面展示仓库、队列、turn、日志与脏目录原因,并提供
|
||||
全局暂停、仓库启停、取消排队、中止 turn 及操作者/原因审计。
|
||||
|
||||
## 步骤
|
||||
|
||||
1. 读取现有前端路由、API 契约并确定挂接边界
|
||||
2. 实现独立 Gitea Workflow 管理 API 适配器与服务注入契约
|
||||
3. 接入 server.js 路由、鉴权与前端资源版本计算
|
||||
4. 实现 Workflow 管理页面脚本、样式并挂到现有入口
|
||||
5. 补充最小 API 与前端契约测试
|
||||
6. 运行语法检查、单测与回归并修复问题
|
||||
7. 汇总变更、清理临时清单并回报交付
|
||||
|
||||
## 边界
|
||||
|
||||
- 新增管理适配器与前端资源;核心队列/工作区/会话实现由注入的 service 提供。
|
||||
- server.js 只做路由挂接和鉴权,不复制领域状态机。
|
||||
- 兼容 service 不可用时的只读空状态,控制操作返回明确的 503。
|
||||
- 不安装依赖,不修改与本任务无关的现有未提交文件。
|
||||
10
.planning/gitea-workflow-core/findings.md
Normal file
10
.planning/gitea-workflow-core/findings.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Gitea Workflow 核心模块发现
|
||||
|
||||
- PRD 位于 `docs/gitea-workflow/PRD.md`,当前只确认了需求基线,`ARCHITECTURE.md` 尚不存在。
|
||||
- 现有 Node 项目使用 CommonJS,核心业务模块集中在 `lib/`,测试多为 `scripts/*-unit.js` 的直接执行脚本。
|
||||
- `server.js` 体量很大,本轮应新增独立文件并避免直接改写入口。
|
||||
- 当前工作区已有用户未提交改动和多个 Gitea 规划文件,需避免覆盖。
|
||||
- `home-cc-web` 的 codebase-memory 索引状态为 `ready`(6760 节点、15125 边);整体由 `server`、`app`、`task-board`、`codex` 等包组成,本轮新增模块可独立放入 `lib/`。
|
||||
- 项目无测试框架脚本,单元测试惯例是 `node scripts/*-unit.js` 直接执行并使用 Node 内置 `assert`。
|
||||
- 当前并行实现已形成两层契约:`gitea-workflow-domain/store/queue/service` 是可复用领域与 Webhook 编排层;`gitea-workflow-core.js` 保留轻量兼容服务并补充 turn/配置脱敏/审计 API。server.js 当前通过 `gitea-workflow-service.js` 注入 `setRunner` 与 `enqueueNormalizedTask`,不需要复制状态机。
|
||||
- 生产 runner 仍依赖 Gitea host/token、`gitea-mcp` 可执行文件、工作区 clone/fetch 和 Codex App 回执查询;这些属于集成配置/外部依赖,不在本轮单元测试中连接真实 Gitea。
|
||||
11
.planning/gitea-workflow-core/progress.md
Normal file
11
.planning/gitea-workflow-core/progress.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Gitea Workflow 核心模块进度
|
||||
|
||||
## Log
|
||||
|
||||
- 已读取项目 AGENTS、规划技能、PRD 与现有 lib/test 风格。
|
||||
- 已确认本轮使用独立 `lib/gitea-workflow-*.js` 文件和独立单元测试脚本。
|
||||
- 已新增 `gitea-workflow-domain.js`、`gitea-workflow-store.js`、`gitea-workflow-queue.js`、`gitea-workflow-service.js`;并补齐现有 `gitea-workflow-core.js` 的 turn、配置脱敏、规范化任务入队和 server runner 挂接契约。
|
||||
- 核心单测从 6 项扩展为 7 项,覆盖验签、mention/Bot 过滤、delivery 去重、持久化恢复、并发队列、MCP 配置和 `enqueueNormalizedTask`。
|
||||
- 已通过 `node --check`、核心单测、Webhook 回归、工作区单测、管理 API 单测和 `npm run regression`。
|
||||
- 已补充 `expectedVersion` 乐观并发校验,旧状态写入会返回 `version_conflict`,并将资源 `resourceKey` 对齐为 `<repoKey>:<kind>:<number>`。
|
||||
- 已清理本轮 server smoke 产生的临时 `config/gitea-*` 状态与空工作区锁目录,避免运行态文件混入交付。
|
||||
21
.planning/gitea-workflow-core/task_plan.md
Normal file
21
.planning/gitea-workflow-core/task_plan.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Gitea Workflow 核心领域模块实施计划
|
||||
|
||||
## Goal
|
||||
|
||||
在不重写 `server.js` 的前提下,为 Gitea Webhook 工作流新增可独立使用的核心领域模块,覆盖配置模型、仓库/会话/turn 状态、持久化、delivery 去重、仓库串行队列、重启恢复与审计事件,并提供测试或可调用接口。
|
||||
|
||||
## Status
|
||||
|
||||
- [x] 读取现有代码与 PRD/架构约束
|
||||
- [x] 实现领域模型、状态机与配置规范化
|
||||
- [x] 实现持久化、去重、队列与重启恢复
|
||||
- [x] 实现审计记录与可调用服务接口
|
||||
- [x] 补充测试并完成静态/运行验证
|
||||
- [x] 汇总接口契约、修改文件与未决集成点
|
||||
|
||||
## Errors Encountered
|
||||
|
||||
| 错误 | 尝试 | 处理 |
|
||||
|---|---:|---|
|
||||
| 全局 planning-with-files 路径不存在 | 1 | 改用项目内 `.codex/skills/planning-with-files` |
|
||||
| `todo-list-csv` 技能文件未发现 | 1 | 手工创建同等格式的任务 CSV |
|
||||
Reference in New Issue
Block a user