makecindy / makecindy/cindy

feat(goal): P0 观测与验收基线——Goal run 结构化观测事件 + 6 类终态测试收口(#2104 roadmap 阶段 1)

Open
#2,105 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
2.7k
Forks
401
Avg merge
21h 48m
Merged PRs (30d)
776

Description

## 使用场景 / Use case

作为 #2104(goal 强化 roadmap)的 **P0 阶段**:先建立 Goal run 的**结构化观测与验收基线**,让长任务的执行过程可审计、可复现,为后续任务图/压缩/并行等阶段的"有证据"推进打底。

目标:任何一次 goal 运行,都能回答——**第几个 turn?状态如何迁移?实际派发了什么?最终状态怎么收口的?预算消耗了多少?为什么停滞/重试/恢复?**

## 当前问题 / Current limitation

- goal-host(`apps/desktop/src/main/goal-host/controller.ts`)已有 `active/paused/blocked/complete/budgetLimited/usageLimited` 状态机、generation/owner、终态持久化 barrier、deferred resume,但**缺少结构化运行观测**:turn 派发、状态迁移、预算消耗、停滞原因、重试/恢复结果没有统一的可审计事件流,排障依赖散落日志。
- 6 类终态场景测试缺失:**正常完成 / 失败 / 暂停 / 上游错误 / 重启恢复 / continuation 交错**——这恰是 #1868/#2045/#2091 等"卡死/残留"问题的温床,需先有确定性测试收口。
- 现有 `maker:goal:status-changed` 推送与 GoalIndicator 展示轮数/token/时长,但**无结构化事件 schema**,无法支撑后续 P1.5(verifier 分层)/P2(work-unit)的观测需求。

## 期望方案 / Proposed solution

**1. Goal run 结构化观测事件**(`goal:run` 事件流,统一 schema):
```ts
interface GoalRunEvent {
type: 'turn-dispatched' | 'turn-finalized' | 'state-transition' | 'budget-consumed'
| 'stall-detected' | 'retry' | 'resumed' | 'terminal';
goalSessionId: string;
generation: number; // 防旧事件串台(与现有 generation 语义一致)
turnIndex: number;
from?: GoalState; to?: GoalState; // 状态迁移
reason?: string; // 停滞原因 / 终态 reason(verdict 原文)
budget?: { tokensUsed: number; turnsUsed: number; noProgressStreak: number };
at: number;
}
```
- 在 fireTurn 派发、finalizeTurn 终态收口、状态迁移、预算检查、停滞检测、retry/resume 处发出;
- 内存环缓冲(最近 N 条)+ 可选 SQLite 落盘(`goal_run_events` 表,audit 用);renderer 订阅展示(复用现有 status-changed 通道扩展)。

**2. 补齐 6 类终态测试**(`goal-host/__tests__/`,基于现有测试模式):
- 正常完成(verdict complete → goalCompletion 持久化 + 删 goal 行)
- 失败(verdict blocked / 上游错误 → blocked + deferred resume 登记)
- 暂停/恢复(pause → resume → continuation 正常续跑)
- 上游错误(provider 异常 → 不残留"运行中",终态原子写回)
- 重启恢复(sessionRestore 重建 → resumeActiveGoals 续跑,不重复派发)
- continuation 交错(旧 generation 的尾事件到达 → 被 generation/instance 边界拦截)

**3. 恢复边界约束(#2104)**:本阶段**不做**宽泛自动恢复(空闲 + 无 claim + 超时清状态);只建立观测与测试,恢复语义收敛留给后续专用恢复路径设计。

**4. 可观测性 UI**(最小):GoalIndicator 增强——显示当前 generation/turnIndex、停滞警示、最近状态迁移(复用现有 chip,不做大改)。

## 已考虑的替代方案 / Alternatives considered

- **纯日志埋点**(console 日志)——排障够用但不可审计、不可测试断言;否决,采用结构化事件。
- **一次性大改 state 机**——超出 P0 范围(维护者 #2104:先观测基线,再试点任务分解);本阶段只加观测不改语义。
- **不做观测直接上 verifier**(P1.5)——没有基线数据无法评估 verifier 收益;P0 前置。

---

*关联:#2104(roadmap 总入口)· 范围:观测事件 + 6 类测试 + 最小 UI,无语义变更、无 breaking change。*

Contributor guide

Open the contributing guide

Research direction

Start with apps/desktop/src/main/goal-host/controller.ts and the existing tests under goal-host/__tests__ to understand the current state machine, persistence barriers, and resume behavior. Trace fireTurn, finalizeTurn, status changes, budget checks, stall detection, retry, and resume before defining the event flow. Done means the structured events, six terminal-state tests, optional SQLite audit storage, and the minimal GoalIndicator additions are covered without changing recovery semantics.

Written by the indexing model from the issue text.

Assessment

Tech stack
sqlite, typescript
Domain
database, desktop-dev, observability-sre, testing-qa
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.