Priority: P2 request-routing correctness.
Evidence
At #707 a8c322a02ce7f5453dab0a3d89393b9b729f6b4d, sdk/src/client.rs:
validate_opaque rejects empty/trimmed/control/overlength values and /, but permits ?, #, percent escapes and dot-segment strings.
ClientBuilder::build interpolates project_id into a raw application_root string.
lifecycle_url interpolates operation_id into a raw URL before parsing; request_route appends to the same raw application_root.
An accepted ID such as project#suffix changes the rest of the URL into a fragment; operation?x turns the lifecycle suffix into query material. Validation as an opaque string does not make that string safe URL syntax. This is a directly visible client construction defect, not proof of a server authorization bypass.
Change
Retain a parsed base/application URL and construct dynamic project/operation identifiers as encoded path segments using the maintained URL API. Keep contract validation separate: values forbidden by the canonical identity contract must fail before I/O; accepted values must remain one exact segment and must not create queries/fragments or be decoded/re-encoded twice. Explicitly handle dot segments according to the identifier contract rather than allowing URL normalization to reinterpret identity.
Reuse one small SDK path constructor across execute/cancel/status/events, preserve supported base-path prefixes, and remove format-then-reparse duplication. Do not build a custom percent encoder or broaden server authorization. Keep operation routes chosen by the canonical executable binding, not caller-selected arbitrary routes.
Acceptance
Allowed IDs round-trip as one segment; forbidden ?/#/%/dot cases either fail with the documented typed validation or remain literal encoded identity, never changing the endpoint shape. Assert exact outgoing request path/query/fragment in the existing controlled transport fixture, including a base URL with a prefix. Ordinary project/operation IDs retain identical requests. No network service or credential is needed for pure URL tests.
Distinct from #984's frame limits and #985's credential formatting. Source review only; no SDK tests executed. Target #707; keep it draft.
Priority: P2 request-routing correctness.
Evidence
At #707
a8c322a02ce7f5453dab0a3d89393b9b729f6b4d, sdk/src/client.rs:validate_opaquerejects empty/trimmed/control/overlength values and/, but permits?,#, percent escapes and dot-segment strings.ClientBuilder::buildinterpolates project_id into a raw application_root string.lifecycle_urlinterpolates operation_id into a raw URL before parsing;request_routeappends to the same raw application_root.An accepted ID such as
project#suffixchanges the rest of the URL into a fragment;operation?xturns the lifecycle suffix into query material. Validation as an opaque string does not make that string safe URL syntax. This is a directly visible client construction defect, not proof of a server authorization bypass.Change
Retain a parsed base/application URL and construct dynamic project/operation identifiers as encoded path segments using the maintained URL API. Keep contract validation separate: values forbidden by the canonical identity contract must fail before I/O; accepted values must remain one exact segment and must not create queries/fragments or be decoded/re-encoded twice. Explicitly handle dot segments according to the identifier contract rather than allowing URL normalization to reinterpret identity.
Reuse one small SDK path constructor across execute/cancel/status/events, preserve supported base-path prefixes, and remove format-then-reparse duplication. Do not build a custom percent encoder or broaden server authorization. Keep operation routes chosen by the canonical executable binding, not caller-selected arbitrary routes.
Acceptance
Allowed IDs round-trip as one segment; forbidden
?/#/%/dotcases either fail with the documented typed validation or remain literal encoded identity, never changing the endpoint shape. Assert exact outgoing request path/query/fragment in the existing controlled transport fixture, including a base URL with a prefix. Ordinary project/operation IDs retain identical requests. No network service or credential is needed for pure URL tests.Distinct from #984's frame limits and #985's credential formatting. Source review only; no SDK tests executed. Target #707; keep it draft.