Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/skilllite-agent/src/skills/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub struct LoadedSkill {
/// Load skills from directories, parse SKILL.md, generate tool definitions.
/// Also loads evolved skills from `_evolved/` subdirectories (EVO-4),
/// skipping archived ones based on `.meta.json`.
/// Skills are project-level only: evolution writes to workspace/.skills/_evolved/.
/// Skills are project-level only: evolution writes under the effective workspace skills root.
pub fn load_skills(skill_dirs: &[String]) -> Vec<LoadedSkill> {
let mut skills = Vec::new();

Expand Down
2 changes: 1 addition & 1 deletion crates/skilllite-assistant/src/i18n/messages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ export const enMessages: Record<string, string> = {
"evolution.detail.reviewNoJudgement":
"No system judgement yet (last evolution decision missing or empty).",
"evolution.detail.reviewPendingEmpty":
"No pending skills. Newly evolved skills appear under .skills/_evolved/_pending/.",
"No pending skills. Newly evolved skills appear under skills/_evolved/_pending/ or legacy .skills/_evolved/_pending/.",
"evolution.diff.sectionTitleEvolution": "Evolution",
"evolution.diff.sectionTitleRest": " prompt diff",
"evolution.diff.evolvedBadge": "✨ Evolved this run",
Expand Down
2 changes: 1 addition & 1 deletion crates/skilllite-assistant/src/i18n/messages/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ export const zhMessages: Record<string, string> = {
"evolution.detail.reviewPendingRefresh": "刷新列表",
"evolution.detail.reviewNoJudgement": "暂无系统审核结论(最近一次进化判断未记录或为空)。",
"evolution.detail.reviewPendingEmpty":
"暂无待确认技能。进化生成的新技能会出现在 .skills/_evolved/_pending/。",
"暂无待确认技能。进化生成的新技能会出现在 skills/_evolved/_pending/,或旧版 .skills/_evolved/_pending/。",
"evolution.diff.sectionTitleEvolution": "进化",
"evolution.diff.sectionTitleRest": "变更对比",
"evolution.diff.evolvedBadge": "✨ 本轮已进化",
Expand Down
34 changes: 32 additions & 2 deletions crates/skilllite-commands/src/evolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ use skilllite_core::protocol::{NewSkill, NodeResult};
use skilllite_core::skill::manifest;

/// Resolve workspace for project-level skill evolution.
/// Uses SKILLLITE_WORKSPACE env or current_dir. Returns workspace/.skills.
/// Uses SKILLLITE_WORKSPACE env or current_dir, then applies the shared
/// `skills/` default with `.skills/` legacy fallback policy.
fn resolve_skills_root(workspace: Option<&str>) -> Option<PathBuf> {
let ws: PathBuf = workspace
.filter(|s| !s.is_empty())
Expand All @@ -47,7 +48,10 @@ fn resolve_skills_root(workspace: Option<&str>) -> Option<PathBuf> {
} else {
std::env::current_dir().ok()?.join(ws)
};
Some(ws.join(".skills"))
Some(
skilllite_core::skill::discovery::resolve_skills_dir_with_legacy_fallback(&ws, "skills")
.effective_path,
)
}

#[derive(Debug)]
Expand Down Expand Up @@ -1013,6 +1017,32 @@ pub fn cmd_repair_skills(skills_filter: Option<Vec<String>>, from_source: bool)
mod tests {
use super::*;

#[test]
fn resolve_skills_root_prefers_default_skills_dir_when_present() {
let tmp = tempfile::tempdir().expect("temp workspace");
let default_skills = tmp.path().join("skills");
let legacy_skills = tmp.path().join(".skills");
std::fs::create_dir_all(&default_skills).expect("create skills");
std::fs::create_dir_all(&legacy_skills).expect("create legacy skills");

let resolved = resolve_skills_root(Some(tmp.path().to_string_lossy().as_ref()))
.expect("resolve skills root");

assert_eq!(resolved, default_skills);
}

#[test]
fn resolve_skills_root_falls_back_to_legacy_skills_dir() {
let tmp = tempfile::tempdir().expect("temp workspace");
let legacy_skills = tmp.path().join(".skills");
std::fs::create_dir_all(&legacy_skills).expect("create legacy skills");

let resolved = resolve_skills_root(Some(tmp.path().to_string_lossy().as_ref()))
.expect("resolve skills root");

assert_eq!(resolved, legacy_skills);
}

#[test]
fn normalize_filters_validate_allowed_values() {
assert_eq!(
Expand Down
2 changes: 1 addition & 1 deletion crates/skilllite-evolution/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn try_log_evolution_run_outcome(chat_root: &Path, reason: &str) {
///
/// Returns [EvolutionRunResult]: SkippedBusy if another run in progress, NoScope if nothing to evolve, Completed(txn_id) otherwise.
/// When force=true (manual trigger), bypass decision thresholds.
/// skills_root: project-level dir (workspace/.skills). When None, skips skill evolution.
/// skills_root: effective project-level skills root. When None, skips skill evolution.
pub async fn run_evolution<L: EvolutionLlm>(
chat_root: &Path,
skills_root: Option<&Path>,
Expand Down
2 changes: 1 addition & 1 deletion crates/skilllite-evolution/src/skill_synth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//! - **Retry**: 任何 LLM 输出 JSON 解析失败时,将错误反馈给大模型并重试 1 次
//! - 代码修改/修复仅由大模型完成,不使用正则或模式匹配
//!
//! All evolved skills live in `chat/skills/_evolved/` with `.meta.json` metadata.
//! All evolved skills live under `<skills-root>/_evolved/` with `.meta.json` metadata.
//! A10: Newly generated skills go to `_evolved/_pending/` until user confirms.

mod env_helper;
Expand Down
41 changes: 41 additions & 0 deletions tasks/TASK-2026-069-evolution-run-skills-root/CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Technical Context

## Current State

- Relevant crates/files:
- `crates/skilllite-commands/src/evolution.rs`
- `crates/skilllite-commands/src/evolution_desktop.rs`
- `crates/skilllite-commands/src/evolution_status.rs`
- `crates/skilllite-core/src/skill/discovery.rs`
- Current behavior:
- `evolution_desktop::resolve_skills_root` uses `resolve_skills_dir_with_legacy_fallback(&root, "skills")`.
- `evolution_status` counts pending skills from the same effective skills root.
- `evolution::cmd_run` now uses the same `skills/` default with `.skills/` legacy fallback policy.
- When `skills/` exists, generated pending skills and desktop pending/status/confirm operations target the same root.

## Architecture Fit

- Layer boundaries involved:
- CLI entry crate dispatches to `skilllite-commands`.
- `skilllite-commands` may call lower-layer `skilllite-core` discovery helpers.
- Interfaces to preserve:
- Existing `evolution run` CLI flags and JSON response shape.
- Existing legacy `.skills` fallback behavior for projects without `skills/`.

## Dependency and Compatibility

- New dependencies: none.
- Backward compatibility notes:
- Existing projects with only `.skills/` continue to use `.skills/`.
- Projects with `skills/` get consistent behavior across run/pending/status/confirm.

## Design Decisions

- Decision: Update `evolution.rs` root resolution to call the same discovery helper used by desktop reads.
- Rationale: Reuses the established project skills-root policy and avoids duplicating fallback rules.
- Alternatives considered: Change desktop pending/status/confirm back to `.skills` only.
- Why rejected: That would undo the newer `skills/` default and break current `skilllite init` style workspaces.

## Open Questions

- [ ] Whether assistant background authorize should pass `--workspace` to `evolution run`; tracked as residual risk outside this minimal skills-root fix.
37 changes: 37 additions & 0 deletions tasks/TASK-2026-069-evolution-run-skills-root/PRD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# PRD

## Background

The recent desktop L2 evolution bridge delegates pending/status/confirm operations to CLI JSON commands. Those read pending evolved skills from the effective workspace skills directory, preferring `skills/` and falling back to `.skills/` only for legacy workspaces. The evolution run command still writes generated pending skills under `.skills/`, creating a split-brain path when `skills/` exists.

## Objective

Ensure evolution-generated pending skills are written to the same effective skills root that desktop pending/status/confirm operations read.

## Functional Requirements

- FR-1: `skilllite evolution run --workspace <ws>` must resolve its skills root with the shared `skills/` plus legacy fallback policy.
- FR-2: `.skills`-only workspaces must remain compatible.
- FR-3: Regression tests must prove both root-selection cases.

## Non-Functional Requirements

- Security: No new path traversal or permission bypass behavior.
- Performance: No measurable impact; root resolution is local path checking only.
- Compatibility: Preserve `.skills` fallback for existing legacy projects.

## Constraints

- Technical: Keep the fix in command-layer Rust code and reuse existing lower-layer discovery helpers.
- Timeline: N/A for autonomous execution.

## Success Metrics

- Metric: Root selected by `evolution run` matches desktop pending/status root.
- Baseline: Workspaces with `skills/` present write pending skills under `.skills`.
- Target: Workspaces with `skills/` present write pending skills under `skills`, while `.skills`-only workspaces still use `.skills`.

## Rollout

- Rollout plan: Ship as a narrow bug fix with command-level regression tests.
- Rollback plan: Revert the command helper change and tests if unexpected compatibility issues appear.
55 changes: 55 additions & 0 deletions tasks/TASK-2026-069-evolution-run-skills-root/REVIEW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Review Report

## Scope Reviewed

- Files/modules:
- `crates/skilllite-commands/src/evolution.rs`
- `crates/skilllite-agent/src/skills/mod.rs`
- `crates/skilllite-evolution/src/run.rs`
- `crates/skilllite-evolution/src/skill_synth/mod.rs`
- `crates/skilllite-assistant/src/i18n/messages/en.ts`
- `crates/skilllite-assistant/src/i18n/messages/zh.ts`
- `tasks/TASK-2026-069-evolution-run-skills-root/*`
- `tasks/board.md`
- Commits/changes:
- Aligned `evolution run` skills-root resolution with the shared `skills/` plus legacy `.skills` fallback policy.
- Added focused unit regressions for default `skills/` preference and `.skills` fallback.
- Updated user-facing pending-empty messages and stale internal comments.

## Findings

- Critical: pre-fix path split made generated pending skills invisible/unconfirmable when a workspace had `skills/`.
- Major: fixed by reusing the shared root-resolution helper in `evolution run`.
- Minor: residual background authorize `--workspace` omission remains a separate follow-up risk outside this fix.

## Quality Gates

- Architecture boundary checks: `pass`
- Security invariants: `pass`
- Required tests executed: `pass`
- Docs sync (EN/ZH): `pass`

## Test Evidence

- Commands run:
- `cargo fmt --check`
- `cargo test -p skilllite-commands --features agent resolve_skills_root`
- `cargo test -p skilllite-commands --features agent`
- `cargo test -p skilllite`
- `cargo clippy --all-targets -- -D warnings`
- `cargo test`
- `python3 scripts/validate_tasks.py`
- Key outputs:
- Initial focused test attempt failed before running because Cargo 1.83 did not support edition 2024 dependency metadata; `rustup update stable && rustup default stable` updated to Rust/Cargo 1.96.
- Focused regressions: `2 passed; 0 failed`.
- `cargo test -p skilllite-commands --features agent`: `41 passed; 0 failed`.
- `cargo test -p skilllite`: integration/unit tests passed, including `20` skill management tests and `2` E2E minimal tests.
- `cargo clippy --all-targets -- -D warnings`: finished successfully.
- `cargo test`: full workspace passed; final suites included `skilllite-sandbox` `94 passed` and all doc-tests completed without failures.
- `python3 scripts/validate_tasks.py`: `Task validation passed (69 task directories checked).`

## Decision

- Merge readiness: `ready`
- Follow-up actions:
- Consider a separate task for assistant authorize background run workspace argument and UI-only API key propagation.
21 changes: 21 additions & 0 deletions tasks/TASK-2026-069-evolution-run-skills-root/STATUS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Status Journal

## Timeline

- 2026-06-17:
- Progress: Confirmed a concrete path split where `evolution run` writes pending skills under `.skills` while desktop pending/status/confirm read the effective `skills` root.
- Blockers: None.
- Next step: Done; fix, tests, review, and board update completed.
- 2026-06-17:
- Progress: Updated `evolution run` skills-root resolution to use the shared `skills/` default with `.skills/` fallback policy; added focused unit regressions and synced user-facing pending-empty messages.
- 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
73 changes: 73 additions & 0 deletions tasks/TASK-2026-069-evolution-run-skills-root/TASK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# TASK Card

## Metadata

- Task ID: `TASK-2026-069`
- Title: Evolution run skills-root alignment
- Status: `done`
- Priority: `P0`
- Owner: `agent`
- Contributors:
- Created: `2026-06-17`
- Target milestone:

## Problem

Recent L2 evolution bridge work made desktop pending/status/confirm paths resolve the effective workspace skills directory as `skills/` with legacy fallback to `.skills/`. The synchronous `skilllite evolution run` path still hardcodes `workspace/.skills`. In a workspace where `skills/` exists, evolution can generate pending skills under `.skills/_evolved/_pending` while desktop UI reads `skills/_evolved/_pending`, making generated skills invisible and unconfirmable.

## Scope

- In scope:
- Align `skilllite evolution run` skills-root resolution with desktop pending/status/confirm paths.
- Add focused regression coverage proving workspaces with `skills/` present use `skills/_evolved`.
- Keep the legacy `.skills` fallback when `skills/` is absent.
- Out of scope:
- Broad evolution run architecture changes.
- Changing pending skill file formats or evolution DB schema.
- Reworking assistant background process API key handling.

## Acceptance Criteria

- [x] `evolution run` resolves the same effective skills root as pending/status/confirm for explicit workspaces.
- [x] Regression tests cover `skills/` preference and `.skills` legacy fallback.
- [x] Required Rust and task validation commands pass.

## Risks

- Risk: Changing the evolution run write root could surprise legacy workspaces.
- Impact: Existing `.skills`-only workspaces must keep working.
- Mitigation: Use the existing `resolve_skills_dir_with_legacy_fallback` helper so `.skills` remains selected only when `skills/` is absent.
- Risk: Over-widening path semantics.
- Impact: Could change unrelated skill discovery behavior.
- Mitigation: Limit the fix to the command-layer skills root used by `evolution run`.

## Validation Plan

- Required tests:
- Unit regression tests in `skilllite-commands`.
- CLI/commands behavior tests required by policy.
- Commands to run:
- `cargo fmt --check`
- `cargo clippy --all-targets -- -D warnings`
- `cargo test -p skilllite-commands --features agent`
- `cargo test -p skilllite`
- `cargo test`
- `python3 scripts/validate_tasks.py`
- Manual checks:
- Inspect the changed files after edits.

## Regression Scope

- Areas likely affected:
- `skilllite evolution run`
- Desktop evolution manual trigger and post-authorize background run
- Pending evolved skill visibility and confirmation
- Explicit non-goals:
- DB workspace default semantics when `--workspace` is omitted.
- Tauri UI copy or frontend state management.

## Links

- Source TODO section: daily critical bug-finding automation, 2026-06-17.
- Related PRs/issues:
- Related docs: `docs/en/ASSISTANT-SPLIT-ARCHITECTURE.md`, `docs/zh/ASSISTANT-SPLIT-ARCHITECTURE.md`
3 changes: 2 additions & 1 deletion tasks/board.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Task Board

Last updated: 2026-06-10 (TASK-2026-068 evolution workspace db scope done)
Last updated: 2026-06-17 (TASK-2026-069 evolution run skills-root alignment done)

## In Progress

Expand All @@ -17,6 +17,7 @@ Last updated: 2026-06-10 (TASK-2026-068 evolution workspace db scope done)

## Done

- `TASK-2026-069-evolution-run-skills-root` - 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`
- `TASK-2026-066-utf8-evolution-log-truncate` - Status: `done` - Owner: `agent`
Expand Down
Loading