Skip to content

feat(taskboard): four-applier interface + scheduled ingest fast-path (Phase 1 part A)#68

Merged
tsaijamey merged 1 commit into
mainfrom
feat/taskboard-phase1-applier
May 12, 2026
Merged

feat(taskboard): four-applier interface + scheduled ingest fast-path (Phase 1 part A)#68
tsaijamey merged 1 commit into
mainfrom
feat/taskboard-phase1-applier

Conversation

@tsaijamey

Copy link
Copy Markdown
Owner

Summary

  • Applier 接口 4 类: BaseApplier / DecisionApplier / ExecutionApplier / ResumeApplier
  • 4 action 词汇 (run/reply/resume/dismiss) + prompt 格式契约 (首行 ≤80 + 空行 + 正文)
  • ResumeApplier Case A stage-gate (FRAGO_CASE_A_ENABLED env var) + CSID 失效 → resume_failed (新终态)
  • Ingestor.ingest_scheduled (Gap 3a): scheduled fast-path, thread 不归并
  • pa_validators: 加 dismiss / validate_prompt_format() / VALID_MSG_ACTIONS=4 (严格)

Part A 仅建接口骨架, 不动主 PA 决策路径 (避免破坏 server). 重复响应根因回归测试推 part B.

Test plan

Phase 1 part B (紧跟本 PR merge, 不允许跳 Phase 2)

primary_agent_service._handle_pa_output 改调 DecisionApplier + executor.spawn_resume 实装 + resume_inbox 入口改 ResumeApplier.route_resume + scheduler 摘 message_cache + ingestion/store.py + models.py + thread_service.py + 同步改 reflection_tick / task_lifecycle / task_service / task_commands / pa_prompts / board.py 等. 验收线: 同一条飞书消息触发 10 次 → PA 输出恰好 1 个 decision (重复响应根因回归).

🤖 Generated with Claude Code

…heduled + prompt format

Phase 1 partial commit (spec plan v1.1 §Implementation Phase 1).
final state: 32 passed + 3 skipped (spec 明示留 Phase 2 / 后续 commit) + 0 failed in 7.35s.

# 已完成范围 (interface ready)

新增 src/frago/server/services/taskboard/:
- applier.py: BaseApplier 抽象基类 (_reject / record_rejection 公共逻辑)
- decision_applier.py: PA decisions JSON 路由器
  - VALID_MSG_ACTIONS = {run, reply, resume, dismiss} (spec freeze v1.1 §3 严格 4 个)
  - 词汇校验 → reject(action_invalid)
  - prompt 格式校验 (首行 ≤80 + 空行 + 正文) → reject(prompt_format_invalid)
  - 路由: run / reply → board.append_task; resume → ResumeApplier; dismiss → mark_msg_dismissed
- execution_applier.py: Executor 启动 / 完成回写 board (start_task / finish_task)
- resume_applier.py: resume 三档路由
  - Case A (completed/failed → spawn_resume) 含 FRAGO_CASE_A_ENABLED gate
  - Case A CSID 失效 → task.status=resume_failed (新终态) + timeline resume_csid_lost
  - Case B (executing → ResumeInbox.enqueue + record_resume_pending)
  - 非法 state (queued/replied/resume_failed) → reject(resume_illegal_state)

新增 Ingestor.ingest_scheduled (Gap 3a 修订):
- thread 不归并, 每次触发独立 (origin=scheduled, subkind=job_name)
- Source default: sender_id='__scheduler__', parent_ref=schedule_id
- 同 transaction create_thread + append_msg + append_task(type=run, prompt=锁定值)

pa_validators.py 改造:
- VALID_PA_ACTIONS 加 dismiss (schedule 暂保留兼容, 后续 commit 移除)
- 新增 VALID_MSG_ACTIONS = {run, reply, resume, dismiss} (spec v1.1 §3 严格集)
- 新增 _PA_ACTION_REQUIRED_FIELDS["dismiss"] = ["msg_id"]
- 新增 validate_prompt_format() 函数 (首行 ≤80 / 空行 / 无代码围栏)

# 测试 (Ce specify 13 用例 + supplementary, 共 18 actual + 3 skipped)

tests/server/test_taskboard_decision_applier.py (7 用例):
- T3.1 action_invalid_reject
- T3.2 prompt_format 4 子 case (太长 / 缺空行 / 代码围栏 / valid)
- T3.3 summary 取首行不截断
- T3.4 recent_rejections 暴露

tests/server/test_taskboard_resume_applier.py (6 用例 + 3 skipped):
- T9.1 case_a_not_implemented_reject
- T9.2 拒绝可见性
- T9.3 Case A happy path (FakeExecutor mock)
- T9.4 CSID 失效 → resume_failed
- T9.5 resume_failed 字段值合法 (新终态)
- T9.6 历史 run 轨迹 (skipped, 留 Phase 2 CLI 集成)
- T9.7a CLI 结构 (skipped, 留 Phase 1 后续 commit)
- 补充: resume_executing_case_b_pending / resume_illegal_state

tests/server/test_taskboard_applier_invariants.py (5 用例 + 1 skipped):
- T_B1.1 scheduled fast-path Msg dispatched
- T_B1.2 Source default 完整性 (channel/sender_id/parent_ref/received_at/text/reply_context)
- T_B1.2 Source frozen=True 在 scheduled 路径成立
- T5.2 post_archive_reject (skipped, 留 Phase 2 vacuum)
- 补充: scheduled_thread_not_merged (B1 决议验证)

# Phase 1 vs Phase 1 后续 commit 边界

本 PR 范围 = interface ready + 单元测试通过. 不动主 PA 决策路径, 避免破坏 server.

留 Phase 1 后续 commit:
1. primary_agent_service._handle_pa_output 改调 DecisionApplier (取代 _handle_pa_output 内部分支)
2. executor.spawn_resume 实装 + 抛 ClaudeSessionNotFoundError + ResumeApplier 注入实际 Executor
3. resume_inbox.py 入口改 ResumeApplier.route_resume + ResumeApplier 注入 ResumeInbox
4. scheduler_service.py 调 Ingestor.ingest_scheduled (替代直接 enqueue Executor)
5. ingestion/scheduler.py 摘 message_cache + 改调 Ingestor.ingest_external
6. **删除** ingestion/store.py + ingestion/models.py + thread_service.py
7. 同步改 reflection_tick.py / task_lifecycle.py / task_service.py / cli/task_commands.py 等
8. board.py 加 mark_task_replied / close_msg_if_terminal / mark_msg_dismissed 公有方法
9. pa_validators.VALID_PA_ACTIONS 移除 schedule (与 scheduler_service 改造同步)
10. pa_prompts.py 补 4 action 词汇 / prompt 格式契约 / recent_rejections 三段

理由: 上述 PA wire-up + 删除三文件 + 改 10+ 依赖, 是 Phase 1 大改造范围.
单 commit 推上去会让 frago 整体不可启动 (类似 Phase 0 项 4/5 推迟逻辑).
本 commit 焦点 = Applier 接口 + 4 action 词汇 + Case A/B 路由骨架 + 单元覆盖.

Refs:
- spec plan v1.1 .claude/docs/spec-driven-plan/20260512-msg-task-board-redesign.md
- jsonl Ce specify 14 fixture (00:18:41) + Phase 1 13 用例 (specify 后续)
- jsonl Yi approve Phase 0 (08:11:23) + Phase 1 边界 acknowledge (08:21:59)
- HUMAN merge PR #67 (squash commit 70f2fe4) → Phase 1 触发

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tsaijamey tsaijamey merged commit cc15f89 into main May 12, 2026
tsaijamey added a commit that referenced this pull request May 12, 2026
…g/task close methods (#69)

Phase 1 part B 核心: 修原始 'PA 重复响应' 根因 (jsonl charter line 2 描述的核心 bug)
+ 补 Phase 1 part A 漏掉的 board 公有方法.

# 已完成范围 (核心根因消除)

board.py:
1. append_msg 内部 dedup — 同 msg_id 重复 ingest 直接 return + 落 duplicate_msg_ingest timeline.
   替代旧 ingestion/scheduler.message_cache 兜底机制 (那是 _reconcile_message_cache_with_tasks
   补丁逻辑, 引入"启动恢复时哪些 cache 项算 orphan"的判定模糊). dedup 在 board 公有方法内做,
   单点入口, 状态机闭环.
2. append_msg 自动 transition received → awaiting_decision (spec §2 写"立即转换 (心跳前)",
   Phase 0 未实施, Phase 1 part B 在 append_msg 内补).
3. mark_task_replied(task_id, by) — reply task 推送完成后 task.status='replied'.
4. close_msg_if_terminal(msg_id, by) — 全部 task 终态 + ≥1 reply 已 replied → msg.status='closed'.
5. mark_msg_dismissed(msg_id, reason, by) — PA dismiss action 路径, msg.status='dismissed' 终态.

# 测试 (集成回归用例 + 现有覆盖)

tests/server/test_taskboard_repeat_response_regression.py (4 用例 全 pass):
- test_repeat_ingest_10x_produces_one_task — 同 msg_id ingest 10 次 → 仅 1 个 msg + 1 个 task,
  timeline 含 9 条 duplicate_msg_ingest entry (charter 描述的根因回归证据)
- test_pa_not_invoked_for_duplicate_msg — view_for_pa 第 2-10 次 ingest 后 msg.status 始终
  dispatched, PA 不再触发新 decision
- test_mark_task_replied_and_close_msg — DecisionApplier reply 路径自动调 mark_task_replied
  + close_msg_if_terminal, msg.status 闭环到 closed
- test_dismiss_action_marks_msg_dismissed — PA dismiss action → msg.status=dismissed

uv run pytest tests/server/test_taskboard_*.py -v
  total: 36 passed + 3 skipped + 0 failed in 0.27s
  - Phase 0 14 用例继续 pass (regression-free)
  - Phase 1 part A 18 用例继续 pass (regression-free, 15 + 3 skipped 维持)
  - Phase 1 part B 4 新用例 全 pass

# Phase 1 part B vs 后续 part 边界

本 PR 焦点 = 重复响应根因消除 (board.append_msg dedup) + Applier 调用的 board 公有方法补全.
原始 charter 'message_cache.json 长期清不掉导致 PA 重复响应' 根因到此实质消除——
当后续 PR 切换 ingestion/scheduler 调 Ingestor.ingest_external 时, dedup 自动生效.

留下次 follow-up PR (10+ 文件 ~400 行改, 单 PR 风险高):
1. primary_agent_service.py _handle_pa_output 改调 DecisionApplier + 删 _reconcile_message_cache_with_tasks
2. executor.py 加 spawn_resume(claude_session_id, prompt) → (run_id, pid)
3. resume_inbox.py 入口改 ResumeApplier.route_resume
4. scheduler_service.py 调 Ingestor.ingest_scheduled
5. ingestion/scheduler.py 摘 message_cache + 调 Ingestor.ingest_external
6-8. 删除 ingestion/store.py + models.py + thread_service.py + cli/thread_commands.py
9. 同步改 reflection_tick / task_lifecycle / task_service / cli/task_commands /
   pa_validators / pa_prompts / pa_context_builder (8 个文件)
10. 3 skipped 测试 (T9.5/T9.6/T9.7a) 真实化转 pass (依赖 CLI / spec 后续部署)

理由: 删除 store/models/thread_service 需改 13 个依赖 import (grep 实测), 单 PR 改动量
~500-800 行, 单次 long-context 会话内编译错风险高. 上述 1-10 拆 part B follow-up 跟进.
本 PR 实测 36 passed 不破任何既有功能, 根因回归测试 pass, 可独立 merge.

Refs:
- spec plan v1.1 .claude/docs/spec-driven-plan/20260512-msg-task-board-redesign.md
- jsonl charter line 2: 'message_cache.json 长期清不掉导致 PA 重复响应' (根因)
- Phase 0 PR #67 (commit 70f2fe4, squash merge)
- Phase 1 part A PR #68 (commit cc15f89, squash merge)
- HUMAN long-context prompt 触发 part B 实施

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tsaijamey added a commit that referenced this pull request May 12, 2026
…cade + iteration close-out (#74)

Yi #133 + HUMAN #152 + Orchestrator #158 lock Phase 4 范围 (~1500 行原 lock,
不 inflate). 整个 frago Msg/Task 重构迭代收官 PR.

## 4 项 deliverable (Yi #133 lock)

1. **ingestion/store.py 物理删除** ✓
   - 旧路径 src/frago/server/services/ingestion/store.py 已 git rm
   - TaskStore 类迁入 taskboard/legacy_store.py (clean import path, board 单一持
     久化层, IngestedTask 持有执行上下文 sidecar 字段)
   - 9 caller files imports 从 ingestion.store → taskboard.legacy_store cascade

2. **ingestion/models.py 物理删除** ✓
   - 整文件 git rm (114 行)
   - IngestedTask + SubTask + TaskStatus 三类迁入 taskboard/models.py
   - taskboard/__init__.py 增加 TaskStatus 导出
   - 9 caller files imports 从 ingestion.models → taskboard.models cascade

3. **frontend API contract 维持** ✓
   - /pa/tasks endpoint 不破坏 wire 兼容 (返回 IngestedTask asdict 形态)
   - frontend TypeScript IngestedTask 类型 (client/src/types/*) 不动

4. **rename test_vacuum_fold.py + .gitignore allowlist 通配** ✓ (Kai #110 承诺 close-out)
   - tests/server/test_vacuum_fold.py → tests/server/test_taskboard_vacuum_fold.py
   - .gitignore 移除 `!tests/server/test_vacuum_fold.py` 行 (回归 test_taskboard_*.py 通配)

## 验收

- **56 passed + 0 skipped + 0 failed** (含 9 测试模块 regression-free)
- **ruff All checks passed** (touched files)
- **grep verify 0 残留 import**:
  - `from frago.server.services.ingestion.{store,models}` → 0 matches
  - `import frago.server.services.ingestion.{store,models}` → 0 matches
- **production path 3 cases pass** (test_repeat_response_production_path.py 不退化)

## ingestion/ 目录现状

仅保留 __init__.py + scheduler.py (active scheduler 模块, 仍是消息入口).
store.py + models.py 已物理删, legacy 类全部迁入 taskboard layer.

## Iteration close-out 声明

整个 frago Msg/Task 重构迭代 (始于 spec 20260512-msg-task-board-redesign)
通过 8 个 PR 完成:
- PR #67 Phase 0 (timeline.jsonl persistence + 4-applier skeleton)
- PR #68 Phase 1A (4 Applier interfaces + ingest_scheduled)
- PR #69 Phase 1B-1 (msg/task close methods)
- PR #70 Phase 1B-2a (production-path repeat-response root-cause fix)
- PR #71 Phase 2 (vacuum + fold + CLI)
- PR #72 Phase 1B-2b stage 1 (thread_service deletion + 7 board methods)
- PR #73 Phase 3 (PA cleanup full rewrite)
- **本 PR Phase 4 (ingestion 物理删 + cascade) — 收官**

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
@tsaijamey tsaijamey deleted the feat/taskboard-phase1-applier branch May 12, 2026 08:28
tsaijamey added a commit that referenced this pull request May 13, 2026
…heduled + prompt format (#68)

Phase 1 partial commit (spec plan v1.1 §Implementation Phase 1).
final state: 32 passed + 3 skipped (spec 明示留 Phase 2 / 后续 commit) + 0 failed in 7.35s.

# 已完成范围 (interface ready)

新增 src/frago/server/services/taskboard/:
- applier.py: BaseApplier 抽象基类 (_reject / record_rejection 公共逻辑)
- decision_applier.py: PA decisions JSON 路由器
  - VALID_MSG_ACTIONS = {run, reply, resume, dismiss} (spec freeze v1.1 §3 严格 4 个)
  - 词汇校验 → reject(action_invalid)
  - prompt 格式校验 (首行 ≤80 + 空行 + 正文) → reject(prompt_format_invalid)
  - 路由: run / reply → board.append_task; resume → ResumeApplier; dismiss → mark_msg_dismissed
- execution_applier.py: Executor 启动 / 完成回写 board (start_task / finish_task)
- resume_applier.py: resume 三档路由
  - Case A (completed/failed → spawn_resume) 含 FRAGO_CASE_A_ENABLED gate
  - Case A CSID 失效 → task.status=resume_failed (新终态) + timeline resume_csid_lost
  - Case B (executing → ResumeInbox.enqueue + record_resume_pending)
  - 非法 state (queued/replied/resume_failed) → reject(resume_illegal_state)

新增 Ingestor.ingest_scheduled (Gap 3a 修订):
- thread 不归并, 每次触发独立 (origin=scheduled, subkind=job_name)
- Source default: sender_id='__scheduler__', parent_ref=schedule_id
- 同 transaction create_thread + append_msg + append_task(type=run, prompt=锁定值)

pa_validators.py 改造:
- VALID_PA_ACTIONS 加 dismiss (schedule 暂保留兼容, 后续 commit 移除)
- 新增 VALID_MSG_ACTIONS = {run, reply, resume, dismiss} (spec v1.1 §3 严格集)
- 新增 _PA_ACTION_REQUIRED_FIELDS["dismiss"] = ["msg_id"]
- 新增 validate_prompt_format() 函数 (首行 ≤80 / 空行 / 无代码围栏)

# 测试 (Ce specify 13 用例 + supplementary, 共 18 actual + 3 skipped)

tests/server/test_taskboard_decision_applier.py (7 用例):
- T3.1 action_invalid_reject
- T3.2 prompt_format 4 子 case (太长 / 缺空行 / 代码围栏 / valid)
- T3.3 summary 取首行不截断
- T3.4 recent_rejections 暴露

tests/server/test_taskboard_resume_applier.py (6 用例 + 3 skipped):
- T9.1 case_a_not_implemented_reject
- T9.2 拒绝可见性
- T9.3 Case A happy path (FakeExecutor mock)
- T9.4 CSID 失效 → resume_failed
- T9.5 resume_failed 字段值合法 (新终态)
- T9.6 历史 run 轨迹 (skipped, 留 Phase 2 CLI 集成)
- T9.7a CLI 结构 (skipped, 留 Phase 1 后续 commit)
- 补充: resume_executing_case_b_pending / resume_illegal_state

tests/server/test_taskboard_applier_invariants.py (5 用例 + 1 skipped):
- T_B1.1 scheduled fast-path Msg dispatched
- T_B1.2 Source default 完整性 (channel/sender_id/parent_ref/received_at/text/reply_context)
- T_B1.2 Source frozen=True 在 scheduled 路径成立
- T5.2 post_archive_reject (skipped, 留 Phase 2 vacuum)
- 补充: scheduled_thread_not_merged (B1 决议验证)

# Phase 1 vs Phase 1 后续 commit 边界

本 PR 范围 = interface ready + 单元测试通过. 不动主 PA 决策路径, 避免破坏 server.

留 Phase 1 后续 commit:
1. primary_agent_service._handle_pa_output 改调 DecisionApplier (取代 _handle_pa_output 内部分支)
2. executor.spawn_resume 实装 + 抛 ClaudeSessionNotFoundError + ResumeApplier 注入实际 Executor
3. resume_inbox.py 入口改 ResumeApplier.route_resume + ResumeApplier 注入 ResumeInbox
4. scheduler_service.py 调 Ingestor.ingest_scheduled (替代直接 enqueue Executor)
5. ingestion/scheduler.py 摘 message_cache + 改调 Ingestor.ingest_external
6. **删除** ingestion/store.py + ingestion/models.py + thread_service.py
7. 同步改 reflection_tick.py / task_lifecycle.py / task_service.py / cli/task_commands.py 等
8. board.py 加 mark_task_replied / close_msg_if_terminal / mark_msg_dismissed 公有方法
9. pa_validators.VALID_PA_ACTIONS 移除 schedule (与 scheduler_service 改造同步)
10. pa_prompts.py 补 4 action 词汇 / prompt 格式契约 / recent_rejections 三段

理由: 上述 PA wire-up + 删除三文件 + 改 10+ 依赖, 是 Phase 1 大改造范围.
单 commit 推上去会让 frago 整体不可启动 (类似 Phase 0 项 4/5 推迟逻辑).
本 commit 焦点 = Applier 接口 + 4 action 词汇 + Case A/B 路由骨架 + 单元覆盖.

Refs:
- spec plan v1.1 .claude/docs/spec-driven-plan/20260512-msg-task-board-redesign.md
- jsonl Ce specify 14 fixture (00:18:41) + Phase 1 13 用例 (specify 后续)
- jsonl Yi approve Phase 0 (08:11:23) + Phase 1 边界 acknowledge (08:21:59)
- HUMAN merge PR #67 (squash commit ec937a6) → Phase 1 触发

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tsaijamey added a commit that referenced this pull request May 13, 2026
…g/task close methods (#69)

Phase 1 part B 核心: 修原始 'PA 重复响应' 根因 (jsonl charter line 2 描述的核心 bug)
+ 补 Phase 1 part A 漏掉的 board 公有方法.

# 已完成范围 (核心根因消除)

board.py:
1. append_msg 内部 dedup — 同 msg_id 重复 ingest 直接 return + 落 duplicate_msg_ingest timeline.
   替代旧 ingestion/scheduler.message_cache 兜底机制 (那是 _reconcile_message_cache_with_tasks
   补丁逻辑, 引入"启动恢复时哪些 cache 项算 orphan"的判定模糊). dedup 在 board 公有方法内做,
   单点入口, 状态机闭环.
2. append_msg 自动 transition received → awaiting_decision (spec §2 写"立即转换 (心跳前)",
   Phase 0 未实施, Phase 1 part B 在 append_msg 内补).
3. mark_task_replied(task_id, by) — reply task 推送完成后 task.status='replied'.
4. close_msg_if_terminal(msg_id, by) — 全部 task 终态 + ≥1 reply 已 replied → msg.status='closed'.
5. mark_msg_dismissed(msg_id, reason, by) — PA dismiss action 路径, msg.status='dismissed' 终态.

# 测试 (集成回归用例 + 现有覆盖)

tests/server/test_taskboard_repeat_response_regression.py (4 用例 全 pass):
- test_repeat_ingest_10x_produces_one_task — 同 msg_id ingest 10 次 → 仅 1 个 msg + 1 个 task,
  timeline 含 9 条 duplicate_msg_ingest entry (charter 描述的根因回归证据)
- test_pa_not_invoked_for_duplicate_msg — view_for_pa 第 2-10 次 ingest 后 msg.status 始终
  dispatched, PA 不再触发新 decision
- test_mark_task_replied_and_close_msg — DecisionApplier reply 路径自动调 mark_task_replied
  + close_msg_if_terminal, msg.status 闭环到 closed
- test_dismiss_action_marks_msg_dismissed — PA dismiss action → msg.status=dismissed

uv run pytest tests/server/test_taskboard_*.py -v
  total: 36 passed + 3 skipped + 0 failed in 0.27s
  - Phase 0 14 用例继续 pass (regression-free)
  - Phase 1 part A 18 用例继续 pass (regression-free, 15 + 3 skipped 维持)
  - Phase 1 part B 4 新用例 全 pass

# Phase 1 part B vs 后续 part 边界

本 PR 焦点 = 重复响应根因消除 (board.append_msg dedup) + Applier 调用的 board 公有方法补全.
原始 charter 'message_cache.json 长期清不掉导致 PA 重复响应' 根因到此实质消除——
当后续 PR 切换 ingestion/scheduler 调 Ingestor.ingest_external 时, dedup 自动生效.

留下次 follow-up PR (10+ 文件 ~400 行改, 单 PR 风险高):
1. primary_agent_service.py _handle_pa_output 改调 DecisionApplier + 删 _reconcile_message_cache_with_tasks
2. executor.py 加 spawn_resume(claude_session_id, prompt) → (run_id, pid)
3. resume_inbox.py 入口改 ResumeApplier.route_resume
4. scheduler_service.py 调 Ingestor.ingest_scheduled
5. ingestion/scheduler.py 摘 message_cache + 调 Ingestor.ingest_external
6-8. 删除 ingestion/store.py + models.py + thread_service.py + cli/thread_commands.py
9. 同步改 reflection_tick / task_lifecycle / task_service / cli/task_commands /
   pa_validators / pa_prompts / pa_context_builder (8 个文件)
10. 3 skipped 测试 (T9.5/T9.6/T9.7a) 真实化转 pass (依赖 CLI / spec 后续部署)

理由: 删除 store/models/thread_service 需改 13 个依赖 import (grep 实测), 单 PR 改动量
~500-800 行, 单次 long-context 会话内编译错风险高. 上述 1-10 拆 part B follow-up 跟进.
本 PR 实测 36 passed 不破任何既有功能, 根因回归测试 pass, 可独立 merge.

Refs:
- spec plan v1.1 .claude/docs/spec-driven-plan/20260512-msg-task-board-redesign.md
- jsonl charter line 2: 'message_cache.json 长期清不掉导致 PA 重复响应' (根因)
- Phase 0 PR #67 (commit ec937a6, squash merge)
- Phase 1 part A PR #68 (commit 44841bb, squash merge)
- HUMAN long-context prompt 触发 part B 实施

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tsaijamey added a commit that referenced this pull request May 13, 2026
…cade + iteration close-out (#74)

Yi #133 + HUMAN #152 + Orchestrator #158 lock Phase 4 范围 (~1500 行原 lock,
不 inflate). 整个 frago Msg/Task 重构迭代收官 PR.

## 4 项 deliverable (Yi #133 lock)

1. **ingestion/store.py 物理删除** ✓
   - 旧路径 src/frago/server/services/ingestion/store.py 已 git rm
   - TaskStore 类迁入 taskboard/legacy_store.py (clean import path, board 单一持
     久化层, IngestedTask 持有执行上下文 sidecar 字段)
   - 9 caller files imports 从 ingestion.store → taskboard.legacy_store cascade

2. **ingestion/models.py 物理删除** ✓
   - 整文件 git rm (114 行)
   - IngestedTask + SubTask + TaskStatus 三类迁入 taskboard/models.py
   - taskboard/__init__.py 增加 TaskStatus 导出
   - 9 caller files imports 从 ingestion.models → taskboard.models cascade

3. **frontend API contract 维持** ✓
   - /pa/tasks endpoint 不破坏 wire 兼容 (返回 IngestedTask asdict 形态)
   - frontend TypeScript IngestedTask 类型 (client/src/types/*) 不动

4. **rename test_vacuum_fold.py + .gitignore allowlist 通配** ✓ (Kai #110 承诺 close-out)
   - tests/server/test_vacuum_fold.py → tests/server/test_taskboard_vacuum_fold.py
   - .gitignore 移除 `!tests/server/test_vacuum_fold.py` 行 (回归 test_taskboard_*.py 通配)

## 验收

- **56 passed + 0 skipped + 0 failed** (含 9 测试模块 regression-free)
- **ruff All checks passed** (touched files)
- **grep verify 0 残留 import**:
  - `from frago.server.services.ingestion.{store,models}` → 0 matches
  - `import frago.server.services.ingestion.{store,models}` → 0 matches
- **production path 3 cases pass** (test_repeat_response_production_path.py 不退化)

## ingestion/ 目录现状

仅保留 __init__.py + scheduler.py (active scheduler 模块, 仍是消息入口).
store.py + models.py 已物理删, legacy 类全部迁入 taskboard layer.

## Iteration close-out 声明

整个 frago Msg/Task 重构迭代 (始于 spec 20260512-msg-task-board-redesign)
通过 8 个 PR 完成:
- PR #67 Phase 0 (timeline.jsonl persistence + 4-applier skeleton)
- PR #68 Phase 1A (4 Applier interfaces + ingest_scheduled)
- PR #69 Phase 1B-1 (msg/task close methods)
- PR #70 Phase 1B-2a (production-path repeat-response root-cause fix)
- PR #71 Phase 2 (vacuum + fold + CLI)
- PR #72 Phase 1B-2b stage 1 (thread_service deletion + 7 board methods)
- PR #73 Phase 3 (PA cleanup full rewrite)
- **本 PR Phase 4 (ingestion 物理删 + cascade) — 收官**

Co-authored-by: Claude Opus 4.7 <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