Third and final sub-project from the control-panel architecture ask,
scoped down after discovering the assumed infrastructure doesn't exist:
Dashboard V2's node.props lives only in the frontend's in-memory
DashboardProvider (explicitly "No persistence"), and
Widget.validate_control_values has never had a real caller -- only its
own REST endpoint handler invokes it, and nothing in the traced human
edit flow does.
Adds a minimal MCP-process-local node store (node_store.py, mirroring
widgets/registry.py's plain-dict idiom) and one new tool that gives
validate_control_values its first real caller: shallow-merges provided
control values onto a node's current values (matching
DashboardProvider.updateProps's own merge semantics), validates the
candidate, and only commits (a single dict reassignment) on success --
so a validation failure is atomic by construction, not by explicit
rollback code. Registered in app.py (a tool exported from tool/__init__.py
but missing from app.py's import list is invisible to a real MCP client
despite passing every direct-call test -- caught this by adding a
Client(mcp)-based registration test, not just _impl calls).
Persistence, default-seeding, generated-value propagation, and the
broader value-precedence contract from the original ask are explicitly
out of scope -- documented as follow-ups in the accompanying spec.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Rewrites the spec's Design section to match what shipped: EnricherFn's
signature includes the whole schema (not just its own field), documents
the check_dependencies alias-resolution fix as the first real exercise of
that function, and clarifies the x-dependsOn gate is a coarse pre-filter
alongside Balloons' unchanged fine-grained guard, not a replacement for
it. All three were caught while writing the implementation plan (before
any code was written), so this is a documentation-only reconciliation --
no behavior differs from what the plan specified.
Marks all plan steps complete with actual results (442 tests passing,
clean pre-commit on every task, no frontend files touched).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Corrects three things found while writing the plan, ahead of the spec:
EnricherFn needs the whole schema (not just its own field) since Balloons'
real enricher reads a sibling $defs entry; check_dependencies resolves
x-dependsOn by raw name instead of alias, so it has never actually worked
(verified directly against a real parsed model); and the x-dependsOn gate
is coarser than Balloons' actual guard (dimensions non-empty, series
non-empty) so the enricher keeps its own fine-grained check. Spec gets
reconciled with these once implementation confirms them.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Second slice of the broader control-panel architecture ask (first was the
composite-control registry). Derives the dependency graph from each
dynamic field's existing x-dependsOn declaration rather than a duplicate
Python-level declaration; disambiguates an x-dependsOn entry naming
another dynamic field (an ordering edge) from one naming a plain field
(a gate, via the previously-dead check_dependencies). Cycle detection
runs once at widget-registration time. Widget.enrich_schema is retired
outright in favor of a per-path enricher registry, with Balloons
retrofitted as the sole proof case -- no concrete second real widget
exists yet, so this is validated against synthetic multi-field test
cases plus the one real single-field regression.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Update both to reflect the recursive $defs field-order fix discovered
while implementing (a top-level-only reorder was verified against the
real widget registry to leave nested $defs entries -- e.g.
$defs.DataBinding -- unreordered, since DataBinding is never itself the
top-level config_class). Mark plan steps complete with the actual
verification results (test counts, mypy narrowing fix, pre-commit
auto-fixes) instead of predictions.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Verified that Pydantic always places inherited fields ahead of a
subclass's own fields in model_fields, regardless of redeclaration
position -- the mixin refactor as originally specced would have silently
reordered DataBinding's rendered form (metrics before dataset picker).
Add an explicit, opt-in field_order override to build_configuration_schema
(ClassVar on the model, validated as an exact permutation of the schema's
properties, ValueError on mismatch) so DataBinding can pin its field order
back to today's exact sequence. Confirmed build_configuration_schema has
no other call site today, so this is additive with no blast radius.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fix invalid MRO in DataBinding(BaseModel, MetricControl) -> single
inheritance from MetricControl (confirmed the two-base form raises
TypeError against pydantic directly). Store registry entries as
CompositeControlInfo (name/title/description/model) instead of discarding
metadata. Expose a stable superset_core.widgets re-export and a read-only
list_composite_controls() accessor instead of a public mutable dict.
Document decorator-side-effect registration semantics for extensions.
Explicitly scope out multi-composite inheritance until a second composite
exists to motivate the collision/merge contract. Strengthen the
schema-identity test to a frozen golden fixture checked at the REST/MCP
boundary. Soften "functionally complete" phrasing on the ad-hoc metric
list[Any] field.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
First slice of the broader control-panel architecture ask: a mixin-based
composite-control mechanism in superset_core so reusable field-level
building blocks (starting with MetricControl) can be composed into widget
control models via plain inheritance, without changing served schema shape.
Dependency-graph/cycle-detection and value-propagation contract are scoped
out to follow-up specs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extends the widget control panel's x-control vocabulary with
column/metric-reference controls so fields like dimensions and metrics
render dataset-aware pickers instead of plain text/JSON inputs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>