12. Extension Points
Blueprint uses a closed typed core for common semantics and named references for behavior that cannot be expressed portably.
typed declaration
-> stable symbolic ref
-> assembly verifies required binding
-> client/server registry resolves implementation
-> runtime records ref + implementation versionPrefer typed vocabulary first
Use:
UIUpperCaseTransform, not a custom “uppercase” callback;ActorHasGrantCondition, not an evaluator for a basic grant check;RelationshipUI, not a custom picker for a normal reference;ActionPlacementUI, not app-local button logic.
This keeps validators and interpreters exhaustive.
Field transform reference
const name = NameField();
const siteId = ReferenceIdField('site_id');
FieldUI(
derivation: UIFieldDerivation(
sourceFields: [name, siteId],
transforms: [
UIFieldTransformRef(
'ops.area.code',
arguments: {'separator': '-'},
),
],
),
)The client binds ops.area.code to a pure transformation function. The ref is dot-qualified, stable, and testable. A missing required binding fails assembly/editor construction instead of silently degrading.
Evaluator reference
Rule(
id: 'ops.area.release.instrument_state',
kind: RuleKind.custom,
condition: EvaluatorCondition(
ref: 'ops.area.release.instrument_state',
metadata: {'contract_version': 1},
),
)final runtime = BlueprintRuntimeEngine(
blueprint: blueprint,
evaluators: {
'ops.area.release.instrument_state': instrumentStateEvaluator,
},
);The action record must pin both the symbolic ref and the implementation revision used for the decision.
Client and server extension catalog
vyuh_blueprint declares required imperative seams:
const descriptor = BlueprintDescriptor(
name: 'ops',
title: 'Operations',
version: '1.0.0',
modules: [opsModule],
extensionRefs: [
BlueprintExtensionRef(
ref: 'ops.area.release.instrument_state',
side: BlueprintExtensionSide.server,
kind: BlueprintExtensionKind.ruleEvaluator,
),
BlueprintExtensionRef(
ref: 'ops.area.layout',
side: BlueprintExtensionSide.client,
kind: BlueprintExtensionKind.form,
isRequired: false,
),
],
);Kinds include action handler, rule evaluator, policy resolver, effect handler, route, widget, detail tab, empty state, dashboard widget, report block, form, and custom.
UI attach points
FieldUI(editorRef: 'ops.instrument.selector')
RelationshipUI(
pickerRef: 'ops.area.category-picker',
rendererRef: 'ops.area.category-link',
)
ActionUI(formRef: 'ops.area.retire-form')
EntityUI(
detailTabRefs: ['ops.area.environment-monitoring'],
)The declaration names what is required. vyuh_blueprint_ui client registries bind those refs to Flutter implementations. The domain package remains free of Flutter widgets.
The current client registry is kind-typed:
final bindings = ClientUIBindings(
detailTabs: {
'ops.area.environment-monitoring': (context) =>
EnvironmentMonitoringView(
area: context.row!,
onRefresh: context.refresh,
),
},
fieldEditors: {
'ops.instrument.selector': (context) => InstrumentSelector(
value: context.fieldValue,
enabled: context.fieldEnabled,
onChanged: context.onFieldChanged,
),
},
);ClientRenderContext supplies the protocol client, assembled app, current actor, route, collection, selected row, refresh callback, field state, and the standard record-mutation boundary. A custom surface composes those public contracts instead of bypassing the protocol.
Declared and bound refs are resolved by kind. Declared-but-unbound refs render a deterministic placeholder; bound-but-undeclared refs appear as drift in the DevTools binding report.
Descriptor contributions are also extension
Use EntityDescriptor when an extension changes the effective domain shape: new facets, fields, relationships, actions, aggregates, or UI defaults.
Use a registry ref when the shape is already declared but needs imperative execution or rendering.
| Need | Correct mechanism |
|---|---|
| Add qualification state to user | EntityDescriptor |
| Render a normal enum | built-in FieldUI |
| Call an instrument-specific decision service | evaluator ref |
| Custom multi-part template designer | form/widget ref |
| Add an action from another aspect | contributed facet/action |
| Change tenant threshold | configuration/policy, not an extension |
Extension rules
- Refs are namespaced and stable.
- Required refs are checked at assembly/bootstrap.
- Implementations declare compatible contract versions.
- Server refs cannot execute in a client registry, and vice versa.
- Pure transformations remain pure; side effects use action/effect contracts.
- Runtime decisions capture ref and implementation revision.
- Extensions receive typed context, not unrestricted database access.
- Missing optional UI refs fall back only when the declaration defines a safe built-in fallback.
Checkpoint
Classify each requirement:
- a barcode field editor;
- a site-specific code prefix;
- an external instrument readiness check;
- a new maintenance facet on equipment;
- a custom timeline detail tab.
Choose typed vocabulary, configuration, descriptor, or registry ref for each.
Next: Operations capstone.
References: UI extension vocabulary · Blueprint UI.