feat: Update project documentation and workflow to reflect changes in task dispatching and testing processes

- Changed references from "kilo" to "Agent 子代理" in various documentation files to align with the new task dispatching method.
- Updated CLAUDE.md to reflect the new agent-based execution model and modified task dispatch template.
- Revised PROGRESS.md to indicate successful testing and acceptance of M1.2 with detailed results.
- Enhanced communication and workflow documentation to clarify the use of Agent 子代理 for real-time collaboration and task management.
- Updated team member soul files with lessons learned from recent testing and integration experiences.
- Refactored test files to remove unused code and improve clarity, ensuring compliance with zero warning standards.
- Documented specific experiences and lessons learned during M1.2 testing, including handling of edge cases and configuration validation.
This commit is contained in:
2026-07-03 14:14:50 +08:00
parent 5770b7007c
commit 2a6e14b976
19 changed files with 280 additions and 147 deletions

View File

@@ -7,18 +7,21 @@
//!
//! 经过代码审查,插件管理 API 的 Custom 消息已在 ServiceManager 中完整处理:
//! - `plugin_enable` / `plugin_disable` → `set_plugin_enabled()` → `broadcast_plugin_states()`
//! - `plugin_rollback` / `plugin_switch` / `plugin_install` / `plugin_check_updates` 已注册但未完整实现业务逻辑
//! - `plugin_rollback` / `plugin_switch` / `plugin_install` 均为完整实现:
//! `rollback_plugin`(service_manager.rs:432) / `switch_plugin_version`(:617) /
//! `install_plugin`(:665),张明远 #25 修复handle_manager_message Custom 分支)后落地
//! - HTTP routes 的 `plugin_enable_route`/`plugin_disable_route` 发送 `Message::Custom{kind:"plugin_enable",...}`
//! 到 `Destination::Manager`ServiceManager 能够接收并处理。
//! - `/api/plugins` 通过 `HttpState::plugin_states()` 读取状态,状态由
//! `Message::Custom{kind:"plugin_states",...}` 广播更新ServiceManager 在每次
//! enable/disable 后调用 `broadcast_plugin_states()` 发出该消息。
//! - **结论**enable/disable 命令闭环已修复CLAUDE.md #25 已修复项),基本链路可工作。
//! plugin_rollback/switch/install/check_updates 仅发送消息ServiceManager 侧仅打印日志,
//! 尚无完整业务逻辑,属 M1.2 已知待实现项。
//! - **结论**Custom 六种 plugin_* kind 全部闭环enable/disable/rollback/switch/install
//! 均为完整实现;唯 `plugin_check_updates` 为部分实现——`check_plugin_updates`(:730)
//! 会真实查询 repo但结果仅 println、不回传 HTTP/不广播,结果回传链路属 Phase 2
//! 待补(非回归缺陷)。详见 2026-07-03 王浩然风险3全量核查报告.showen/TEAM_CHAT.md
use anyhow::Result;
use showen_v2::core::config::AppConfig;
use showen_v2::core::config::{self, AppConfig};
use showen_v2::core::message::{Destination, Envelope, Message, PlayerStatusData};
use showen_v2::core::plugin::{Platform, Plugin, PluginContext, PluginInfo};
use showen_v2::core::service_manager::ServiceManager;
@@ -121,7 +124,6 @@ struct RecordingPlugin {
id: String,
deps: Vec<String>,
events: Arc<Mutex<Vec<String>>>,
ctx: Option<PluginContext>,
}
impl RecordingPlugin {
@@ -130,17 +132,12 @@ impl RecordingPlugin {
id: id.to_string(),
deps: deps.into_iter().map(str::to_string).collect(),
events,
ctx: None,
}
}
fn record(&self, entry: impl Into<String>) {
lock_events(&self.events).push(entry.into());
}
fn sender(&self) -> Option<std::sync::mpsc::Sender<Envelope>> {
self.ctx.as_ref().map(|c| c.tx.clone())
}
}
impl Plugin for RecordingPlugin {
@@ -161,8 +158,7 @@ impl Plugin for RecordingPlugin {
self.deps.clone()
}
fn init(&mut self, ctx: PluginContext) -> Result<()> {
self.ctx = Some(ctx);
fn init(&mut self, _ctx: PluginContext) -> Result<()> {
self.record(format!("init:{}", self.id));
Ok(())
}
@@ -412,9 +408,9 @@ fn test_config_update_with_corrupted_json_does_not_reload() {
)));
manager.start_all().expect("start_all should succeed");
// 写入损坏的 JSON模拟 http 路由验证失败后不写文件Manager 不会收到 reload 请求)
// 在 routes.rs 中 handle_config_update 先用 config::parse_str 验证,验证失败则直接返回 400
// 不会发送 ConfigReloadRequest。因此这里我们验证:不发 ConfigReloadRequest 则不广播 ConfigReloaded。
// routes.rs 的 handle_config_update 先用 config::parse_str 验证请求体,
// 损坏的 JSON 验证失败直接返回 400不写盘、也不发送 ConfigReloadRequest。
// 因此这里验证链路的后半段:不发 ConfigReloadRequest 则不广播 ConfigReloaded。
let sender = manager.sender();
// 不发 ConfigReloadRequest只发 Shutdown
@@ -492,23 +488,39 @@ fn test_playlist_snapshot_includes_current_index() {
fs::remove_dir_all(dir).expect("test dir should be removed");
}
/// 播放列表为空时,系统不应崩溃(边界条件)。
/// 播放列表为空时,配置校验应在入口边界拒绝(防退化)。
///
/// 说明:本测试的原始版本假设空 playlist 配置可以通过 `AppConfig::from_file`
/// 加载并启动 ServiceManager但产品在 config.rs `validate()` 中故意
/// `bail!("playlist 不能为空")`(自 init 提交即存在的校验行为)。
/// 运行时也不存在把 config.playlist 清空的路径:
/// - `DELETE /api/videos/:name` 只删除磁盘上的视频文件,不修改 config.playlist
/// - `POST /api/config` 在写盘前用 `config::parse_str` 验证,空 playlist 直接 400
/// - `ConfigReloadRequest` 走 `from_file`,同样会被 validate 拦下。
/// 因此"运行时空 playlist"在 AppConfig 层不可达,本测试改为锁定两个入口
/// 边界的拒绝行为,防止该校验未来被误删。
#[test]
fn test_empty_playlist_does_not_panic() {
let (mut manager, _events, dir) = setup("empty_playlist", 0);
fn test_empty_playlist_rejected() {
let dir = unique_test_dir("empty_playlist_rejected");
let config_path = write_test_config(&dir, "test-title", 0);
// 空 playlist 仍能正常启动和关闭
let sender = manager.sender();
manager.start_all().expect("start_all with empty playlist should succeed");
sender
.send(Envelope {
from: "test".to_string(),
to: Destination::Manager,
message: Message::Shutdown,
})
.expect("shutdown should send");
// 启动边界AppConfig::from_file 必须拒绝空 playlist
let err = AppConfig::from_file(&config_path)
.expect_err("from_file should reject empty playlist config");
assert!(
format!("{err:#}").contains("playlist 不能为空"),
"from_file error should mention empty playlist, got: {err:#}"
);
manager.run().expect("run with empty playlist should succeed");
// HTTP 边界POST /api/config 依赖 config::parse_str 的这个错误返回 400
// routes.rs handle_config_update 先验证后写盘)
let raw = config_json_with_playlist("test-title", 0);
let err = config::parse_str(&raw, config_path.clone())
.expect_err("parse_str should reject empty playlist config");
assert!(
format!("{err:#}").contains("playlist 不能为空"),
"parse_str error should mention empty playlist, got: {err:#}"
);
fs::remove_dir_all(dir).expect("test dir should be removed");
}