diff --git a/crates/skilllite-assistant/src-tauri/src/skilllite_bridge/integrations/skill_rpc.rs b/crates/skilllite-assistant/src-tauri/src/skilllite_bridge/integrations/skill_rpc.rs index 98da629a..8b312aa6 100644 --- a/crates/skilllite-assistant/src-tauri/src/skilllite_bridge/integrations/skill_rpc.rs +++ b/crates/skilllite-assistant/src-tauri/src/skilllite_bridge/integrations/skill_rpc.rs @@ -141,7 +141,10 @@ pub fn repair_skills( let mut cmd = std::process::Command::new(skilllite_path); crate::windows_spawn::hide_child_console(&mut cmd); - cmd.arg("evolution").arg("repair-skills"); + cmd.arg("evolution") + .arg("repair-skills") + .arg("--workspace") + .arg(root.to_string_lossy().as_ref()); for name in skill_names { cmd.arg(name); } diff --git a/crates/skilllite-commands/src/evolution.rs b/crates/skilllite-commands/src/evolution.rs index b7ce40c5..ae6c4b72 100644 --- a/crates/skilllite-commands/src/evolution.rs +++ b/crates/skilllite-commands/src/evolution.rs @@ -25,32 +25,11 @@ use skilllite_agent::types::AgentConfig; use crate::error::bail; use crate::Result; -use skilllite_core::config::env_keys::paths as env_paths; use skilllite_core::paths; use skilllite_core::protocol::{NewSkill, NodeResult}; use skilllite_core::skill::discovery::resolve_skills_dir_with_legacy_fallback; use skilllite_core::skill::manifest; -/// Resolve workspace for legacy project-level skill commands. -/// Uses SKILLLITE_WORKSPACE env or current_dir. Returns workspace/.skills. -fn resolve_skills_root(workspace: Option<&str>) -> Option { - let ws: PathBuf = workspace - .filter(|s| !s.is_empty()) - .map(PathBuf::from) - .or_else(|| { - std::env::var(env_paths::SKILLLITE_WORKSPACE) - .ok() - .map(PathBuf::from) - }) - .unwrap_or_else(|| std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))); - let ws = if ws.is_absolute() { - ws - } else { - std::env::current_dir().ok()?.join(ws) - }; - Some(ws.join(".skills")) -} - fn resolve_run_skills_root(workspace: &str) -> PathBuf { let ws = crate::evolution_status::resolve_workspace_root(workspace); resolve_skills_dir_with_legacy_fallback(&ws, "skills").effective_path @@ -254,7 +233,7 @@ pub fn cmd_backlog( } /// `skilllite evolution reset` — delete all evolved data, return to seed state. -pub fn cmd_reset(force: bool) -> Result<()> { +pub fn cmd_reset(workspace: &str, force: bool) -> Result<()> { if !force { println!("⚠️ 这将删除所有进化产物(规则、示例、Skill),回到种子状态。"); println!(" 已有进化经验将永久丢失。种子规则不受影响。"); @@ -263,15 +242,15 @@ pub fn cmd_reset(force: bool) -> Result<()> { return Ok(()); } - let root = paths::chat_root(); + let root = crate::evolution_status::chat_root_for_workspace(workspace); // Re-seed prompts (overwrite evolved rules/examples with seed data) skilllite_evolution::seed::ensure_seed_data_force(&root); println!("✅ Prompts 已重置为种子状态"); // Remove evolved skills (project-level, includes _pending) - let evolved_dir = resolve_skills_root(None).map(|sr| sr.join("_evolved")); - if let Some(evolved_dir) = evolved_dir.filter(|p| p.exists()) { + let evolved_dir = resolve_run_skills_root(workspace).join("_evolved"); + if evolved_dir.exists() { let count = std::fs::read_dir(&evolved_dir) .ok() .into_iter() @@ -776,10 +755,14 @@ fn is_fetchable_source(source: &str) -> bool { /// `skilllite evolution repair-skills [SKILL_NAME...]` — 验证技能并修复失败的。 /// 不传技能名时验证并修复所有失败技能;传一个或多个技能名时仅验证并修复这些技能,缩短执行时间。 /// `from_source`: 对下载的技能失败时自动从源头更新,不交互询问(桌面/CI 等非 TTY 时传 true)。 -pub fn cmd_repair_skills(skills_filter: Option>, from_source: bool) -> Result<()> { - let skills_root = resolve_skills_root(None).ok_or_else(|| { - crate::Error::validation("无法解析工作区。请设置 SKILLLITE_WORKSPACE 或在项目目录运行。") - })?; +pub fn cmd_repair_skills( + workspace: &str, + skills_filter: Option>, + from_source: bool, +) -> Result<()> { + let ws_root = crate::evolution_status::resolve_workspace_root(workspace); + skilllite_core::config::load_dotenv_from_dir(&ws_root); + let skills_root = resolve_run_skills_root(workspace); let config = AgentConfig::from_env(); if config.api_key.is_empty() { diff --git a/docs/en/ASSISTANT-SPLIT-ARCHITECTURE.md b/docs/en/ASSISTANT-SPLIT-ARCHITECTURE.md index 693f6c12..eeaba5a1 100644 --- a/docs/en/ASSISTANT-SPLIT-ARCHITECTURE.md +++ b/docs/en/ASSISTANT-SPLIT-ARCHITECTURE.md @@ -126,6 +126,8 @@ Priority commands for parity with today’s Desktop bridge: | `skilllite evolution proposal-status --json ` | `EvolutionProposalStatusSnapshot` | **Shipped**; `--workspace` | | `skilllite evolution confirm/reject --json` | `EvolutionOpSnapshot` | **Shipped**; `--workspace` | | `skilllite evolution run --json` | `NodeResult` | **Shipped**; `--workspace`, `--proposal-id`, `--log-manual-trigger` | +| `skilllite evolution reset --workspace` | human output | **Shipped**; destructive reset is scoped to the selected workspace `chat/` and effective skills root | +| `skilllite evolution repair-skills --workspace` | human output | **Shipped**; desktop repair validates the selected workspace skills root (`skills/` with `.skills` fallback) | | `skilllite runtime probe --json` | `RuntimeUiSnapshot` | **Shipped** | | `skilllite runtime provision --json` | stderr progress JSON lines + `ProvisionRuntimesResult` on stdout | **Shipped**; `--python` / `--node` / `--force` | | `skilllite skills list --json --workspace` | `DesktopSkillSnapshot[]` (desktop `DesktopSkillInfo`) | **Shipped** | diff --git a/docs/zh/ASSISTANT-SPLIT-ARCHITECTURE.md b/docs/zh/ASSISTANT-SPLIT-ARCHITECTURE.md index 3227f2bd..67d2f05a 100644 --- a/docs/zh/ASSISTANT-SPLIT-ARCHITECTURE.md +++ b/docs/zh/ASSISTANT-SPLIT-ARCHITECTURE.md @@ -128,6 +128,8 @@ flowchart TB | `skilllite evolution proposal-status --json` | 单条 backlog | **已落地**;`--workspace` | | `skilllite evolution confirm/reject --json` | 操作结果 | **已落地**;`--workspace` | | `skilllite evolution run --json` | `NodeResult` | **已落地**;`--workspace`、`--proposal-id`、`--log-manual-trigger` | +| `skilllite evolution reset --workspace` | 人类可读输出 | **已落地**;破坏性重置限定在所选工作区的 `chat/` 与有效技能根 | +| `skilllite evolution repair-skills --workspace` | 人类可读输出 | **已落地**;桌面修复验证所选工作区技能根(`skills/`,缺失时回退 `.skills`) | | `skilllite runtime probe --json` | `RuntimeUiSnapshot` | **已落地** | | `skilllite runtime provision --json` | stderr 进度 JSON 行 + stdout `ProvisionRuntimesResult` | **已落地**;`--python` / `--node` / `--force` | | `skilllite skills list --json --workspace` | `DesktopSkillSnapshot[]`(对齐 `DesktopSkillInfo`) | **已落地** | diff --git a/skilllite/src/cli.rs b/skilllite/src/cli.rs index 865d6c91..5cb5c0c3 100644 --- a/skilllite/src/cli.rs +++ b/skilllite/src/cli.rs @@ -1157,6 +1157,9 @@ pub enum EvolutionAction { /// Reset to seed state — delete all evolved rules, examples, and skills Reset { + /// Project workspace root + #[arg(long, short = 'w', default_value = ".")] + workspace: String, /// Skip confirmation prompt #[arg(long, short)] force: bool, @@ -1226,6 +1229,10 @@ pub enum EvolutionAction { /// Repair skills: validate then LLM-fix failures. Without names, repair all failed; with names, only validate/repair those (faster when many skills). RepairSkills { + /// Project workspace root + #[arg(long, short = 'w', default_value = ".")] + workspace: String, + /// 仅验证并修复这些技能(目录名);不传则处理全部失败技能 #[arg(value_name = "SKILL_NAME")] skills: Vec, diff --git a/skilllite/src/dispatch/mod.rs b/skilllite/src/dispatch/mod.rs index 40059028..543688df 100644 --- a/skilllite/src/dispatch/mod.rs +++ b/skilllite/src/dispatch/mod.rs @@ -526,8 +526,8 @@ fn register_agent(reg: &mut CommandRegistry) { workspace, proposal_id, ), - EvolutionAction::Reset { force } => { - skilllite_commands::evolution::cmd_reset(*force) + EvolutionAction::Reset { workspace, force } => { + skilllite_commands::evolution::cmd_reset(workspace, *force) } EvolutionAction::Disable { rule_id } => { skilllite_commands::evolution::cmd_disable(rule_id) @@ -566,9 +566,11 @@ fn register_agent(reg: &mut CommandRegistry) { *log_manual_trigger, ), EvolutionAction::RepairSkills { + workspace, skills, from_source, } => skilllite_commands::evolution::cmd_repair_skills( + workspace, if skills.is_empty() { None } else { diff --git a/skilllite/tests/cli_evolution_workspace.rs b/skilllite/tests/cli_evolution_workspace.rs index 63699a37..c4ecf134 100644 --- a/skilllite/tests/cli_evolution_workspace.rs +++ b/skilllite/tests/cli_evolution_workspace.rs @@ -2,7 +2,7 @@ mod common; -use common::{skilllite_bin, stdout_str}; +use common::{create_calculator_skill, skilllite_bin, stdout_str}; use std::path::Path; use std::process::{Command, Output}; @@ -40,6 +40,21 @@ fn authorize_capability(workspace: &Path, tool_name: &str) { ); } +fn write_file(path: &Path, content: &str) { + std::fs::create_dir_all(path.parent().expect("file parent")).expect("create parent"); + std::fs::write(path, content).expect("write file"); +} + +fn run_reset(args: &[&str], current_dir: &Path, env_workspace: &Path) -> Output { + Command::new(skilllite_bin()) + .args(args) + .current_dir(current_dir) + .env("NO_COLOR", "1") + .env("SKILLLITE_WORKSPACE", env_workspace) + .output() + .expect("failed to spawn skilllite") +} + #[test] fn evolution_backlog_workspace_flag_overrides_env_workspace() { let env_workspace = tempfile::tempdir().expect("env workspace"); @@ -88,3 +103,121 @@ fn evolution_backlog_workspace_flag_overrides_env_workspace() { "env workspace backlog row should not leak into target query: {notes:?}" ); } + +#[test] +fn evolution_reset_defaults_to_current_workspace_roots() { + let env_workspace = tempfile::tempdir().expect("env workspace"); + let target_workspace = tempfile::tempdir().expect("target workspace"); + + let env_rules = env_workspace.path().join("chat/prompts/rules.json"); + let target_rules = target_workspace.path().join("chat/prompts/rules.json"); + write_file(&env_rules, "env rules must remain"); + write_file(&target_rules, "target evolved rules"); + std::fs::create_dir_all(target_workspace.path().join("skills/_evolved/generated")) + .expect("create evolved skills"); + + let out = run_reset( + &["evolution", "reset", "--force"], + target_workspace.path(), + env_workspace.path(), + ); + + assert!( + out.status.success(), + "reset failed: {}", + String::from_utf8_lossy(&out.stderr) + ); + assert_eq!( + std::fs::read_to_string(&env_rules).expect("env rules still exist"), + "env rules must remain" + ); + assert_ne!( + std::fs::read_to_string(&target_rules).expect("target rules reseeded"), + "target evolved rules" + ); + assert!( + !target_workspace.path().join("skills/_evolved").exists(), + "reset should remove evolved skills under the current workspace skills root" + ); +} + +#[test] +fn evolution_reset_workspace_flag_overrides_env_workspace_roots() { + let env_workspace = tempfile::tempdir().expect("env workspace"); + let target_workspace = tempfile::tempdir().expect("target workspace"); + let cwd = tempfile::tempdir().expect("cwd"); + + let env_rules = env_workspace.path().join("chat/prompts/rules.json"); + let target_rules = target_workspace.path().join("chat/prompts/rules.json"); + write_file(&env_rules, "env rules must remain"); + write_file(&target_rules, "target evolved rules"); + std::fs::create_dir_all(target_workspace.path().join("skills/_evolved/generated")) + .expect("create evolved skills"); + + let target_arg = target_workspace.path().to_string_lossy(); + let out = run_reset( + &[ + "evolution", + "reset", + "--force", + "--workspace", + target_arg.as_ref(), + ], + cwd.path(), + env_workspace.path(), + ); + + assert!( + out.status.success(), + "reset failed: {}", + String::from_utf8_lossy(&out.stderr) + ); + assert_eq!( + std::fs::read_to_string(&env_rules).expect("env rules still exist"), + "env rules must remain" + ); + assert_ne!( + std::fs::read_to_string(&target_rules).expect("target rules reseeded"), + "target evolved rules" + ); + assert!( + !target_workspace.path().join("skills/_evolved").exists(), + "reset should remove evolved skills under the explicit workspace skills root" + ); +} + +#[test] +fn evolution_repair_skills_prefers_workspace_skills_over_legacy() { + let workspace = tempfile::tempdir().expect("workspace"); + let cwd = tempfile::tempdir().expect("cwd"); + std::fs::create_dir_all(workspace.path().join("skills")).expect("create modern skills root"); + create_calculator_skill(workspace.path()); + + let workspace_arg = workspace.path().to_string_lossy(); + let out = Command::new(skilllite_bin()) + .args([ + "evolution", + "repair-skills", + "--workspace", + workspace_arg.as_ref(), + "--from-source", + ]) + .current_dir(cwd.path()) + .env("NO_COLOR", "1") + .env("API_KEY", "sk-test") + .env("OPENAI_API_KEY", "sk-test") + .output() + .expect("failed to spawn skilllite"); + + assert!( + out.status.success(), + "repair-skills should validate the empty modern skills root instead of legacy .skills: stdout={}, stderr={}", + String::from_utf8_lossy(&out.stdout), + String::from_utf8_lossy(&out.stderr) + ); + assert!( + stdout_str(&out).contains("所有技能验证通过"), + "repair-skills stdout should report no repairs needed: {}", + stdout_str(&out) + ); +} diff --git a/tasks/TASK-2026-070-evolution-reset-repair-workspace/CONTEXT.md b/tasks/TASK-2026-070-evolution-reset-repair-workspace/CONTEXT.md new file mode 100644 index 00000000..fdf3facd --- /dev/null +++ b/tasks/TASK-2026-070-evolution-reset-repair-workspace/CONTEXT.md @@ -0,0 +1,49 @@ +# Technical Context + +## Current State + +- Relevant crates/files: + - `skilllite/src/cli.rs`: `EvolutionAction` argument definitions. + - `skilllite/src/dispatch/mod.rs`: CLI dispatch to command crate. + - `crates/skilllite-commands/src/evolution.rs`: reset, run, repair command implementations. + - `crates/skilllite-commands/src/evolution_status.rs`: workspace and chat-root helpers. + - `crates/skilllite-core/src/skill/discovery.rs`: effective skills-root resolver. + - `crates/skilllite-assistant/src-tauri/src/skilllite_bridge/integrations/skill_rpc.rs`: desktop repair subprocess. +- Current behavior: + - `cmd_run` resolves the requested workspace, sets `SKILLLITE_WORKSPACE` to the absolute workspace, uses `paths::chat_root()`, and uses `resolve_skills_dir_with_legacy_fallback`. + - `cmd_reset` does not accept a workspace and directly uses `paths::chat_root()`, which ignores relative `SKILLLITE_WORKSPACE` values. + - `cmd_reset` deletes only `/.skills/_evolved` through a legacy helper. + - `cmd_repair_skills` validates only the legacy helper result, so `skills/_evolved` can be skipped. + +## Architecture Fit + +- Layer boundaries involved: + - Entry CLI (`skilllite`) remains responsible for argument parsing and dispatch. + - `skilllite-commands` remains responsible for command behavior. + - `skilllite-core` continues to provide shared skills-root discovery. +- Interfaces to preserve: + - Existing command names and `--from-source` repair behavior. + - Legacy `.skills` fallback when `skills/` does not exist. + +## Dependency and Compatibility + +- New dependencies: None. +- Backward compatibility notes: + - New `--workspace/-w` flags align reset/repair with adjacent evolution commands. + - Default workspace `"."` changes reset away from legacy global fallback, but fixes the destructive split-brain behavior and remains explicit for callers. + +## Design Decisions + +- Decision: Reuse `resolve_run_skills_root` for reset and repair. + - Rationale: It already implements the modern `skills/` then `.skills` fallback used by `evolution run`. + - Alternatives considered: Keep the old helper and special-case `_evolved`. + - Why rejected: The old helper encodes the broken `.skills`-only behavior. +- Decision: Use `chat_root_for_workspace` for reset. + - Rationale: It gives deterministic `/chat` behavior for relative and absolute workspaces without mutating process-global environment. + - Alternatives considered: Set `SKILLLITE_WORKSPACE` and keep `paths::chat_root()`. + - Why rejected: Environment mutation is unnecessary and easier to get wrong in tests or embedded callers. + +## Open Questions + +- [x] Should unrelated legacy commands be changed in this task? No; keep the fix scoped to reset/repair because they are the concrete high-impact paths found. +- [x] Are docs required? No user docs currently document reset/repair workspace flags in detail; PR/task artifacts and CLI help cover the new flags. diff --git a/tasks/TASK-2026-070-evolution-reset-repair-workspace/PRD.md b/tasks/TASK-2026-070-evolution-reset-repair-workspace/PRD.md new file mode 100644 index 00000000..be5e0397 --- /dev/null +++ b/tasks/TASK-2026-070-evolution-reset-repair-workspace/PRD.md @@ -0,0 +1,42 @@ +# PRD + +## Background + +SkillLite evolution data is now project-scoped for desktop and CLI L2 flows. `status`, `backlog`, `pending`, `confirm`, `reject`, and `run` accept or derive a workspace and use project-local `chat/` plus the effective skills root (`skills/` with `.skills` fallback). + +The remaining maintenance commands `reset` and `repair-skills` did not move with that contract. A destructive reset can therefore modify the wrong chat store while leaving current evolved skills in place, and repair can silently validate an empty legacy tree. + +## Objective + +Make evolution reset and repair target the same workspace roots as the rest of the evolution command surface. + +Prevent split-brain behavior where chat state and evolved skills are reset or validated in different workspaces. + +## Functional Requirements + +- FR-1: `evolution reset` MUST accept `--workspace/-w` with default `"."` and use `/chat` for prompt/log/snapshot reset. +- FR-2: `evolution reset --force` MUST remove `_evolved` under the effective skills root for the selected workspace. +- FR-3: `evolution repair-skills` MUST accept `--workspace/-w` with default `"."` and validate the effective skills root for that workspace. +- FR-4: Desktop repair calls MUST pass the resolved workspace explicitly to the CLI subprocess. + +## Non-Functional Requirements + +- Security: No sandbox, permission, or LLM credential handling changes. +- Performance: Root resolution remains filesystem-only and should not add meaningful overhead. +- Compatibility: Legacy `.skills` workspaces continue to work through existing fallback behavior; explicit workspace selection is available for non-current workspaces. + +## Constraints + +- Technical: Keep dependency direction unchanged (`skilllite` entry dispatches into `skilllite-commands`). +- Timeline: N/A for autonomous execution; this is a focused CLI bugfix. + +## Success Metrics + +- Metric: Regression tests cover reset root selection and repair root selection. +- Baseline: Reset/repair use legacy `.skills` or global chat roots in at least one common workspace scenario. +- Target: Tests fail on the old behavior and pass with workspace-aware resolution. + +## Rollout + +- Rollout plan: Ship as a CLI/desktop bugfix with task evidence and focused tests. +- Rollback plan: Revert the CLI argument and resolver changes if unexpected compatibility issues are found. diff --git a/tasks/TASK-2026-070-evolution-reset-repair-workspace/REVIEW.md b/tasks/TASK-2026-070-evolution-reset-repair-workspace/REVIEW.md new file mode 100644 index 00000000..5dec0695 --- /dev/null +++ b/tasks/TASK-2026-070-evolution-reset-repair-workspace/REVIEW.md @@ -0,0 +1,49 @@ +# Review Report + +## Scope Reviewed + +- Files/modules: + - `crates/skilllite-commands/src/evolution.rs` + - `skilllite/src/cli.rs` + - `skilllite/src/dispatch/mod.rs` + - `crates/skilllite-assistant/src-tauri/src/skilllite_bridge/integrations/skill_rpc.rs` + - `skilllite/tests/cli_evolution_workspace.rs` + - `docs/en/ASSISTANT-SPLIT-ARCHITECTURE.md` + - `docs/zh/ASSISTANT-SPLIT-ARCHITECTURE.md` +- Commits/changes: + - Initial implementation commit: `64f28da fix(evolution): scope reset and repair to workspace roots` + - Final task-evidence updates recorded after validation. + +## Findings + +- Critical: Fixed confirmed workspace split-brain bug where `evolution reset --force` could reset a different chat root and miss `skills/_evolved`. +- Major: Fixed repair path where `evolution repair-skills` could validate legacy `.skills` and report success while modern `skills/_evolved` remained unvalidated. +- Minor: None. + +## Quality Gates + +- Architecture boundary checks: `pass` +- Security invariants: `pass` +- Required tests executed: `pass` +- Docs sync (EN/ZH): `pass` + +## Test Evidence + +- Commands run: + - `cargo test -p skilllite --test cli_evolution_workspace` + - `cargo fmt --check` + - `python3 scripts/validate_tasks.py` + - `cargo clippy --all-targets -- -D warnings` + - `cargo test` + - `cargo test -p skilllite` +- Key outputs: + - Focused test: `test result: ok. 4 passed; 0 failed`. + - Task validation: `Task validation passed (70 task directories checked).` + - Clippy: `Finished dev profile ...` + - Full test suite: final doc-tests completed with `test result: ok`. + - Package test: `skilllite` integration tests including `cli_evolution_workspace` passed. + +## Decision + +- Merge readiness: `ready` +- Follow-up actions: Consider a separate task for workspace-scoping `disable` / `explain` if those legacy prompt-maintenance commands need project-local behavior. diff --git a/tasks/TASK-2026-070-evolution-reset-repair-workspace/STATUS.md b/tasks/TASK-2026-070-evolution-reset-repair-workspace/STATUS.md new file mode 100644 index 00000000..95fa6e78 --- /dev/null +++ b/tasks/TASK-2026-070-evolution-reset-repair-workspace/STATUS.md @@ -0,0 +1,25 @@ +# Status Journal + +## Timeline + +- 2026-07-08: + - Progress: Confirmed that `evolution reset` and `repair-skills` still use legacy/global roots after recent workspace-scoping fixes. Drafted task, PRD, and context before implementation. + - Blockers: None. + - Next step: Implement workspace-aware reset/repair and add focused regression tests. +- 2026-07-08: + - Progress: Implemented workspace-aware `reset` and `repair-skills`, added CLI/desktop argument propagation, updated EN/ZH architecture docs, and added regression tests. + - Blockers: None. + - Next step: Open PR after final commit/push. +- 2026-07-08: + - Progress: Validation passed: `cargo fmt --check`; `cargo clippy --all-targets -- -D warnings`; `cargo test`; `cargo test -p skilllite`; `cargo test -p skilllite --test cli_evolution_workspace` (4 passed); `python3 scripts/validate_tasks.py` (70 task directories checked). + - Blockers: None. + - Next step: None. + +## Checkpoints + +- [x] PRD drafted before implementation (or `N/A` recorded) +- [x] Context drafted before implementation (or `N/A` recorded) +- [x] Implementation complete +- [x] Tests passed +- [x] Review complete +- [x] Board updated diff --git a/tasks/TASK-2026-070-evolution-reset-repair-workspace/TASK.md b/tasks/TASK-2026-070-evolution-reset-repair-workspace/TASK.md new file mode 100644 index 00000000..ddcf89dd --- /dev/null +++ b/tasks/TASK-2026-070-evolution-reset-repair-workspace/TASK.md @@ -0,0 +1,80 @@ +# TASK Card + +## Metadata + +- Task ID: `TASK-2026-070` +- Title: Fix evolution reset and repair workspace roots +- Status: `done` +- Priority: `P0` +- Owner: `agent` +- Contributors: +- Created: `2026-07-08` +- Target milestone: critical bugfix + +## Problem + +Recent workspace-scoping fixes aligned `evolution run`, status, backlog, pending, confirm, and reject with project-local `chat/` and `skills/` roots. Two maintenance commands still use legacy resolution: + +- `evolution reset --force` uses `paths::chat_root()` and deletes only `.skills/_evolved`. +- `evolution repair-skills` validates only `.skills`. + +In a modern workspace that uses `skills/`, this can reset the wrong chat store, leave evolved skills behind after a destructive reset, or report successful repair while broken evolved skills remain unvalidated. + +## Scope + +- In scope: + - Add workspace-aware root resolution for `evolution reset`. + - Align `evolution repair-skills` with the same effective `skills/` then `.skills/` fallback used by evolution run/pending/confirm. + - Add regression coverage for modern `skills/` workspaces. +- Out of scope: + - Changing LLM repair logic. + - Refactoring unrelated legacy commands (`disable`, `explain`) unless required by tests. + - Changing sandbox or skill execution policy. + +## Acceptance Criteria + +- [x] `skilllite evolution reset --force --workspace ` resets `/chat` and removes `/skills/_evolved`. +- [x] `skilllite evolution reset --force` run from a workspace defaults to that workspace instead of mixing global chat with project skills. +- [x] `skilllite evolution repair-skills --workspace ` validates the effective skills root, preferring `/skills` with legacy `.skills` fallback. +- [x] Focused regression tests cover the destructive reset path without requiring an LLM API key. +- [x] Required Rust formatting, linting, tests, and task validation are run and recorded. + +## Risks + +- Risk: Changing the default reset target from legacy global data to the current workspace can surprise users who intentionally reset `~/.skilllite`. + - Impact: A user may need to pass an explicit workspace for global data. + - Mitigation: Match the workspace default already used by adjacent evolution subcommands and preserve explicit `--workspace` control. +- Risk: Repair now scans a different tree in workspaces that have both `skills/` and `.skills/`. + - Impact: Duplicate skills can remain ambiguous. + - Mitigation: Reuse the existing `resolve_skills_dir_with_legacy_fallback` behavior rather than introducing a new resolver. + +## Validation Plan + +- Required tests: + - Focused integration tests in `skilllite/tests/cli_evolution_workspace.rs`. + - Required command-scope tests from `spec/testing-policy.md`. +- Commands to run: + - `cargo fmt --check` - passed. + - `cargo clippy --all-targets -- -D warnings` - passed. + - `cargo test` - passed. + - `cargo test -p skilllite` - passed. + - `cargo test -p skilllite --test cli_evolution_workspace` - passed (4 tests). + - `python3 scripts/validate_tasks.py` - passed (70 task directories checked). +- Manual checks: + - Re-read modified files and task board after updates. + +## Regression Scope + +- Areas likely affected: + - CLI parsing for `skilllite evolution reset` and `repair-skills`. + - Desktop bridge repair command arguments. + - Evolution workspace root selection. +- Explicit non-goals: + - No changes to skill synthesis prompts or sandbox enforcement. + - No new dependencies. + +## Links + +- Source TODO section: N/A (daily critical bug investigation). +- Related PRs/issues: PR #95, PR #101 workspace-scope fixes. +- Related docs: `docs/en/ASSISTANT-SPLIT-ARCHITECTURE.md`, `docs/zh/ASSISTANT-SPLIT-ARCHITECTURE.md`. diff --git a/tasks/board.md b/tasks/board.md index 708bdcda..e0769652 100644 --- a/tasks/board.md +++ b/tasks/board.md @@ -1,6 +1,6 @@ # Task Board -Last updated: 2026-06-19 (TASK-2026-069 evolution workspace run scope done) +Last updated: 2026-07-08 (TASK-2026-070 evolution reset/repair workspace roots done) ## In Progress @@ -17,6 +17,7 @@ Last updated: 2026-06-19 (TASK-2026-069 evolution workspace run scope done) ## Done +- `TASK-2026-070-evolution-reset-repair-workspace` - Status: `done` - Owner: `agent` - `TASK-2026-069-evolution-workspace-run-scope` - Status: `done` - Owner: `agent` - `TASK-2026-068-evolution-workspace-db-scope` - Status: `done` - Owner: `agent` - `TASK-2026-067-utf8-llm-error-truncate` - Status: `done` - Owner: `agent`