Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "coval"
version = "0.8.3"
version = "0.9.0"
edition = "2021"
description = "CLI for Coval AI agent evaluation platform"
license = "MIT"
Expand Down
19 changes: 8 additions & 11 deletions api-coverage-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ only when coverage actually changes.
| Metric | Value |
| --- | ---: |
| Reconciliation status | PASS |
| Published operations | 181 |
| Published operations | 183 |
| First-class CLI operations | 119 |
| Reviewed gaps | 62 |
| Reviewed gaps | 64 |
| Client operations | 143 |
| Published request fields on covered operations | 365 |
| Request fields modeled by the CLI | 359 |
| Reviewed request-field gaps | 6 |
| Published request fields on covered operations | 437 |
| Request fields modeled by the CLI | 437 |
| Reviewed request-field gaps | 0 |

Catalog: https://api.coval.dev/v1/openapi

Expand Down Expand Up @@ -112,6 +112,8 @@ Catalog: https://api.coval.dev/v1/openapi
- `POST /personas/{persona_id}/versions/{version_id}/revert`
- `POST /review-annotations:withMetricOutputs`
- `POST /review-projects/disagreement-state`
- `POST /review-projects/{project_id}/complete-conversation`
- `POST /sofia/delegation-token`
- `POST /test-sets/{test_set_id}/agents:add`
- `POST /test-sets/{test_set_id}/duplicate`
- `POST /test-sets/{test_set_id}/versions/{version_id}/revert`
Expand All @@ -138,9 +140,4 @@ Catalog: https://api.coval.dev/v1/openapi

## All current request-field gaps

- `PATCH /run-templates/{id} agent_id`
- `PATCH /run-templates/{id} persona_id`
- `PATCH /run-templates/{id} test_set_id`
- `POST /run-templates agent_id`
- `POST /run-templates persona_id`
- `POST /run-templates test_set_id`
- None.
111 changes: 12 additions & 99 deletions api-coverage.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

[snapshot]
catalog_url = "https://api.coval.dev/v1/openapi"
reviewed_at = "2026-09-14"
published_operations = 181
reviewed_at = "2026-10-05"
published_operations = 183
cli_supported_operations = 119
published_request_fields = 365
cli_modeled_request_fields = 359
published_request_fields = 437
cli_modeled_request_fields = 437

[[allowed_extra]]
operation = "POST /test-cases/{test_case_id}/media:upload-url"
Expand Down Expand Up @@ -376,6 +376,14 @@ reason = "Workspace management remains to be modeled under COVAL-2079."
operation = "POST /workspaces/{workspace_id}/archive"
reason = "Workspace management remains to be modeled under COVAL-2079."

[[known_gap]]
operation = "POST /review-projects/{project_id}/complete-conversation"
reason = "Project-scoped completion remains to be modeled under COVAL-2079; this explicit write needs a deliberate command workflow rather than being exposed by the parity refresh."

[[known_gap]]
operation = "POST /sofia/delegation-token"
reason = "Customer API-key delegation token minting remains to be modeled under COVAL-2079 with an explicit security-sensitive CLI workflow."

# =============================================================================
# REQUEST FIELDS
# =============================================================================
Expand All @@ -391,36 +399,6 @@ reason = "Workspace management remains to be modeled under COVAL-2079."
# Pydantic model; coval-ai/backend records the known divergence in
# src/services/api/tests/v1/openapi_parity_baseline.txt.

[[known_field_gap]]
operation = "PATCH /run-templates/{run_template_id}"
field = "agent_id"
reason = "Documented but not served: the transport forbids extra fields and takes the plural `agent_ids` array the CLI already sends, so modeling this name would break the call. Spec drift tracked under COVAL-5825."

[[known_field_gap]]
operation = "PATCH /run-templates/{run_template_id}"
field = "persona_id"
reason = "Documented but not served: the transport forbids extra fields and takes the plural `persona_ids` array the CLI already sends, so modeling this name would break the call. Spec drift tracked under COVAL-5825."

[[known_field_gap]]
operation = "PATCH /run-templates/{run_template_id}"
field = "test_set_id"
reason = "Documented but not served: the transport forbids extra fields and takes the plural `test_set_ids` array the CLI already sends, so modeling this name would break the call. Spec drift tracked under COVAL-5825."

[[known_field_gap]]
operation = "POST /run-templates"
field = "agent_id"
reason = "Documented but not served: the transport forbids extra fields and takes the plural `agent_ids` array the CLI already sends, so modeling this name would break the call. Spec drift tracked under COVAL-5825."

[[known_field_gap]]
operation = "POST /run-templates"
field = "persona_id"
reason = "Documented but not served: the transport forbids extra fields and takes the plural `persona_ids` array the CLI already sends, so modeling this name would break the call. Spec drift tracked under COVAL-5825."

[[known_field_gap]]
operation = "POST /run-templates"
field = "test_set_id"
reason = "Documented but not served: the transport forbids extra fields and takes the plural `test_set_ids` array the CLI already sends, so modeling this name would break the call. Spec drift tracked under COVAL-5825."

# Fields the CLI sends that the published schema does not declare. Most are served
# but undocumented, so removing them from the CLI would lose working behavior.

Expand All @@ -429,21 +407,6 @@ operation = "PATCH /conversations/uploaded/{conversation_id}"
field = "audio_reference"
reason = "Served but deliberately hidden from the published schema as an internal field; the API still accepts it."

[[allowed_extra_field]]
operation = "PATCH /metrics/{metric_id}"
field = "case_insensitive"
reason = "Served but undocumented regex-matching field; recorded as spec drift in the backend's own openapi_parity_baseline.txt."

[[allowed_extra_field]]
operation = "PATCH /metrics/{metric_id}"
field = "match_mode"
reason = "Served but undocumented regex-matching field; recorded as spec drift in the backend's own openapi_parity_baseline.txt."

[[allowed_extra_field]]
operation = "PATCH /metrics/{metric_id}"
field = "position"
reason = "Served but undocumented regex-matching field; recorded as spec drift in the backend's own openapi_parity_baseline.txt."

[[allowed_extra_field]]
operation = "PATCH /review-annotations/{annotation_id}"
field = "completion_status"
Expand All @@ -454,26 +417,6 @@ operation = "PATCH /review-annotations/{annotation_id}"
field = "status"
reason = "Advertised by the CLI but absent from the served contract, which ignores unknown fields, so the flag silently does nothing. Tracked under COVAL-5824."

[[allowed_extra_field]]
operation = "PATCH /run-templates/{run_template_id}"
field = "agent_ids"
reason = "Served but undocumented: the run-template models take these plural arrays. The published singular form is stale spec, tracked under COVAL-5825."

[[allowed_extra_field]]
operation = "PATCH /run-templates/{run_template_id}"
field = "persona_ids"
reason = "Served but undocumented: the run-template models take these plural arrays. The published singular form is stale spec, tracked under COVAL-5825."

[[allowed_extra_field]]
operation = "PATCH /run-templates/{run_template_id}"
field = "test_set_ids"
reason = "Served but undocumented: the run-template models take these plural arrays. The published singular form is stale spec, tracked under COVAL-5825."

[[allowed_extra_field]]
operation = "POST /api-keys"
field = "environment"
reason = "Served but undocumented; recorded as spec drift in the backend's own openapi_parity_baseline.txt."

[[allowed_extra_field]]
operation = "POST /conversations/simulated/{simulation_id}/resimulate"
field = "dev_id"
Expand All @@ -483,33 +426,3 @@ reason = "Served but deliberately hidden from the published schema as an interna
operation = "POST /conversations/uploaded:submit"
field = "audio"
reason = "Served but deliberately hidden from the published schema as an internal field; the API still accepts it."

[[allowed_extra_field]]
operation = "POST /metrics"
field = "case_insensitive"
reason = "Served but undocumented regex-matching field; recorded as spec drift in the backend's own openapi_parity_baseline.txt."

[[allowed_extra_field]]
operation = "POST /metrics"
field = "match_mode"
reason = "Served but undocumented regex-matching field; recorded as spec drift in the backend's own openapi_parity_baseline.txt."

[[allowed_extra_field]]
operation = "POST /metrics"
field = "position"
reason = "Served but undocumented regex-matching field; recorded as spec drift in the backend's own openapi_parity_baseline.txt."

[[allowed_extra_field]]
operation = "POST /run-templates"
field = "agent_ids"
reason = "Served but undocumented: the run-template models take these plural arrays. The published singular form is stale spec, tracked under COVAL-5825."

[[allowed_extra_field]]
operation = "POST /run-templates"
field = "persona_ids"
reason = "Served but undocumented: the run-template models take these plural arrays. The published singular form is stale spec, tracked under COVAL-5825."

[[allowed_extra_field]]
operation = "POST /run-templates"
field = "test_set_ids"
reason = "Served but undocumented: the run-template models take these plural arrays. The published singular form is stale spec, tracked under COVAL-5825."
95 changes: 95 additions & 0 deletions src/client/models/metric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pub struct Metric {
pub metric_type: MetricType,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt: Option<String>,
/// Agent Judge evidence tools. An empty list disables all tools.
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled_tools: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub categories: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -116,6 +119,47 @@ pub struct CreateMetricRequest {
pub metric_type: MetricType,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt: Option<String>,
/// Agent Judge evidence tools. An empty list disables all tools.
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled_tools: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub judge_mode: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub aggregation_method: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub unit: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub detection_preset: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub harmonics_to_noise_ratio_threshold_offset_db: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub jitter_threshold_multiplier: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub loud_threshold_db: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub low_pitch_threshold_multiplier: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mad_z_score_threshold: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metric_attribute: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metric_metadata: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min_fry_segment_seconds: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pause_detection_preset: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pitch_change_threshold_hz: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub significant_changes_threshold_hz: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub soft_threshold_db: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub span_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub threshold_preset: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub value_source: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub categories: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -202,6 +246,57 @@ pub struct UpdateMetricRequest {
pub metric_type: Option<MetricType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt: Option<String>,
/// Agent Judge evidence tools. An empty list disables all tools.
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled_tools: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub judge_mode: Option<String>,
/// SQL and custom-trace aggregation; an explicit null resets it to the default.
#[serde(
default,
deserialize_with = "super::explicit_option",
skip_serializing_if = "Option::is_none"
)]
pub aggregation_method: Option<Option<String>>,
/// Display unit; an explicit null clears it.
#[serde(
default,
deserialize_with = "super::explicit_option",
skip_serializing_if = "Option::is_none"
)]
pub unit: Option<Option<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub detection_preset: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub harmonics_to_noise_ratio_threshold_offset_db: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub jitter_threshold_multiplier: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub loud_threshold_db: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub low_pitch_threshold_multiplier: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mad_z_score_threshold: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metric_attribute: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metric_metadata: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min_fry_segment_seconds: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pause_detection_preset: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pitch_change_threshold_hz: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub significant_changes_threshold_hz: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub soft_threshold_db: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub span_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub threshold_preset: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub value_source: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub categories: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down
50 changes: 50 additions & 0 deletions src/client/models/persona.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,18 @@ pub struct CreatePersonaRequest {
/// `situate_speaker`.
#[serde(skip_serializing_if = "Option::is_none")]
pub audio_degradation: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub silent_mode: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub multi_phone_config: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub initialization_parameters: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub custom_persona_data: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub voice: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub custom_voice_id: Option<String>,
/// Tag names. Omitted leaves tags unchanged; an empty list clears them.
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<String>>,
Expand Down Expand Up @@ -158,6 +170,44 @@ pub struct UpdatePersonaRequest {
skip_serializing_if = "Option::is_none"
)]
pub audio_degradation: Option<Option<serde_json::Value>>,
// Advanced persona values are stored in metadata. The API uses field
// presence to distinguish an omitted update from an explicit null clear.
#[serde(
default,
deserialize_with = "super::explicit_option",
skip_serializing_if = "Option::is_none"
)]
pub silent_mode: Option<Option<bool>>,
#[serde(
default,
deserialize_with = "super::explicit_option",
skip_serializing_if = "Option::is_none"
)]
pub multi_phone_config: Option<Option<serde_json::Value>>,
#[serde(
default,
deserialize_with = "super::explicit_option",
skip_serializing_if = "Option::is_none"
)]
pub initialization_parameters: Option<Option<serde_json::Value>>,
#[serde(
default,
deserialize_with = "super::explicit_option",
skip_serializing_if = "Option::is_none"
)]
pub custom_persona_data: Option<Option<String>>,
#[serde(
default,
deserialize_with = "super::explicit_option",
skip_serializing_if = "Option::is_none"
)]
pub voice: Option<Option<String>>,
#[serde(
default,
deserialize_with = "super::explicit_option",
skip_serializing_if = "Option::is_none"
)]
pub custom_voice_id: Option<Option<String>>,
/// Tag names. Omitted leaves tags unchanged; an empty list clears them.
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<String>>,
Expand Down
Loading
Loading