16 KiB
Request URL Resolution 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: Replace the temporary line-loss request URL hardcode in src/service/server.rs with a unified bootstrap-target resolver that prefers current page context, then deterministic submit plans, then skill metadata, and finally about:blank.
Architecture: Add a small service-owned resolver that returns a narrow SubmitBootstrapTarget result and centralizes precedence rules. Reuse DeterministicExecutionPlan.target_url as the authoritative source for deterministic line-loss scenes, then add minimal skill metadata fallback for configured direct browser-script skills, while keeping callback-host behavior unchanged.
Tech Stack: Rust, serde/serde_json, tungstenite, zeroclaw skill loader, staged SKILL.toml manifests, cargo test
Task 1: Add resolver-focused red tests for precedence
Files:
-
Modify:
src/service/server.rs:422-467 -
Test:
src/service/server.rs(crate-local resolver tests) -
Test:
tests/service_ws_session_test.rs -
Step 1: Write the failing page-context precedence test
In a crate-local unit test inside src/service/server.rs, add a focused resolver test that exercises the request-url resolver with:
- non-empty
page_url = "https://already-open.example.com/page" - an instruction that would otherwise match deterministic line-loss logic
- configured direct skill metadata present
Assert the resolved bootstrap target uses the explicit non-empty page_url and reports PageContext source.
- Step 2: Run the test to verify it fails
Run: cargo test page_context_bootstrap_target_wins_over_deterministic_and_skill_fallback --lib -- --nocapture
Expected: FAIL because no unified resolver/source enum exists yet.
- Step 3: Write the failing deterministic-precedence test
In src/service/server.rs crate-local tests, add a focused test for a deterministic line-loss instruction with no page_url.
Use the same instruction shape already accepted by decide_deterministic_submit(...), and assert:
-
resolver source is
DeterministicPlan -
resolved
request_urlequalsDeterministicExecutionPlan.target_url -
no raw
instruction.contains("线损")fallback is needed -
Step 4: Run the test to verify it fails
Run: cargo test deterministic_bootstrap_target_uses_plan_target_url --lib -- --nocapture
Expected: FAIL because service still uses derive_request_url_from_instruction(...).
- Step 5: Write the failing skill-fallback test
In src/service/server.rs crate-local tests, add a focused test for:
- no
page_url - instruction not deterministic
- configured direct-submit skill metadata provides
bootstrap_url
Assert resolver source is SkillConfig and request_url matches metadata.
- Step 6: Run the test to verify it fails
Run: cargo test skill_metadata_bootstrap_url_is_used_when_no_page_context_or_plan_exists --lib -- --nocapture
Expected: FAIL because skill metadata is not read today.
- Step 7: Write the failing malformed-metadata fallback test
In src/service/server.rs crate-local tests, add a focused test for malformed bootstrap_url metadata, with no page context and no deterministic plan.
Assert the resolver:
-
ignores malformed metadata
-
returns
Fallback -
resolves to
about:blank -
Step 8: Run the test to verify it fails
Run: cargo test malformed_skill_bootstrap_url_falls_back_to_about_blank --lib -- --nocapture
Expected: FAIL because malformed metadata is not handled by a resolver yet.
Task 2: Introduce the bootstrap-target resolver in service code
Files:
-
Modify:
src/service/server.rs:280-467 -
Modify:
src/service/mod.rs:17-22 -
Test:
src/service/server.rs(crate-local resolver tests) -
Step 1: Add the narrow resolver types in service code
In src/service/server.rs, add:
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct SubmitBootstrapTarget {
pub request_url: String,
pub expected_domain: Option<String>,
pub source: BootstrapTargetSource,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum BootstrapTargetSource {
PageContext,
DeterministicPlan,
SkillConfig,
Fallback,
}
Keep them scoped to service code. Do not create a generic cross-runtime planning object.
- Step 2: Add a minimal resolver entry point
Implement a service-owned function in src/service/server.rs, conceptually:
pub(crate) fn resolve_submit_bootstrap_target(
request: &crate::agent::SubmitTaskRequest,
workspace_root: &Path,
settings: &SgClawSettings,
) -> SubmitBootstrapTarget
Initial behavior for this step:
-
return
PageContextonly whenrequest.page_urlexists and is non-empty after trimming -
add a crate-local regression that empty/whitespace
page_urldoes not short-circuit later precedence tiers -
otherwise fall through to existing behavior temporarily so the new tests can compile incrementally
-
Step 3: Update service startup to call the resolver
At the callback-host startup call site in serve_client(...), replace:
let bootstrap_url = initial_request_url_for_submit_task(&request);
with resolver usage:
let bootstrap_target = resolve_submit_bootstrap_target(&request, context.workspace_root(), &settings);
let bootstrap_url = bootstrap_target.request_url;
Use the current settings-loading seam already used elsewhere in service code. Keep callback-host startup behavior otherwise unchanged.
- Step 4: Keep resolver visibility crate-local
Do not make the resolver types broadly public for integration tests. Keep the resolver and BootstrapTargetSource crate-local, and keep source-level assertions in src/service/server.rs unit tests.
Only re-export/remove existing initial_request_url_for_submit_task(...) seams through src/service/mod.rs if production callers still require that wiring.
- Step 5: Run the first precedence test to verify it passes
Run: cargo test page_context_bootstrap_target_wins_over_deterministic_and_skill_fallback --lib -- --nocapture
Expected: PASS.
- Step 6: Commit
git add src/service/server.rs src/service/mod.rs
git commit -m "refactor(service): add submit bootstrap target resolver scaffold"
Task 3: Make deterministic submit the authoritative source for line-loss bootstrap URLs
Files:
-
Modify:
src/service/server.rs:422-467 -
Modify:
src/compat/deterministic_submit.rs:13-101 -
Test:
src/service/server.rs(crate-local resolver tests) -
Test:
tests/service_ws_session_test.rs -
Step 1: Write a small service-side seam for deterministic resolution
In src/service/server.rs, update the resolver so that when page_url is absent it calls:
crate::compat::deterministic_submit::decide_deterministic_submit(
&request.instruction,
request.page_url.as_deref(),
request.page_title.as_deref(),
)
Only DeterministicSubmitDecision::Execute(plan) should produce a deterministic bootstrap target.
Treat NotDeterministic and Prompt { .. } as “no deterministic bootstrap target” for service startup.
- Step 2: Use
plan.target_urldirectly
Map DeterministicSubmitDecision::Execute(plan) to:
request_url = plan.target_url.clone()expected_domain = Some(plan.expected_domain.clone())source = BootstrapTargetSource::DeterministicPlan
Do not reconstruct the URL in server.rs.
- Step 3: Remove the temporary line-loss hardcode
Delete this branch from derive_request_url_from_instruction(...) or remove the function entirely if it is no longer needed:
if instruction.contains("线损") || instruction.contains("lineloss") {
return Some("http://20.76.57.61:18080".to_string());
}
Keep any still-needed legacy Zhihu fallback only if the resolver still requires it after deterministic integration.
- Step 4: Add/adjust a deterministic regression test
In src/service/server.rs crate-local tests, add a focused assertion that line-loss bootstrap URL now comes from DeterministicExecutionPlan.target_url, not raw text matching.
A good assertion shape is:
-
call resolver with deterministic line-loss instruction
-
assert
request_url == "http://20.76.57.61:18080/gsllys/tqLinelossStatis/tqQualifyRateMonitor" -
assert
source == DeterministicPlan -
Step 5: Run deterministic tests to verify they pass
Run: cargo test deterministic_bootstrap_target_uses_plan_target_url --lib -- --nocapture
Expected: PASS.
- Step 6: Run service websocket coverage for the same precedence
Run: cargo test callback_host --test service_ws_session_test -- --nocapture
Expected: PASS with no line-loss hardcode dependency.
- Step 7: Commit
git add src/service/server.rs src/compat/deterministic_submit.rs tests/service_ws_session_test.rs
git commit -m "refactor(service): derive line-loss bootstrap URL from deterministic plan"
Task 4: Add skill-metadata fallback for configured direct-submit skills
Files:
-
Modify:
src/compat/direct_skill_runtime.rs:114-153 -
Modify:
src/service/server.rs:422-467 -
Optionally modify:
src/config/settings.rsonly if a tiny metadata pointer is required -
Modify:
D:/data/ideaSpace/rust/sgClaw/claw/claw/skills/skill_staging/skills/fault-details-report/SKILL.toml -
Optionally modify:
D:/data/ideaSpace/rust/sgClaw/claw/claw/skills/skill_staging/skills/95598-weekly-monitor-report/SKILL.toml -
Test:
src/service/server.rs(crate-local resolver tests) -
Test:
tests/service_ws_session_test.rs -
Step 1: Define the minimal skill metadata shape
Extend staged SKILL.toml parsing expectations to support a narrow metadata seam for browser-script direct skills.
The plan target fields are:
bootstrap_urlexpected_domain
Keep the metadata minimal. Do not add a broad dispatch registry or scene-policy schema.
Recommended TOML shape in the skill manifest:
[tools.metadata]
bootstrap_url = "https://example.com/path"
expected_domain = "example.com"
If the actual skill loader only supports per-tool custom fields in another location, use that established seam instead. Do not invent a parallel config file.
- Step 2: Add a helper that reads fallback metadata for the configured direct skill
In src/compat/direct_skill_runtime.rs, add a helper like:
pub(crate) fn resolve_direct_submit_bootstrap_metadata(
configured_tool: &str,
workspace_root: &Path,
settings: &SgClawSettings,
) -> Result<Option<DirectSubmitBootstrapMetadata>, PipeError>
Recommended shape:
pub(crate) struct DirectSubmitBootstrapMetadata {
pub bootstrap_url: String,
pub expected_domain: Option<String>,
}
Reuse the existing resolve_browser_script_skill(...) lookup path so the service resolver does not duplicate staged-skill discovery logic.
- Step 3: Validate metadata conservatively
When reading fallback metadata:
- accept only non-empty
bootstrap_url - require it to parse as a valid absolute URL
- normalize or preserve
expected_domainonly if non-empty - on malformed metadata, return
Ok(None)for resolver purposes instead of failing service startup
This keeps malformed fallback data from breaking submits and matches the approved spec.
- Step 4: Wire skill metadata into the service resolver
Update resolve_submit_bootstrap_target(...) to:
-
check skill metadata only after page context and deterministic parsing fail
-
use
SkillConfigas the source when metadata resolves -
fall through to
about:blankwhen metadata is missing or malformed -
Step 5: Add a staged-skill fixture update
Update at least one configured direct skill fixture, likely fault-details-report, to include valid fallback metadata.
Use concrete values appropriate for that skill’s target page; do not reuse the line-loss URL.
- Step 6: Run the skill-fallback test to verify it passes
Run: cargo test skill_metadata_bootstrap_url_is_used_when_no_page_context_or_plan_exists --lib -- --nocapture
Expected: PASS.
- Step 7: Run the malformed-metadata test to verify it passes
Run: cargo test malformed_skill_bootstrap_url_falls_back_to_about_blank --lib -- --nocapture
Expected: PASS.
- Step 8: Commit
git add src/compat/direct_skill_runtime.rs src/service/server.rs D:/data/ideaSpace/rust/sgClaw/claw/claw/skills/skill_staging/skills/fault-details-report/SKILL.toml tests/service_ws_session_test.rs
git commit -m "feat(service): add direct skill bootstrap URL fallback metadata"
Task 5: Remove obsolete request-url glue and lock the final precedence contract
Files:
-
Modify:
src/service/server.rs:422-467 -
Modify:
src/service/mod.rs:20-22 -
Test:
src/service/server.rs(crate-local resolver tests) -
Test:
tests/service_ws_session_test.rs -
Step 1: Delete obsolete helper logic
If derive_request_url_from_instruction(...) is no longer needed after resolver landing, delete it completely.
If a tiny legacy Zhihu-only seam still remains, keep it private behind the resolver and remove the old public shape from service::browser_ws_client if no longer needed.
- Step 2: Lock the precedence contract with one final matrix test
In src/service/server.rs crate-local tests, add one table-driven or clearly segmented test that verifies all four final outcomes:
-
non-empty page context wins
-
deterministic plan wins when page context is absent or empty
-
skill metadata wins when page context and deterministic plan are absent
-
fallback becomes
about:blankwhen nothing resolves -
Step 3: Run the focused resolver suite
Run: cargo test bootstrap_target --lib -- --nocapture
Expected: PASS.
- Step 4: Run service websocket regression coverage
Run: cargo test callback_host --test service_ws_session_test -- --nocapture
Expected: PASS.
- Step 5: Commit
git add src/service/server.rs src/service/mod.rs tests/service_ws_session_test.rs
git commit -m "refactor(service): finalize bootstrap target precedence"
Task 6: Full verification and implementation handoff check
Files: None (verification only)
- Step 1: Run focused deterministic and direct-skill tests
Run: cargo test deterministic_submit -- --nocapture
Expected: PASS.
Run: cargo test direct_submit -- --nocapture
Expected: PASS.
- Step 2: Run service submit regression coverage
Run: cargo test --test service_task_flow_test -- --nocapture
Expected: PASS.
Run: cargo test --test service_ws_session_test -- --nocapture
Expected: PASS.
- Step 3: Run targeted config/settings coverage if touched
Run: cargo test service_protocol_update_config_test -- --nocapture
Expected: PASS.
- Step 4: Build the project
Run: cargo build --bin sg_claw
Expected: PASS.
- Step 5: Manual behavior checklist
Verify manually:
- Existing page-attached submits still bootstrap against the current page URL.
- Deterministic line-loss submit without page context boots helper against the line-loss target page from
DeterministicExecutionPlan.target_url. - Non-deterministic configured direct skill without page context uses skill metadata bootstrap URL if present.
- Missing or malformed skill metadata does not crash startup and falls back to
about:blank. - No service code remains that hardcodes line-loss request URL by checking raw instruction text.
- Step 6: Final commit (only if verification revealed required follow-up fixes)
git add -A
git commit -m "test: lock request URL resolution precedence"
Only create this commit if verification required an additional code or test fix.