diff --git a/docs/superpowers/plans/2026-04-09-ws-branch-scene-cleanup-plan.md b/docs/superpowers/plans/2026-04-09-ws-branch-scene-cleanup-plan.md new file mode 100644 index 0000000..cc9be8d --- /dev/null +++ b/docs/superpowers/plans/2026-04-09-ws-branch-scene-cleanup-plan.md @@ -0,0 +1,666 @@ +# WS Branch Scene Cleanup Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Strip `feature/claw-ws` back to websocket plus Zhihu execution only by removing staged scene-skill routing, `skill_staging`-aware loading, and array-style `skillsDir` config behavior from this branch. + +**Architecture:** Treat `feature/claw-ws` as a transport-focused branch, not a business-scene branch. Keep the browser websocket/callback submit path and the existing Zhihu direct workflows, but delete the fault-details / `95598` scene registry, scene-specific prompt injection, staged scene directory expansion, and scene-only docs/tests so the branch stays small and merges cleanly after the real scene implementation lands on `main`. + +**Tech Stack:** Rust 2021, existing sgClaw compat/runtime/orchestration stack, websocket browser backend, callback-host service path, existing `cargo test` suite. + +--- + +## Preconditions + +- Execute this plan **only after** `main` already contains the desired clean scene-skill implementation. +- Run it on `feature/claw-ws`, not on `main`. +- Keep websocket and Zhihu behavior intact; this plan is cleanup, not a redesign. +- Keep `docs/_tmp_sgbrowser_ws_api_doc.txt`; it remains the browser integration contract for this branch. + +## Scope Guardrails + +- Do **not** change the working Zhihu websocket flow in `tests/agent_runtime_test.rs`. +- Do **not** remove `src/browser/ws_backend.rs`, `src/service/server.rs`, or Zhihu routes from `src/compat/workflow_executor.rs`. +- Do **not** add a replacement scene abstraction on this branch. +- Do **not** keep partial scene plumbing “for future use”; delete it completely if it is scene-only. +- Do **not** keep array-style `skillsDir` tests or docs on this branch once the single-path cleanup is complete. + +--- + +## File Map + +### Delete + +- `src/runtime/scene_registry.rs` + - staged scene registry, hard-coded `skill_staging` scene root, scene matching helpers +- `tests/scene_registry_test.rs` + - scene-registry-specific coverage that should disappear with the feature +- `docs/superpowers/specs/2026-04-06-scene-skill-runtime-routing-design.md` + - scene-routing design doc that no longer belongs on the ws-only branch +- `docs/superpowers/plans/2026-04-06-scene-skill-runtime-routing-plan.md` + - scene-routing implementation plan that no longer belongs on the ws-only branch + +### Modify + +- `src/runtime/mod.rs` + - stop exporting deleted scene registry APIs +- `src/runtime/engine.rs` + - remove scene-contract prompt injection and staged scene skill loading +- `src/compat/workflow_executor.rs` + - remove `FaultDetailsReport` route detection/execution while keeping Zhihu routes +- `src/compat/orchestration.rs` + - keep direct Zhihu orchestration only; remove scene-driven primary routing triggers +- `src/config/settings.rs` + - collapse `skillsDir` config handling back to single-path semantics +- `src/compat/config_adapter.rs` + - remove scene-specific skills-dir helpers and keep one resolved skills dir +- `src/compat/runtime.rs` + - stop carrying scene-expanded skills dirs through compat runtime +- `src/agent/task_runner.rs` + - update runtime logging and runtime calls to the single skills-dir contract +- `tests/compat_runtime_test.rs` + - remove fault-details / `95598` assertions and keep Zhihu/direct-route coverage +- `tests/runtime_profile_test.rs` + - remove `95598` scene-contract expectations and keep normal browser-profile coverage +- `tests/compat_config_test.rs` + - remove scene-dir / array-config coverage and add single-path cleanup coverage +- `tests/agent_runtime_test.rs` + - only extend if one extra Zhihu keep-path regression is needed after the config cleanup + +### Keep As-Is Unless A Signature Change Forces A Tiny Edit + +- `src/browser/ws_backend.rs` +- `src/browser/callback_backend.rs` +- `src/browser/callback_host.rs` +- `src/service/server.rs` +- `src/agent/mod.rs` +- `tests/browser_ws_backend_test.rs` +- `tests/service_ws_session_test.rs` +- `tests/task_runner_test.rs` + +--- + +### Task 1: Lock The Cleanup Contract In Failing Tests + +**Files:** +- Modify: `tests/compat_runtime_test.rs` +- Modify: `tests/runtime_profile_test.rs` +- Modify: `tests/compat_config_test.rs` +- Reuse: `tests/agent_runtime_test.rs` + +- [ ] **Step 1: Add the first failing route-removal test** + +In `tests/compat_runtime_test.rs`, add a focused assertion proving the ws branch no longer recognizes the fault-details scene as a direct route: + +```rust +#[test] +fn ws_cleanup_no_longer_detects_fault_details_scene_route() { + use sgclaw::compat::workflow_executor::detect_route; + + assert_eq!( + detect_route( + "导出故障明细", + Some("https://example.invalid/workbench"), + Some("业务台账"), + ), + None, + ); +} +``` + +- [ ] **Step 2: Run the focused route test and verify it fails** + +Run: + +```bash +cargo test --test compat_runtime_test ws_cleanup_no_longer_detects_fault_details_scene_route -- --nocapture +``` + +Expected: FAIL because `FaultDetailsReport` is still detected today. + +- [ ] **Step 3: Add the second failing orchestration-gate test** + +In `tests/compat_runtime_test.rs`, add one focused assertion proving scene keywords no longer open the primary direct-orchestration path: + +```rust +#[test] +fn ws_cleanup_scene_keywords_do_not_trigger_primary_orchestration() { + assert!(!sgclaw::compat::orchestration::should_use_primary_orchestration( + "请处理95598抢修市指监测", + Some("https://95598.example.invalid/dispatch"), + Some("95598抢修市指监测"), + )); +} +``` + +- [ ] **Step 4: Run the orchestration-gate test and verify it fails** + +Run: + +```bash +cargo test --test compat_runtime_test ws_cleanup_scene_keywords_do_not_trigger_primary_orchestration -- --nocapture +``` + +Expected: FAIL because the scene matcher still feeds primary orchestration today. + +- [ ] **Step 5: Add the third failing runtime-instruction test** + +In `tests/runtime_profile_test.rs`, add a focused negative assertion proving browser-attached turns no longer receive the `95598` scene execution contract: + +```rust +#[test] +fn ws_cleanup_browser_profile_does_not_inject_95598_scene_contract() { + let engine = RuntimeEngine::new(RuntimeProfile::BrowserAttached); + let instruction = engine.build_instruction( + "请处理95598-repair-city-dispatch场景,查看抢修市指派单并汇总当前队列", + Some("https://95598.example.invalid/dispatch"), + Some("95598抢修市指监测"), + true, + ); + + assert!(!instruction.contains("95598-repair-city-dispatch.collect_repair_orders")); +} +``` + +- [ ] **Step 6: Run the runtime-profile test and verify it fails** + +Run: + +```bash +cargo test --test runtime_profile_test ws_cleanup_browser_profile_does_not_inject_95598_scene_contract -- --nocapture +``` + +Expected: FAIL because `src/runtime/engine.rs` still injects the scene contract today. + +- [ ] **Step 7: Add the fourth failing config-shape test** + +In `tests/compat_config_test.rs`, add one focused assertion proving ws cleanup goes back to a single configured skills path and no longer accepts array-style `skillsDir` JSON: + +```rust +#[test] +fn ws_cleanup_rejects_array_style_skills_dir_config() { + let root = std::env::temp_dir().join(format!("sgclaw-config-{}", uuid::Uuid::new_v4())); + std::fs::create_dir_all(&root).unwrap(); + let config_path = root.join("sgclaw_config.json"); + std::fs::write( + &config_path, + r#"{ + "apiKey": "sk-test", + "baseUrl": "https://api.deepseek.com", + "model": "deepseek-chat", + "skillsDir": ["skill_lib", "skill_staging"] +}"#, + ) + .unwrap(); + + assert!(sgclaw::config::SgClawSettings::load(Some(config_path.as_path())).is_err()); +} +``` + +- [ ] **Step 8: Run the config-shape test and verify it fails** + +Run: + +```bash +cargo test --test compat_config_test ws_cleanup_rejects_array_style_skills_dir_config -- --nocapture +``` + +Expected: FAIL because the current parser still accepts string-or-array `skillsDir` input. + +- [ ] **Step 9: Re-run the existing Zhihu keep-path test as a safety baseline** + +Run: + +```bash +cargo test --test agent_runtime_test production_submit_task_routes_zhihu_through_ws_backend_without_helper_bootstrap -- --nocapture +``` + +Expected: PASS, proving the behavior we want to keep is already covered before deletion starts. + +--- + +### Task 2: Remove Scene Registry, Scene Prompt Injection, And Fault-Details Routing + +**Files:** +- Delete: `src/runtime/scene_registry.rs` +- Modify: `src/runtime/mod.rs` +- Modify: `src/runtime/engine.rs` +- Modify: `src/compat/workflow_executor.rs` +- Modify: `src/compat/orchestration.rs` +- Modify: `tests/compat_runtime_test.rs` +- Modify: `tests/runtime_profile_test.rs` +- Delete: `tests/scene_registry_test.rs` + +- [ ] **Step 1: Remove the runtime scene module export surface** + +Update `src/runtime/mod.rs` so it no longer declares or re-exports scene registry items. + +Target shape: + +```rust +mod engine; +mod profile; +mod tool_policy; + +pub use engine::{ + is_zhihu_hotlist_task, + is_zhihu_write_task, + task_requests_zhihu_article_publish, + RuntimeEngine, +}; +pub use profile::RuntimeProfile; +pub use tool_policy::ToolPolicy; +``` + +- [ ] **Step 2: Delete `src/runtime/scene_registry.rs`** + +Remove the file entirely. Do not leave a stub module or comments about future scene support. + +- [ ] **Step 3: Remove scene-aware prompt injection from `src/runtime/engine.rs`** + +Delete: +- the `resolve_scene_skills_dir_path` import +- the `DispatchMode` / `match_scene_instruction` imports +- `REPAIR_CITY_DISPATCH_EXECUTION_PROMPT` +- `build_scene_execution_contract(...)` +- the `if let Some(scene_contract) = ...` block inside `RuntimeEngine::build_instruction(...)` +- staged scene directory loading inside `load_runtime_skills(...)` + +The resulting instruction assembly should keep: +- browser tool contract +- Zhihu hotlist/export prompts +- Zhihu publish guard +- page context + +Do **not** change Zhihu prompt text. + +- [ ] **Step 4: Remove the fault-details route from `src/compat/workflow_executor.rs`** + +Shrink `WorkflowRoute` back to Zhihu-only variants: + +```rust +pub enum WorkflowRoute { + ZhihuHotlistExportXlsx, + ZhihuHotlistScreen, + ZhihuArticleEntry, + ZhihuArticleDraft, + ZhihuArticlePublish, + ZhihuArticleAutoPublishGenerated, +} +``` + +Delete: +- `FAULT_DETAILS_SCENE_ID` +- the scene check at the top of `detect_route(...)` +- `WorkflowRoute::FaultDetailsReport` +- `execute_fault_details_route(...)` +- any scene-only helpers used only by that path + +Keep the Zhihu route order unchanged. + +- [ ] **Step 5: Simplify `src/compat/orchestration.rs` to Zhihu-only direct routing** + +After the fault-details route is gone, keep `should_use_primary_orchestration(...)` and the two execute functions focused on: +- Zhihu direct routes detected by `detect_route(...)` +- existing Zhihu export/dashboard fallback behavior + +Do not add new conditions. + +- [ ] **Step 6: Remove scene-only tests and replace them with cleanup assertions** + +In `tests/compat_runtime_test.rs` and `tests/runtime_profile_test.rs`: +- delete `fault-details` assertions that require the old route to exist +- delete `95598` scene-contract assertions that require the old prompt injection to exist +- keep the new negative cleanup tests from Task 1 +- keep the existing Zhihu assertions intact + +Delete `tests/scene_registry_test.rs` completely. + +- [ ] **Step 7: Run the focused cleanup tests** + +Run: + +```bash +cargo test --test compat_runtime_test ws_cleanup_no_longer_detects_fault_details_scene_route -- --nocapture && cargo test --test compat_runtime_test ws_cleanup_scene_keywords_do_not_trigger_primary_orchestration -- --nocapture && cargo test --test runtime_profile_test ws_cleanup_browser_profile_does_not_inject_95598_scene_contract -- --nocapture +``` + +Expected: PASS. + +- [ ] **Step 8: Re-run the focused Zhihu runtime tests** + +Run: + +```bash +cargo test --test compat_runtime_test zhihu_ -- --nocapture +``` + +Expected: PASS, proving the Zhihu direct routes still work after the scene deletion. + +- [ ] **Step 9: Commit Task 2** + +```bash +git add src/runtime/mod.rs src/runtime/engine.rs src/compat/workflow_executor.rs src/compat/orchestration.rs tests/compat_runtime_test.rs tests/runtime_profile_test.rs +git rm src/runtime/scene_registry.rs tests/scene_registry_test.rs +git commit -m "refactor: remove scene routing from ws branch" +``` + +--- + +### Task 3: Collapse `skillsDir` Back To Single-Path Semantics + +**Files:** +- Modify: `src/config/settings.rs` +- Modify: `src/compat/config_adapter.rs` +- Modify: `src/compat/runtime.rs` +- Modify: `src/agent/task_runner.rs` +- Modify if needed: `tests/agent_runtime_test.rs` +- Modify: `tests/compat_config_test.rs` + +- [ ] **Step 1: Change config parsing to a single configured skills path** + +In `src/config/settings.rs`, replace the string-or-array parser with a single optional string field. + +Target shape: + +```rust +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DeepSeekSettings { + pub api_key: String, + pub base_url: String, + pub model: String, + pub skills_dir: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SgClawSettings { + // ... + pub skills_dir: Option, + // ... +} +``` + +And in `RawSgClawSettings`: + +```rust +#[serde(rename = "skillsDir", alias = "skills_dir", default)] +skills_dir: Option, +``` + +Delete `deserialize_skills_dirs(...)` entirely. + +- [ ] **Step 2: Keep relative-path resolution, but only for one path** + +Replace `resolve_configured_skills_dirs(...) -> Vec` with a single-path helper such as: + +```rust +fn resolve_configured_skills_dir(raw: Option, config_dir: &Path) -> Option { + raw.map(|value| value.trim().to_string()) + .filter(|value| !value.is_empty()) + .map(PathBuf::from) + .map(|path| if path.is_absolute() { path } else { config_dir.join(path) }) +} +``` + +- [ ] **Step 3: Collapse compat config adapter back to one resolved skills dir** + +In `src/compat/config_adapter.rs`: +- keep `zeroclaw_default_skills_dir(...)` +- change `resolve_skills_dir(...)` and `resolve_skills_dir_from_sgclaw_settings(...)` to return a single `PathBuf` +- delete `resolve_scene_skills_dir_from_sgclaw_settings(...)` +- delete `resolve_scene_skills_dir_path(...)` +- delete any helper branches that append `skill_staging/skills` + +Recommended shape: + +```rust +pub fn resolve_skills_dir_from_sgclaw_settings( + workspace_root: &Path, + settings: &SgClawSettings, +) -> PathBuf { + settings + .skills_dir + .as_ref() + .map(|dir| normalize_configured_skills_dir(dir)) + .unwrap_or_else(|| zeroclaw_default_skills_dir(workspace_root)) +} +``` + +- [ ] **Step 4: Update runtime callers to the single-path contract** + +In `src/compat/runtime.rs` and `src/agent/task_runner.rs`: +- stop passing vectors of skills dirs around +- update logging from `skills dirs resolved to [...]` to a single-path message such as `skills dir resolved to ...` +- keep the rest of the runtime behavior unchanged + +In `src/runtime/engine.rs`, if the method still needs a collection internally, convert the one path at the call site instead of preserving public multi-root plumbing. + +- [ ] **Step 5: Replace config tests with single-path cleanup coverage** + +In `tests/compat_config_test.rs`: +- keep single-string `skillsDir` resolution tests +- remove `resolve_scene_skills_dir_path_*` coverage +- remove array-acceptance expectations +- keep the new rejecting-array test from Task 1 + +Add one focused positive test like: + +```rust +#[test] +fn ws_cleanup_resolves_single_configured_skills_dir() { + let root = std::env::temp_dir().join(format!("sgclaw-skills-{}", uuid::Uuid::new_v4())); + std::fs::create_dir_all(root.join("skill_lib/skills")).unwrap(); + + let settings = DeepSeekSettings { + api_key: "key".to_string(), + base_url: "https://api.deepseek.com".to_string(), + model: "deepseek-chat".to_string(), + skills_dir: Some(root.join("skill_lib")), + }; + + assert_eq!( + resolve_skills_dir(&root, &settings), + root.join("skill_lib/skills"), + ); +} +``` + +- [ ] **Step 6: Run the focused config tests** + +Run: + +```bash +cargo test --test compat_config_test ws_cleanup_ -- --nocapture +``` + +Expected: PASS. + +- [ ] **Step 7: Re-run the Zhihu websocket keep-path test** + +Run: + +```bash +cargo test --test agent_runtime_test production_submit_task_routes_zhihu_through_ws_backend_without_helper_bootstrap -- --nocapture +``` + +Expected: PASS. + +- [ ] **Step 8: Commit Task 3** + +```bash +git add src/config/settings.rs src/compat/config_adapter.rs src/compat/runtime.rs src/agent/task_runner.rs tests/compat_config_test.rs tests/agent_runtime_test.rs +git commit -m "refactor: restore single skills dir on ws branch" +``` + +--- + +### Task 4: Remove Scene-Only Docs And Residual Test References + +**Files:** +- Delete: `docs/superpowers/specs/2026-04-06-scene-skill-runtime-routing-design.md` +- Delete: `docs/superpowers/plans/2026-04-06-scene-skill-runtime-routing-plan.md` +- Modify: `tests/compat_runtime_test.rs` +- Modify: `tests/runtime_profile_test.rs` +- Modify: `tests/compat_config_test.rs` + +- [ ] **Step 1: Delete the two scene-only planning documents** + +Remove exactly these files: +- `docs/superpowers/specs/2026-04-06-scene-skill-runtime-routing-design.md` +- `docs/superpowers/plans/2026-04-06-scene-skill-runtime-routing-plan.md` + +Keep the websocket/browser docs and Zhihu docs. + +- [ ] **Step 2: Sweep remaining tests for scene-only names** + +Remove or rewrite any remaining test blocks that still require: +- `fault-details-report` +- `95598-repair-city-dispatch` +- `resolve_scene_skills_dir_path` +- `resolve_scene_skills_dir_from_sgclaw_settings` +- `scene_registry` + +Do not delete Zhihu-related assertions during this sweep. + +- [ ] **Step 3: Run a focused grep-style audit from the shell** + +Run: + +```bash +git grep -n "fault-details-report\|95598-repair-city-dispatch\|resolve_scene_skills_dir_path\|resolve_scene_skills_dir_from_sgclaw_settings\|scene_registry" -- src tests docs +``` + +Expected: no matches in `src/` or `tests/`; doc matches should be gone after the deletions. + +- [ ] **Step 4: Commit Task 4** + +```bash +git add tests/compat_runtime_test.rs tests/runtime_profile_test.rs tests/compat_config_test.rs +git rm docs/superpowers/specs/2026-04-06-scene-skill-runtime-routing-design.md docs/superpowers/plans/2026-04-06-scene-skill-runtime-routing-plan.md +git commit -m "docs: remove ws-only scene planning artifacts" +``` + +--- + +### Task 5: Verify The Branch Is Back To WS Plus Zhihu Only + +**Files:** +- Verify only unless a failing test proves one tiny follow-up fix is needed + +- [ ] **Step 1: Run the retained Zhihu websocket regression** + +Run: + +```bash +cargo test --test agent_runtime_test production_submit_task_routes_zhihu_through_ws_backend_without_helper_bootstrap -- --nocapture +``` + +Expected: PASS. + +- [ ] **Step 2: Run websocket/backend focused coverage** + +Run: + +```bash +cargo test --test browser_ws_backend_test -- --nocapture && cargo test --test service_ws_session_test -- --nocapture +``` + +Expected: PASS. + +- [ ] **Step 3: Run direct-route/runtime Zhihu coverage** + +Run: + +```bash +cargo test --test compat_runtime_test zhihu_ -- --nocapture && cargo test --test task_runner_test -- --nocapture +``` + +Expected: PASS. + +- [ ] **Step 4: Run config/runtime verification after the single-dir cleanup** + +Run: + +```bash +cargo test --test compat_config_test -- --nocapture && cargo test --test runtime_profile_test -- --nocapture +``` + +Expected: PASS. + +- [ ] **Step 5: Build the affected binaries** + +Run: + +```bash +cargo build --bin sgclaw --bin sg_claw --bin sg_claw_client +``` + +Expected: PASS. + +- [ ] **Step 6: Audit the remaining branch diff against `main`** + +Run: + +```bash +git diff --stat main...HEAD +``` + +Expected: the remaining meaningful differences are websocket/browser transport work and Zhihu-related behavior, not scene-routing or staged-scene config churn. + +- [ ] **Step 7: Commit the final verification pass** + +```bash +git add src/config/settings.rs src/compat/config_adapter.rs src/compat/runtime.rs src/compat/workflow_executor.rs src/compat/orchestration.rs src/runtime/mod.rs src/runtime/engine.rs tests/compat_config_test.rs tests/runtime_profile_test.rs tests/compat_runtime_test.rs tests/agent_runtime_test.rs tests/task_runner_test.rs +git commit -m "test: verify ws branch cleanup preserves zhihu websocket flow" +``` + +--- + +## Verification Checklist + +### Cleanup regressions + +```bash +cargo test --test compat_runtime_test ws_cleanup_ -- --nocapture +cargo test --test runtime_profile_test ws_cleanup_ -- --nocapture +cargo test --test compat_config_test ws_cleanup_ -- --nocapture +``` + +Expected: scene detection, scene prompt injection, and array-style `skillsDir` behavior are gone. + +### Retained Zhihu websocket behavior + +```bash +cargo test --test agent_runtime_test production_submit_task_routes_zhihu_through_ws_backend_without_helper_bootstrap -- --nocapture +cargo test --test browser_ws_backend_test -- --nocapture +cargo test --test service_ws_session_test -- --nocapture +cargo test --test compat_runtime_test zhihu_ -- --nocapture +``` + +Expected: websocket submit path and Zhihu direct workflows still pass. + +### Runtime/config verification + +```bash +cargo test --test compat_config_test -- --nocapture +cargo test --test runtime_profile_test -- --nocapture +cargo test --test task_runner_test -- --nocapture +``` + +Expected: runtime/config plumbing is stable after the single-dir cleanup. + +### Build verification + +```bash +cargo build --bin sgclaw --bin sg_claw --bin sg_claw_client +``` + +Expected: the branch still compiles cleanly. + +--- + +## Notes For The Engineer + +- The current scene support touches three different seams: runtime prompt injection, direct route detection/execution, and multi-root `skillsDir` plumbing. Remove all three; deleting only one leaves conflict-prone leftovers. +- If collapsing `skillsDir` to `Option` creates more churn than expected, keep the internal representation temporarily as a one-element collection, but the public config contract and tests on this branch must still go back to a single configured path. +- Do not delete browser websocket or callback-host code just because it is adjacent to the scene work; this plan is about stripping scene behavior, not reworking transport. +- If `git diff --stat main...HEAD` still shows scene-specific files after Task 5, stop and remove them before merging `main` back into this branch. \ No newline at end of file