Use when defining or editing MPS language constraints — property validators / setters / getters, referent search scopes (imperative or inherited via `ScopeProvider.getScope`), `referentSetHandler` side effects, default-scope blocks, `canBeChild` / `canBeParent` / `canBeAncestor` / `canBeRoot` placement rules, `defaultConcreteConcept` for abstract concepts, `set <read-only>` and `{name}` aliasing, and scope helpers (`SimpleRoleScope`, `ListScope`, `CompositeScope`, `HidingByNameScope`). Reach for this skill whenever the task involves authoring or modifying `<lang>/languageModels/constraints.mps`.
78
100%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
The constraints aspect controls runtime rules about nodes: what values properties may hold, how a property is stored when set, what nodes a reference may point to, and where a node of a concept is allowed to appear in the AST. It lives in the language's constraints model (<lang>/languageModels/constraints.mps) and uses the language jetbrains.mps.lang.constraints.
Constraints are enforced at edit time and also consulted by the typesystem/editor. They are not the place for structural cardinalities (that belongs in structure) or for types (that belongs in typesystem).
ConceptConstraints root per concept. Set its concept reference; everything else is child blocks.propertyValidator) — pure acceptance check; returns boolean; never mutates state.propertySetter) — normalises and must explicitly assign node.<prop> = propertyValue (a body that forgets this silently drops the value).propertyGetter) — only when the displayed value differs from the stored value (derived URL, aliased name).set <read-only> means no setter at all — direct assignment to the property is rejected by MPS. An empty-body setter is almost always a bug.canBeChild / canBeParent / canBeAncestor are inherited by sub-concepts; per-property and per-reference constraints are not — duplicate the ConceptConstraints root or hoist the rule up.InheritedNodeScopeFactory + ScopeProvider.getScope (behavior) — preferred whenever the scope naturally belongs to an ancestor (block, declaration, program, module). Composes cleanly along the containment chain with parent scope.ConstraintFunction_ReferentSearchScope_Scope — only when the scope is tiny, highly local, and has no natural ancestor owner.referentSetHandler performs side effects (auto-rename referrer, copy fields, keeps original reference). Never overload the scope to do this.defaultConcreteConcept on ConceptConstraints chooses which concrete subconcept replaces an abstract pick — it is a single reference, not a child block.come from, parent scope, kind.isSubConceptOf(...), link/C : role/, concept/C/) are not BaseLanguage. Each is a dedicated jetbrains.mps.lang.scopes / jetbrains.mps.lang.smodel concept — construct those concepts, not strings (see references/scope-fqn-reference.md).Concept_IsSubConceptOfOperation matches a concept and its sub-concepts; Concept_IsExactlyOperation excludes sub-concepts. Use isExactly for guards that must not fire on specialisations.mps_mcp_insert_root_node_from_json, mps_mcp_update_node, mps_mcp_parse_java_and_insert). Do not hand-edit .mps files. Validate with mps_mcp_check_root_node_problems and rebuild the language; if the new constraint is invisible at runtime, run mps_mcp_reload_all.constraints model; create it with mps_mcp_create_model (modelName: "<lang>.constraints" — aspect ID constraints, case-sensitive, no @ suffix; see aspect-model-stereotypes.md) if absent.jetbrains.mps.lang.constraints to the model's used languages. For scope bodies also add jetbrains.mps.lang.smodel, jetbrains.mps.lang.scopes, jetbrains.mps.lang.behavior, jetbrains.mps.baseLanguage; import models jetbrains.mps.scope and jetbrains.mps.lang.core.behavior.
Default dependency on solution jetbrains.mps.kernel (which ships model jetbrains.mps.scope). mps_mcp_model_dependency adds it automatically when importing jetbrains.mps.scope; mps_mcp_create_module type=language does not. See references/scope-fqn-reference.md → "Required module-level dependency".ConceptConstraints root (mps_mcp_create_root_node); set concept ref. Minimal blueprint and validated FQN/c-ref in references/concept-roots.md.NodePropertyConstraint / NodeReferentConstraint / canBe* children as needed. For inherited scopes, also add a getScope method to the owning ancestor's ConceptBehavior (see mps-aspect-behavior).mps-model-manipulation skill for smodel / collections / closures syntax. Prefer mps_mcp_parse_java_and_insert for the BaseLanguage skeleton, then drop in scope / constraint-specific concepts via MCP.mps_mcp_check_root_node_problems and rebuild the language.mps-aspect-behavior — ScopeProvider.getScope lives in the behavior aspect; this is the partner of every InheritedNodeScopeFactory constraint. Behavior is also where most validator/getter delegation lands.mps-aspect-structure-concepts — structural cardinalities belong in structure, not here. Add INamedConcept / IResolveInfo / ScopeProvider interfaces on the concept declaration before writing the matching constraint or scope.mps-aspect-editor-menus-and-keymaps — completion menus consume the scope built here; substitute / transformation menus are the place for actions that go beyond picking an existing target.mps-aspect-actions — when a constructor-style initialization is needed, use NodeFactory (actions aspect), not a constraint.mps-aspect-intentions — when a suggested fix belongs on a node, write an intention; constraints only accept or reject.mps-model-manipulation — full BaseLanguage / smodel / collections / closures reference for constraint and scope bodies. Indispensable for ancestor<concept = X, +>, selectMany, siblings.ofConcept<X>.all(...), anonymous-class scopes, and the smodel operation FQNs.mps-aspect-typesystem — type checks belong in typesystem; constraints are about value/scope/placement.mps-aspect-textgen and mps-aspect-constraints-co-located utility classes — see references/concept-roots.md for the BaseLanguage ClassConcept co-location pattern (used by XmlNameUtil).references/concept-roots.md when creating the ConceptConstraints root, setting defaultConcreteConcept to a concrete subconcept of an abstract concept, looking up the validated concept c-ref, or co-locating a BaseLanguage helper ClassConcept next to constraint roots.references/property-constraints.md when writing NodePropertyConstraint bodies — choosing validator vs. setter vs. getter, the {name} aliasing pattern with set <read-only>, derived-property getters (e.g. Element.details_url), sibling-uniqueness validators (State.isInitial), and full JSON blueprints for validator and getter bodies.references/referent-constraints.md when writing NodeReferentConstraint — both scope styles (imperative ConstraintFunction_ReferentSearchScope_Scope and ancestor-supplied InheritedNodeScopeFactory), the verbatim Calculator / StateChart / Kaja examples (SimpleRoleScope.forNamedElements, ListScope with anonymous getName, CompositeScope + addScope, model.rootsIncludingImported), the meta-level scope offering structure declarations (conceptNode.getAggregationLinkDeclarations() for LinkDeclaration referents), referentSetHandler patterns (auto-rename, multi-field copy, keeps original reference), and concept-level Default Scope.references/scope-helpers.md when picking among EmptyScope, ListScope, SimpleRoleScope, CompositeScope, FilteringScope, DelegatingScope, ModelsScope, ModelPlusImportedScope, Scopes.forConcepts, HidingByNameScope, or ListScope.forResolvableElements; or when caching with for model [...], visible roots [C], visible nodes [C]. Includes the verbatim StateChart Stateful_Behavior.getScope (ListScope + anonymous getName + HidingByNameScope over parent scope) idiom.references/scope-fqn-reference.md when constructing scope bodies via MCP — the full surface-syntax-to-FQN table (parent scope, come from, isSubConceptOf vs isExactly, RefConcept_Reference vs ConceptIdRefExpression, LinkIdRefExpression, Node_GetAncestorOperation + OperationParm_Concept, Node_GetDescendantsOperation, Node_GetModelOperation, Model_RootsIncludingImportedOperation, Node_GetParentOperation, Node_IsInstanceOfOperation), required used-languages and model imports, the validated ScopeProvider.getScope overriddenMethod ref, and JSON blueprints for the getScope skeleton, parent scope return, and the guarded local-scope if-branch.references/canbe-rules.md when authoring ConstraintFunction_CanBeAChild / CanBeAParent / CanBeAnAncestor / CanBeARoot — each block's parameter set (note canBeAnAncestor has only 3 parameters; no parentNode or link), the parentNode.parent.isInstanceOf(...) grandparent-traversal pattern (Kaja RoutineDefinition and Require), the node.isInRole(link/C : role/) pattern, the model parameter for canBeRoot, and the minimal ConstraintFunction_CanBeAChild blueprint.references/common-failures.md when a setter runs but stores nothing, a completion popup is empty, the editor reports "cannot be child", a constraint does not apply to a sub-concept, or constraint edits seem invisible at runtime.52c9d63
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.