Skip to main content

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::Response and Mode::ResponseWithoutDefaults ignore string checks named uuid, rfc3339, datetime, date, duration, email, url, and regex across 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 reachabilityAdded fieldsModels
request-only6JobCreateRequestJobMetadataVariant1, JobCreateRequestJobMetadataVariant2, JobCreateRequestJobMetadataVariant3
request-and-response8LanguageOption, TargetMetadata
response-only39AttackDetailResponse, AttackMultiTurnDetailResponse, AttackMultiTurnOutput, AttackOutput, CustomAttackOutput, CustomJobMetadata, DynamicJobMetadata, Goal, JobResponse, PromptDetailResponse, PromptSetSummary, RemediationDetail, ScoreTrendResponse, StaticJobMetadata, StreamDetailResponse, StreamDetailResponseGoalVariant2, StreamIterationData, SubCategoryModel, SubCategoryStats
standalone-schema2ValidationError

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 modelFrozen schemaAdded fields
AuthConfigRequestVariant3s010755basic_password, basic_username
BasicAuthAuthConfigs010721basic_password, basic_username
ConnectionParamsRequestVariant2s010829audio_curl, audio_request_json
RestConnectionParamss010789audio_curl, audio_request_json
StreamingConnectionParamss010821audio_curl, audio_request_json
WebSocketConnectionParamss010824audio_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/maximum and integer constraints, and array minItems/maxItems, remain enforced in both response modes. For example, AsrResponseSchema.asr must 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_order effect still parses start_time and end_time timestamp 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 operationTypeScript response contractFormat occurrencesCompared paths
RedTeamScansClient.createJobResponseSchema18169
RedTeamScansClient.listJobListResponseSchema18173
RedTeamScansClient.getJobResponseSchema18169
RedTeamScansClient.abortJobAbortResponseSchema13
RedTeamScansClient.set_runtime_profileNo counterpart; vendor structure18163
RedTeamScansClient.get_categoriesz.array(CategoryModelSchema)020
RedTeamClient.get_scan_metadataz.object({}).passthrough()01
RedTeamReportsClient.list_attacksAttackListResponseSchema528
RedTeamReportsClient.get_attack_detailAttackDetailResponseSchema1380
RedTeamReportsClient.get_static_reportStaticJobReportSchema0141
RedTeamReportsClient.get_static_runtime_policyRuntimeSecurityProfileResponseSchema012
RedTeamReportsClient.get_static_asrNo counterpart; vendor structure120
RedTeamReportsClient.get_static_remediationRemediationResponseSchema010
RedTeamReportsClient.get_dynamic_reportDynamicJobReportSchema08
RedTeamReportsClient.get_dynamic_runtime_policyRuntimeSecurityProfileResponseSchema012
RedTeamReportsClient.get_dynamic_asrNo counterpart; vendor structure120
RedTeamReportsClient.get_dynamic_remediationRemediationResponseSchema015
RedTeamReportsClient.list_goalsGoalListResponseSchema223
RedTeamReportsClient.list_goal_streamsStreamListResponseSchema1496
RedTeamReportsClient.get_stream_detailStreamDetailResponseSchema22101
RedTeamReportsClient.download_reportz.unknown()01
RedTeamReportsClient.generate_partial_reportz.unknown()01
RedTeamReportsClient.regenerateNo counterpart; vendor structure13
RedTeamReportsClient.get_statusNo counterpart; vendor structure02
RedTeamReportsClient.get_multi_turn_attack_detailAttackMultiTurnDetailResponseSchema1399
RedTeamReportsClient.override_attackNo counterpart; vendor structure14
RedTeamReportsClient.override_multi_turn_attackNo counterpart; vendor structure14
RedTeamReportsClient.override_streamNo counterpart; vendor structure14
RedTeamClient.get_quotaQuotaSummarySchema013
RedTeamCustomAttackReportsClient.get_reportCustomAttackReportResponseSchema138
RedTeamCustomAttackReportsClient.get_prompt_setsPromptSetsReportResponseSchema126
RedTeamCustomAttackReportsClient.get_prompts_by_setz.array(PromptDetailResponseSchema)533
RedTeamCustomAttackReportsClient.get_prompt_detailPromptDetailResponseSchema532
RedTeamCustomAttackReportsClient.list_custom_attacksCustomAttacksListResponseSchema538
RedTeamCustomAttackReportsClient.get_attack_outputsz.array(CustomAttackOutputSchema)618
RedTeamCustomAttackReportsClient.get_property_statsz.array(PropertyStatisticResultSchema)09
RedTeamCustomAttackReportsClient.override_attackNo counterpart; vendor structure14
RedTeamClient.get_error_logsErrorLogListResponseSchema519
RedTeamClient.download_error_logsCSV bytes; no counterpart00
RedTeamClient.get_target_profile_error_logsErrorLogListResponseSchema519
RedTeamClient.get_goal_categoriesGoalCategoryListResponseSchema07
RedTeamClient.get_languagesTenantLanguagesResponseSchema09
RedTeamReportsClient.get_download_urlNo counterpart; vendor structure03
RedTeamClient.get_scan_statisticsScanStatisticsResponseSchema019
RedTeamClient.get_score_trendScoreTrendResponseSchema010
RedTeamClient.update_sentimentSentimentResponseSchema14
RedTeamClient.get_sentimentSentimentResponseSchema14

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 operationRejected 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 moduleMethodChecked arguments
adapters.rsgetuuid
adapters.rsupdateuuid
adapters.rsdeleteuuid
custom_attack_reports.rsget_reportjob_id
custom_attack_reports.rsget_prompt_setsjob_id
custom_attack_reports.rsget_prompts_by_setjob_id, prompt_set_id
custom_attack_reports.rsget_prompt_detailjob_id, prompt_id
custom_attack_reports.rslist_custom_attacksjob_id
custom_attack_reports.rsget_attack_outputsjob_id, attack_id
custom_attack_reports.rsget_property_statsjob_id
custom_attack_reports.rsoverride_attackjob_id, attack_id
custom_attacks.rsget_prompt_setuuid
custom_attacks.rsupdate_prompt_setuuid
custom_attacks.rsarchive_prompt_setuuid
custom_attacks.rsget_prompt_set_referenceuuid
custom_attacks.rsget_prompt_set_version_infouuid
custom_attacks.rsdownload_templateuuid
custom_attacks.rsupload_prompts_csvuuid
custom_attacks.rslist_promptsuuid
custom_attacks.rsget_promptset_uuid, prompt_uuid
custom_attacks.rsupdate_promptset_uuid, prompt_uuid
custom_attacks.rsdelete_promptset_uuid, prompt_uuid
custom_attacks.rsprompt_pagesuuid
facade.rsget_score_trendtarget_id
facade.rsget_error_logsjob_id
facade.rsget_target_profile_error_logstarget_id
facade.rsdownload_error_logsjob_id
facade.rsget_sentimentjob_id
network_broker.rsget_channeluuid
network_broker.rsupdate_channeluuid
reports.rslist_attacksjob_id
reports.rsget_attack_detailjob_id, attack_id
reports.rsget_multi_turn_attack_detailjob_id, attack_id
reports.rsget_static_reportjob_id
reports.rsget_static_runtime_policyjob_id
reports.rsget_static_remediationjob_id
reports.rsget_dynamic_reportjob_id
reports.rsget_dynamic_runtime_policyjob_id
reports.rsget_dynamic_remediationjob_id
reports.rslist_goalsjob_id
reports.rslist_goal_streamsjob_id, goal_id
reports.rsget_stream_detailstream_id
reports.rsdownload_reportjob_id
reports.rsgenerate_partial_reportjob_id
reports.rsget_static_asrjob_id
reports.rsget_dynamic_asrjob_id
reports.rsregeneratejob_id
reports.rsget_statusjob_id
reports.rsoverride_attackjob_id, attack_id
reports.rsoverride_multi_turn_attackjob_id, attack_id
reports.rsoverride_streamjob_id, stream_id
reports.rsget_download_urljob_id
scans.rsgetjob_id
scans.rsset_runtime_profilejob_id
scans.rsabortjob_id
targets.rsqueryvalue
targets.rsstart_profilinguuid
targets.rsdelete_copilot_tokenuuid
targets.rsgetuuid
targets.rsupdateuuid
targets.rsdeleteuuid
targets.rsget_profileuuid
targets.rsupdate_profileuuid

Reproduction and delivery boundaries​

  • tools/g25-repro and docs/checks/phase-18-reproduction.json retain the pre-fix Rust/frozen-Zod reproduction, including the three intentionally rejected cases.
  • tools/g25_fixtures.py derives root, format, enum, structural and typed-projection cases; tools/g25-oracle.mts derives independent expected outcomes. crates/scm-sdk/tests/g25_response_compatibility.rs executes 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/ and docs/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.