Skip to content

Commit b2c46bd

Browse files
committed
fix: close systemic gateway reliability gaps
1 parent a7799d6 commit b2c46bd

60 files changed

Lines changed: 9034 additions & 906 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/publish.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,11 @@ jobs:
4848
run: bun run build
4949

5050
- name: Publish to npm
51-
run: npm publish --access public
51+
env:
52+
PACKAGE_VERSION: ${{ github.event.release.tag_name || inputs.version }}
53+
run: |
54+
if [[ "${PACKAGE_VERSION#v}" == *-* ]]; then
55+
npm publish --access public --tag next
56+
else
57+
npm publish --access public --tag latest
58+
fi

AGENTS.md

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ Gateway ─ Worker + Scheduler + Watchdog + Dashboard
1717
```
1818

1919
- 插件在 `plugin/supertask.ts` 注册 8 个 `supertask_*` 工具:`add/next/status/retry/list/get/schedule/upgrade`;运行态与执行终态只允许 Gateway 写入,不得恢复外部 `start/done/fail`
20-
- Worker 通过参数数组直接执行 `opencode run --agent <task.agent> --format json <task.prompt>`;退出码决定成功或失败,Gateway 统一写任务状态和执行记录。
20+
- Worker 先启动等待握手的 launcher,持久化 launcher PID 后才通过参数数组执行 `opencode run --agent <task.agent> --format json <task.prompt>`;新 run 使用 `gated-v3-token-guardian`,每 run UUID 必须同时写入 `task_runs.locked_by` 和 launcher argv。launcher 只能在整个进程组排空后通过不传递给 OpenCode 的 IPC 发回绑定 UUID 的证明;无证明退出必须隔离,不得结算或释放批次。退出码决定成功或失败,Gateway 统一写任务状态和执行记录。当前只有 Unix 独立进程组能提供可恢复的整树退出证明;Windows 在引入 Job Object 前必须拒绝启动 Worker,不得退回不完整的父子 PID 扫描。
21+
- Worker 启动的受管 OpenCode 进程设置 `SUPERTASK_MANAGED_RUN=1`;该上下文必须拒绝 `supertask_upgrade`,避免升级流程删除并等待承载自己的 Gateway。升级只能从外部 CLI 或非队列 OpenCode 会话发起。
2122
- `agents/supertask-runner.md``plugin/task.ts` 是旧架构遗留,不是当前 npm 运行链路;不得重新接入嵌套 runner。
2223
- pm2 是可选守护层:仅显式运行 `supertask install` 时允许安装;插件加载不得静默安装全局依赖。前台运行使用 `supertask gateway`
2324

@@ -50,11 +51,15 @@ bun run dev -- db check # 检查数据库完整性与业务统计
5051
- 数据库默认位于 `~/.local/share/opencode/tasks.db`;测试或隔离运行通过 `SUPERTASK_DB_PATH` 覆盖。
5152
- 配置文件位于 `~/.config/opencode/supertask.json`,默认值在 `src/gateway/config.ts`
5253
- 数据库初始化时启用 WAL、创建 `gateway_lock` 并自动执行 `drizzle/` migrations。
53-
- Gateway 用 SQLite `BEGIN IMMEDIATE` + `gateway_lock` 保证单实例;Dashboard 默认只监听 `127.0.0.1:4680`
54-
- Gateway 仅在 Worker、Scheduler、Watchdog 和 Dashboard 启动完成后写入 `gateway_lock.ready_at`;PM2 `online` 不能单独作为就绪依据,进程 PID 必须匹配新鲜 ready 锁。
55-
- `/health` 必须同时反映组件活跃度和连续失败;`supertask doctor` 汇总 OpenCode、数据库、PM2 ready 锁、Dashboard 与日志轮转。显式 `supertask install` 配置有限保留的 PM2 日志轮转。
56-
- 数据库检查、备份、清空和恢复统一经过 `DatabaseMaintenanceService`;CLI 清空/恢复必须显式确认并拒绝运行中任务,且只可自动停启 PID 与当前数据库新鲜 ready 锁一致的 PM2 Gateway;前台或无法确认归属的进程必须拒绝误杀。清空/恢复前必须自动创建校验通过的安全备份,默认在操作失败时也恢复原 Gateway 状态,`--keep-stopped` 除外。
57-
- Dashboard 清空只能豁免当前 Gateway PID,仍必须服务端确认、拒绝运行中任务并在同一事务内先备份后删除;不得恢复为直接 `DELETE` 三张表的路由实现。
54+
-`0005` 起 migration 必须遵循 expand/contract 并保持 N-1 二进制兼容:只允许新增表、非唯一索引,以及可空或带默认值的新增列。删除、重命名、收紧约束和数据改写必须延后到旧版本不再是自动回滚目标后执行;测试会拒绝破坏该约束的 SQL。
55+
- Gateway 用 SQLite `BEGIN IMMEDIATE` + `gateway_lock` 保证单实例;进程身份必须同时识别直接 Gateway 入口与公开的 `supertask gateway`/CLI 入口。Dashboard 默认只监听 `127.0.0.1:4680`
56+
- Gateway 必须先完成恢复收敛、Scheduler 初始化和 Dashboard 绑定,最后才启动 Worker,并把包版本与 `gateway_lock.ready_at` 一起写入;PM2 `online` 不能单独作为就绪依据,进程 PID、版本和运行作用域必须匹配新鲜 ready 锁。
57+
- PM2 替换已有 Gateway 前必须先用保存的运行环境验证管理命令可执行;若新旧进程无法共用同一可回滚的 PM2 管理路径,必须在删除旧进程前失败关闭。
58+
- PM2 替换、数据库维护、卸载与 macOS supervisor 检查必须先共用 `PM2_HOME/supertask-gateway.manage.sqlite` canonical SQLite 事务锁;兼容旧 custom lock 时还必须从 PM2 dump/运行环境和 LaunchAgent 恢复全部旧路径,并按固定顺序同时持有,不能因后续 CLI 缺少旧环境变量而绕过旧 supervisor。已安装 LaunchAgent 的 `PM2_HOME` 与当前 CLI 不同必须在任何修改前失败关闭,避免两个 PM2 daemon 争用同一 Gateway。不得恢复 PID/stale 文件锁。PM2 kill timeout 不得低于 Worker shutdown grace 加 15 秒,`stop/delete` 命令 timeout 不得低于实际 kill timeout 加 5 秒,管理锁必须持有到命令返回;显式低值必须在删除旧进程前失败关闭。
59+
- macOS supervisor 只有在 `jlist` 成功且确认 Gateway 缺失、同时 `dump.pm2` 明确包含 Gateway 时才可 `resurrect`;状态未知、`errored``stopped` 和卸载后的空 dump 都不得触发重启。卸载必须停止并移除项目 LaunchAgent。
60+
- `/health` 必须分别反映 Worker、Scheduler、Watchdog 和历史清理的活跃度与连续失败;`supertask doctor` 还要验证 macOS LaunchAgent 与 PM2 dump 可恢复性。PM2 替换/回滚必须保留既有 Bun 路径、完整运行环境和数据库作用域。
61+
- 数据库检查、备份、清空和恢复统一经过 `DatabaseMaintenanceService`;CLI 清空/恢复必须显式确认并拒绝运行中任务,且只可自动停启 PID 与当前数据库新鲜 ready 锁一致的 PM2 Gateway;前台或无法确认归属的进程必须拒绝误杀。清空/恢复前必须自动创建校验通过的安全备份;清空必须动态删除全部业务表数据(包括 N+1 expand-only 表),通过延迟外键检查支持循环依赖,并保留 `gateway_lock` 与 migration 元数据。恢复来源必须从已打开的 SQLite 连接生成包含已提交 WAL 页的一致快照,并拒绝当前数据库的符号链接/硬链接别名。恢复必须动态校验 source/live 业务表和可写列:source-only 未知表/列在删除前失败关闭,共有未来列完整复制,live-only 列只允许可空/默认值且 live-only 新表必须清空,避免 N/N-1 形成混合时间点;随后在当前连接的排他事务内原位替换,不得关闭连接后 rename 换库。默认在操作失败时也恢复原 Gateway 状态,`--keep-stopped` 除外。
62+
- Dashboard 清空只能豁免当前 Gateway PID,仍必须服务端确认、拒绝运行中任务并在同一事务内先备份后动态删除全部业务表;不得恢复为路由内直接 `DELETE` 的实现。
5863
- Dashboard 的浏览器写请求必须通过同源检查,数据库字符串进入 HTML 前必须调用 `esc`;API 的 ID、状态和配置不得直接断言类型。
5964

6065
## 文档维护
@@ -68,13 +73,15 @@ bun run dev -- db check # 检查数据库完整性与业务统计
6873
- 任务状态:`pending | running | done | failed | dead_letter | cancelled`;执行记录状态:`running | done | failed`
6974
- `cwd` 是任务的项目隔离键;插件必须使用 OpenCode 工具上下文的 `directory`,不得信任模型传入的 `cwd`,查询和状态变更必须保持同一作用域。
7075
- 队列顺序保持 `urgency DESC → importance DESC → createdAt ASC → id ASC`;全局并发和同一 `batchId` 串行必须依据数据库运行态,在 Gateway 重启后仍成立;不同批次可并行,依赖任务仅在同 cwd 的 `dependsOn` 完成后运行。
71-
- `maxRetries` 表示首次执行之外允许的重试次数;失败任务按指数退避,耗尽后进入 `dead_letter`,手动重试会重置重试预算
76+
- `maxRetries` 表示首次执行之外允许的重试次数;失败任务按指数退避,耗尽后进入 `dead_letter`。手动重试必须在同一写事务内确认依赖仍存在、同 `cwd` 且可恢复或已完成,才可重置重试预算,避免和历史清理并发制造悬空 `pending`
7277
- `retryBackoffMs``timeoutMs` 可按任务覆盖;调度模板克隆时必须保留 `cwd/batchId/maxRetries/retryBackoffMs/timeoutMs`
73-
- 运行中任务进入 `cancelled` 后,Worker 必须在轮询周期内终止对应进程树并关闭 run;Gateway 关闭时先按 `shutdownGracePeriodMs` drain,只有剩余任务才重置为 `pending`
78+
- 运行中任务进入 `cancelled` 后,Worker 必须在轮询周期内终止对应进程树并关闭 run;只有确认整棵进程树退出后才能关闭、重试、释放批次或在 Gateway 停机时重置为 `pending`,否则必须保持隔离
7479
- 删除任务必须拒绝 `running` 状态、仍有 `running` 执行记录,或仍被 `pending/running/failed/dead_letter` 任务依赖的前置任务;手动删除与过期清理都必须防止子进程失联和依赖悬空。
75-
- Watchdog 处理数据库中的旧 PID 前必须校验进程命令与配置的 OpenCode 可执行文件匹配;Unix 上仅对确认的独立进程组发送信号。
80+
- Watchdog 处理当前 guardian PID 前必须同时校验 launcher 路径、配置的 OpenCode 参数和 `locked_by` 中的每 run UUID;旧 v2/legacy 记录只要 PID/PGID 仍存活或无法确认就必须隔离且不得发信号,只有二者均明确消失才可恢复。组长已退出但进程组仍存活时不得按 `not-running` 恢复。旧版 `started_at`/`heartbeat_at` 同时为 NULL 的 running run 必须视为 stale 并按协议隔离,不得永久占用并发且逃逸诊断。Unix 使用独立进程组;Windows 在 Job Object 隔离完成前禁止启动 Worker。
81+
- 旧版无 child PID 的 run 默认保持隔离;`run abandon` 只允许 `launch_protocol IS NULL`、owner PID 已退出、child PID 为空且关联任务已取消的记录,并要求显式 `ABANDON` 确认。未知非空协议和当前 guardian 协议必须 fail-closed。
7682
- Watchdog 的 `checkIntervalMs` 是心跳检查间隔,`cleanupIntervalMs` 是数据清理间隔,两者不可混用;配置经 `validateConfig` 校验后才允许运行或保存。
7783
- 调度模板支持 `cron | delayed | recurring`,Scheduler 将模板克隆为普通任务并受 `maxInstances` 限制;`delayed` 成功生成一次后必须自动禁用。
84+
- `cron/recurring` 达到 `maxInstances` 时必须推进下一触发点,`delayed` 保持等待;到期模板扫描必须有界。不可恢复依赖应在状态事件中递归收敛,下游链不得在每个 Worker poll 全局扫描。
7885

7986
## 代码规范
8087

@@ -85,6 +92,7 @@ bun run dev -- db check # 检查数据库完整性与业务统计
8592
- SQL 中 `NOT IN` 对 NULL 值不生效,需加 `OR column IS NULL`
8693
- `tasks.createdAt/startedAt/finishedAt``task_runs.startedAt/finishedAt` 是秒级时间;`retryAfter/scheduledAt/heartbeatAt/lockedAt` 和模板调度时间是毫秒值,比较时必须显式统一单位
8794
- 修改 `src/core/db/schema.ts` 后运行 `bun run db:generate`,并提交对应的 `drizzle/*.sql``drizzle/meta/*`
95+
- 不得通过提高 migration 兼容性测试的基线来放行破坏性 migration;必须采用跨版本 expand/contract。
8896

8997
## 测试规范
9098

@@ -100,7 +108,7 @@ bun run dev -- db check # 检查数据库完整性与业务统计
100108
## 发布流程
101109

102110
- 修改代码 → 测试通过 → `bun run build` → git commit/push
103-
- 创建 GitHub Release 自动触发 CI 发布到 npm;工作流通过 npm Trusted Publisher/OIDC 获取短期发布凭据,不使用长期 `NPM_TOKEN`
111+
- 创建 GitHub Release 自动触发 CI 发布到 npm;稳定版进入 `latest`,预发布版进入 `next`工作流通过 npm Trusted Publisher/OIDC 获取短期发布凭据
104112
- npm 包设置必须信任仓库 `vbgate/opencode-supertask``publish.yml`,Allowed actions 仅启用 `npm publish`
105113
- **不要手动 npm publish**,通过 `gh release create v<x.y.z>` 触发
106114
- 升级版本号在 `package.json``version` 字段

0 commit comments

Comments
 (0)