Board constraints
Board Constraints let a Board reject a process mutation when a required business fact is not true. For example, a request might need an owner before it enters review, or a resolution before it enters done.
Constraints are synchronous. Moltaro checks them before committing the governed mutation, returns every applicable failure in deterministic order, and makes no partial change.
The three parts
Section titled “The three parts”- A Board Statement is a reusable named true-or-false expression owned by one Board.
- A Board Constraint gives a failed statement its process meaning and safe message.
- A Binding selects when the constraint applies and references one or more
Board Statements, requiring all of them (
AND) or any of them (OR).
Keeping the Binding separate lets one Statement be reused at several process
points and lets a Binding combine two or three independent facts. Complex
conditions inside one fact are still composed in its Statement with AND,
OR, NOT, and parentheses.
Binding points
Section titled “Binding points”A Binding can select:
- one exact transition from status A to status B;
- every entry into one status;
- every exit from one status;
- a status invariant that must remain true while the item stays in one status.
If several Bindings apply, all of them must pass. Within one Binding, its AND
or OR operator applies to the Statements relevant to the current target type.
A move from A to B now checks the exact transition, exit-A, and enter-B in one
atomic validation together with the invariant for B.
Which bindings run for each lifecycle operation
Section titled “Which bindings run for each lifecycle operation”The trigger set follows the business event that actually occurred. Moltaro does not invent entry, exit, or transition events for administrative or historical operations.
| Operation | Bindings evaluated |
|---|---|
| Move from A to B | exact transition A -> B, exit A, enter B, invariant B |
| Create or admit an item into B | enter B and invariant B |
| Change a direct Board Item or Board Data fact while in B | invariant B |
| Change a target record | invariants for every governing item on every active Board |
| Reorder within B | invariant B only |
| Remove from A | exit A only |
| Reopen the existing terminal item T into B | transition T -> B, exit T, enter B, invariant B |
| Start a new pass in B | enter B and invariant B; the older pass remains history |
| Import or reconcile historical state into B | invariant B only; historical events are not replayed |
| Delete a status and relocate its items into B | invariant B only; this is an administrative migration |
| Deactivate a Board | no constraint governs while the Board is inactive |
| Reactivate a Board | rebind configuration and validate every governing invariant before activation commits |
Reopen behavior depends on the Board repeat policy. Moving a terminal item back to an open status reuses that item and runs the reopen trigger set above. Creating a new pass creates another Board Item and follows ordinary admission semantics. In both cases the complete resulting Board Item, Board Data, and Target state is evaluated atomically.
Why status invariants matter
Section titled “Why status invariants matter”An entry constraint answers only “may this item enter the status now?” Without an invariant, a required value could be cleared immediately after entry while the item remains in that status.
A status invariant is stronger: Moltaro must reject every supported mutation for which the Binding’s Statement-set evaluation fails while the item remains in the governed status. The first version limits invariants to data whose mutations the platform can observe directly. It rejects an invariant configuration that depends on independently changing related records, a clock, the current user, or an external service rather than silently provide a partial guarantee.
Supported invariant mutations include direct Board Item changes such as due date or cycle, Board Data changes, and Target Entity update, archive, restore, or delete. A failed delete can report missing Target context because the proposed record no longer exists. A single mutation is atomic. Bulk Entity operations retain their existing per-record result contract: a rejected record is rolled back while independent successful records may still commit.
Transition, entry, and exit constraints can use a wider read context because they promise only to validate that particular event.
What a Board Statement can describe
Section titled “What a Board Statement can describe”The expression context has three roots:
BoardItemfor direct process facts such as status, cycle, run, due date, timestamps, and target identity;BoardDatafor fields configured specifically on the Board;Targetfor fields and named Statements of an explicitly selected record type admitted to the Board.
A Statement that does not use Target can apply across target types. The first
version scopes every target-reading Statement to one target definition. A
Binding can combine target-specific Statements through AND or OR and uses
only those applicable to the current item. Configuration must cover every
target the Binding can receive; missing fields never become implicit null.
See Board Statement DSL for the expression profile and its date/time rules.
One record on several Boards
Section titled “One record on several Boards”A Board event remains local to its Board: moving an item on Board A does not pretend that a transition occurred on Board B.
When the same target record is represented on several Boards, changing that record is different. Moltaro evaluates the applicable status invariants for every governing Board Item. One failure blocks the entire record mutation. This prevents one Board from accepting a target state that another Board currently forbids.
Configure and enable a constraint
Section titled “Configure and enable a constraint”Open a Board in administration and select Workflow → Constraints. The page has two authoring areas:
- Create a Statement, choose whether it reads only Board data or one exact target definition, write the expression, and run Validate. The editor’s roots, paths, functions, operators, variables, literal formats, and examples come from the server for that exact scope.
- Create a Constraint with a technical name, display name, and one safe workspace-authored failure message. Value interpolation is not supported.
- Add a Binding, choose its event and status or transition, select one or more
Statements, and require either all (
AND) or any (OR) of them.
Configuration health reports invalid trigger shapes, ineligible Statements, missing target coverage, and unsupported invariant dependencies. Enable at least one healthy Binding, then enable its parent Constraint. A Binding is enforcing only while both switches are enabled and the Board is active. An inactive Board controls nothing; reactivation validates every current governing invariant and is rejected until its data is valid again.
Enabling or strengthening a status invariant on an active Board validates all current governing items in the same transaction. Moltaro rejects the configuration change atomically when any item would violate the new rule, so an active Board cannot be left with a newly enabled but already broken invariant.
Constraints are not automation actions
Section titled “Constraints are not automation actions”A Constraint has no side effects and performs no network calls. Actions that send notifications, invoke logic, update other records, or call an external system belong to planned after-commit Board Automation Rules.
What users see when a constraint rejects a change
Section titled “What users see when a constraint rejects a change”The attempted mutation is not committed. The dialog or record editor shows the workspace-authored failure message for every failed applicable Constraint in stable Board, Constraint, and Binding order. Messages never interpolate field values, record values, expressions, or inaccessible data. The server publishes only aggregated, low-cardinality operational metrics for Constraint outcomes; those metrics contain no record values, expressions, or configuration details. Authorization still controls which configuration details a caller may inspect.
Preview and execute use the same evaluation plan. A transition that fails in preview cannot succeed merely because it was submitted, and a successful preview is still rechecked atomically during execution against current data. Owners and administrators do not bypass an enforcing Constraint.
Troubleshooting
Section titled “Troubleshooting”The Constraint is saved but does not run. Check that the Board is active, the Constraint is enabled, at least one applicable Binding is enabled, and the Binding health reports both valid configuration and available runtime support. A Statement alone never enforces anything.
A Binding cannot be enabled. Validate every referenced Statement, check the
trigger’s required status or transition fields, and confirm target coverage.
For a status invariant, remove clock, actor, inverse, calculated, projection,
or independently changing relationship dependencies. Calendar extraction such
as YEAR(Target.DueAt) over a DateTimeOffset is also event-only because it
depends on the configured workspace time zone.
A previously healthy definition becomes unhealthy. A field or Entity Statement may have been renamed, removed, or changed to an incompatible type. Open the Statement, refresh its server-provided authoring profile, correct the source-positioned diagnostic, validate again, and then re-enable the affected configuration. Runtime evaluation fails closed while required configuration is unhealthy; it does not execute a stale plan.
A Target edit is rejected by a different Board. The same record can have governing items on several active Boards. Inspect each item currently in a status with an invariant. Every governing invariant must pass for the one Target mutation to commit.
An inactive Board allows data to drift. This is intentional: an inactive Board controls nothing. Reactivation validates current governing invariants and is rejected until the data or configuration is corrected.
V1 limits
Section titled “V1 limits”- one Board Statement can read no Target or one exact target definition, not a polymorphic union of target schemas;
- a Binding evaluator is an ordered non-empty set of Board Statements combined
with
ANDorOR; C# evaluators are not part of V1; - status invariants are mutation-local and do not follow deep or transitive dependencies;
- failure messages are one workspace-authored string without localization variants or value interpolation;
- Constraints reject synchronous mutations only; delayed actions, notifications, network calls, retries, and outbox delivery belong to future Automation Rules.