9. Actors, Work, and Policy
Identity answers “who are you?” Access answers “what may you do here?” Work assignment answers “who should receive this task now?”
Blueprint keeps these questions separate.
Actor/task declarations come from vyuh_blueprint; the product action catalog comes from package:vyuh_iam/vyuh_iam.dart.
Actor sets are boolean algebra
const eligibleReviewers = AllOf([
Role(RoleRef('access.quality_reviewer')),
Qualified(QualificationRef('training.gmp_current')),
NoneOf([
Initiator(),
]),
]);The sealed actor-set vocabulary supports:
Anyone,NoOne,ActorUser,Initiator,ActorInField;Role,Group,Grant,Qualified;AllOf,AnyOf,NoneOf;Locked, which permits scoped configuration to narrow but not broaden.
Runtimes can enumerate the eligible set or test one actor against it.
Action access is not task assignment
An action gate:
Rule(
id: 'ops.area.activate.actor',
kind: RuleKind.access,
condition: ActorInSetCondition(eligibleReviewers),
)A task assignment:
const reviewAssignment = AssignmentSpec(
claimMode: ClaimMode.pool,
selectors: [
RoleSelector(roleRef: RoleRef('access.quality_reviewer')),
GroupSelector(groupRef: GroupRef('access.hyderabad_quality')),
],
);Role and group membership are expanded late at claim time, so staffing changes do not strand pooled tasks. UserSelector freezes a concrete user at spawn. DerivedSelector(fieldPath: ProjectedFieldRef('self.identity.owner_id')) resolves from context.
Declare a task template
final qualityReviewTask = TaskTemplate(
name: 'area_activation_review',
title: 'Review area activation',
kind: 'approval',
completionActionRef: ActionRef(
'ops.area.governance.activate',
),
defaultAssignment: reviewAssignment,
completion: const ClaimSingle(),
expiry: const Duration(hours: 24),
escalations: const [
TaskEscalation(
after: Duration(hours: 8),
assignment: AssignmentSpec(
selectors: [
RoleSelector(roleRef: RoleRef('access.quality_manager')),
],
),
),
],
separationOfDuties: const [
SeparationOfDuties(
subject: SodSubject.completer,
notEqualTo: SodInitiator(),
locked: true,
),
],
);Register templates at module scope:
Module(
// ...
taskTemplates: [qualityReviewTask],
)Completion always names an action. There is no task-completion path that bypasses the action journal.
Spawn work through the effect channel
Action(
name: 'request_activation',
effects: const [
TaskEffect(
templateRef: TaskTemplateRef('ops.area_activation_review'),
consistencyOverride: ConsistencyMode.eventual,
),
],
)TaskEffect targets workflow.task.lifecycle.create. The spawning module must depend on a workflow module that exports that trigger. Outbox delivery, causality, recursion limits, and retry behavior remain common runtime machinery.
Declaration, configuration, and resolution
| Plane | Example |
|---|---|
| Declaration | role ref, actor set, task defaults, SoD lock |
| Configuration | tenant/site assignment override, membership, grant, expiry |
| Resolution | effective policies, concrete eligible users, disabled reason |
| Execution | claim, delegate, complete, expire action records |
Do not put concrete tenant membership into the product Blueprint. Do not let a site override weaken a locked separation-of-duties constraint.
Effective actions
Bootstrap gathers facet actions into an EffectiveActionCatalog. At request time, EffectiveActionResolver evaluates placement, access, policies, and context into EffectiveActionSets:
catalog action
+ placement
+ actor/scope
+ lifecycle state
+ policy/configuration
= hidden | disabled(reason) | enabledThe UI renders this result. It must not replicate authorization logic with ad-hoc button conditions.
Product action catalogs
IAM administration uses the same fully qualified action identities as the runtime:
const opsPermissions = IamProductDescriptor(
product: 'ops',
title: 'Operations',
actions: [
IamActionDescriptor(
action: 'ops.area.governance.activate',
title: 'Activate Area',
entityName: 'area',
facetName: 'governance',
),
IamActionDescriptor(
action: 'ops.session.refresh',
title: 'Refresh Session',
resourceType: 'session',
grantEditable: false,
),
],
);editableActions includes only actions administrators may grant. Public, self-service, and system actions remain visible in the complete inventory with grantEditable: false. validate() catches duplicate or invalid entries, and validateEntityCoverage() reports entities missing from the product catalog.
Checkpoint
Model dual-control retirement:
- the requester can submit retirement;
- a qualified quality reviewer completes it;
- the completer cannot be the initiator;
- the task escalates to a quality manager;
- the completion action still evaluates its own grant and lifecycle rules.
Next: Runtime execution.
References: Runtime operating model · Vocabulary.