Response Compatibility
Scope and decision
The audit covers 47 operations, 46 JSON response roots, and 36 TypeScript counterparts. The baseline is TypeScript 0.34.0 at 4a80dbdb3dc14365e1c25d838ac0bd139485d48c; the data-plane vendor contract is Red Team 0.93.0. The management projection addendum covers Red Team management 0.86.0.
For existing operations, response validation uses the actual TypeScript operation schema, with the shared response policy below. Eleven operations have no TypeScript counterpart; ten have JSON responses and one returns CSV bytes. Their vendor structure remains authoritative. This is a compatibility correction, not a claim of live service acceptance.
Loosened response rules
- Both
Mode::ResponseandMode::ResponseWithoutDefaultsignore string checks nameduuid,rfc3339,datetime,date,duration,email,url, andregexacross the SDK. Strings remain strings and their contents are preserved. - Vendor-only string length bounds are request-only. Existing TypeScript response bounds remain enforced; using the exact baseline schema also retains its requiredness, nullability and types instead of imposing stricter vendor fields.
- Optional vendor-added typed projections preserve unsupported JSON in extension storage when the typed field cannot represent it. An explicitly set typed field supersedes that fallback. Baseline-field extension collisions remain errors.
This typed-over-raw precedence also applies intentionally to every request-reachable projection model in the data-plane corpus: JobCreateRequestJobMetadataVariant1, JobCreateRequestJobMetadataVariant2, JobCreateRequestJobMetadataVariant3, LanguageOption, TargetMetadata. If a caller supplies both a typed projection and an extension with the same wire key, the typed value is serialized and the raw extension is omitted. Callers must leave the typed projection unset to serialize a preserved raw value. Projection classifications are recorded in projections.json. The reviewed legacy/vendor schema pairings are pinned separately in projection-bindings.json and generation fails if they change.
Standalone schema projections are classified separately: ValidationError. They are not labeled as public-operation request or response projections.
| Projection reachability | Added fields | Models |
|---|---|---|
request-only | 6 | JobCreateRequestJobMetadataVariant1, JobCreateRequestJobMetadataVariant2, JobCreateRequestJobMetadataVariant3 |
request-and-response | 8 | LanguageOption, TargetMetadata |
response-only | 39 | AttackDetailResponse, AttackMultiTurnDetailResponse, AttackMultiTurnOutput, AttackOutput, CustomAttackOutput, CustomJobMetadata, DynamicJobMetadata, Goal, JobResponse, PromptDetailResponse, PromptSetSummary, RemediationDetail, ScoreTrendResponse, StaticJobMetadata, StreamDetailResponse, StreamDetailResponseGoalVariant2, StreamIterationData, SubCategoryModel, SubCategoryStats |
standalone-schema | 2 | ValidationError |
Baseline decoding errors remain generic and redacted to avoid exposing service values through serde diagnostics. Projection decoding retains a value copy for lossless fallback; public-client responses remain subject to the existing body and validation resource limits.
Management 0.86.0 projection addendum
The separate management overlay adds the following optional typed projections. They use the same typed-over-raw precedence: a supplied typed value, including explicit null, supersedes a same-key extension. Leaving the typed field unset preserves the raw extension. A representable value now populates its typed Patch field and is removed from extensions; callers reading extensions must use the typed field for such values. Wrong-type values remain in extension storage; these conveniences do not narrow the frozen request or response validators.
These pairings and generated Rust names are recorded in the management overlay ledger, whose generator checks baseline identities and completeness. They are additional to the 55 data-plane projection field occurrences above; the historical projection-bindings.json owns only that data-plane corpus.
Observable decode transition for StreamingConnectionParamsTargetConnectionConfig (s010462): previously Unknown(JsonValue), now Variant5(VertexAIConnectionParams). Previously unknown shape: An object with string project_id, location, engine_id and customer_sa_email, and no matching legacy provider shape. Legacy branch order and precedence remain unchanged; payloads matching an earlier branch still select it.
Observable decode transition for StreamingConnectionParamsMultiTurnConfig (s010504): previously Unknown(JsonValue), now Variant3(MultiTurnClientSessionConfig). Previously unknown shape: An object with explicit type: "client_session". Legacy branch order and precedence remain unchanged; payloads matching an earlier branch still select it.
| Management projection model | Frozen schema | Added fields |
|---|---|---|
AuthConfigRequestVariant3 | s010755 | basic_password, basic_username |
BasicAuthAuthConfig | s010721 | basic_password, basic_username |
ConnectionParamsRequestVariant2 | s010829 | audio_curl, audio_request_json |
RestConnectionParams | s010789 | audio_curl, audio_request_json |
StreamingConnectionParams | s010821 | audio_curl, audio_request_json |
WebSocketConnectionParams | s010824 | audio_curl, audio_request_json |
Rules deliberately retained
- Unknown response enum strings and unknown object keys were already supported. They remain supported.
- String type checks, baseline presence/type/bounds checks, resource limits and finite JSON rules remain. Defaults still follow the selected response mode; this fix does not disable defaults.
- For operations without a TypeScript counterpart, vendor numeric
minimum/maximumand integer constraints, and arrayminItems/maxItems, remain enforced in both response modes. For example,AsrResponseSchema.asrmust remain within 0–100. These retained constraints can still reject an unexpected service value. No TypeScript counterpart establishes broader numeric domains or structural cardinality for these operations; the accepted plan retains those constraints while relaxing only the scoped string compatibility policy. - Frozen refinements and transforms remain active. In particular, the
time_ordereffect still parsesstart_timeandend_timetimestamp content in response modes and can reject unparseable timestamps or invalid ordering. Bypassing a string-format assertion does not bypass a separate semantic refinement. - Request schemas, caller-supplied UUID checks, safe path encoding and authentication/destination policies remain strict. See the full UUID inventory below.
The independent frozen-Zod reproduction rejects all three disputed structural cases: missing category_display_name, numeric tsg_id, and null sub_category. They remain rejected. In contrast, opaque IDs such as job-1, p-1, and map-1, and timestamp strings containing a space or lacking an offset are accepted in responses.
Reading the comparison evidence
The generated field ledger contains every paired field for every operation. Each descriptor includes presence (optional), nullability, node kind, checks (including string min/max), and object unknown-field policy where present. Exact original branch paths and node IDs are retained. The source audit binds the underlying source hashes.
Union branch positions are removed only from the grouping key. A nullable wrapper and a union containing ZodNull can describe the same accepted nulls despite different local descriptions. Likewise, branch alternatives can accept a document rejected by another branch. The local comparison categories are not semantic equivalence claims. The independent Zod/AJV outcome corpus and Rust regressions decide acceptance.
Ledger categories distinguish matching local descriptions, differing local descriptions, vendor-only paths, TypeScript-only paths, opaque baseline contracts, and operations with no TypeScript counterpart. An absent baseline field in a passthrough object does not mean its value is rejected.
Deliberately retained strictness relative to the vendor
The frozen TypeScript schema is selected exclusively for paired response operations; it is not unioned with the vendor schema. Consequently, some documents allowed by the vendor are rejected because TypeScript also rejects them. This is a deliberate compatibility boundary and a residual read-failure risk.
The generated mutation corpus contains 213 cases across 6 operations for which the mutated document passes strict vendor validation but fails both frozen TypeScript and the selected response policy. These are corpus cases, including repeated branch witnesses, not unique service documents or observed service failures. The ledger lists their case indices, operation roots, paths and mutation kinds. Counts use the oracle outcome after mutation, not the valid starting witness.
Examples: create_job_v1_scan_post at $.report_stats.report_summary; create_job_v1_scan_post at $.report_stats.total_goals; create_job_v1_scan_post at $.report_stats.total_streams.
Mutation counts: type:array 42, type:boolean 42, type:null 33, type:number 33, type:object 45, type:string 18.
Format occurrences across the strict vendor response trees are duration 4, rfc3339 56, uuid 124; 28 operations are affected. Counts include repeated union branches, not only distinct fields. Strict request tests use the independent oracle: an alternative union branch can legitimately accept a mutation.
The shared relaxation reaches 190 frozen roots with format checks. The source audit lists each root and format node, including management, IAM, Model Security, Gateway and request-named roots callable with response mode. This is an intentional response-policy difference from frozen TypeScript format refinements; it does not mean TypeScript ignores every format in those products.
Operation coverage
Each row links the operation to its actual frozen response expression. Full vendor operation IDs, root names and field descriptors are in the ledger. Format counts include branch occurrences.
| Rust operation | TypeScript response contract | Format occurrences | Compared paths |
|---|---|---|---|
RedTeamScansClient.create | JobResponseSchema | 18 | 169 |
RedTeamScansClient.list | JobListResponseSchema | 18 | 173 |
RedTeamScansClient.get | JobResponseSchema | 18 | 169 |
RedTeamScansClient.abort | JobAbortResponseSchema | 1 | 3 |
RedTeamScansClient.set_runtime_profile | No counterpart; vendor structure | 18 | 163 |
RedTeamScansClient.get_categories | z.array(CategoryModelSchema) | 0 | 20 |
RedTeamClient.get_scan_metadata | z.object({}).passthrough() | 0 | 1 |
RedTeamReportsClient.list_attacks | AttackListResponseSchema | 5 | 28 |
RedTeamReportsClient.get_attack_detail | AttackDetailResponseSchema | 13 | 80 |
RedTeamReportsClient.get_static_report | StaticJobReportSchema | 0 | 141 |
RedTeamReportsClient.get_static_runtime_policy | RuntimeSecurityProfileResponseSchema | 0 | 12 |
RedTeamReportsClient.get_static_asr | No counterpart; vendor structure | 1 | 20 |
RedTeamReportsClient.get_static_remediation | RemediationResponseSchema | 0 | 10 |
RedTeamReportsClient.get_dynamic_report | DynamicJobReportSchema | 0 | 8 |
RedTeamReportsClient.get_dynamic_runtime_policy | RuntimeSecurityProfileResponseSchema | 0 | 12 |
RedTeamReportsClient.get_dynamic_asr | No counterpart; vendor structure | 1 | 20 |
RedTeamReportsClient.get_dynamic_remediation | RemediationResponseSchema | 0 | 15 |
RedTeamReportsClient.list_goals | GoalListResponseSchema | 2 | 23 |
RedTeamReportsClient.list_goal_streams | StreamListResponseSchema | 14 | 96 |
RedTeamReportsClient.get_stream_detail | StreamDetailResponseSchema | 22 | 101 |
RedTeamReportsClient.download_report | z.unknown() | 0 | 1 |
RedTeamReportsClient.generate_partial_report | z.unknown() | 0 | 1 |
RedTeamReportsClient.regenerate | No counterpart; vendor structure | 1 | 3 |
RedTeamReportsClient.get_status | No counterpart; vendor structure | 0 | 2 |
RedTeamReportsClient.get_multi_turn_attack_detail | AttackMultiTurnDetailResponseSchema | 13 | 99 |
RedTeamReportsClient.override_attack | No counterpart; vendor structure | 1 | 4 |
RedTeamReportsClient.override_multi_turn_attack | No counterpart; vendor structure | 1 | 4 |
RedTeamReportsClient.override_stream | No counterpart; vendor structure | 1 | 4 |
RedTeamClient.get_quota | QuotaSummarySchema | 0 | 13 |
RedTeamCustomAttackReportsClient.get_report | CustomAttackReportResponseSchema | 1 | 38 |
RedTeamCustomAttackReportsClient.get_prompt_sets | PromptSetsReportResponseSchema | 1 | 26 |
RedTeamCustomAttackReportsClient.get_prompts_by_set | z.array(PromptDetailResponseSchema) | 5 | 33 |
RedTeamCustomAttackReportsClient.get_prompt_detail | PromptDetailResponseSchema | 5 | 32 |
RedTeamCustomAttackReportsClient.list_custom_attacks | CustomAttacksListResponseSchema | 5 | 38 |
RedTeamCustomAttackReportsClient.get_attack_outputs | z.array(CustomAttackOutputSchema) | 6 | 18 |
RedTeamCustomAttackReportsClient.get_property_stats | z.array(PropertyStatisticResultSchema) | 0 | 9 |
RedTeamCustomAttackReportsClient.override_attack | No counterpart; vendor structure | 1 | 4 |
RedTeamClient.get_error_logs | ErrorLogListResponseSchema | 5 | 19 |
RedTeamClient.download_error_logs | CSV bytes; no counterpart | 0 | 0 |
RedTeamClient.get_target_profile_error_logs | ErrorLogListResponseSchema | 5 | 19 |
RedTeamClient.get_goal_categories | GoalCategoryListResponseSchema | 0 | 7 |
RedTeamClient.get_languages | TenantLanguagesResponseSchema | 0 | 9 |
RedTeamReportsClient.get_download_url | No counterpart; vendor structure | 0 | 3 |
RedTeamClient.get_scan_statistics | ScanStatisticsResponseSchema | 0 | 19 |
RedTeamClient.get_score_trend | ScoreTrendResponseSchema | 0 | 10 |
RedTeamClient.update_sentiment | SentimentResponseSchema | 1 | 4 |
RedTeamClient.get_sentiment | SentimentResponseSchema | 1 | 4 |
Query options
get_target_profile_error_logs accepts only limit under the pinned vendor contract. Supplied skip and search remain errors; they are not silently dropped. There is no evidence from populated tenant error logs that establishes those options are applied by the service. CLI consumers adopting this release must make --offset and --search usage errors for this operation.
All operation-level rejected legacy query options in the audit are listed below; this patch does not change them.
| Rust operation | Rejected options |
|---|---|
RedTeamReportsClient.list_attacks | [{"disposition": "validation_error_before_auth", "name": "search"}] |
RedTeamReportsClient.list_goal_streams | [{"disposition": "validation_error_before_auth", "name": "limit"}, {"disposition": "validation_error_before_auth", "name": "search"}, {"disposition": "validation_error_before_auth", "name": "skip"}] |
RedTeamCustomAttackReportsClient.get_prompt_sets | [{"disposition": "validation_error_before_auth", "name": "search"}] |
RedTeamCustomAttackReportsClient.get_prompts_by_set | [{"disposition": "validation_error_before_auth", "name": "search"}] |
RedTeamCustomAttackReportsClient.list_custom_attacks | [{"disposition": "validation_error_before_auth", "name": "search"}] |
RedTeamClient.get_error_logs | [{"disposition": "validation_error_before_auth", "name": "search"}] |
RedTeamClient.get_target_profile_error_logs | [{"disposition": "validation_error_before_auth", "name": "search"}, {"disposition": "validation_error_before_auth", "name": "skip"}] |
RedTeamClient.get_scan_statistics | [{"disposition": "validation_error_before_auth", "name": "date_range"}, {"disposition": "validation_error_before_auth", "name": "target_id"}] |
Retained caller UUID checks
All 76 common::uuid call sites are retained. The table includes methods beyond the 47 vendor operations and pagination helpers. Each listed argument has its own check. These are deliberately retained Rust request constraints; response IDs are no longer format-checked. TypeScript operation IDs use string parameters, so callers should not infer that every string accepted by TypeScript will pass Rust request validation.
| Source module | Method | Checked arguments |
|---|---|---|
adapters.rs | get | uuid |
adapters.rs | update | uuid |
adapters.rs | delete | uuid |
custom_attack_reports.rs | get_report | job_id |
custom_attack_reports.rs | get_prompt_sets | job_id |
custom_attack_reports.rs | get_prompts_by_set | job_id, prompt_set_id |
custom_attack_reports.rs | get_prompt_detail | job_id, prompt_id |
custom_attack_reports.rs | list_custom_attacks | job_id |
custom_attack_reports.rs | get_attack_outputs | job_id, attack_id |
custom_attack_reports.rs | get_property_stats | job_id |
custom_attack_reports.rs | override_attack | job_id, attack_id |
custom_attacks.rs | get_prompt_set | uuid |
custom_attacks.rs | update_prompt_set | uuid |
custom_attacks.rs | archive_prompt_set | uuid |
custom_attacks.rs | get_prompt_set_reference | uuid |
custom_attacks.rs | get_prompt_set_version_info | uuid |
custom_attacks.rs | download_template | uuid |
custom_attacks.rs | upload_prompts_csv | uuid |
custom_attacks.rs | list_prompts | uuid |
custom_attacks.rs | get_prompt | set_uuid, prompt_uuid |
custom_attacks.rs | update_prompt | set_uuid, prompt_uuid |
custom_attacks.rs | delete_prompt | set_uuid, prompt_uuid |
custom_attacks.rs | prompt_pages | uuid |
facade.rs | get_score_trend | target_id |
facade.rs | get_error_logs | job_id |
facade.rs | get_target_profile_error_logs | target_id |
facade.rs | download_error_logs | job_id |
facade.rs | get_sentiment | job_id |
network_broker.rs | get_channel | uuid |
network_broker.rs | update_channel | uuid |
reports.rs | list_attacks | job_id |
reports.rs | get_attack_detail | job_id, attack_id |
reports.rs | get_multi_turn_attack_detail | job_id, attack_id |
reports.rs | get_static_report | job_id |
reports.rs | get_static_runtime_policy | job_id |
reports.rs | get_static_remediation | job_id |
reports.rs | get_dynamic_report | job_id |
reports.rs | get_dynamic_runtime_policy | job_id |
reports.rs | get_dynamic_remediation | job_id |
reports.rs | list_goals | job_id |
reports.rs | list_goal_streams | job_id, goal_id |
reports.rs | get_stream_detail | stream_id |
reports.rs | download_report | job_id |
reports.rs | generate_partial_report | job_id |
reports.rs | get_static_asr | job_id |
reports.rs | get_dynamic_asr | job_id |
reports.rs | regenerate | job_id |
reports.rs | get_status | job_id |
reports.rs | override_attack | job_id, attack_id |
reports.rs | override_multi_turn_attack | job_id, attack_id |
reports.rs | override_stream | job_id, stream_id |
reports.rs | get_download_url | job_id |
scans.rs | get | job_id |
scans.rs | set_runtime_profile | job_id |
scans.rs | abort | job_id |
targets.rs | query | value |
targets.rs | start_profiling | uuid |
targets.rs | delete_copilot_token | uuid |
targets.rs | get | uuid |
targets.rs | update | uuid |
targets.rs | delete | uuid |
targets.rs | get_profile | uuid |
targets.rs | update_profile | uuid |
Reproduction and delivery boundaries
tools/g25-reproanddocs/checks/phase-18-reproduction.jsonretain the pre-fix Rust/frozen-Zod reproduction, including the three intentionally rejected cases.tools/g25_fixtures.pyderives root, format, enum, structural and typed-projection cases;tools/g25-oracle.mtsderives independent expected outcomes.crates/scm-sdk/tests/g25_response_compatibility.rsexecutes them in Rust.- The SDK transport regressions cover typed attacks and error logs. The reported 17 airs-cli-rs loopback tests on worf have not been executed here; no consumer adoption is claimed.
- Live qualification and publication have separate receipts in
docs/checks/anddocs/phase-status.json. This generated contract report alone establishes neither.
Regenerate with python3 tools/g25_audit.py followed by python3 tools/g25_report.py; verify freshness with each command’s --check option.