Skip to content

feat: 管道状态持久化 + 交互式卡片按钮 (Phase C) - #22

Merged
lishuceo merged 3 commits into
mainfrom
feat/pipeline-phase-c
Feb 18, 2026
Merged

feat: 管道状态持久化 + 交互式卡片按钮 (Phase C)#22
lishuceo merged 3 commits into
mainfrom
feat/pipeline-phase-c

Conversation

@lishuceo

Copy link
Copy Markdown
Owner

Summary

  • 新增 PipelineStore (SQLite) 持久化管道状态,支持服务崩溃后恢复并通知用户
  • /dev 命令改为先发送确认卡片(含成本预估),用户点击「确认执行」后才开始管道,新增「中止」「取消」「重试」交互按钮
  • 新增 Pipeline Runner 模块统一管理管道生命周期,会话锁延迟到确认时获取,避免阻塞聊天

Test plan

  • npm test — 200 tests 全部通过(含新增 store/runner/orchestrator abort 测试)
  • npm run typecheck — TypeScript 类型检查通过
  • 发送 /dev <简单任务> → 看到确认卡片(含「确认执行」和「取消」按钮)
  • 点击「取消」→ 卡片更新为已取消状态
  • 再次 /dev <任务> → 确认 → 看到管道进度卡片(含「中止」按钮)
  • 管道完成/失败 → 失败时看到「重试」按钮
  • 管道执行中重启服务 → 启动后被中断管道的卡片更新为中断状态(含重试按钮)

🤖 Generated with Claude Code

- 新增 PipelineStore (SQLite) 持久化管道状态,支持崩溃恢复
- 新增确认卡片:/dev 命令后先确认再执行,避免误触昂贵管道
- 新增中止/取消/重试按钮,用户可全程控制管道生命周期
- 新增 Pipeline Runner 统一管理管道生命周期(创建→确认→执行→完成)
- Orchestrator 支持 abort(),可中止正在运行的管道
- WebSocket 模式注册卡片交互 HTTP 端点
- 服务启动时恢复被中断的管道并通知用户
- 会话锁延迟到确认时获取,避免确认卡片阻塞会话

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Feb 18, 2026

Copy link
Copy Markdown

Claude finished @lishuceo's task in 2m 16s —— View job


PR Review: Pipeline State Persistence + Interactive Card Buttons (Phase C)

  • Check for previous review comments
  • Gather PR diff and context
  • Review all changed files (11 files, +1412/-157)
  • Post inline comments for issues found
  • Post summary

⚠️ Issues Found

Overall assessment: Well-structured PR with clean separation of concerns. The PipelineStore (SQLite persistence), PipelineRunner (lifecycle management), and interactive card buttons are solid additions. The test coverage is thorough (store CRUD, CAS atomicity, orchestrator abort/retry/cost-tracking). However, there are a few issues worth addressing before merging:

Severity Issue File Confidence
🔴 Critical No user authorization on card actions — any user in the chat can confirm/cancel/abort/retry another user's pipeline src/feishu/event-handler.ts:88 85
🟡 Warning Abort status overwriteabortPipeline() sets status to 'aborted', but startPipeline()'s completion handler overwrites it to 'failed' src/pipeline/runner.ts:271-285 80
🟡 Warning TOCTOU between get() and tryStart() CAS — redundant status check in handlePipelineConfirm could become inconsistent src/pipeline/runner.ts:108-119 82
🟡 Warning Pipeline ID collision risk — 2 bytes of randomness (65K range) with ms-precision timestamp src/pipeline/store.ts:229-231 78
🔴 Info Module-level SQLite singletonpipelineStore opens DB at import time; fragile for tests and startup error handling src/pipeline/store.ts:233 90

What looks good

  • CAS double-click prevention via tryStart() — correct SQLite-level atomicity
  • Graceful shutdown in src/index.ts — marks running pipelines as interrupted and closes the DB
  • Crash recoveryrecoverInterruptedPipelines() correctly marks running→interrupted on startup and notifies users
  • Session lock deferred to confirm — avoids blocking chat while waiting for user confirmation
  • Comprehensive test suite — store tests cover CRUD, CAS, expiration; orchestrator tests cover happy path, retry logic, cost accumulation, abort, and callback resilience
  • Card builder functions are well-organized with proper phase state rendering

Comment thread src/feishu/event-handler.ts
Comment thread src/pipeline/runner.ts
Comment thread src/pipeline/runner.ts
Comment thread src/pipeline/store.ts Outdated
Comment thread src/pipeline/store.ts
- 卡片交互处理器验证操作者 open_id 与管道创建者一致,防止越权操作
- abortPipeline 不再提前写入 aborted 状态,改由 startPipeline 完成
  处理器检查 orchestrator.isAborted() 决定最终状态
- generatePipelineId 随机部分从 2 字节增加到 4 字节,降低碰撞概率

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Feb 18, 2026

Copy link
Copy Markdown

Claude finished @lishuceo's task in 2m 37s —— View job


PR Review: Pipeline State Persistence + Interactive Card Buttons (Phase C) — Follow-up

  • Check previous review comments and resolve fixed issues
  • Read current diff and all 11 changed files
  • Review fix commit (db6bd75) for correctness and new issues
  • Post summary

Previous Issues: 3 of 5 Fixed

The fix commit db6bd75 addressed 3 of the 5 issues from the previous review:

Issue Status Details
Card action authorization ✅ Fixed Added operatorId extraction and validation (event-handler.ts:86-98)
Abort status overwrite ✅ Fixed abortPipeline() no longer writes status; startPipeline() checks orchestrator.isAborted() (runner.ts:199-201)
Pipeline ID collision risk ✅ Fixed randomBytes(2)randomBytes(4) (store.ts:230)
TOCTOU race condition Acknowledged Author correctly noted the early-exit is an optimization; tryStart() CAS is the sole source of truth
Module-level singleton Acknowledged Follows established project pattern; config validates before pipeline code executes

All 3 fixed threads have been resolved. The test (store.test.ts:283) was also updated to match the new 8-hex-char format.

New Review: No New Issues Found

The fix commit is clean. The isAborted() method added to PipelineOrchestrator is minimal and correct. The auth check implementation is sound — the record && operatorId && record.userId !== operatorId guard correctly rejects mismatched users while allowing actions to proceed if operator info is unavailable (graceful degradation).

✅ Approved

Well-structured PR with thorough test coverage (store CRUD, CAS atomicity, orchestrator abort/retry/cost tracking, callback resilience). All previously identified issues are resolved or acknowledged.

- 卡片操作鉴权改为 fail closed:operatorId 缺失时拒绝操作
- 将 CAS tryStart 移至 handlePipelineConfirm 同步执行,避免
  CAS 失败时返回卡住的进度卡片
- PipelineStore 使用独立 DB 文件(pipelines.db),避免与
  SessionDatabase 共用同一文件导致 SQLITE_BUSY
- catch 块中 feishuClient.updateCard 增加内层 try-catch
- 提取 ensureThread 为共享模块,消除 runner.ts 与
  event-handler.ts 的代码重复

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Feb 18, 2026

Copy link
Copy Markdown

Claude finished @lishuceo's task in 2m 32s —— View job


PR Review: Pipeline State Persistence + Interactive Card Buttons (Phase C) — Re-review

  • Check previous review comments and resolve fixed issues
  • Gather current diff and read all changed files
  • Review latest commit (f85c4f9) for correctness and new issues
  • Post summary

Latest Commit: f85c4f9 — "fix: 修复 deep review 发现的关键问题"

Reviewed all 7 files changed in this commit (+83/-88 lines). The changes address real issues and improve code quality:

Change Assessment
Separate pipeline DBpipelineStore now uses config.db.pipelineDbPath instead of sharing sessionDbPath ✅ Correct. Prevents table namespace collisions between sessions and pipelines
ensureThread extraction — Deduplicated thread creation into src/feishu/thread-utils.ts, shared by event-handler.ts and runner.ts ✅ Clean refactor. Eliminates the duplicated ensureThreadForPipeline
Auth check hardened — Changed from permissive (skip if no operatorId) to fail-closed (reject if no operatorId) ✅ Correct security posture. Lines 94-98 of event-handler.ts
CAS moved to handlePipelineConfirmtryStart() now runs synchronously before returning the progress card, with startPipeline doing a defensive fallback check ✅ Correct. Prevents users from seeing a stuck progress card on CAS failure
Error card update wrapped in try-catchrunner.ts:227-236 ✅ Prevents cascading failures when Feishu API is unreachable
Test mocks updated — Config mocks include pipelineDbPath, thread-utils.js mock added ✅ Consistent with code changes

Previous Issues Status

All 5 issues from the original review are now resolved or acknowledged across the two fix commits:

Issue Final Status
Card action authorization ✅ Fixed (db6bd75) + hardened to fail-closed (f85c4f9)
Abort status overwrite ✅ Fixed (db6bd75)
Pipeline ID collision risk ✅ Fixed (db6bd75)
TOCTOU race condition ✅ Acknowledged — CAS is sole source of truth; improved in f85c4f9 by moving CAS earlier
Module-level singleton ✅ Acknowledged — follows project pattern; now uses separate DB path

New Issues Found: None

The latest commit is clean. I traced through the CAS flow (handlePipelineConfirmstartPipeline), abort status propagation (orchestrator.isAborted()'aborted' status in store), and error handling paths — all are correct. The ensureThread extraction is a clean refactor with no behavioral changes.

Minor style note (not blocking): In runner.ts:37, the import { ensureThread } is placed after the interface definition rather than with the other imports at the top of the file. This doesn't affect functionality.

✅ Approved

Well-structured PR with comprehensive fixes across 3 commits. The pipeline persistence, interactive card buttons, crash recovery, and auth controls are all solid. Test coverage is thorough (store CRUD, CAS atomicity, orchestrator abort/retry/cost tracking, callback resilience).

@lishuceo
lishuceo merged commit 134f4d7 into main Feb 18, 2026
1 check passed
@lishuceo
lishuceo deleted the feat/pipeline-phase-c branch February 18, 2026 14:33
lishuceo added a commit that referenced this pull request Apr 8, 2026
* feat: 管道状态持久化 + 交互式卡片按钮 (Phase C)

- 新增 PipelineStore (SQLite) 持久化管道状态,支持崩溃恢复
- 新增确认卡片:/dev 命令后先确认再执行,避免误触昂贵管道
- 新增中止/取消/重试按钮,用户可全程控制管道生命周期
- 新增 Pipeline Runner 统一管理管道生命周期(创建→确认→执行→完成)
- Orchestrator 支持 abort(),可中止正在运行的管道
- WebSocket 模式注册卡片交互 HTTP 端点
- 服务启动时恢复被中断的管道并通知用户
- 会话锁延迟到确认时获取,避免确认卡片阻塞会话

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: 修复 PR review 反馈 — 卡片操作鉴权 + abort 状态覆盖 + ID 碰撞风险

- 卡片交互处理器验证操作者 open_id 与管道创建者一致,防止越权操作
- abortPipeline 不再提前写入 aborted 状态,改由 startPipeline 完成
  处理器检查 orchestrator.isAborted() 决定最终状态
- generatePipelineId 随机部分从 2 字节增加到 4 字节,降低碰撞概率

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: 修复 deep review 发现的关键问题

- 卡片操作鉴权改为 fail closed:operatorId 缺失时拒绝操作
- 将 CAS tryStart 移至 handlePipelineConfirm 同步执行,避免
  CAS 失败时返回卡住的进度卡片
- PipelineStore 使用独立 DB 文件(pipelines.db),避免与
  SessionDatabase 共用同一文件导致 SQLITE_BUSY
- catch 块中 feishuClient.updateCard 增加内层 try-catch
- 提取 ensureThread 为共享模块,消除 runner.ts 与
  event-handler.ts 的代码重复

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant