Describe the Bug
CatalogItem (packages/genui/lib/src/model/catalog_item.dart) is declared as final class CatalogItem with a const constructor that takes required Schema dataSchema:
const CatalogItem({
required this.name,
required Schema dataSchema,
required this.widgetBuilder,
this.exampleData = const [],
this.isImplicitlyFlexible = false,
}) : _originalSchema = dataSchema;
_originalSchema is private. When item.dataSchema is called, it constructs an ObjectSchema on the fly, injecting the 'component' property and prepending 'component' into required:
ObjectSchema get dataSchema {
...
final List<Object?> requiredProps =
originalMap['required'] as List<Object?>? ?? <Object?>[];
return ObjectSchema.fromMap(<String, Object?>{
...originalMap,
'properties': <String, Object?>{
...properties,
'component': <String, Object?>{
'type': 'string',
'enum': <String>[name],
},
},
'required': <Object?>['component', ...requiredProps],
'additionalProperties': true,
});
}
Because CatalogItem does not provide a copyWith method (unlike Catalog.copyWith), a host application or custom wrapper that wants to customize a widget builder (e.g. to wrap components with analytics, custom styling, or theme decorators) must construct a new CatalogItem:
CatalogItem(
name: item.name,
dataSchema: item.dataSchema, // Passes already-injected schema
widgetBuilder: (ctx) => wrapWithTheme(item.widgetBuilder(ctx)),
)
Because item.dataSchema already contains 'component' in requiredProps, the new CatalogItem.dataSchema prepends 'component' a second time:
required: ['component', 'component', ...]
If wrapped again (e.g., theming + instrumentation), it accumulates duplicates repeatedly.
JSON Schema specifies that required should be an array of unique strings. When catalog.definition or toCapabilitiesJson() is sent to the LLM agent, this malformed schema is exposed. Furthermore, hand-rolling constructor invocations is fragile because any future fields added to CatalogItem will be dropped.
Steps to Reproduce
import 'package:flutter_test/flutter_test.dart';
import 'package:genui/genui.dart';
void main() {
test('CatalogItem reconstruction duplicates component in required', () {
final original = BasicCatalogItems.button;
final wrapped = CatalogItem(
name: original.name,
dataSchema: original.dataSchema,
widgetBuilder: original.widgetBuilder,
);
final requiredProps = wrapped.dataSchema.value['required'] as List<Object?>;
final componentOccurrences =
requiredProps.where((prop) => prop == 'component').length;
expect(componentOccurrences, 1); // Fails: Actual is 2
});
}
Expected Behavior
CatalogItem should provide a copyWith method:
CatalogItem copyWith({
String? name,
Schema? dataSchema,
CatalogWidgetBuilder? widgetBuilder,
List<ExampleBuilderCallback>? exampleData,
bool? isImplicitlyFlexible,
}) => CatalogItem(
name: name ?? this.name,
dataSchema: dataSchema ?? _originalSchema,
widgetBuilder: widgetBuilder ?? this.widgetBuilder,
exampleData: exampleData ?? this.exampleData,
isImplicitlyFlexible: isImplicitlyFlexible ?? this.isImplicitlyFlexible,
);
- The
dataSchema getter should ensure 'component' is not duplicated if already present in requiredProps (e.g., using LinkedHashSet or checking requiredProps.contains('component')).
Environment Details
- SDK/Package Name & Version:
package:genui (packages/genui/lib/src/model/catalog_item.dart)
- Protocol Version: v0.9.1
Describe the Bug
CatalogItem(packages/genui/lib/src/model/catalog_item.dart) is declared asfinal class CatalogItemwith aconstconstructor that takesrequired Schema dataSchema:_originalSchemais private. Whenitem.dataSchemais called, it constructs anObjectSchemaon the fly, injecting the'component'property and prepending'component'intorequired:Because
CatalogItemdoes not provide acopyWithmethod (unlikeCatalog.copyWith), a host application or custom wrapper that wants to customize a widget builder (e.g. to wrap components with analytics, custom styling, or theme decorators) must construct a newCatalogItem:Because
item.dataSchemaalready contains'component'inrequiredProps, the newCatalogItem.dataSchemaprepends'component'a second time:required: ['component', 'component', ...]If wrapped again (e.g., theming + instrumentation), it accumulates duplicates repeatedly.
JSON Schema specifies that
requiredshould be an array of unique strings. Whencatalog.definitionortoCapabilitiesJson()is sent to the LLM agent, this malformed schema is exposed. Furthermore, hand-rolling constructor invocations is fragile because any future fields added toCatalogItemwill be dropped.Steps to Reproduce
Expected Behavior
CatalogItemshould provide acopyWithmethod:dataSchemagetter should ensure'component'is not duplicated if already present inrequiredProps(e.g., usingLinkedHashSetor checkingrequiredProps.contains('component')).Environment Details
package:genui(packages/genui/lib/src/model/catalog_item.dart)