Dedup field schemas omit the boolean and inherit input shapes
- Status: Done
- Kind: Bug
- Authors: jp
- Date: 2026-09-04
- Label: package=jp_config
- Label: type=bug
Both dedup fields accept more input shapes than their generated schema describes.
MergedString::dedup(crates/jp_config/src/types/string.rs) isOption<StringDedup>, so the schema advertisesoff/exact/block/containsplus null.deserialize_string_dedupalso acceptstrue,false, and"inherit", and the field's doc comment advertises the boolean shorthand.MergedVec::dedup(crates/jp_config/src/types/vec.rs) isOption<bool>, so the schema advertises a boolean plus null.deserialize_dedupalso accepts"inherit".
deserialize_with does not alter the inferred schema: the schema comes from the field type in Field::generate_schema_type (crates/contrib/schematic_macros/src/common/field.rs), which never consults the deserializer.
Impact today
None observable. Nothing exports a JSON Schema for AppConfig. SchemaBuilder::build_root::<AppConfig>() appears only in RFD 061 and RFD 063, both unimplemented, and jp init uses ConfigEnum for variant listing rather than the schema renderer. So this is metadata that will be wrong for the first schema consumer that validates a user's config, not something anyone can currently hit.
When that consumer arrives, a config writing the documented dedup = false gets flagged as invalid while continuing to load correctly.
Fix
EnableConfig (crates/jp_config/src/conversation/tool.rs) solves the same problem with schema_union_with, which unions caller-supplied variants into the derived schema — enable_input_shapes there declares the bool and the legacy strings, and test_enable_schema pins the result.
That mechanism is container-only: schema_union_with lives on MacroArgs (crates/contrib/schematic_macros/src/common/macros.rs), which is FromDeriveInput. A plain field cannot reach it. Two ways out:
- Add a field-level
schema_union_withtoschematic_macros, and point bothdedupfields at a shared function returning the bool and"inherit"variants. - Promote each
dedupto its own#[derive(Config)]container with a hand-writtenDeserialize, mirroringEnableConfig. Heavier, and it changes the persisted shape of a field that reaches conversation config deltas.
The first is the smaller change and fixes both fields at once. Either way, add a schema-shape test in the style of test_enable_schema.
Context
Raised in review on #1064 (comment 3926664348), for the string field only. Deferred there because the vec field has the same gap, no schema consumer exists yet, and the fix needs a schematic_macros change that is out of scope for a config-merge bug fix.