Workflow policy - HULKs/labello GitHub Wiki
Workflow policy ownership
This inventory locates exhaustive workflow matches and their responsibilities. It distinguishes shape and replay validity from API trust policy and storage transaction policy. Selective searches for one event kind, idempotency lookups, and test-only matches are not exhaustive policy owners.
Event payload matches
| Location | Rule | Owner |
|---|---|---|
labello-domain/event.rs::EventPayload::event_type |
Maps every payload variant to its persisted event type. | Domain shape |
labello-domain/event/validation.rs::EventLogEntry::validate_shape |
Checks schema support and persisted type/payload agreement. | Domain shape |
labello-domain/event/validation.rs::EventLogEntry::task_id |
Projects an optional task identity without deciding access or transactions. | Domain query |
labello-domain/state/replay.rs::ImageState::apply_event |
Applies every event variant to rebuildable image state in sequence. | Domain replay validity |
labello-api/handlers/workflow/event_policy.rs::validate_payload |
Rejects server-owned ingress and validates request-bound metadata. | API trust boundary |
labello-api/handlers/workflow/event_policy.rs::required_role_for_payload |
Binds caller-owned payloads to actor identity and dataset role. | API authorization |
labello-api/handlers/workflow/event_policy.rs::validate_annotation_assignment_payload |
Restricts ordinary assignment ingress to its task and allowed mutation kinds. | API trust boundary |
labello-api/handlers/workflow/event_policy.rs::validate_admin_repair_payload |
Restricts administrator repair ingress and server-owned variants. | API authorization |
labello-storage/assignment/mod.rs::validate_annotation_batch |
Validates an atomic assignment event batch against its task and image. | Storage transaction policy |
labello-storage/assignment/migration.rs::validate_annotation_command |
Couples annotation mutations to migration invalidation in one transaction. | Storage transaction policy |
event/wire.rs owns version-specific decoding and encoding only. It may
upcast or downcast representations, but it must not decide authorization,
assignment eligibility, or filesystem transaction order.
Assignment-kind matches
| Location | Rule | Owner |
|---|---|---|
labello-storage/assignment/mod.rs::role_for_kind |
Selects the dataset role required by repository assignment operations. | Storage workflow boundary |
labello-storage/assignment/mod.rs::exact_active_assignment |
Validates exact owner, task, kind, status, and lease for a mutation. | Storage transaction policy |
labello-storage/assignment/claim.rs::assignment_kind_cache_key |
Names the process-local availability cache partition. | Storage cache mechanics |
labello-storage/assignment/claim.rs::effective_assignment_status |
Projects task eligibility after expired assignment effects. | Storage workflow policy |
labello-storage/assignment/claim.rs::status_matches_kind |
Maps task status to claimable assignment kind. | Storage workflow policy |
labello-storage/assignment/migration.rs::migration_role |
Limits manual migration to annotation and review assignments. | Storage migration policy |
labello-api/handlers/workflow/event_policy.rs::validate_assignment_request |
Verifies request identity and expected assignment kind before delegation. | API trust boundary |
The domain AssignmentKind remains a serialized shape. Role checks are not
moved into the domain because they depend on dataset membership and route
authority. Claimability remains in storage because it depends on repository
state, leases, and atomic append behavior.
Pure review policy
labello-domain/review/policy.rs owns review-round projection, duplicate
reviewer detection, and distinct approval counting. These functions depend
only on domain events and values. Storage still owns event loading, locking,
role enforcement, assignment completion, and the event batch written after a
review.
Keep planners specific to their policy. A planner is extracted only when it is independent of Axum, filesystem access, client DTOs, egui, and authorization context.
Repository transaction boundary
DatasetRepository remains the storage facade, but repository mechanics no
longer own feature validation or event-batch construction:
| Location | Responsibility |
|---|---|
labello-storage/repository/events.rs |
Loads and atomically appends the authoritative event log, rebuilds the replay cache, and invalidates derived caches after an explicit rebuild. |
labello-storage/assignment/transaction.rs |
Applies the common single-image commit order after annotation, review, or migration code has validated its request and constructed its feature-specific batch. |
labello-storage/assignment/{mod,review,migration}.rs |
Owns feature authorization, exact-state validation, and the event payloads required for that transition. |
labello-storage/sync.rs |
Owns offline-fragment validation and resequencing before using the same authoritative append and rebuildable-cache mechanics. |
The single-image commit order is:
- Load the replay-validated state derived from
events.jsonl. - Complete assignment/migration invalidation policy for the planned batch.
- Apply the entire batch to a cloned next state so any invalid event rejects the batch before persistence.
- Invalidate affected polling projections and aggregate generations before event-log publication, while the image lock is held.
- Atomically publish
events.jsonl. - Publish the rebuildable
state.jsoncache only after the event log. - Invalidate aggregate statistics and assignment-availability caches after durable publication.
Existing repository recovery tests prove that a missing or stale state.json
is rebuilt from a complete event batch. Assignment batch tests prove a failed
batch leaves no partial events, while exact-version migration and workflow
claim tests prove concurrent writers have one winner. Preserve those lock scopes and tests when changing transaction policy.
Overlapping-box visibility
labello-domain/box_visibility.rs owns dataset-configured, per-image selection.
It orders current non-deleted boxes by effective exact-version review status,
then annotation ID, and greedily compares each box against retained boxes of its
class. Overlap groups are not transitive: a suppressed box cannot suppress
another box. Skeleton geometry does not participate. The full image participates
before any task or view filter, so changing workflows cannot expose a duplicate.
ImageState::visible_annotations supplies ordinary review targets and inspector
objects. The shared UI applies the same policy to drafts, correction previews,
pending prelabels, canvas overlays, object lists, and selection. A changed draft
does not inherit approval of the stored geometry. active_annotations remains
the preserved source for audit, statistics, and export.
skipped_migration_groups excludes only Pending target dispositions whose guides
are hidden. Migration cursors, confirmation validation, digests, and review
ordering use that same set. Annotated or explicitly excluded dispositions remain
part of the existing migration workflow; hiding a guide never deletes its
skeleton. Frozen target sets and dispositions are retained unchanged. A
confirmation digest binds the skipped target IDs. Review assignment fingerprints
bind the visibility result, so an outdated target set cannot be submitted.
The storage transaction selects the current dataset policy and stamps new events with it. Replay uses that historical policy, while live reads overlay the current configuration. See event history for wire compatibility. Visibility changes alone do not append deletion events, change historical task completion, or alter export selection.
Item queue ownership
labello-domain/workflow owns object identity, preparation, partial geometry and
edit proposals, confirmation receipts, independent-review policy and replayable
item history. state/workflow_replay.rs validates each workflow event boundary.
It has no filesystem, HTTP or UI dependencies.
labello-storage/assignment/workflow owns queue candidates, live eligibility,
display/seen publication, history reacquisition, draft transactions and reservation
release. Dataset admission serializes independent-review fallback, claims and
mutations before per-image locks. Configuration guards precede admission.
Availability is advisory and reads reusable image projections without holding
dataset admission; every claim and mutation repeats its applicable checks.
prelabel/workflow.rs owns dataset-managed preparation and generation scheduling.
Display and departure use the invalidated image projections to locate releasable reservations. Only candidate images are reloaded under their image locks; exact ownership, workflow selection and retained history are checked again before release. Claims also use these projections to skip finished, excluded and unavailable items before reloading a candidate under its image lock. Unprepared annotation images are evaluated with their prospective preparation, and the selected image still publishes preparation and validates the exact lease transaction under that lock. Warm navigation does not reread unrelated image histories.
The client exposes typed work-item requests; API handlers validate request identity
and authenticate the actor. labello-ui/work_items projects one leased item into
its editor and navigates server history. It does not decide final completion,
independent-review exceptions, score ownership or prelabel configuration.
Overview edits to an already confirmed object preserve its finished Objects pass. Replay tracks the versions revised in a displayed annotation Overview separately from the original completion receipt, preserving its score ownership. Editing an object in Objects still requires confirmation. Empty saved edit proposals do not make their author an image contributor; actual proposals and decisions do.