Add fixed direct-submit skill loading from configured staged skills and validate directSubmitSkill early so malformed configs fail before routing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
18 KiB
Direct Skill Invocation Without LLM 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: Let the current pipe submit-task flow accept natural-language input but directly invoke one fixed staged browser skill without calling any model, while reserving a clean switch back to LLM-based routing later.
Architecture: Keep the existing BrowserMessage::SubmitTask entrypoint and add one narrow pre-routing seam before the current compat/LLM chain. When a new config field points to a fixed direct-submit skill, sgClaw loads that skill package from the configured external skills root, finds the target browser_script tool, executes it through the existing browser-script wrapper, and returns the result directly. When the field is absent, the current behavior stays unchanged. This preserves a future path where each skill can later declare direct_browser or llm_agent dispatch without rewriting the submit pipeline again.
Tech Stack: Rust 2021, existing BrowserPipeTool, current submit-task agent entrypoint, current browser-script skill executor, current sgClaw JSON config loader, zeroclaw skill manifest loader.
Recommended First Skill
Use fault-details-report.collect_fault_details from:
D:/data/ideaSpace/rust/sgClaw/claw/claw/skills/skill_staging/scenes/fault-details-report/scene.jsonD:/data/ideaSpace/rust/sgClaw/claw/claw/skills/skill_staging/skills/fault-details-report/SKILL.tomlD:/data/ideaSpace/rust/sgClaw/claw/claw/skills/skill_staging/skills/fault-details-report/scripts/collect_fault_details.js
Why this one first:
- it is clearly a report/export skill
- it exposes exactly one browser-script tool:
collect_fault_details - it has the smallest contract surface (
periodonly) - its current JS is deterministic and simple, so the first slice can focus on plumbing instead of browser scraping complexity
Scope Guardrails
- Do not redesign the existing submit-task protocol.
- Do not remove or rewrite the current LLM/compat path; leave it as the fallback/default path.
- Do not introduce generic NL intent routing in this slice; this is one fixed direct skill only.
- Do not modify
third_party/zeroclawskill manifest schema in phase 1. - Do not add Excel export wiring in the first slice unless a test explicitly requires it.
- Do not invent a new browser-script execution model; reuse the existing wrapper semantics.
File Map
Existing files to modify
- Modify:
src/config/settings.rs- add a minimal config field for one direct-submit skill name
- Modify:
src/agent/mod.rs- add a narrow pre-routing branch before the current compat/LLM path
- Modify:
src/compat/browser_script_skill_tool.rs- expose the smallest reusable helper for direct browser-script execution
- Modify:
src/compat/mod.rsor the nearest module export surface- export the new narrow direct-skill runtime module if needed
- Modify:
tests/compat_config_test.rs- add config coverage for the new direct-submit field
- Modify:
tests/browser_script_skill_tool_test.rs- add coverage for the reusable direct-execution helper
- Modify:
tests/agent_runtime_test.rs- prove submit-task can bypass the model and directly invoke the fixed skill
New files to create
- Create:
src/compat/direct_skill_runtime.rs- small runtime for loading one configured skill, resolving one configured tool, deriving minimal args, and executing it directly
Files to reuse without changing behavior
- Reuse:
src/compat/runtime.rs - Reuse:
src/compat/orchestration.rs - Reuse:
src/compat/config_adapter.rs - Reuse:
third_party/zeroclaw/src/skills/mod.rs
Task 1: Add A Minimal Direct-Submit Skill Config Field
Files:
-
Modify:
src/config/settings.rs -
Modify:
tests/compat_config_test.rs -
Step 1: Write the failing config test for the new field
In tests/compat_config_test.rs, add a focused config-load test proving the browser config file can declare one fixed direct-submit skill.
Test shape:
#[test]
fn sgclaw_settings_load_direct_submit_skill_from_browser_config() {
let root = std::env::temp_dir().join(format!("sgclaw-direct-skill-{}", 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-runtime",
"baseUrl": "https://api.deepseek.com",
"model": "deepseek-chat",
"skillsDir": "D:/data/ideaSpace/rust/sgClaw/claw/claw/skills/skill_staging",
"directSubmitSkill": "fault-details-report.collect_fault_details"
}"#,
)
.unwrap();
let settings = sgclaw::config::SgClawSettings::load(Some(config_path.as_path()))
.unwrap()
.expect("expected sgclaw settings from config file");
assert_eq!(
settings.direct_submit_skill.as_deref(),
Some("fault-details-report.collect_fault_details")
);
}
- Step 2: Run the focused config test and verify it fails
Run:
cargo test --test compat_config_test sgclaw_settings_load_direct_submit_skill_from_browser_config -- --nocapture
Expected: FAIL because the config field does not exist yet.
- Step 3: Implement the minimal config field
In src/config/settings.rs, add:
direct_submit_skill: Option<String>toSgClawSettingsdirect_submit_skill: Option<String>toRawSgClawSettings- field normalization in
SgClawSettings::new(...)
Recommended JSON key shape:
#[serde(rename = "directSubmitSkill", alias = "direct_submit_skill", default)]
direct_submit_skill: Option<String>,
Rules:
-
trim empty values to
None -
keep
DeepSeekSettingsunchanged for this slice unless a compile error proves it must mirror the field -
do not alter unrelated config semantics
-
Step 4: Re-run the focused config test
Run:
cargo test --test compat_config_test sgclaw_settings_load_direct_submit_skill_from_browser_config -- --nocapture
Expected: PASS.
- Step 5: Re-run the broader config file tests
Run:
cargo test --test compat_config_test -- --nocapture
Expected: PASS.
- Step 6: Commit Task 1
git add src/config/settings.rs tests/compat_config_test.rs
git commit -m "feat: add direct submit skill config"
Task 2: Extract A Reusable Browser-Script Direct Execution Helper
Files:
-
Modify:
src/compat/browser_script_skill_tool.rs -
Modify:
tests/browser_script_skill_tool_test.rs -
Step 1: Write the first failing helper test
In tests/browser_script_skill_tool_test.rs, add a focused test proving direct code can execute a packaged browser script without constructing a full Tool object first.
Test shape:
#[tokio::test]
async fn execute_browser_script_tool_runs_packaged_script_with_expected_domain() {
// build temp skill script
// call the helper directly
// assert Action::Eval was sent with wrapped args and normalized domain
}
Required assertions:
-
the helper reads the packaged JS file
-
it wraps args with
const args = ... -
it normalizes URL-like
expected_domain -
it returns the serialized payload string on success
-
Step 2: Run the helper test and verify it fails
Run:
cargo test --test browser_script_skill_tool_test execute_browser_script_tool_runs_packaged_script_with_expected_domain -- --nocapture
Expected: FAIL because the helper does not exist yet.
- Step 3: Add the second failing helper test for required-domain validation
Add a focused failure-path test proving the helper rejects missing or invalid expected_domain before any browser command is sent.
- Step 4: Run the validation test and verify it fails
Run:
cargo test --test browser_script_skill_tool_test execute_browser_script_tool_rejects_missing_expected_domain -- --nocapture
Expected: FAIL because the helper does not exist yet.
- Step 5: Implement the minimal reusable helper
In src/compat/browser_script_skill_tool.rs, extract the smallest reusable function, for example:
pub async fn execute_browser_script_tool<T: Transport + 'static>(
tool: &SkillTool,
skill_root: &Path,
browser_tool: BrowserPipeTool<T>,
args: Value,
) -> anyhow::Result<ToolResult>
Rules:
-
reuse the current path validation, script loading, wrapping,
Action::Eval, and payload formatting logic already used byBrowserScriptSkillTool::execute -
do not change outward behavior of
BrowserScriptSkillTool -
keep the helper narrow and browser-script-only
-
Step 6: Refactor
BrowserScriptSkillTool::executeto call the helper
Keep existing behavior and tests green while removing duplicate execution logic.
- Step 7: Re-run the browser-script tests
Run:
cargo test --test browser_script_skill_tool_test -- --nocapture
Expected: PASS.
- Step 8: Commit Task 2
git add src/compat/browser_script_skill_tool.rs tests/browser_script_skill_tool_test.rs
git commit -m "refactor: extract direct browser script execution helper"
Task 3: Add A Narrow Direct Skill Runtime For One Fixed Skill
Files:
-
Create:
src/compat/direct_skill_runtime.rs -
Modify:
src/compat/mod.rsor nearest module export point -
Reuse:
src/compat/config_adapter.rs -
Reuse:
third_party/zeroclaw/src/skills/mod.rs -
Step 1: Write the first failing direct-runtime test
Add a focused test in tests/agent_runtime_test.rs or a new narrow compat test proving code can resolve the configured external skills root, load fault-details-report, find collect_fault_details, and execute it directly.
Recommended shape:
#[test]
fn direct_skill_runtime_executes_fault_details_report_without_provider() {
// config points at skill_staging root
// direct_submit_skill points at fault-details-report.collect_fault_details
// browser response returns report-artifact payload
// assert no provider/http path is touched
}
- Step 2: Run the focused direct-runtime test and verify it fails
Run the narrowest test command for the new test.
Expected: FAIL because the direct runtime does not exist yet.
- Step 3: Implement
src/compat/direct_skill_runtime.rs
Add a narrow runtime with responsibilities only to:
- resolve the configured skills dir with
resolve_skills_dir_from_sgclaw_settings(...) - load skills from that directory with
load_skills_from_directory(...) - parse the configured tool name into
skill_name+tool_name - find the matching skill and matching tool
- verify
tool.kind == "browser_script" - derive the minimal argument object
- call the new browser-script helper
- return the output string or a clear
PipeError
Do not add generic routing, scenes, or model fallback here.
- Step 4: Keep argument derivation intentionally minimal
For the first slice, derive only:
expected_domainfrompage_urlwhen present, otherwise fail with a clear messageperiodfrom the instruction using a narrow deterministic pattern such asYYYY-MM
If the period cannot be derived, return a concise error telling the user to provide it explicitly. Do not guess.
- Step 5: Re-run the focused direct-runtime test
Run the same test command again.
Expected: PASS.
- Step 6: Commit Task 3
git add src/compat/direct_skill_runtime.rs src/compat/mod.rs tests/agent_runtime_test.rs
git commit -m "feat: add fixed direct skill runtime"
Task 4: Insert The Pre-Routing Seam In Submit-Task Entry
Files:
-
Modify:
src/agent/mod.rs -
Modify:
tests/agent_runtime_test.rs -
Step 1: Write the first failing submit-path bypass test
In tests/agent_runtime_test.rs, add a focused regression proving that when directSubmitSkill is configured, BrowserMessage::SubmitTask can succeed without any model/provider being configured.
Test shape:
#[test]
fn submit_task_uses_direct_skill_mode_without_llm_configuration() {
// config contains skillsDir + directSubmitSkill, but no reachable provider
// natural-language instruction includes period and page_url
// expect TaskComplete success from direct skill result
}
Required assertions:
-
task succeeds even if provider would be unavailable
-
output contains the report artifact payload
-
no summary like
未配置大语言模型 -
Step 2: Run the bypass test and verify it fails
Run:
cargo test --test agent_runtime_test submit_task_uses_direct_skill_mode_without_llm_configuration -- --nocapture
Expected: FAIL because submit-task still goes into the current LLM-oriented path.
- Step 3: Add the second failing priority test
Add one focused test proving the direct-submit branch runs before the existing compat/LLM branch.
The easiest assertion is that the mode log becomes something new like:
direct_skill_primary
and the normal mode logs do not appear for that turn.
- Step 4: Run the priority test and verify it fails
Run the narrow test command for the new test.
Expected: FAIL because the mode does not exist yet.
- Step 5: Add the narrow pre-routing branch in
src/agent/mod.rs
In handle_browser_message_with_context(...), after config load/logging and before the existing should_use_primary_orchestration(...) / compat::runtime path:
- check
settings.direct_submit_skill - if present, emit mode log
direct_skill_primary - call the new direct runtime
- send
TaskCompleteand return immediately
Rules:
-
if
direct_submit_skillis absent, keep existing behavior byte-for-byte where possible -
do not modify
compat::runtime.rsorcompat::orchestration.rsfor this slice -
do not silently fall through to LLM when direct execution fails; return the direct error clearly so the first slice is debuggable
-
Step 6: Re-run the focused submit-path tests
Run:
cargo test --test agent_runtime_test submit_task_uses_direct_skill_mode_without_llm_configuration -- --nocapture
cargo test --test agent_runtime_test direct_skill_mode_logs_direct_skill_primary -- --nocapture
Expected: PASS.
- Step 7: Re-run existing no-LLM submit regression coverage
Run:
cargo test --test agent_runtime_test -- --nocapture
Expected: PASS, including existing cases where no direct skill is configured and the old no-LLM failure still applies.
- Step 8: Commit Task 4
git add src/agent/mod.rs tests/agent_runtime_test.rs
git commit -m "feat: route submit tasks through fixed direct skill mode"
Task 5: Lock The Future Migration Seam Without Implementing LLM Dispatch Yet
Files:
-
Modify only if needed:
src/config/settings.rs -
Modify only if needed:
src/compat/direct_skill_runtime.rs -
Reuse: docs/plan only unless code needs one tiny naming fix
-
Step 1: Keep the config naming compatible with future per-skill dispatch
Document and preserve this future meaning in code naming:
- current field: one fixed direct skill for submit-task bootstrap
- future model: each skill can declare dispatch mode such as
direct_browserorllm_agent
Prefer neutral names in helper code like:
direct skill modedirect submit skill
Avoid hard-coding fault_details into generic APIs.
- Step 2: Add one small negative test for fallback behavior
Add a focused test proving that when directSubmitSkill is not configured, submit-task still behaves exactly as before and can still return the existing no-LLM message.
If an existing test already proves this, keep it and do not add another.
- Step 3: Re-run the focused end-to-end verification set
Run:
cargo test --test compat_config_test -- --nocapture
cargo test --test browser_script_skill_tool_test -- --nocapture
cargo test --test agent_runtime_test -- --nocapture
Expected: PASS.
- Step 4: Build the main binary
Run:
cargo build --bin sgclaw
Expected: PASS.
- Step 5: Commit Task 5
git add src/config/settings.rs src/compat/direct_skill_runtime.rs src/compat/browser_script_skill_tool.rs src/agent/mod.rs tests/compat_config_test.rs tests/browser_script_skill_tool_test.rs tests/agent_runtime_test.rs
git commit -m "test: verify fixed direct skill submit path"
Verification Checklist
Config loading
cargo test --test compat_config_test -- --nocapture
Expected: directSubmitSkill loads correctly and existing config behavior remains intact.
Browser-script helper
cargo test --test browser_script_skill_tool_test -- --nocapture
Expected: direct helper preserves the existing browser-script execution semantics.
Submit-path bypass
cargo test --test agent_runtime_test -- --nocapture
Expected: configured direct skill bypasses the model path, while unconfigured submit-task behavior stays unchanged.
Build
cargo build --bin sgclaw
Expected: the binary compiles cleanly.
Notes For The Engineer
- The key to keeping this slice small is to avoid changing
compat::runtime.rsandcompat::orchestration.rs; they remain the future LLM path. fault-details-report.collect_fault_detailsis only the bootstrap skill. The plumbing must stay generic enough that the configured tool name can later point to another staged browser skill.- Phase 1 should not add per-skill dispatch metadata to the external skill manifests yet. Keep that decision in sgClaw config first; move it into skill metadata only after the direct path is proven useful.
- Once the intranet model is ready, the clean next step is to add a dispatch policy layer that chooses between
direct_browserandllm_agentbefore the current compat path is entered, reusing this same pre-routing seam.