Files
HarborForge/plans/NEXT_WAVE_DEV_DIRECTION.md

12 KiB
Raw Blame History

HarborForge Calendar System — Design Document

Date: 2026-03-22


Overview

为 HarborForge 新增日程表Calendar系统支持 Agent/人类用户的任务调度、周期性计划、以及通过 OpenClaw 插件心跳自动唤醒 Agent 执行日程。

同时记录一项项目结构调整:

  • Propose 改名为 Proposal
  • Proposal Accept 不再生成单个 Feature Task
  • 改为在 Proposal 下维护多个 EssentialAccept 时将该 Proposal 下所有 Essential 按类型生成对应的 story task并落到 Proposal 选择的 Milestone
  • story 整个大类改为 restricted只允许通过 Proposal Accept 创建
  • Essential 拥有独立的 EssentialCode编码风格参考 ProjectCode / TaskCode 等既有结构

同时包含一个 bug fixacc-mgr 用户密码不可修改,前端隐藏修改密码入口。


一、数据模型

1.1 TimeSlot日程槽

字段 类型 说明
slot_id int (PK) 已物化 slot 的数据库 ID
user_id FK -> users.id 所属用户
date date 日期
slot_type Enum(Work, OnCall, Entertainment, System) 槽类型
estimated_duration int (1-50) 预估时长(分钟),设计上限 50 分钟,超过需拆分
scheduled_at time (00:00-23:00) 计划开始时间
started_at time, nullable 实际开始时间
attended bool, default false 是否已出席
actual_duration int (0-65535), nullable 实际时长(分钟),无上限
event_type Enum(Job, Entertainment, SystemEvent), nullable 事件类型
event_data JSON, nullable 事件详情(见下方 Event 子类型)
priority int (0-99) 优先级
status Enum(NotStarted, Ongoing, Deferred, Skipped, Paused, Finished, Aborted) 状态
plan_id FK -> schedule_plans.id, nullable 来源计划(物化自 plan 时填入,被 edit/cancel 后置 NULL
created_at datetime 创建时间
updated_at datetime 更新时间

1.2 Event 子类型(存储在 event_data JSON 中)

Job:

{
  "type": "Task|Support|Meeting|Essential",
  "code": "TASK-42",
  "working_sessions": ["session-id-1", "session-id-2"]
}

SystemEvent:

{
  "event": "ScheduleToday|SummaryToday|ScheduledGatewayRestart"
}
  • ScheduleToday — 每日日程规划
  • SummaryToday — 每日总结
  • ScheduledGatewayRestart — OpenClaw 网关计划重启前触发;插件收到后应持久化状态、发送最终心跳、暂停定时任务

Entertainment:

待设计

1.3 SchedulePlan周期性计划

字段 类型 说明
id int (PK) plan-id
user_id FK -> users.id 所属用户
slot_type Enum(Work, OnCall, Entertainment, System) 槽类型
estimated_duration int (1-50) 预估时长
event_type Enum(Job, Entertainment, SystemEvent), nullable 事件类型
event_data JSON, nullable 事件详情
at_time time 每天的计划时间 (--at HH:mm)
on_day Enum(Sun, Mon, Tue, Wed, Thu, Fri, Sat), nullable 星期几 (--on-day)
on_week int (1-4), nullable 第几周 (--on-week)
on_month Enum(Jan-Dec), nullable 月份 (--on-month)
created_at datetime 创建时间
updated_at datetime 更新时间

周期参数层级约束:

  • 使用 --on-month--on-week 必须也用
  • 使用 --on-week--on-day 必须也用
  • --at 始终必填

示例:

  • --at 09:00 --on-day Sun --on-week 1 --on-month Jan → 每年一月第一周周日 09:00
  • --at 09:00 --on-day Sun --on-week 1 → 每月第一周周日 09:00
  • --at 09:00 --on-day Sun → 每周日 09:00
  • --at 09:00 → 每天 09:00

1.4 Agent 表

字段 类型 说明
id int (PK)
user_id FK -> users.id, UNIQUE 关联用户
agent_id VARCHAR, UNIQUE OpenClaw agent name ($AGENT_ID)
claw_identifier VARCHAR OpenClaw 实例 identifier与 Monitor 碰巧一致,无 FK
status Enum(Idle, OnCall, Busy, Exhausted, Offline), default Idle Agent 当前状态
last_heartbeat datetime, nullable 最后心跳时间
created_at datetime 创建时间

1.5 MinimumWorkload最小工作量配置

{
  "daily":   { "work": 0, "on_call": 0, "entertainment": 0 },
  "weekly":  { "work": 0, "on_call": 0, "entertainment": 0 },
  "monthly": { "work": 0, "on_call": 0, "entertainment": 0 },
  "yearly":  { "work": 0, "on_call": 0, "entertainment": 0 }
}

值为分钟数 (0-65535)。存储方式待定(可作为用户级配置存 JSON 字段或独立表)。


二、Slot ID 策略

  • 已物化的 slot:使用数据库自增 ID
  • Plan 虚拟 slot(未物化):使用 plan-{plan_id}-{date} 格式
  • 当虚拟 slot 被 editcancel 时,物化到数据库,获得真实 ID同时该日期该 plan 不再生成虚拟 slot

三、存储与缓存策略

3.1 Plan 不预展开

Plan 只存规则,不为每一天生成数据行。

3.2 物化时机

以下情况写入 time_slots 表:

  1. hf calendar schedule 手动创建
  2. Plan 的虚拟 slot 被 editcancel(物化后断开 plan 关联)
  3. 每天预计算:服务器每日将当天所有 plan 匹配的 slot 物化到缓存/数据库

3.3 当日缓存

  • 每天(凌晨或首次心跳时)预计算当天所有 plan → 物化为当日 slot 缓存
  • 当天新增 schedule / plan-schedule 影响当天时,同步更新缓存

3.4 不可变性

  • cancel / edit 不能操作过去的 slot
  • plan-cancel / plan-edit 不追溯过去已物化的 slot

四、时区

统一使用 HarborForge 服务器时区,不做用户级时区。


五、验证规则

schedule / plan-schedule 提交时验证:

  1. Overlap 检测:与同日已有 slot 时间重叠 → 拒绝,报错
  2. 最小工作量检查:不满足 MinimumWorkload 配置 → 警告,但允许提交

六、Agent 唤醒机制

6.1 心跳流程

  1. 插件每分钟向 HarborForge 服务器发送心跳
  2. 服务器返回该插件claw_identifier对应所有 Agent 当前需要执行的日程
    • 筛选条件:当天 slotstatus 为 NotStarted 或 Deferredscheduled_at 已过
  3. 插件检查 Agent 状态

6.2 唤醒逻辑

Agent 状态为 Idle

  • 唤醒 Agent提供提示词开始工作
  • 向服务器设置 Agent status = Busy 或 OnCall取决于 slot_type
  • 设置 slot: attended = true, started_at = now, status = Ongoing

Agent 状态非 Idle

  • 设置 slot status = Deferred

6.3 多 Slot 竞争

  • 选 priority 最高的执行,其余 Deferred 且 priority += 1
  • 通知 Agent 当前任务完成后重新规划所有 Deferred + NotStarted 的 slot

6.4 状态转移

触发条件 Agent Status 变化
超过 2 分钟无心跳 → Offline
无待执行日程 → Idle
被 TimeSlot 唤醒 → Busy / OnCall
完成 TimeSlot → Idle
API rate-limit 或 billing 错误 → Exhausted
Exhausted 恢复计时器到期 → Idle

6.5 Exhausted 状态详细规则

前提: 所有 Agent 只使用一个主模型,没有 fallback 模型。

进入条件:

  • Agent 调用 LLM API 时收到 rate-limit 错误HTTP 429 等)
  • Agent 调用 LLM API 时收到 billing 相关错误(额度不足、配额耗尽等)

恢复逻辑:

  • 解析错误信息,查找类似 reset in X minsretry after Xresets at <timestamp> 的模式
  • 如果能解析出时间 → 设置 X 分钟 后恢复为 Idle
  • 如果无法解析 → 默认 5 小时 后恢复为 Idle

Exhausted 期间行为:

  • Agent 视为不可用,不被心跳唤醒
  • 待执行的 slot 设为 Deferred
  • 前端/Monitor 显示 Exhausted 状态 + 预计恢复时间

Agent 表扩展字段:

字段 类型 说明
exhausted_at datetime, nullable 进入 Exhausted 的时间
recovery_at datetime, nullable 预计恢复时间
exhaust_reason Enum(RateLimit, Billing), nullable 原因

七、CLI 命令

7.1 日程操作

# 创建单次日程
hf calendar schedule <slot-type> <scheduled-at> <estimated-duration> \
  [--job <code>] [--date <yyyy-mm-dd>]

# 查看某天日程
hf calendar show [--date <yyyy-mm-dd>]

# 取消日程plan 来源的 slot 物化后断开 plan
hf calendar cancel [--date <yyyy-mm-dd>] <slot-id>

# 编辑日程plan 来源的 slot 物化后断开 plan
hf calendar edit [--date <yyyy-mm-dd>] <slot-id> \
  [--slot-type <type>] [--estimated-duration <mins>] \
  [--job <code>] [--scheduled-at <HH:mm>]

# 列出所有有已物化日程的未来日期(纯 plan 的不算)
hf calendar date-list

7.2 计划操作

# 创建周期性计划
hf calendar plan-schedule <slot-type> <estimated-duration> \
  --at <HH:mm> [--on-day <day>] [--on-week <1-4>] [--on-month <month>]

# 列出所有计划
hf calendar plan-list

# 取消计划(不追溯过去)
hf calendar plan-cancel <plan-id>

# 编辑计划(不追溯过去)
hf calendar plan-edit <plan-id> \
  [--at <HH:mm>] [--on-day <day>] [--on-week <1-4>] [--on-month <month>] \
  [--slot-type <type>] [--estimated-duration <mins>]

7.3 用户创建Agent 支持)

hf user create <username> [--agent-id <id>] [--claw-identifier <id>] ...
  • --agent-id + --claw-identifier 必须同时出现或同时不出现
  • pcexec 模式下:
    • --agent-id$AGENT_ID
    • --claw-identifieropenclaw config get plugins.harbor-forge.identifier
  • 后端 POST /users 扩展:接受 agent_id + claw_identifier,创建 User 同时写入 agents 表

八、项目结构调整记录(与 Calendar 相关联的设计备注)

8.1 命名调整

  • Propose 统一改名为 Proposal

8.2 新结构

Project
└─ Proposal
   ├─ Essential
   │  ├─ feature
   │  ├─ improvement
   │  └─ refactor
   └─ accept -> 将该 Proposal 下所有 Essential 生成对应类型的 story task并创建到 Proposal 选择的 Milestone

8.3 Proposal Accept 语义变更

旧行为:

  • Proposal Accept 后生成单个 story/feature task

新行为:

  • Proposal Accept 不再生成单个 Feature Task
  • 每个 Proposal 可维护多个 Essential
  • Accept 时,遍历该 Proposal 下全部 Essential
  • 按 Essential 类型映射生成对应的 story task
    • feature -> story/feature
    • improvement -> story/improvement
    • refactor -> story/refactor
  • 生成目标 Milestone 由 Proposal 在 Accept 前或 Accept 时明确选择

8.4 Story 创建限制

  • story 整个大类视为 restricted
  • 任何 story/* task 都不允许通过通用 task create endpoint 直接创建
  • story 仅允许通过 Proposal Accept 工作流生成

8.5 Essential

新增 Essential 概念,用于承载 Proposal 下的可落地核心条目。

建议字段:

字段 类型 说明
id int (PK)
essential_code VARCHAR(64), UNIQUE EssentialCode风格参考 ProjectCode / TaskCode / MilestoneCode / ProposalCode
proposal_id FK -> proposals.id 所属 Proposal
type Enum(feature, improvement, refactor) Essential 类型
title VARCHAR(255) 标题
description TEXT, nullable 描述
created_by_id FK -> users.id, nullable 创建人
created_at datetime 创建时间
updated_at datetime 更新时间

8.6 代码生成

  • Essential 拥有独立 EssentialCode
  • 编码规则参考现有类似结构,例如:
    • ProjectCode
    • MilestoneCode
    • TaskCode
    • ProposalCode
  • 具体前缀与编号策略待后续单独定稿

九、前端

  • 每个用户可查看/调整自己的日程表
  • Admin 可查看/调整所有用户的日程表
  • 日程表页面展示(具体 UI 待设计)

十、Bug Fix

  • acc-mgr 用户后端禁止修改密码admin 也不行)
  • 前端acc-mgr 用户不显示修改密码入口

十一、待定项

  • Entertainment 事件子类型设计
  • MinimumWorkload 存储方式JSON 字段 vs 独立表)
  • 前端日程表 UI 详细设计
  • Agent 唤醒的提示词模板
  • Proposal / Essential 的详细数据模型、API、前端页面与 accept 流程实现细节
  • Story restricted 后,现有 task create / propose accept / milestone flow 的兼容迁移策略