Skip to content

[COVAL-5821] Model the dropped persona request fields - #124

Merged
callumreid merged 5 commits into
mainfrom
callum/cli-persona-request-fields
Sep 4, 2026
Merged

[COVAL-5821] Model the dropped persona request fields#124
callumreid merged 5 commits into
mainfrom
callum/cli-persona-request-fields

Conversation

@callumreid

Copy link
Copy Markdown
Contributor

Depends on #122 for the audit exceptions; the code here stands alone. Touches common.rs and test_case.rs, so it conflicts with nothing in #123 but wants landing after it.

Why

CreatePersonaRequest and UpdatePersonaRequest declared seven fewer fields than the API publishes and serves, and the background-sound requests dropped acoustic_source_type. input_json::finish deserializes into the struct, so passing any of them by flag or --input-json returned 200 and changed nothing.

The part worth reviewing carefully: null is not one rule

backend src/services/api/v1/personas/common/shared_requests.py applies these fields two different ways, and the CLI has to match each:

Field Server CLI
voice_volume, voice_speed, hold_music_timeout_seconds, situate_speaker, audio_degradation in model_fields_set and is Nonedelete the stored value Option<Option<T>> + explicit_option
background_sound_volume is not None guard → null is the same as omitted plain Option<T>
tags "None means don't update; [] clears" plain Option<Vec<String>>

Modeling all seven the same way would have turned half the clears into silent no-ops — the exact failure skip_serializing_if alone produces, and the reason COVAL-5805 introduced explicit_option. acoustic_source_type is cleared by an explicit null too (background_sounds/service.py line 491).

Note this is the opposite of metrics in #123, where null and omitted genuinely do mean the same thing, so tags there is plain Option. The rule is per field, not per resource.

Other changes

  • explicit_option moves from test_case.rs to common.rs now that a second resource needs it. test_case.rs keeps its behavior; its tests are untouched and pass.
  • --audio-degradation takes a bare preset id (cell-handoff) or the full {"preset": …} object, so the common case needs no JSON on the command line.
  • A flag can only set a value, so clearing goes through --input-json '{"situate_speaker":null}'. Documented in the README.

Verification

Eight new tests. Two matter most: one asserts an explicit null is actually sent as null rather than dropped, and one asserts an unset clearable field is still omitted — the two halves of the contract.

cargo fmt --check && cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-targets --all-features

Running the #122 audit against this tree moves modeled request fields from 280/360 to 296/360 and reports exactly these 16 entries as stale exceptions, with no new extras.

Minor version bump to 0.8.0.

On merge order

Cut from main, so it does not carry #122's api-coverage.toml sections. After #122 merges, rebase and delete the 16 [[known_field_gap]] entries for POST/PATCH /personas and POST/PATCH /personas/background-sounds, regenerate api-coverage-report.md, and re-apply the version bump.

test_update_check_notifies_when_outdated fails under parallel cargo test here exactly as on clean main. Pre-existing, COVAL-5826.

CreatePersonaRequest and UpdatePersonaRequest declared seven fewer fields than
the API publishes and serves, so `coval personas create/update` discarded them
in silence: background_sound_volume, voice_volume, voice_speed,
hold_music_timeout_seconds, situate_speaker, audio_degradation, and tags. The
background-sound create and update requests dropped acoustic_source_type the
same way.

Null does not mean the same thing for every one of these, so the update request
does not treat them alike. shared_requests.py applies voice_volume, voice_speed,
hold_music_timeout_seconds, situate_speaker, and audio_degradation through
`model_fields_set`, deleting the stored value when the key is present and null;
those get the explicit_option treatment so an intentional clear survives. It
applies background_sound_volume through an `is not None` guard, and tags follow
the "None means don't update; [] clears" contract, so both stay plain Option.
Modeling all seven the same way would have made half the clears no-ops.

acoustic_source_type is likewise cleared by an explicit null on update.

explicit_option moves from test_case.rs to common.rs now that a second resource
needs it. --audio-degradation takes a bare preset id or the full JSON object, so
the common case needs no JSON on the command line.
@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Team

Run ID: d77f5f57-1d97-413e-9de9-3f858635aeb0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@callumreid
callumreid marked this pull request as ready for review September 3, 2026 22:38

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

The release bridge publishes any main version without a matching
tag, so the first feature PR to merge would cut an incomplete
release. Feature PRs stay at the base version; a dedicated
version-bump PR releases the series.
@callumreid
callumreid merged commit ac5a51d into main Sep 4, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant