Class Parameter
The assignment of one ParameterKey to one ObjectClass, with the local rules governing it. Always called an assignment — binding is reserved for Revit placement.
Purpose
A class parameter is the assignment of one ParameterKey to one ObjectClass, carrying the rules that apply locally: whether the parameter is required, how far down the tree it reaches, and which values are allowed at that class.
The word is assignment, never binding. Binding is reserved for Revit placement — project binding, family application, binding kind — and using it here loses that distinction. The aggregate still has method names from before the rename (bind, reviseBinding), and the resolver still calls a selected assignment a “binding” internally; the vocabulary of the contract and of this catalog is assignment.
No lifecycle
A class parameter has no lifecycle. It exists or it does not.
AssignmentOrigin looks like a status and is not one — it is provenance:
| Origin | Meaning |
|---|---|
direct | Authored on this class. The only origin that accepts deletion and allowed-value edits |
rendered | A materialized overlay produced from an ancestor’s assignment |
legacy_unknown | Provenance was never recorded. The default when rehydrating a row that has no origin |
AssignmentOrigin is server-owned and hidden from stewards: it is set by the server on create, on rehydrate, and by the promote paths (promoteToDirect, promoteToDirectAndReviseBinding), and it is not something a steward chooses in the Airtable UI.
Invariants
- One assignment pairs exactly one object class with exactly one parameter key.
- Origin fields are mutually exclusive (
assertOriginFields,INVALID_CLASS_PARAMETER_ORIGIN_FIELDS): adirectrow must not carry a child behavior, and arenderedrow must not carry an inheritance mode. This is a domain invariant checked on create, on rehydrate, and on revise — not a contract-layer nicety. Onlylegacy_unknownrows may carry both. - Allowed values must be unique by
parameterValueId(DUPLICATE_CLASS_PARAMETER_ALLOWED_VALUE). - At most one allowed value may be the default (
MULTIPLE_DEFAULT_CLASS_PARAMETER_ALLOWED_VALUES). Allowed-value state is validated before the no-change early return, so an idempotent replay still surfaces a violation instead of silently passing. - Deletion and allowed-value editing apply to
directrows only.
InheritanceMode — direct rows only
unique applies the assignment to this class alone; apply_to_descendants also reaches every descendant. An assignment always includes itself.
The retired wire vocabulary self_only / self_plus_descendants / descendants_only is gone from the HTTP contract — the request schemas reject those values. Persistence and Assign scripts resolve inheritance_modes.handle only (unique / apply_to_descendants). Display name is steward-facing. Inheritance.ts still maps leftover self_* tokens if one is read. descendants_only has no canonical equivalent and is rejected outright.
ChildBehavior — rendered rows only
inherit (the default) takes the ancestor’s assignment as-is, suppress removes it for this class only, and override replaces it with this class’s local values, again for this class only.
Effective parameter set
The answer to “what parameters does this class actually have” is a read model resolved on request, not a stored field. getEffectiveParameterSet resolves one class and resolveEffectiveParameterSets resolves a batch, and each decision comes back with a resolution kind and a human-readable explanation:
| Kind | Meaning |
|---|---|
direct | Assigned directly on this class |
inherited | Reached from an ancestor, with the path in the explanation |
override | A local direct assignment supersedes the inherited one |
suppressed | A local overlay removes the inherited assignment; the suppressing row’s id is reported |
rendered rows are a cache for Airtable’s benefit — they let a steward see inherited parameters as rows in a grid. The resolution above is authoritative. When the two disagree, the resolver is right and the rendered rows need resyncing, which is what the rendered-template sync handler does after a parent-graph change or a new assignment.
Domain Events
Four names on one payload shape (classParameterId, objectClassId, parameterKeyId, and allowedValueCount on the replace event):
| Event | Emitted by |
|---|---|
ClassParameterAssigned | bind — a new direct assignment |
ClassParameterAssignmentUpdated | reviseBinding, promoteToDirect, promoteToDirectAndReviseBinding — only when something actually changed |
ClassParameterAllowedValuesReplaced | replaceAllowedValues, when the set differs |
ClassParameterUnassigned | markUnassigned |
ClassParameterAssigned is also consumed inside the module: it triggers the rendered parameter template sync.
OpenAPI Operations
- listClassParameters
- assignClassParameters
- upsertClassParameter
- unassignClassParameter
- replaceClassParameterAllowedValues
- validateClassParameterAllowedValues
- getEffectiveParameterSet
- resolveEffectiveParameterSets
ADRs
The platform series lives in docs/adr/07_Architectural_Decision_Records.md. The per-file series is separate and the numbers collide — see docs/adr/README.md.
| ADR | Decision |
|---|---|
| Platform ADR-0046 | The object-class hierarchy is a single-parent tree, so inheritance follows one path to the root |
| Platform ADR-0048 | domain_id is the public identifier; never a rec… id in a payload |
| Per-file ADR-0006 | Canonical parameter keys and values with per-level junctions |