Type chart config and settings from compiled presentation contract
Problem
Tighten Dataface-owned chart config and settings surfaces into typed models after the compiled presentation-defaults contract exists, so schema generation and validation reflect the real runtime contract instead of loose DotDict bags.
Context
- Background research:
ai_notes/refactors/CHART_DEFAULTS_AND_PRECEDENCE_AUDIT.mdai_notes/refactors/COMPILED_PRESENTATION_DEFAULTS_ARCHITECTURE.md- This task intentionally depends on
compile-effective-chart-presentation-defaults-before-render. - Reason for the dependency:
- today the repo already has meaningful Pydantic coverage for authored faces, charts, variables, layout models, and some explicit mark/encoding surfaces
- but chart settings/style and the broader presentation-defaults system are still partly represented as
dictorDotDictbags - if we tighten typing before the presentation-defaults contract is cleaned up, we risk encoding today's accidental precedence and split-brain behavior into schema generation, docs, and validators
- the repo is pre-launch, so this follow-on should move to the desired contract directly rather than preserving legacy shapes "just in case"
- Current typing state:
- strong:
Face,Chart,Variable, layout modelsCompiledFace,CompiledChart- several explicit mark/encoding models
- partial
ThemeConfig/StructureConfig - typed
FaceStylewithSpacingValuesandBorderStyle
- weak:
- chart
style - chart
settings - the merged runtime config tree returned by
get_config() - many Dataface-owned config/default sections that are still just YAML merged into
DotDict
- chart
- Desired outcome:
- type the Dataface-owned contract more exhaustively
- do not attempt to model the entire Vega-Lite schema
- keep Vega-Lite passthrough surfaces open with
extra=\"allow\"or equivalent patterns where appropriate - remove or reject accidental Dataface-owned loose bags rather than preserving them as compatibility surfaces
- This task is also a prerequisite for cleaner tooling and docs work, especially:
tasks/workstreams/dft-core/tasks/m1-make-dataface-yaml-schema-exhaustive-from-typed-contract.md- Relevant code areas:
dataface/core/compile/types.pydataface/core/compile/compiled_types.pydataface/core/compile/config.pydataface/core/compile/theme_types.pydataface/core/render/chart/pipeline.pyapps/ide/scripts/generate_schema.py- Constraint:
- type the Dataface-owned language and config surfaces, not the full upstream Vega-Lite JSON schema.
Possible Solutions
- Generate a broader schema from the current models immediately. This would help IDE coverage, but it would still encode messy precedence behavior and loose chart settings surfaces.
- Recommended: type the Dataface-owned chart config and settings surfaces after compiled presentation defaults are defined, while keeping Vega-Lite passthrough open with
extra=\"allow\". This gives us stronger validation and schema generation from the cleaned runtime contract rather than from today's transitional state. - Model the entire Vega-Lite schema in Pydantic. Rejected. Too large, the wrong maintenance burden, and unnecessary for Dataface's needs.
- Keep chart settings and runtime config as mostly open bags forever and rely on docs only. Rejected. That preserves contract drift and keeps IDE/schema/runtime alignment weak.
Plan
- Re-evaluate the post-refactor runtime contract once compiled presentation defaults exist and identify which Dataface-owned surfaces should become typed first.
- Introduce typed models for the Dataface-owned presentation/config/settings surfaces that runtime actually reads and owns, such as: - runtime config sections - compiled presentation-defaults objects - chart-family settings models - typed subsets of axis/scale/theme fields that Dataface reads or generates
- Keep Vega-Lite passthrough open where necessary:
- axis passthrough
- scale passthrough
- theme/structure Vega config passthrough
- advanced
markandencodingpassthrough where Dataface intentionally supports it - Replace loose chart settings/style bags where Dataface owns behavior with explicit models or model unions, while preserving only intentional advanced escape hatches.
- Decide and document where chart style should remain: - typed as a Dataface-owned surface - open passthrough - or intentionally small and narrow rather than a large generic bag
- Update schema/tooling generation so it reflects the stronger typed contract rather than a partial hand-curated subset.
- Add tests that fail loudly when: - a typed contract field is missing from schema generation - runtime accepts fields the schema omits - Dataface-owned settings drift back into untyped bags without explicit intent
- Remove or explicitly reject any remaining Dataface-owned loose surfaces that are not intentional passthrough contracts.
Implementation Progress
- New file:
dataface/core/compile/chart_settings_types.py ChartSettings— Pydantic model with typed Dataface-owned keys (orientation,x_axis,y_axis,x_scale,y_scale,columns,header_wrap) andextra="allow"for Vega-Lite passthroughget()method for passthrough key access;to_dict()for serializationcompiled_types.py:CompiledChart.settingschanged fromDotDict | NonetoChartSettings | None; model validator updated to useChartSettings.model_validate();to_dict()callssettings.to_dict()normalize_charts.py: settings wrapping changed fromDotDict()toChartSettings.model_validate()render/chart/models.py:ChartIntent.settingsandResolvedChart.settingstyped asChartSettings | Nonerender/chart/pipeline.py: replacedgetattr(settings, ...)with direct attribute access;DotDictimport removedrender/chart/standard_renderer.py: allgetattr(settings, "orientation", None)→settings.orientation; allgetattr(settings, "x_axis", None)→settings.x_axis;DotDictimport removedrender/chart/spark_bar.py: usesChartSettings()default instead of{}render/chart/table.py: simplified settings access (removed isinstance dict guard where unnecessary)render/chart/serialization.py:settings.to_dict()for JSON outputrender/json_format.pyandrender/yaml_format.py: added PydanticBaseModelserialization fallbackrender/utils.py:settings.series→settings.get("series")for passthrough key accessrender/chart/decisions.py:DotDict(merged)→ChartSettings.model_validate(merged)for enrichment settings merge- Tests:
tests/core/test_chart_settings_typing.py— 13 tests covering typed fields, validation, passthrough, contract drift guards, and round-trip serialization - Updated:
tests/core/compile/test_dotdict.py— settings tests updated forChartSettingsinstead ofDotDict - All core tests pass; mypy clean on changed files
QA Exploration
- [x] N/A — non-UI task (compile/render layer typing, no browser-visible changes)
Review Feedback
- [x] Review cleared