12 KiB
Request URL Resolution Design
Goal: Replace the temporary line-loss hardcoded request URL logic in src/service/server.rs with a single bootstrap-target resolution path that prefers current page context first, then deterministic submit results, then skill-owned metadata, and only finally falls back to about:blank.
Status: Approved design direction for the next slice.
Problem
The current callback-host bootstrap path still derives the first helper-page request URL in src/service/server.rs:
initial_request_url_for_submit_task(...)prefersrequest.page_url- then
derive_request_url_from_instruction(...) - then falls back to
about:blank
This is currently patched with a temporary line-loss branch:
- if instruction contains
线损orlineloss - return
http://20.76.57.61:18080
That temporary branch is the wrong ownership boundary:
- service code is guessing scene intent from raw instruction text
- deterministic submit already has a structured execution plan with
target_url - future direct-submit skills may also need a bootstrap URL, but should not require new Rust hardcoded branches every time
The result is duplicated routing knowledge and brittle request URL derivation.
Decision Summary
- Introduce one sgClaw-owned bootstrap target resolver for service submit bootstrap.
- Resolution order is:
- explicit
request.page_url - deterministic submit execution plan
- skill metadata fallback
about:blank
- explicit
- Deterministic submit is the primary source of truth for deterministic scenes such as line loss.
- Skill metadata provides the compatibility fallback for direct browser-script skills that do not have a deterministic plan.
- Remove the current line-loss text-match hardcode from
src/service/server.rsonce the resolver is in place.
Recommended Architecture
1. Add a dedicated bootstrap-target result type
Introduce a small sgClaw-side result type dedicated to callback-host bootstrap only.
Recommended shape:
pub struct SubmitBootstrapTarget {
pub request_url: String,
pub expected_domain: Option<String>,
pub source: BootstrapTargetSource,
}
pub enum BootstrapTargetSource {
PageContext,
DeterministicPlan,
SkillConfig,
Fallback,
}
This type should remain intentionally small.
It is not a generic execution-plan object. Its only job is to answer:
- what URL should the helper page bootstrap against on first submit?
- where did that value come from?
Keeping this object narrow avoids coupling callback-host bootstrap to unrelated execution details.
2. Move URL derivation into one resolver
Replace the current initial_request_url_for_submit_task(...) branching with a single resolver, conceptually:
- If
SubmitTaskRequest.page_urlexists and is non-empty, use it. - Else attempt deterministic parsing through
decide_deterministic_submit(...).- If it returns
Execute(plan), useplan.target_url.
- If it returns
- Else inspect configured direct-submit skill metadata.
- If metadata exposes a bootstrap URL, use it.
- Else return
about:blank.
This keeps service bootstrap logic declarative and removes scene-specific guessing from server.rs.
3. Deterministic submit becomes the primary truth for line loss
src/compat/deterministic_submit.rs already contains structured line-loss routing data:
DeterministicExecutionPlan.expected_domainDeterministicExecutionPlan.target_url
For line-loss requests, service bootstrap should use plan.target_url rather than reconstructing or hardcoding a URL in server.rs.
That means the current temporary branch:
if instruction.contains("线损") || instruction.contains("lineloss") {
return Some("http://20.76.57.61:18080".to_string());
}
should disappear entirely after the resolver is introduced.
This is the cleanest fix because the deterministic parser already owns the scene contract.
4. Skill metadata is the fallback, not the primary owner
For non-deterministic direct browser-script skills, service may still need a bootstrap URL even when there is no page context.
The fallback should come from skill-owned metadata with minimal fields:
bootstrap_urlexpected_domain
Recommended semantics:
bootstrap_url: the page URL service should use when opening the helper/bootstrap contextexpected_domain: the hostname direct runtime can use when page context is absent
This metadata should only be consulted after page context and deterministic parsing fail.
That preserves the user-selected policy:
- deterministic plan first
- skill metadata second
It also avoids forcing deterministic scenes to duplicate already-structured routing data in skill config.
Ownership Boundary
sgClaw owns bootstrap resolution policy
sgClaw should own the policy and precedence order for request URL resolution.
That includes:
- checking current page context first
- deciding when deterministic parsing is authoritative
- deciding when skill metadata is an allowed fallback
- falling back to
about:blankwhen nothing else resolves
This policy belongs in sgClaw because it is part of submit-path orchestration, not part of an individual skill script.
deterministic submit owns deterministic scene targets
If a scene already resolves to DeterministicExecutionPlan, then that plan owns:
- the authoritative
target_url - the authoritative
expected_domain
Service should consume that plan rather than re-deriving equivalent information from raw text.
skill metadata owns direct-skill bootstrap hints
When there is no deterministic plan, the skill package may own the minimal hints needed for bootstrap compatibility:
bootstrap_urlexpected_domain
The skill should not own resolution precedence. It only provides data for the fallback tier.
File-Level Design
src/service/server.rs
Change responsibilities here from “derive request URL by ad hoc branch logic” to “ask the resolver for a bootstrap target”.
Expected changes:
- replace
initial_request_url_for_submit_task(...)logic with a call into a resolver - delete
derive_request_url_from_instruction(...)or reduce it to thin legacy glue during migration - remove the line-loss text-match hardcode entirely
- keep callback-host startup logic unchanged apart from where
bootstrap_urlcomes from
src/compat/deterministic_submit.rs
No routing policy should move into service from this module.
Expected role in the new design:
- continue producing
DeterministicExecutionPlan - expose enough information for service bootstrap resolution to reuse
plan.target_url - remain the source of truth for deterministic line-loss target selection
src/compat/direct_skill_runtime.rs
This module currently resolves skill tool execution and derives expected_domain from task context.
For this slice, it does not need a full behavior rewrite.
Expected role:
- add or expose a helper for reading direct skill metadata if service needs it
- keep runtime execution behavior stable unless required by the new metadata seam
A later slice may allow runtime execution to use skill-owned expected_domain fallback too, but that is not required to land this service TODO.
src/config/settings.rs
If sgClaw configuration needs to point to direct-skill metadata or enable fallback behavior explicitly, add the minimum structure here.
However, this slice should avoid creating a second parallel source of target URLs inside sgClaw config if the same information can be read from skill metadata.
The key rule is:
- do not replace one hardcode with a different hardcoded config map inside
settings.rs
Skill metadata loading seam
The design assumes a small read path that can answer:
- for the configured direct-submit skill, is there a
bootstrap_url? - is there an
expected_domain?
The exact storage location can follow the existing staged-skill packaging model, but the new metadata should remain minimal and execution-adjacent rather than introducing a new wide dispatch schema.
Data Flow
Current desired flow
- service receives
ClientMessage::SubmitTask - service converts it to
SubmitTaskRequest - service resolves
SubmitBootstrapTarget - service passes
SubmitBootstrapTarget.request_urlintoLiveBrowserCallbackHost::start_with_browser_ws_url(...) - callback-host bootstraps helper page using that URL
- remaining task execution continues unchanged
Resolution behavior examples
Case A: page context exists
- request includes
page_url=https://www.zhihu.com - resolver returns
PageContext - service uses that URL directly
Case B: line-loss deterministic request, no page context
- request has no
page_url - deterministic parser returns
Execute(plan) - resolver returns
DeterministicPlanwithrequest_url=plan.target_url - service uses line-loss target URL from the plan
Case C: direct-submit skill with configured bootstrap URL, no page context, not deterministic
- request has no
page_url - deterministic parser returns
NotDeterministic - configured direct skill metadata exposes
bootstrap_url - resolver returns
SkillConfig
Case D: nothing resolves
- no page context
- no deterministic plan
- no skill metadata bootstrap URL
- resolver returns
Fallbackwithabout:blank
Testing Strategy
Resolver-focused tests
Add focused tests covering precedence:
page_urlwins over everything else- deterministic line-loss
Execute(plan)wins whenpage_urlis absent - skill metadata fallback is used only when no deterministic plan exists
about:blankremains the terminal fallback
Regression coverage for the removed TODO
Add a regression proving that the service no longer depends on:
instruction.contains("线损")instruction.contains("lineloss")
The line-loss bootstrap URL should now come from the deterministic plan only.
Direct skill fallback tests
Add tests for:
- configured skill metadata with valid
bootstrap_url - missing
bootstrap_url - malformed
bootstrap_url - mismatch between metadata and current page context precedence
Malformed bootstrap_url metadata should be treated as unusable fallback data rather than a hard error for service bootstrap resolution:
- if page context exists, page context still wins
- if deterministic plan exists, deterministic plan still wins
- if malformed metadata is the only candidate, resolver should ignore it and fall through to
about:blank
Existing callback-host tests remain stable
Do not redesign callback-host behavior in this slice.
The callback-host tests should only need enough updates to reflect the new bootstrap URL source, not a new helper lifecycle contract.
Migration Plan Shape
Recommended implementation order:
- Introduce the bootstrap-target resolver and narrow result type.
- Wire deterministic line-loss resolution into it using
DeterministicExecutionPlan.target_url. - Remove the temporary line-loss hardcode from
server.rs. - Add skill metadata fallback for configured direct-submit skills.
- Expand tests to lock precedence and fallback behavior.
This order lands the TODO removal early without forcing the full fallback design to be implemented blindly first.
Explicit Non-Goals
This slice does not:
- redesign callback-host lifecycle
- redesign deterministic scene parsing
- redesign direct-submit routing ownership
- introduce a broad scene registry for request URL derivation
- change browser command protocol
- rewrite direct skill execution behavior beyond what is needed for metadata lookup
- replace all current uses of page context with skill metadata
Design Rule
For service bootstrap request URL resolution:
- current page context stays first
- deterministic execution plans are the authoritative source for deterministic scenes
- skill metadata provides a narrow fallback for non-deterministic direct skills
about:blankremains the final fallbacksrc/service/server.rsmust not contain scene-specific text-match hardcodes such as the current line-loss TODO