Documentation
¶
Overview ¶
Package work holds the work management use cases: containers and the items inside them.
Index ¶
- Constants
- func BucketSnapshot(bucket domain.Bucket) usecase.Output
- func BulkOperationNames() []string
- func CommentSnapshot(comment domain.Comment) usecase.Output
- func ContainerSnapshot(container domain.Container) usecase.Output
- func ContainerSnapshots(ctx context.Context, structure StructureResolver, ...) ([]usecase.Output, error)
- func ItemOutput(item domain.WorkItem) usecase.Output
- func ItemSnapshot(item domain.WorkItem) usecase.Output
- func LabelSnapshot(label domain.Label) usecase.Output
- func PageSize(requested int) int
- func PresetOutput(preset domain.Preset) usecase.Output
- func RecurrenceSnapshot(rule domain.RecurrenceRule) usecase.Output
- func ReminderSnapshot(reminder domain.Reminder) usecase.Output
- func StructureValuesFrom(raw map[string]any) (domain.StructureValues, error)
- func StructureValuesOutput(values domain.StructureValues) map[string]any
- func TemplateSnapshot(template domain.Template) usecase.Output
- func TranslationAnswerKeys() map[string][]string
- type ActivityJournal
- type AddComment
- func (h AddComment) Descriptor() usecase.Descriptor
- func (h AddComment) Execute(ctx context.Context, actor appshared.ActorContext, cmd AddCommentCommand) (domain.Comment, error)
- func (h AddComment) FileDisplaced(ctx context.Context, actor appshared.ActorContext, itemID shared.ID, ...) (domain.Comment, error)
- type AddCommentCommand
- type AddLabel
- type AddMember
- type AiProviders
- type AiTranslate
- type Anchored
- type ArchiveContainer
- type ArchiveContainerCommand
- type ArchiveWorkItem
- type AssignWorkItem
- type AssignmentCommand
- type AssignmentWriter
- type AttachMedia
- type AttachmentCommand
- type Authorizer
- type AutoAssignOutcome
- type AutoAssignWorkItem
- type BucketDeletion
- type BucketInserter
- type BucketWriter
- type BulkOperation
- type BulkOutcome
- type BulkResult
- type BulkUpdateWorkItems
- type BulkUpdateWorkItemsCommand
- type CalendarFeedWriter
- type Catalogue
- type ChangeCommentCommand
- type ChangePreset
- type ChangePresetCommand
- type ChangeReminderCommand
- type ChangeTemplateCommand
- type ClearCover
- type ClearDueDate
- type CloseStarter
- type CommentWriter
- type CompleteWorkItem
- type CompletionCommand
- type CompletionWriter
- type ContainerLifecycleCommand
- type ContainerWriter
- type CoverCommand
- type CoverWriter
- type CreateBucket
- type CreateBucketCommand
- type CreateCalendarFeed
- type CreateContainer
- type CreateContainerCommand
- type CreateLabel
- type CreateLabelCommand
- type CreateReminder
- type CreateReminderCommand
- type CreateSavedView
- type CreateSavedViewCommand
- type CreateTemplate
- type CreateTemplateCommand
- type CreateWorkItem
- type CreateWorkItemCommand
- type CustomFieldWriter
- type DefineCustomField
- type DefineCustomFieldCommand
- type DeleteBucket
- type DeleteBucketCommand
- type DeleteComment
- type DeleteCustomField
- type DeleteCustomFieldCommand
- type DeleteLabel
- type DeleteLabelCommand
- type DeleteReminder
- type DeleteSavedView
- type DeleteTemplate
- type DetachMedia
- type DueDateCommand
- type DueDateWriter
- type DuplicateResult
- type DuplicateWorkItem
- type DuplicateWorkItemCommand
- type EditComment
- type EmbedItems
- type EmbedOutcome
- type EmbeddingQueue
- type EmergencyNotices
- type EntryGate
- type ExportItems
- type ExportView
- type ExportedItems
- type ExportedView
- type FigureReader
- type FireReminders
- type FlowGate
- type GetContainer
- type GetContainerQuery
- type GetRecurrence
- type GetSavedView
- type GetTemplate
- type GetWorkItem
- type GetWorkItemQuery
- type GiveKudos
- type InstantiateTemplate
- type InstantiateTemplateCommand
- type InstantiationResult
- type ItemAttachmentSet
- type ItemAttachmentWriter
- type ItemLabelSet
- type ItemLabelWriter
- type ItemMemberSet
- type ItemMemberWriter
- type ItemQuota
- type Jobs
- type KeptOutReader
- type KeptOwnValue
- type KudosDesk
- type LabelCommand
- type LabelWriter
- type LastMember
- type LifecycleCommand
- type LifecycleWriter
- type ListActivity
- type ListActivityQuery
- type ListBuckets
- type ListBucketsQuery
- type ListCalendarFeeds
- type ListComments
- type ListCommentsQuery
- type ListContainers
- type ListContainersQuery
- type ListCustomFields
- type ListCustomFieldsQuery
- type ListEmergencyNotices
- type ListJumbleDestinations
- type ListLabels
- type ListLabelsQuery
- type ListPresets
- type ListPrivateHubAccesses
- type ListPrivateHubs
- type ListReminders
- type ListRemindersQuery
- type ListSavedViews
- type ListSavedViewsQuery
- type ListTemplates
- type ListTemplatesQuery
- type ListTrash
- type ListTrashQuery
- type ListWorkItems
- type ListWorkItemsQuery
- type MaterializationOutcome
- type MaterializeOccurrences
- type MediaReferences
- type MemberCommand
- type MintedFeed
- type Moderation
- type MoveContainer
- type MoveContainerCommand
- type MoveResult
- type MoveRevoker
- type MoveWorkItem
- type MoveWorkItemCommand
- type Narrowing
- type OccurrenceSignals
- type OpenPrivateHub
- type OpenPrivateHubCommand
- type OwnHubs
- type OwnersNotice
- type Ownership
- type Permitting
- type PlacementWriter
- type PresetDesk
- type PreviewContainerStructure
- type PrivacyRevocations
- type PrivateHubRow
- type PrivateHubsRule
- type QueryItems
- type QueryMeaning
- type ReadCalendarFeed
- type ReadKudos
- type Reader
- type RebuildOutcome
- type RebuildSearchIndex
- type RecurrenceWriter
- type ReindexAccepted
- type ReindexSearch
- type ReminderNotifier
- type ReminderOutcome
- type ReminderSignals
- type ReminderWriter
- type RemoveLabel
- type RemoveMember
- type RemoveRecurrence
- type RemoveRecurrenceCommand
- type RenameContainer
- type RenameContainerCommand
- type ReopenWorkItem
- type ReorderBucket
- type ReorderBucketCommand
- type ReorderContainer
- type ReorderContainerCommand
- type ReorderWorkItem
- type ReorderWorkItemCommand
- type RestoreContainer
- type RestoreLabel
- type RestoreLabelCommand
- type RestoreWorkItem
- type RestrictedAccounts
- type RevokeCalendarFeed
- type SavePreset
- type SavePresetCommand
- type SavedViewCommand
- type SavedViewWriter
- type SearchItems
- type SearchItemsQuery
- type SearchMeaning
- type SeedEmbedding
- type SemanticSearch
- type SetContainerStructure
- type SetContainerStructureCommand
- type SetCover
- type SetCustomField
- type SetCustomFieldCommand
- type SetDueDate
- type SetHubPrivacy
- type SetHubPrivacyCommand
- type SetRecurrence
- type SetRecurrenceCommand
- type SetWorkspaceStructure
- type SetWorkspaceStructureCommand
- type ShareSavedView
- type SkipOccurrence
- type SkipOccurrenceCommand
- type Starter
- type StructureFlattener
- type StructurePreview
- type StructureResolver
- type StructureResult
- type TakeBackKudos
- type TemplateWriter
- type TranslateCommand
- type Translation
- type TrashContainer
- type TrashListing
- type TrashWorkItem
- type UnarchiveContainer
- type UnarchiveWorkItem
- type UnassignWorkItem
- type UpdateBucket
- type UpdateBucketCommand
- type UpdateCommand
- type UpdateContainerPolicies
- type UpdateContainerPoliciesCommand
- type UpdateCustomField
- type UpdateCustomFieldCommand
- type UpdateLabel
- type UpdateLabelCommand
- type UpdateReminder
- type UpdateSavedView
- type UpdateTemplate
- type UpdateWorkItem
- type Visibility
- type WithdrawPreset
- type WorkspaceReader
- type WorkspaceStructureAnswer
- type WorkspaceStructureResult
Constants ¶
const ( AddCommentName = "AddComment" // CommentCreatedAction is the audit code. Stable: an auditor filters on it and a SIEM rule // matches on it (audit.md §2). CommentCreatedAction audit.Action = "comment.created" )
const ( AddLabelName = "AddLabel" RemoveLabelName = "RemoveLabel" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). ItemLabelAddedAction audit.Action = "item.label_added" ItemLabelRemovedAction audit.Action = "item.label_removed" )
const ( AddMemberName = "AddMember" RemoveMemberName = "RemoveMember" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). ItemMemberAddedAction audit.Action = "item.member_added" ItemMemberRemovedAction audit.Action = "item.member_removed" )
const ( ArchiveContainerName = "ArchiveContainer" UnarchiveContainerName = "UnarchiveContainer" // The audit codes. Two, not one: an auditor asking what was taken out of use must not have to // read a change list to find out whether the entry means it went or came back (audit.md §2). ContainerArchivedAction audit.Action = "container.archived" ContainerUnarchivedAction audit.Action = "container.unarchived" )
const ( ArchiveWorkItemName = "ArchiveWorkItem" UnarchiveWorkItemName = "UnarchiveWorkItem" // The audit codes. Two, not one: an auditor asking what was taken out of use must not have to // read a change list to find out whether the entry means it went or came back (audit.md §2). ItemArchivedAction audit.Action = "item.archived" ItemUnarchivedAction audit.Action = "item.unarchived" )
const ( AssignWorkItemName = "AssignWorkItem" UnassignWorkItemName = "UnassignWorkItem" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). ItemAssignedAction audit.Action = "item.assigned" ItemUnassignedAction audit.Action = "item.unassigned" )
const ( AttachMediaName = "AttachMedia" DetachMediaName = "DetachMedia" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). ItemAttachmentAddedAction audit.Action = "item.attachment_added" ItemAttachmentRemovedAction audit.Action = "item.attachment_removed" )
const ( BulkUpdateWorkItemsName = "BulkUpdateWorkItems" // ItemBulkAppliedAction is the audit code for the bulk itself: who sent one, how big it was, // and how much of it applied. // // It does not describe the changes - each operation writes its own entry through the use case // that performed it, and the request identifier on all of them is what ties them together // (audit.md §2). What this entry adds is the act of sending five hundred changes at once, which // no per-entry record shows. ItemBulkAppliedAction audit.Action = "item.bulk_applied" )
const ( CreateCalendarFeedName = "CreateCalendarFeed" ListCalendarFeedsName = "ListCalendarFeeds" RevokeCalendarFeedName = "RevokeCalendarFeed" // The audit codes. A feed is a credential, so both its minting and its revocation are // auditable acts in their own right (audit.md §2). FeedCreatedAction audit.Action = "calendar.feed_created" FeedRevokedAction audit.Action = "calendar.feed_revoked" // FeedReadAction is the act the public route performs: somebody fetched a calendar. Declared // here so that the fetch and the two management acts share one target type. FeedReadAction audit.Action = "calendar.feed_read" )
const ( EditCommentName = "EditComment" DeleteCommentName = "DeleteComment" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). CommentUpdatedAction audit.Action = "comment.updated" CommentDeletedAction audit.Action = "comment.deleted" )
const ( UpdateCustomFieldName = "UpdateCustomField" DeleteCustomFieldName = "DeleteCustomField" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). CustomFieldUpdatedAction audit.Action = "custom_field.updated" CustomFieldDeletedAction audit.Action = "custom_field.deleted" )
const ( UpdateReminderName = "UpdateReminder" DeleteReminderName = "DeleteReminder" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). ReminderUpdatedAction audit.Action = "reminder.updated" ReminderDeletedAction audit.Action = "reminder.deleted" )
const ( UpdateSavedViewName = "UpdateSavedView" DeleteSavedViewName = "DeleteSavedView" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). ViewUpdatedAction audit.Action = "view.updated" ViewDeletedAction audit.Action = "view.deleted" )
const ( UpdateTemplateName = "UpdateTemplate" DeleteTemplateName = "DeleteTemplate" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). TemplateUpdatedAction audit.Action = "template.updated" TemplateDeletedAction audit.Action = "template.deleted" )
const ( CompleteWorkItemName = "CompleteWorkItem" ReopenWorkItemName = "ReopenWorkItem" // ItemCompletedAction and ItemReopenedAction are the audit codes. Stable: an auditor filters on // them and a SIEM rule matches on them (audit.md §2). ItemCompletedAction audit.Action = "item.completed" ItemReopenedAction audit.Action = "item.reopened" )
const ( CreateBucketName = "CreateBucket" ListBucketsName = "ListBuckets" // BucketCreatedAction is the audit code. Stable: an auditor filters on it and a SIEM rule // matches on it (audit.md §2). BucketCreatedAction audit.Action = "bucket.created" // BucketReadAction is the audit code of an attempted read. Declared even though an ordinary read // writes no entry: a *refused* read does, and it is recorded against the action that was refused // rather than against a generic "denied" (audit.md §4). BucketReadAction audit.Action = "bucket.read" )
const ( CreateContainerName = "CreateContainer" // ContainerCreatedAction is the audit code. Stable: an auditor filters on it and a SIEM rule // matches on it (audit.md §2). ContainerCreatedAction audit.Action = "container.created" )
The identity of this use case, in the one place that decides it. The registry derives the REST operation, the MCP tool and the automation action from the name (usecase.Descriptor).
const ( CreateLabelName = "CreateLabel" ListLabelsName = "ListLabels" // LabelCreatedAction is the audit code. Stable: an auditor filters on it and a SIEM rule // matches on it (audit.md §2). LabelCreatedAction audit.Action = "label.created" // LabelReadAction is the audit code of an attempted read. Declared even though an ordinary read // writes no entry: a *refused* read does, and it is recorded against the action that was // refused rather than against a generic "denied" (audit.md §4). LabelReadAction audit.Action = "label.read" )
const ( CreateWorkItemName = "CreateWorkItem" // ItemCreatedAction is the audit code. Stable: an auditor filters on it and a SIEM rule // matches on it (audit.md §2). ItemCreatedAction audit.Action = "item.created" )
const ( DefineCustomFieldName = "DefineCustomField" ListCustomFieldsName = "ListCustomFields" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). CustomFieldDefinedAction audit.Action = "custom_field.defined" // CustomFieldReadAction is declared even though an ordinary read writes no entry: a *refused* // read does, and it is recorded against the action that was refused rather than against a // generic "denied" (audit.md §4). CustomFieldReadAction audit.Action = "custom_field.read" )
const ( DeleteBucketName = "DeleteBucket" // BucketDeletedAction is the audit code. Stable: an auditor filters on it and a SIEM rule // matches on it (audit.md §2). BucketDeletedAction audit.Action = "bucket.deleted" )
const ( DuplicateWorkItemName = "DuplicateWorkItem" // ItemDuplicatedAction is the audit code. One entry per duplication rather than one per entry // it produced: the act an auditor asks about is "who copied this, and to where", and the // entries the copy consists of are what that act produced rather than acts of their own // (audit.md §2). ItemDuplicatedAction audit.Action = "item.duplicated" )
const ( ListPrivateHubAccessesName = "ListPrivateHubAccesses" ListEmergencyNoticesName = "ListEmergencyNotices" // PrivateHubAccessesReadAction is the code a refused read of a hub's accesses is recorded under. PrivateHubAccessesReadAction audit.Action = "container.emergency_accesses_read" // EmergencyNoticesReadAction is declared for the catalogue; reading one's own notices refuses // nobody anything beyond the credential, so nothing is recorded under it. EmergencyNoticesReadAction audit.Action = "container.emergency_notices_read" // EmergencyNoticeDays is how long the app shows a member that their hub was opened. EmergencyNoticeDays = 30 )
const ( ExportItemsName = "ExportItems" // ItemsExportedAction is the audit code of an export without a saved view. A bulk read of // somebody's work, exactly as a view's export is, and recorded the same way (audit.md §2). ItemsExportedAction audit.Action = "items.exported" )
const ( ExportViewName = "ExportView" // ViewExportedAction is the audit code. An export is a bulk read of somebody's work, which is // exactly the act an auditor asks about after the fact (audit.md §2). ViewExportedAction audit.Action = "view.exported" )
const ( InstantiateTemplateName = "InstantiateTemplate" // TemplateInstantiatedAction is the audit code. One entry for one act, whatever the tree's // size (audit.md §2). TemplateInstantiatedAction audit.Action = "template.instantiated" )
const ( ReadKudosName = "ReadKudos" GiveKudosName = "GiveKudos" TakeBackKudosName = "TakeBackKudos" // The audit codes. All three are written to the trail only when refused, by the authorisation // service: a trail of every give would let an auditor count who acknowledged whom (UC-WRK-29 // check 6), so giving and taking back are a named exception to gate SG-13 (security.md §13). KudosReadAction audit.Action = "kudos.read" KudosGivenAction audit.Action = "kudos.given" KudosTakenBackAction audit.Action = "kudos.taken_back" )
const ( ListCommentsName = "ListComments" // CommentsReadAction is the audit code of an attempted read, declared for the reason // ActivityReadAction is: an ordinary read writes no entry, a refused one does. CommentsReadAction audit.Action = "comment.list_read" )
const ( ListPrivateHubsName = "ListPrivateHubs" // PrivateHubsReadAction is the code a refused read is recorded under. PrivateHubsReadAction audit.Action = "private_hubs.read" )
const ( DefaultRuleBatch = 25 DefaultOccurrenceBatch = 50 )
Default bounds for one pass.
const ( MoveContainerName = "MoveContainer" // ContainerMovedAction is the audit code. One code for a change of hub and for a reorder within // one, because both are "this collection sits somewhere else now" - an auditor recognises a // reorder in the entry by the parent being unchanged (audit.md §2). ContainerMovedAction audit.Action = "container.moved" )
const ( MoveWorkItemName = "MoveWorkItem" ReorderWorkItemName = "ReorderWorkItem" // ItemMovedAction is the audit code for both operations. One code, because domain-model.md §4 gives // movement one event and an auditor asking "who moved this" means both - a reorder is recognisable in the // entry by the parent being unchanged (audit.md §2). ItemMovedAction audit.Action = "item.moved" )
const ( OpenPrivateHubName = "OpenPrivateHub" // EmergencyAccessOpenedAction is the trail's entry for the workspace owner's opening of a // private hub (ADR-0073 §4, UC-ID-16 check 4). EmergencyAccessOpenedAction audit.Action = "container.emergency_access_opened" )
const ( // DefaultPageSize is what a caller that names no size gets. DefaultPageSize = 50 // MaxPageSize is the ceiling. Bulk export goes through an :export job instead, which is why this // is a clamp rather than a refusal: a client asking for 500 rows wants as many as it can have, // and 200 of them is a better answer than an error. MaxPageSize = 200 )
The page size limits of api-guidelines.md §4, and the shape a paged answer takes.
In the application layer rather than in the REST adapter, because all three channels page: an MCP tool call and an automation action ask for a page the same way a request does, and a limit enforced in one adapter is a limit the other two do not have.
const ( ListActivityName = "ListActivity" // ActivityReadAction is the audit code of an attempted read, declared for the reason // ItemReadAction is: an ordinary read writes no entry, a refused one does. Its own code rather // than the entry's, because "who tried to read the history of this" is a question an auditor // asks separately from "who tried to read this" (audit.md §4). ActivityReadAction audit.Action = "item.activity_read" )
const ( GetContainerName = "GetContainer" ListContainersName = "ListContainers" // ContainerReadAction is the audit code of an attempted read. Declared even though an ordinary // read writes no entry: a *refused* read does, and it is recorded against the action that was // refused rather than against a generic "denied" (audit.md §4, access.Request.Action). ContainerReadAction audit.Action = "container.read" )
const ( ListTrashName = "ListTrash" // TrashReadAction is the audit code of an attempted read. Declared even though an ordinary read // writes no entry: a refused one does, recorded against the action that was refused (audit.md §4). TrashReadAction audit.Action = "trash.read" )
const ( GetWorkItemName = "GetWorkItem" ListWorkItemsName = "ListWorkItems" // ItemReadAction is the audit code of an attempted read, declared for the same reason // ContainerReadAction is: an ordinary read writes no entry, a refused one does. ItemReadAction audit.Action = "item.read" )
const ( SetRecurrenceName = "SetRecurrence" RemoveRecurrenceName = "RemoveRecurrence" GetRecurrenceName = "GetRecurrence" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). RecurrenceSetAction audit.Action = "recurrence.set" RecurrenceChangedAction audit.Action = "recurrence.changed" RecurrenceRemovedAction audit.Action = "recurrence.removed" // RecurrenceReadAction is the audit code of an attempted read, declared for the reason // CommentsReadAction is: an ordinary read writes no entry, a refused one does. RecurrenceReadAction audit.Action = "recurrence.read" )
const ( CreateReminderName = "CreateReminder" ListRemindersName = "ListReminders" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). ReminderCreatedAction audit.Action = "reminder.created" // RemindersReadAction is the audit code of an attempted read, declared for the reason // CommentsReadAction is: an ordinary read writes no entry, a refused one does. RemindersReadAction audit.Action = "reminder.list_read" )
const ( SavePresetName = "SavePreset" ChangePresetName = "ChangePreset" WithdrawPresetName = "WithdrawPreset" )
const ( PresetSavedAction audit.Action = "presets.saved" PresetChangedAction audit.Action = "presets.changed" PresetWithdrawnAction audit.Action = "presets.withdrawn" )
The audit codes and target of the saved presets.
const ( CreateSavedViewName = "CreateSavedView" ListSavedViewsName = "ListSavedViews" GetSavedViewName = "GetSavedView" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). ViewCreatedAction audit.Action = "view.created" ViewReadAction audit.Action = "view.read" )
const ( SetCoverName = "SetCover" ClearCoverName = "ClearCover" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). ItemCoverSetAction audit.Action = "item.cover_set" ItemCoverClearedAction audit.Action = "item.cover_cleared" )
const ( SetCustomFieldName = "SetCustomField" // ItemCustomFieldSetAction is the audit code. Stable: an auditor filters on it and a SIEM rule // matches on it (audit.md §2). ItemCustomFieldSetAction audit.Action = "item.custom_field_set" )
const ( SetDueDateName = "SetDueDate" ClearDueDateName = "ClearDueDate" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). ItemDueSetAction audit.Action = "item.due_set" ItemDueClearedAction audit.Action = "item.due_cleared" )
const ( SetHubPrivacyName = "SetHubPrivacy" // HubPrivacyChangedAction is the audit code: who made a hub private, or shared again. Its own // code, because "who shut the administrators out of this hub" is a question an access review // asks on its own. HubPrivacyChangedAction audit.Action = "container.privacy_changed" )
const ( CreateTemplateName = "CreateTemplate" ListTemplatesName = "ListTemplates" GetTemplateName = "GetTemplate" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). TemplateCreatedAction audit.Action = "template.created" // TemplatesReadAction is the audit code of an attempted read, declared for the reason // CommentsReadAction is: an ordinary read writes no entry, a refused one does. TemplatesReadAction audit.Action = "template.read" )
const ( TrashContainerName = "TrashContainer" RestoreContainerName = "RestoreContainer" // The audit codes. `.deleted` rather than `.trashed`, as domain-model.md §4 names the container // half of this - the item half is `.trashed` there, and the names are kept as the document // writes them rather than harmonised afterwards. ContainerDeletedAction audit.Action = "container.deleted" ContainerRestoredAction audit.Action = "container.restored" )
const ( TrashWorkItemName = "TrashWorkItem" RestoreWorkItemName = "RestoreWorkItem" // The audit codes. This is the first deletion path in the system, so the two directions are // separate codes rather than one with a flag: "what was deleted here" is the question an auditor // asks, and answering it must not require reading a change list (audit.md §2). ItemTrashedAction audit.Action = "item.trashed" ItemRestoredAction audit.Action = "item.restored" )
const ( UpdateBucketName = "UpdateBucket" ReorderBucketName = "ReorderBucket" // The audit codes. Two rather than one, for the reason the container's rename and policy update // have two: "who renamed this column" and "who rearranged the board" are different questions of // the trail, and an auditor filtering on one must not have to read the change list of the other // to find out which it is looking at (audit.md §2). BucketUpdatedAction audit.Action = "bucket.updated" BucketReorderedAction audit.Action = "bucket.reordered" )
const ( RenameContainerName = "RenameContainer" UpdateContainerPoliciesName = "UpdateContainerPolicies" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). // // Two codes rather than one, because the two answer different questions of the trail. "Who // changed how this collection works" is an administrative question and "who renamed it" is not, // and an auditor filtering on one must not have to read the change list of the other to find out // which it is looking at. ContainerRenamedAction audit.Action = "container.renamed" ContainerPoliciesUpdatedAction audit.Action = "container.policies_updated" )
const ( UpdateLabelName = "UpdateLabel" DeleteLabelName = "DeleteLabel" RestoreLabelName = "RestoreLabel" // The audit codes. Stable: an auditor filters on them and a SIEM rule matches on them // (audit.md §2). LabelUpdatedAction audit.Action = "label.updated" LabelDeletedAction audit.Action = "label.deleted" LabelRestoredAction audit.Action = "label.restored" )
const ( UpdateWorkItemName = "UpdateWorkItem" // ItemUpdatedAction is the audit code. Stable: an auditor filters on it and a SIEM rule matches on // it (audit.md §2). ItemUpdatedAction audit.Action = "item.updated" )
const AiTranslateName = "AiTranslate"
AiTranslateName is the catalogue name (core/application/catalogue).
const AssigneeRestrictedCode = "items.assignee_restricted"
AssigneeRestrictedCode is the answer to a rule that names a person whose processing is restricted: no rule puts them on an entry (UC-PRV-04 check 4, data-protection.md §4). The engine records the action as SKIPPED with it, never as failed.
const (
AutoAssignWorkItemName = "AutoAssignWorkItem"
)
const CloseStarterName = "CloseStarter"
const ContainerStructurePreviewedAction audit.Action = "container.structure_previewed"
ContainerStructurePreviewedAction is the audit code of a refused preview.
const ContainerStructureUpdatedAction audit.Action = "container.structure_updated"
ContainerStructureUpdatedAction is the audit code of a change to a hub's or a collection's own structure.
const DefaultReminderBatch = 100
DefaultReminderBatch is how many reminders one pass settles. Large enough that a normal tenant is done in one, small enough that the transaction stays short.
const EmbeddingSeedName = "search.embedding"
EmbeddingSeedName is how this subscriber is known in the deduplication and in the logs. Stable across versions, for the reason every consumer name is: renaming it makes every event it has already seen look new.
const ListJumbleDestinationsName = "ListJumbleDestinations"
ListJumbleDestinationsName is the catalogue name.
const (
ListPresetsName = "ListPresets"
)
const PresetsReadAction audit.Action = "presets.read"
PresetsReadAction is the audit code of a refused read of the presets.
const (
PreviewContainerStructureName = "PreviewContainerStructure"
)
const QueryItemsName = "QueryItems"
QueryItemsName is the catalogue name (core/application/catalogue).
const ReasonPrivateHubOrphaned = "private_hub_orphaned"
ReasonPrivateHubOrphaned is the audit entry's reason when the system trashed a private hub nobody was left in.
const RecurrenceSkippedAction audit.Action = "recurrence.occurrence_skipped"
RecurrenceSkippedAction is the audit code. Stable: an auditor filters on it and a SIEM rule matches on it (audit.md §2).
const ReindexAskedAction audit.Action = "search.reindex_asked"
ReindexAskedAction records that an administrator asked for the workspace's search documents to be brought current, and how many were stale when they asked.
const ReindexSearchName = "ReindexSearch"
ReindexSearchName is the catalogue name (core/application/catalogue).
const ReorderContainerName = "ReorderContainer"
ReorderContainerName is the catalogue name.
const (
// SearchItemsName is the catalogue name (core/application/catalogue).
SearchItemsName = "SearchItems"
)
const (
SetContainerStructureName = "SetContainerStructure"
)
const (
SetWorkspaceStructureName = "SetWorkspaceStructure"
)
const SkipOccurrenceName = "SkipOccurrence"
SkipOccurrenceName is the catalogue's name for the one part of the materialisation a person asks for.
const StarterClosedAction audit.Action = "workspace.starter_closed"
StarterClosedAction is the audit code of the first start's question answered or skipped.
const TranslationAskedAction audit.Action = "ai.translation_asked"
TranslationAskedAction records that somebody read an entry in another language: the entry and the language, never the text (rule 10). Its own action, because "what was sent to a provider, and about what" is the question a data protection officer asks, and this sends an entry's content somewhere without recording anything the other AI actions record.
const WorkspaceStructureUpdatedAction audit.Action = "workspace.structure_updated"
WorkspaceStructureUpdatedAction is the audit code of a change to the workspace's structure: its values, its locks or its default preset.
Variables ¶
This section is empty.
Functions ¶
func BucketSnapshot ¶
BucketSnapshot is a column as the board lists it.
func BulkOperationNames ¶
func BulkOperationNames() []string
BulkOperationNames are the operations a bulk accepts, in a stable order. The catalogue declares them as the field's enum, so a client is told what it may send rather than having to try.
func CommentSnapshot ¶
CommentSnapshot is a comment as the thread lists it.
func ContainerSnapshot ¶
ContainerSnapshot is a hub or a collection as `GET /containers/{id}` answers it.
func ContainerSnapshots ¶
func ContainerSnapshots( ctx context.Context, structure StructureResolver, containers []domain.Container, ) ([]usecase.Output, error)
ContainerSnapshots are containers as a read answers them, each with its structure in force, resolved in the caller's transaction. Without a wired resolver they carry none.
func ItemOutput ¶
ItemOutput is the shape every channel returns: the field names of the contract
Exported because the lifecycle context answers an entry too: `:retain` hands back the entry it took out of a retention period, and a second mapper for the same shape would be a second place for a field to be forgotten. It is a pure function over the aggregate - no ports, no decisions - which is what makes one service package reading another's a definition rather than a dependency. (api/openapi.yaml, schema WorkItem), so that a REST response, an MCP tool result and an automation action result describe the item in the same words.
func ItemSnapshot ¶
ItemSnapshot is an entry as `GET /items/{id}` answers it.
func LabelSnapshot ¶
LabelSnapshot is a label as the collection lists it.
func PresetOutput ¶
PresetOutput is the Preset schema, for an answer outside this context.
func RecurrenceSnapshot ¶
func RecurrenceSnapshot(rule domain.RecurrenceRule) usecase.Output
RecurrenceSnapshot is a series definition as `GET /items/{id}/recurrence` answers it.
func ReminderSnapshot ¶
ReminderSnapshot is a reminder as the entry lists it.
func StructureValuesFrom ¶
func StructureValuesFrom(raw map[string]any) (domain.StructureValues, error)
StructureValuesFrom reads the eight settings a document carries, as every structure write here reads them: the control plane's installation presets meet the same reading and the same refusals as a workspace's.
func StructureValuesOutput ¶
func StructureValuesOutput(values domain.StructureValues) map[string]any
StructureValuesOutput is the StructureSettings schema, for an answer outside this context.
func TemplateSnapshot ¶
TemplateSnapshot is a template as `GET /templates/{id}` answers it.
func TranslationAnswerKeys ¶
TranslationAnswerKeys is the allow list the gate reads beside the suggestion service's.
Types ¶
type ActivityJournal ¶
type ActivityJournal struct {
Entries activityrepo.Journal
IDs clock.IDGenerator
}
ActivityJournal writes the steps of an item's own history (domain-model.md §3.5).
func (ActivityJournal) RecordStep ¶
func (j ActivityJournal) RecordStep( ctx context.Context, actor appshared.ActorContext, tenantID, itemID, collectionID shared.ID, verb activity.Verb, changeSet map[string]any, at time.Time, ) error
RecordStep writes one step of an entry's history on behalf of something that is not a use case: the push, marking a merge or a change that lost. The entry is named by its identifiers rather than handed over whole, because the caller holds the server's copy as the catalogue answered it and not as the domain type.
type AddComment ¶
type AddComment struct {
Writer CommentWriter
}
AddComment puts a contribution on an entry's discussion.
func (AddComment) Descriptor ¶
func (h AddComment) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4).
func (AddComment) Execute ¶
func (h AddComment) Execute( ctx context.Context, actor appshared.ActorContext, cmd AddCommentCommand, ) (domain.Comment, error)
Execute writes the comment and returns it.
func (AddComment) FileDisplaced ¶
func (h AddComment) FileDisplaced( ctx context.Context, actor appshared.ActorContext, itemID shared.ID, body string, params map[string]string, ) (domain.Comment, error)
FileDisplaced files the version of a free-text field that lost a merge as a system comment on the entry, in the merging person's name (offline-sync.md §5): the body is the text that lost, the heading is `sync.displaced_version` with the field, the device and the reading. The same write as any comment - the same permission, the same event, the same change log entry - so a device that may not comment on the entry is told so, and the push answers the conflict with both values and no comment.
type AddCommentCommand ¶
type AddCommentCommand struct {
// ID is the identifier the client minted, or empty for one the server mints - a device
// commenting offline assigns its own (offline-sync.md §3.2), CreateWorkItemCommand's rule.
ID shared.ID
ItemID shared.ID
// SystemCode and SystemParams make the comment one the server files on the author's behalf
// (offline-sync.md §5). Not declared as inputs: nobody asks for a system comment over a
// channel, and FileDisplaced is the one caller.
SystemCode string
SystemParams map[string]string
// ParentCommentID is the comment being replied to, empty for a top-level comment.
ParentCommentID shared.ID
Body string
}
AddCommentCommand is the input, typed.
type AddLabel ¶
type AddLabel struct {
Writer ItemLabelWriter
}
AddLabel puts a label on an entry.
func (AddLabel) Descriptor ¶
func (h AddLabel) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (AddLabel) Execute ¶
func (h AddLabel) Execute( ctx context.Context, actor appshared.ActorContext, cmd LabelCommand, ) (ItemLabelSet, error)
Execute puts the label on the entry and returns the labels it now carries.
type AddMember ¶
type AddMember struct {
Writer ItemMemberWriter
}
AddMember puts an account on an entry's member list.
func (AddMember) Descriptor ¶
func (h AddMember) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (AddMember) Execute ¶
func (h AddMember) Execute( ctx context.Context, actor appshared.ActorContext, cmd MemberCommand, ) (ItemMemberSet, error)
Execute puts the account on the entry and returns the members it now carries.
type AiProviders ¶
type AiProviders interface {
For(ctx context.Context, actor appshared.ActorContext) (aiprovider.Provider, error)
}
AiProviders answers which provider a workspace uses. An interface here rather than the adapter, because the application layer may not import one (ADR-0001).
type AiTranslate ¶
type AiTranslate struct {
// Reader is the ordinary read of the entry: the permission question and the existence
// question at once, asked before any provider is called.
Reader GetWorkItem
Providers AiProviders
Prompts aiprovider.Prompts
Audit audit.Sink
// UnitOfWork opens the transaction the audit entry is written in. The asking use cases
// record theirs inside one; this one recorded it outside and met
// `postgres.no_transaction_in_context` the first time a person asked.
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
// Timeout bounds the provider call. Zero takes the default above.
Timeout time.Duration
// Kept is whose content stays out of AI (data-protection.md §4.1). Nil keeps nobody out.
Kept KeptOutReader
}
AiTranslate reads one entry in another language (ai-first.md §2's translation row).
A read, not a record: the row says "display only, not persisted" and i18n-l10n.md §7 says user content is never translated in place. So there is no AiSuggestion, no acceptance and nothing written - the answer is handed back and gone. It is the second AI call somebody waits for, after the search's meaning, and bounded the same way.
func (AiTranslate) Descriptor ¶
func (h AiTranslate) Descriptor() usecase.Descriptor
Descriptor registers the translation in all three channels.
func (AiTranslate) Execute ¶
func (h AiTranslate) Execute( ctx context.Context, actor appshared.ActorContext, cmd TranslateCommand, ) (Translation, error)
Execute asks, or refuses.
The order is the one every AI use case keeps: the entry's own permission first, so that this cannot become a way to learn what a workspace has configured; the consent and the provider after it, so that a workspace with AI switched off is told so whatever the entry is.
type Anchored ¶
type Anchored interface {
ReachInto(
ctx context.Context, actor appshared.ActorContext, request access.Request,
containerID shared.ID,
) (access.Reach, error)
}
Anchored is the slice of the authorisation service a list anchored to a container needs: not "may I read this", but "how much of it may I read".
Its own interface rather than a method on Authorizer, for the reason that split Visibility and Moderation off: only the two lists over a collection's entries ask it, and every other use case would have to satisfy it in its test for nothing.
type ArchiveContainer ¶
type ArchiveContainer struct {
Writer ContainerWriter
}
ArchiveContainer makes a hub or a collection read-only, and everything under it with it.
Nothing below is written. A collection inherits an archived hub's state through the read rather than through a stamp of its own (I-C3), so archiving a hub costs one row however many collections sit in it - and unarchiving it restores exactly what it covered, leaving a collection archived in its own right archived.
func (ArchiveContainer) Descriptor ¶
func (h ArchiveContainer) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ArchiveContainer) Execute ¶
func (h ArchiveContainer) Execute( ctx context.Context, actor appshared.ActorContext, cmd ArchiveContainerCommand, ) (domain.Container, error)
Execute archives the container and returns it as it now stands.
type ArchiveContainerCommand ¶
ArchiveContainerCommand is the input, typed. It is the same shape for both verbs: an identifier, and the version the caller read.
type ArchiveWorkItem ¶
type ArchiveWorkItem struct {
Lifecycle LifecycleWriter
}
ArchiveWorkItem takes an entry out of use without deleting it: kept, and read-only.
Nothing below it is archived with it. An entry's children are entries in their own right, unlike the collections of a hub, so there is no inherited stamp here and no I-C3 to enforce - a work package under an archived task stays writable, and that is the model rather than an oversight.
func (ArchiveWorkItem) Descriptor ¶
func (h ArchiveWorkItem) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4).
func (ArchiveWorkItem) Execute ¶
func (h ArchiveWorkItem) Execute( ctx context.Context, actor appshared.ActorContext, cmd LifecycleCommand, ) (domain.WorkItem, error)
Execute archives the entry and returns it as it now stands.
type AssignWorkItem ¶
type AssignWorkItem struct {
Assignment AssignmentWriter
}
AssignWorkItem puts one person on an entry.
Handing an entry from one person to another is this use case rather than an unassignment followed by an assignment: the field is a scalar, so it is one decision, one version and one step of the history - and doing it in two calls would leave a moment in which the work belongs to nobody.
func (AssignWorkItem) Descriptor ¶
func (h AssignWorkItem) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4).
func (AssignWorkItem) Execute ¶
func (h AssignWorkItem) Execute( ctx context.Context, actor appshared.ActorContext, cmd AssignmentCommand, ) (domain.WorkItem, error)
Execute puts the account on the entry and returns it.
type AssignmentCommand ¶
type AssignmentCommand struct {
ItemID shared.ID
AccountID shared.ID
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read none
// and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
AssignmentCommand is the input of both directions, typed. The account is empty for an unassignment, which is the one field the two do not share.
type AssignmentWriter ¶
type AssignmentWriter struct {
Items repository.Items
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
// Gate is what the entry's collection offers it: the refusal before a write of what it
// hides, and the mark on the answer (domain-model.md §2).
Gate EntryGate
Authorizer Authorizer
Visibility Visibility
// Accounts answers whether a rule is about to put a restricted person on the entry: no rule
// does (data-protection.md §4). Nil restricts nobody.
Accounts RestrictedAccounts
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
Activity ActivityJournal
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
}
AssignmentWriter is what both directions of assignment need.
One dependency set held by both use cases rather than two copies of thirteen fields, for the reason CompletionWriter is one: the two operations are the same walk in opposite directions - the same guards, the same four records - and the only thing that differs is whether a person arrives or leaves.
type AttachMedia ¶
type AttachMedia struct {
Writer ItemAttachmentWriter
}
AttachMedia puts a file on an entry.
The bytes are shared, never copied: attaching the same object to three entries is three references to one file, which is what the reference count counts and what the reconciliation job decides deletion by (data-protection.md §5).
func (AttachMedia) Descriptor ¶
func (h AttachMedia) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (AttachMedia) Execute ¶
func (h AttachMedia) Execute( ctx context.Context, actor appshared.ActorContext, cmd AttachmentCommand, ) (ItemAttachmentSet, error)
Execute puts the file on the entry and returns what it now carries.
type AttachmentCommand ¶
AttachmentCommand is the input of both directions, typed.
No expected version. An attachment lives beside the entry rather than on its row, so there is no version of the entry that it would be racing - which is also why neither operation spends one. Two devices attaching two different files at once is the case the OR-set exists to serve, and an optimistic lock on the entry would make one of them fail for no reason (offline-sync.md §4.2).
type Authorizer ¶
type Authorizer interface {
Authorize(ctx context.Context, actor appshared.ActorContext, request access.Request) error
}
Authorizer is the slice of the authorisation service this use case needs. An interface rather than the service, so the use case can be tested without a membership table.
type AutoAssignOutcome ¶
type AutoAssignOutcome struct {
Assigned bool
Strategy domain.AutoAssignStrategy
// Code is the stable reason when nobody was assigned, empty otherwise.
Code string
}
AutoAssignOutcome is what the run did, beside the entry it did it to. A result rather than an error, because "the policy found nobody" is an answer, not a failure: the create that carries the same machinery must not fail the creation over it, and the two callers should not read two different shapes.
type AutoAssignWorkItem ¶
type AutoAssignWorkItem struct {
Assignment AssignmentWriter
Policies repository.AutoAssignPolicies
Groups identityrepo.Groups
// Accounts answers which candidates are under a restriction of processing. Art. 18 is a
// technical state, and an automatic decision about somebody is exactly the processing it
// stops (data-protection.md §4) - so the pool is narrowed here rather than the draw
// being allowed to land on them.
Accounts identityrepo.Accounts
Random clock.RandomSource
}
AutoAssignWorkItem hands an entry out by the collection's assignment policy instead of a named person (domain-model.md §3.6).
The split of work between the layers is the strategy port's contract: this use case resolves the material - which candidates may actually receive the entry, how loaded they are, where the rotation stands - and the domain's strategy only chooses. Eligibility is the same question the manual assignment asks about its one account, asked about every candidate: somebody who cannot see the entry is skipped, not assigned, and never named in an answer (T-04).
func (AutoAssignWorkItem) Descriptor ¶
func (h AutoAssignWorkItem) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4).
func (AutoAssignWorkItem) Execute ¶
func (h AutoAssignWorkItem) Execute( ctx context.Context, actor appshared.ActorContext, cmd AssignmentCommand, ) (domain.WorkItem, AutoAssignOutcome, error)
Execute hands the entry out and returns it together with what happened.
type BucketDeletion ¶
type BucketDeletion struct {
BucketID shared.ID
// TargetBucketID is the column the entries moved to. Zero when this was the last column on the
// board, in which case the entries now carry none - the state the collection was in before
// anybody made a board.
TargetBucketID shared.ID
MovedItems int
}
BucketDeletion is what became of the entries that were in the deleted column.
Reported rather than left to be discovered: a board that silently reassigned a person's cards would be indistinguishable from one that lost them.
type BucketInserter ¶
BucketInserter is the one method the starting columns need of the buckets.
type BucketWriter ¶
type BucketWriter struct {
Buckets repository.Buckets
Containers repository.Containers
Authorizer Authorizer
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
// Text brings the names people type to normal form C on the way in (i18n-l10n.md §5).
Text text.Normalizer
}
BucketWriter is what every use case that changes an existing column shares.
One dependency set rather than one per verb: they read the same bucket, ask the same permission question of the same collection, and owe the same four writes - the row, the event, the change log entry and the audit entry. What differs between them is which domain method decides the new state, which is what bucketChange carries.
type BulkOperation ¶
type BulkOperation struct {
Op string
ItemID shared.ID
// Payload is what the single-entry operation takes in its body, by the field names it takes
// there. It is not inspected here: the catalogue checks it against the operation's own
// declaration, so a field the operation does not know comes back named rather than ignored.
Payload map[string]any
}
BulkOperation is one operation of a bulk: which single-entry operation, on which entry, with the body that operation takes on its own.
type BulkOutcome ¶
BulkOutcome is what one operation did. Exactly one of Output and Err is set, except for an operation of an atomic bulk that was rolled back, which carries the rolled-back refusal.
type BulkResult ¶
type BulkResult struct {
Outcomes []BulkOutcome
Applied int
Failed int
}
BulkResult is the whole answer: one outcome per operation, in the order they were sent.
type BulkUpdateWorkItems ¶
type BulkUpdateWorkItems struct {
Catalogue Catalogue
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
}
BulkUpdateWorkItems applies up to five hundred single-entry operations in one call.
Every operation runs through the catalogue, which means it runs exactly as it would on its own: its own permission check, its own event, its own entry in the item's history, its own per-field records for offline clients and its own metric. One event covering five hundred entries would break a merge in a way nothing downstream recovers from, and one history entry for a bulk would hide what the history exists to show (api-guidelines.md §5).
What this use case owns is therefore only the two things a bulk adds: the order, and the question of what happens when one of them fails.
func (BulkUpdateWorkItems) Descriptor ¶
func (h BulkUpdateWorkItems) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4) - and a bulk is what an agent reaches for when it has worked out that fifty entries need the same change, so the MCP door is the point rather than a side effect (ai-first.md §1.1).
func (BulkUpdateWorkItems) Execute ¶
func (h BulkUpdateWorkItems) Execute( ctx context.Context, actor appshared.ActorContext, cmd BulkUpdateWorkItemsCommand, ) (BulkResult, error)
Execute applies the operations and reports what each one did.
It returns an error only when the bulk itself could not be carried out - an unreadable request, a cap exceeded, a context that ran out. An operation that failed is not that: the bulk was carried out, and what happened to each operation is in the answer. A caller reads the outcomes rather than one status covering five hundred of them.
type BulkUpdateWorkItemsCommand ¶
type BulkUpdateWorkItemsCommand struct {
// Atomic makes the bulk all or nothing. Without it, the operations that succeeded stay applied.
Atomic bool
Operations []BulkOperation
}
BulkUpdateWorkItemsCommand is the input, typed.
type CalendarFeedWriter ¶
type CalendarFeedWriter struct {
Feeds feedrepo.CalendarFeeds
Views viewrepo.SavedViews
Containers repository.Containers
// Permits answers the view's visibility without writing a denial: a view the caller cannot
// see is not found, and a DENIED entry per invisible bookmark makes the trail unreadable
// (T-04, the reasoning GetSavedView already applies).
Permits Permitting
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
// Entropy is where the token's secret half comes from. A port, so that production draws from
// crypto/rand and a test can fix the credential it asserts on (rule 4).
Entropy clock.Entropy
}
CalendarFeedWriter is what the three feed use cases share.
It lives beside the saved views rather than in a package of its own, because minting a feed is a read of a view: the rule that decides who may see which view is viewVisibleTo, in this package, and a second copy of a visibility rule is how two answers to one security question get into a codebase.
type Catalogue ¶
type Catalogue interface {
Invoke(
ctx context.Context, name string, actor appshared.ActorContext, in usecase.Input,
) (usecase.Output, error)
}
Catalogue is the slice of the use case registry a bulk needs: run one use case by name.
Declared here as an interface rather than taking the registry, for the reason every other narrow port in this package is narrow - and for one more: the registry is built *from* this use case's descriptor, so a direct dependency would be a cycle the composition root cannot wire. What is passed in is bound after the catalogue exists.
type ChangeCommentCommand ¶
type ChangeCommentCommand struct {
ItemID shared.ID
CommentID shared.ID
Body string
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read
// none and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
ChangeCommentCommand is the input of both, typed. The body is the edit's; a deletion carries none, which is the one field the two do not share.
type ChangePreset ¶
type ChangePreset struct{ Desk PresetDesk }
ChangePreset renames a saved preset, copies a structure into it again or sets settings on top (UC-WRK-30 check 3). Hubs and collections made from it keep their settings: a preset is copied, never followed.
func (ChangePreset) Descriptor ¶
func (h ChangePreset) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ChangePreset) Execute ¶
func (h ChangePreset) Execute( ctx context.Context, actor appshared.ActorContext, cmd ChangePresetCommand, ) (domain.Preset, error)
Execute changes the preset.
type ChangePresetCommand ¶
type ChangePresetCommand struct {
PresetID shared.ID
Name string
ContainerID shared.ID
Settings domain.StructureValues
ExpectedVersion int
}
ChangePresetCommand is the input, typed. An empty field is left as it is.
type ChangeReminderCommand ¶
type ChangeReminderCommand struct {
ItemID shared.ID
ReminderID shared.ID
Patch domain.ReminderPatch
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read
// none and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
ChangeReminderCommand is the input of both, typed. The patch is the update's; a deletion carries none, which is the one field the two do not share.
type ChangeTemplateCommand ¶
type ChangeTemplateCommand struct {
TemplateID shared.ID
Patch domain.TemplatePatch
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read
// none and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
ChangeTemplateCommand is the input of both, typed. The patch is the update's; a deletion carries none, which is the one field the two do not share.
type ClearCover ¶
type ClearCover struct {
Cover CoverWriter
}
ClearCover takes the cover off an entry.
func (ClearCover) Descriptor ¶
func (h ClearCover) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ClearCover) Execute ¶
func (h ClearCover) Execute( ctx context.Context, actor appshared.ActorContext, cmd CoverCommand, ) (domain.WorkItem, error)
Execute takes the cover off the entry and returns it.
type ClearDueDate ¶
type ClearDueDate struct {
Due DueDateWriter
}
ClearDueDate takes the due date off an entry - the instant, the flag and the zone together, because none of them means anything alone.
func (ClearDueDate) Descriptor ¶
func (h ClearDueDate) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ClearDueDate) Execute ¶
func (h ClearDueDate) Execute( ctx context.Context, actor appshared.ActorContext, cmd DueDateCommand, ) (domain.WorkItem, error)
Execute takes the due date off the entry and returns it.
type CloseStarter ¶
type CloseStarter struct{ Starter Starter }
CloseStarter answers the first start's question for good, answered or skipped alike. A client closes it before creating the starter set, so an interruption never makes the set twice.
func (CloseStarter) Descriptor ¶
func (h CloseStarter) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (CloseStarter) Execute ¶
func (h CloseStarter) Execute(ctx context.Context, actor appshared.ActorContext) error
Execute closes it.
type CommentWriter ¶
type CommentWriter struct {
Comments repository.Comments
Items repository.Items
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
// Gate is what the entry's collection offers it: the refusal before a write of what it
// hides, and the mark on the answer (domain-model.md §2).
Gate EntryGate
Authorizer Authorizer
// Moderation answers the one question the permission cannot: which role the actor holds.
// Only the edit and the deletion consult it - "only the author or an administrator may
// change a comment" is their rule, and it is named where it is enforced (Authorization.go).
Moderation Moderation
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
Activity ActivityJournal
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
// Text brings the body to normal form C on the way in (i18n-l10n.md §5).
Text text.Normalizer
// Holds are the legal holds a deletion is judged against: under one, the deleted comment keeps
// its text until the retention pass clears it (data-retention.md §4). Only the deletion reads
// them.
Holds lifecyclerepo.LegalHolds
}
CommentWriter is what every use case that writes the discussion shares: the same reads, the same permission question, and the same records - the event outwards, the change log entry for offline clients, and the audit entry. What differs between the verbs is which domain method decides the new state, and - for an edit and a deletion - the author-or-administrator rule.
type CompleteWorkItem ¶
type CompleteWorkItem struct {
Completion CompletionWriter
}
CompleteWorkItem marks a task, work package or activity done.
Where the collection's policy rolls up, completing the last open child completes the item above it, and so on upwards until something is still open or the top is reached (invariant I-W5).
func (CompleteWorkItem) Descriptor ¶
func (h CompleteWorkItem) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4) - and completion is the automation trigger and action that matters most, so all three doors leading to this one handler is the point rather than a nicety.
func (CompleteWorkItem) Execute ¶
func (h CompleteWorkItem) Execute( ctx context.Context, actor appshared.ActorContext, cmd CompletionCommand, ) (domain.WorkItem, error)
Execute completes the item and returns it.
type CompletionCommand ¶
type CompletionCommand struct {
ItemID shared.ID
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read none and
// accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
CompletionCommand is the input of both directions, typed.
type CompletionWriter ¶
type CompletionWriter struct {
Items repository.Items
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
// Gate is what the entry's collection offers it: the refusal before a write of what it
// hides, and the mark on the answer (domain-model.md §2).
Gate EntryGate
Authorizer Authorizer
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
Activity ActivityJournal
// Jobs is where an ON_COMPLETION series asks for its next occurrence. The completion is what
// seeds it, which is SY-8's server half: creation is bound to the status transition rather
// than to the event, so two devices completing the same entry produce one transition and one
// follow-up (offline-sync.md §8).
Jobs queue.Queue
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
// Structure answers the completion in force: the collection's own, else its hub's, else the
// workspace's (domain-model.md §2). A build without it reads the collection's own alone.
Structure StructureResolver
// Milestones records the collection, and its hub, a completion left with nothing open
// (UC-WRK-29 check 4). A build without it records none.
Milestones repository.Milestones
}
CompletionWriter is what both directions of completion need.
One dependency set held by both use cases rather than two copies of eleven fields. The two operations are the same walk in opposite directions - the same guards, the same roll-up, the same three records per item touched - and the only thing that differs is which state is being asked for. Keeping the machinery in one place is what makes "reopening rolls up exactly as completing does" true by construction rather than by two people remembering.
type ContainerLifecycleCommand ¶
type ContainerLifecycleCommand struct {
ContainerID shared.ID
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read none
// and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
ContainerLifecycleCommand is the input both verbs take.
type ContainerWriter ¶
type ContainerWriter struct {
Containers repository.Containers
// Policies is where the auto_assign key of the policies document is stored: its own row, so
// the rotation's state can be locked (see work.AutoAssignPolicy). Only UpdateContainerPolicies
// writes it, for the reason only TrashContainer uses Queue - the dependency set is shared, the
// verbs are not.
Policies repository.AutoAssignPolicies
Authorizer Authorizer
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
// Queue is where a deletion asks for the tenant's cleanup to be scheduled. Only TrashContainer
// uses it; the reasoning is at LifecycleWriter.Queue.
Queue queue.Queue
// Text brings the names people type to normal form C on the way in (i18n-l10n.md §5).
Text text.Normalizer
}
ContainerWriter is what every use case that changes an existing container shares.
One dependency set rather than one per verb: they read the same container, ask the same permission question, and owe the same four writes - the row, the event, the change log entry and the audit entry. What differs between them is which domain method decides the new state.
type CoverCommand ¶
type CoverCommand struct {
ItemID shared.ID
Kind domain.CoverKind
ColorToken string
MediaID shared.ID
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read
// none and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
CoverCommand is the input of both directions, typed. The cover itself is empty for a clearing, which is the one part the two do not share.
type CoverWriter ¶
type CoverWriter struct {
Items repository.Items
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
// Gate is what the entry's collection offers it: the refusal before a write of what it
// hides, and the mark on the answer (domain-model.md §2).
Gate EntryGate
Media MediaReferences
Authorizer Authorizer
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
Activity ActivityJournal
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
}
CoverWriter is what both directions of the cover share.
One dependency set held by both use cases rather than two copies, for the reason AssignmentWriter is one: putting a cover on and taking it off are the same walk in opposite directions, and the only thing that differs is whether an image arrives or leaves.
type CreateBucket ¶
type CreateBucket struct {
Buckets repository.Buckets
Containers repository.Containers
Authorizer Authorizer
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
// Text brings the names people type to normal form C on the way in (i18n-l10n.md §5).
Text text.Normalizer
}
CreateBucket adds a column to a collection's board.
Only a collection has a board. A hub holds collections and no items, so a column on one would have nothing to hold - which is the same reasoning that keeps items off a hub, and it is Container.EnsureAcceptsItems that answers it.
func (CreateBucket) Descriptor ¶
func (h CreateBucket) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4).
func (CreateBucket) Execute ¶
func (h CreateBucket) Execute( ctx context.Context, actor appshared.ActorContext, cmd CreateBucketCommand, ) (domain.Bucket, error)
Execute creates the bucket and returns it.
type CreateBucketCommand ¶
type CreateBucketCommand struct {
CollectionID shared.ID
Name string
// WipLimit is a number of which zero means "no limit", exactly as the empty string means "not
// set" for the text fields (domain.BucketAttributes).
WipLimit int
IsDoneBucket bool
ColorToken string
// BeforeBucketID is the column the new one goes to the left of. Empty means the right hand end,
// which is where a board grows unless somebody says otherwise.
BeforeBucketID shared.ID
}
CreateBucketCommand is the input, typed.
type CreateCalendarFeed ¶
type CreateCalendarFeed struct{ Writer CalendarFeedWriter }
CreateCalendarFeed mints a feed over one saved view and answers its token, once.
func (CreateCalendarFeed) Descriptor ¶
func (h CreateCalendarFeed) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (CreateCalendarFeed) Execute ¶
func (h CreateCalendarFeed) Execute( ctx context.Context, actor appshared.ActorContext, viewID shared.ID, ) (MintedFeed, error)
Execute mints the feed.
The permission is the view's own read. A feed grants exactly what its owner may already read and re-asks that question at every fetch, so minting one is not an escalation and needs no permission beyond seeing the view - which is also why a view the caller cannot see answers "not found" here rather than "forbidden" (T-04).
type CreateContainer ¶
type CreateContainer struct {
Containers repository.Containers
Authorizer Authorizer
// OwnHubs answers who may make a private hub; Grants makes its creator its owner. Both are
// needed only for a private hub, and a build without them refuses one rather than making a hub
// nobody holds.
OwnHubs OwnHubs
Grants identityrepository.MembershipGrants
// PrivateHubs is the workspace's switch, read only for a private hub.
PrivateHubs PrivateHubsRule
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
// Text brings the names people type to normal form C on the way in (i18n-l10n.md §5).
Text text.Normalizer
// Structure resolves and seeds the new container's structure (domain-model.md §2). A build
// without it creates containers with no layer of their own and answers no structure.
Structure StructureResolver
// Buckets, Workspaces and Renderer give a new collection the columns its board starts with
// (domain.StartingColumns), named in the workspace's language: the names are content once
// created, so the backend renders them where they are created, as provisioning does. A build
// without them creates collections with no column.
Buckets BucketInserter
Workspaces WorkspaceReader
Renderer i18n.Renderer
}
CreateContainer creates a hub or a collection.
It is the reference use case: everything a write in this system owes is visible in one Execute - the permission check before the transaction, the invariants in the domain, and inside one transaction the row, the event, the change log entry for offline clients and the audit entry. A later use case that leaves one of them out is visibly different from this one, which is the point of having a reference at all.
func (CreateContainer) Descriptor ¶
func (h CreateContainer) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry: what the use case is called, what it needs, what it records, and how to run it. Registering it is what makes it reachable through REST, MCP and automation at once (arc42 §4).
func (CreateContainer) Execute ¶
func (h CreateContainer) Execute( ctx context.Context, actor appshared.ActorContext, cmd CreateContainerCommand, ) (domain.Container, error)
Execute creates the container and returns it.
type CreateContainerCommand ¶
type CreateContainerCommand struct {
Type domain.ContainerType
ParentID shared.ID
Name string
Description string
Icon string
ColorToken string
// Private makes a new hub private, its creator its owner (ADR-0073 §2).
Private bool
// Preset is the preset the new container starts from, empty for none: a hub then takes the
// workspace's default, a collection follows its hub (domain-model.md §2).
Preset domain.PresetKey
}
CreateContainerCommand is the input, typed. The registry hands the use case an untyped map from whichever channel the call arrived through; this is what that map becomes once, here.
type CreateLabel ¶
type CreateLabel struct {
Labels repository.Labels
Containers repository.Containers
Authorizer Authorizer
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
// Text brings the names people type to normal form C on the way in (i18n-l10n.md §5).
Text text.Normalizer
}
CreateLabel adds a label to a collection's vocabulary.
A collection rather than a hub, and rather than the tenant: a label is a vocabulary the people working in one collection agree on, and a workspace-wide list would make every collection pay for every other's (domain-model.md §3.5).
func (CreateLabel) Descriptor ¶
func (h CreateLabel) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (CreateLabel) Execute ¶
func (h CreateLabel) Execute( ctx context.Context, actor appshared.ActorContext, cmd CreateLabelCommand, ) (domain.Label, error)
Execute creates the label and returns it.
type CreateLabelCommand ¶
type CreateLabelCommand struct {
CollectionID shared.ID
Name string
ColorToken string
Description string
}
CreateLabelCommand is the input, typed.
type CreateReminder ¶
type CreateReminder struct {
Writer ReminderWriter
}
CreateReminder puts a reminder on an entry.
func (CreateReminder) Descriptor ¶
func (h CreateReminder) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4).
func (CreateReminder) Execute ¶
func (h CreateReminder) Execute( ctx context.Context, actor appshared.ActorContext, cmd CreateReminderCommand, ) (domain.Reminder, error)
Execute writes the reminder and returns it.
type CreateReminderCommand ¶
type CreateReminderCommand struct {
ItemID shared.ID
OffsetSpec string
// Channels as the caller named them, and empty for the default. Strings rather than the
// domain's type, because an unknown channel is a validation answer rather than a parse error
// (ADR-0011): the domain names the value it refused.
Channels []string
Recipients []shared.ID
}
CreateReminderCommand is the input, typed.
type CreateSavedView ¶
type CreateSavedView struct {
Views viewrepo.SavedViews
Containers repository.Containers
Authorizer Authorizer
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
// Text brings the name to normal form C on the way in (i18n-l10n.md §5).
Text text.Normalizer
}
CreateSavedView saves a view: a query in the DSL, validated here against the same catalogue an ad-hoc one passes, and the layout the client draws it in, stored and never consulted (api-guidelines.md §3).
func (CreateSavedView) Descriptor ¶
func (h CreateSavedView) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4).
func (CreateSavedView) Execute ¶
func (h CreateSavedView) Execute( ctx context.Context, actor appshared.ActorContext, cmd CreateSavedViewCommand, ) (view.SavedView, error)
Execute creates the view and returns it.
The permission is the scope's, and it depends on what is asked. A view is a bookmark, so creating one needs READ on its scope's path - anyone who may read a collection may save a query on it. Creating it already shared publishes workspace furniture, which is STRUCTURE, exactly as :share asks. An ACCOUNT-scoped view is self-service, the way changing one's own preferences is: no matrix cell describes saving a private bookmark, and requiring one would mean a viewer could not keep their own (identity.UpdateAccountPreferences is the precedent).
type CreateSavedViewCommand ¶
type CreateSavedViewCommand struct {
ScopeType view.ViewScope
ScopeID shared.ID
Name string
Layout string
Query map[string]any
Grouping map[string]any
// VisibleFields distinguishes nil - not sent, stored empty - from an empty list a client
// chose, though the two store alike.
VisibleFields []string
Sharing view.Sharing
}
CreateSavedViewCommand is the input, typed.
type CreateTemplate ¶
type CreateTemplate struct {
Writer TemplateWriter
}
CreateTemplate defines a template.
func (CreateTemplate) Descriptor ¶
func (h CreateTemplate) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4).
func (CreateTemplate) Execute ¶
func (h CreateTemplate) Execute( ctx context.Context, actor appshared.ActorContext, cmd CreateTemplateCommand, ) (domain.Template, error)
Execute writes the template and returns it.
type CreateTemplateCommand ¶
type CreateTemplateCommand struct {
Spec domain.TemplateSpec
}
CreateTemplateCommand is the input, typed.
type CreateWorkItem ¶
type CreateWorkItem struct {
Items repository.Items
Quota ItemQuota
Buckets repository.Buckets
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
// Gate is what the entry's collection offers it: the refusal before a write of what it
// hides, and the mark on the answer (domain-model.md §2).
Gate EntryGate
Authorizer Authorizer
Ownership Ownership
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
Activity ActivityJournal
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
// AutoAssign is the machinery behind :auto-assign, reused whole: the create path is
// its second caller - the policy that applies itself to what a collection creates, and the
// explicit `auto_assign` ask - and its Assignment writer is also how an explicit assignee is
// written, with the same guards and the same four records as :assign.
AutoAssign AutoAssignWorkItem
// DueDates is the writer the declared due fields dispatch into, reused whole for the same
// reason.
DueDates DueDateWriter
// Labels and Members are the writers the two set fields dispatch into, reused whole: a label
// put on at creation is the same OR-set element, the same events and the same audit entry as
// one put on a moment later.
Labels ItemLabelWriter
Members ItemMemberWriter
// Covers is the writer the declared cover dispatches into, reused whole for the same reason:
// one place decides what a cover may be, what an image has to be before it may become one, and
// what the change owes.
Covers CoverWriter
// CustomFields is the use case the declared values dispatch into, one key at a time. Whole
// rather than a slice of it, because what judges a value is the definition in force for the
// entry's collection, and that resolution is the thing that may not be duplicated.
CustomFields SetCustomField
// Text brings the title and the notes to normal form C on the way in (i18n-l10n.md §5).
Text text.Normalizer
// Structure resolves the collection's structure in force, which decides whether it offers an
// amount (ensureAmountOffered).
Structure StructureResolver
// Flow refuses an entry created into a column past the first without its required fields,
// where the collection applies that (FlowGate).
Flow FlowGate
}
func (CreateWorkItem) Descriptor ¶
func (h CreateWorkItem) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4).
func (CreateWorkItem) Execute ¶
func (h CreateWorkItem) Execute( ctx context.Context, actor appshared.ActorContext, cmd CreateWorkItemCommand, ) (domain.WorkItem, *AutoAssignOutcome, error)
Execute creates the item and returns it, together with what automatic assignment did when it ran - nil when it did not, which is a different answer from "it ran and found nobody".
type CreateWorkItemCommand ¶
type CreateWorkItemCommand struct {
// ID is the identifier the client minted, or empty for one the server mints. A device
// creating an entry offline assigns its own UUIDv7 so that the entry has its final identity
// at once and a repeated push is idempotent (offline-sync.md §3.2, §9); through the API a
// caller may do the same, and one that does is held to the same rule.
ID shared.ID
Type domain.ItemType
// CollectionID may be left empty when ParentID is given: an item's collection is the one its
// parent is in, and making a client repeat it is making it possible to contradict.
CollectionID shared.ID
ParentID shared.ID
Title string
Notes string
// Amount is how much of it, empty for none: only where the collection's structure in force
// carries AMOUNT for the type (ensureAmountOffered).
Amount string
// BucketID is the column of the collection's board the entry starts in, empty for none.
BucketID shared.ID
// AssigneeID creates the entry already on somebody: the same checks and the same
// records as :assign, in the same transaction as the creation. Empty for nobody.
AssigneeID shared.ID
// AutoAssign asks the collection's assignment policy to hand the new entry out, explicitly -
// an enabled policy applies itself without this flag. Contradicts AssigneeID and is refused
// beside it: a named person and a policy cannot both decide one scalar.
AutoAssign bool
// ContentLanguage is the language the entry is written in, and empty takes the creator's
// locale (i18n-l10n.md §5). What it decides is the text search configuration the entry is
// indexed under, which is the whole of why it is worth carrying (ADR-0034).
ContentLanguage string
// StartAt is when the work begins, nil for no start - the create's own field, a plain
// attribute beside the notes.
StartAt *time.Time
// CalendarUID is the UID a calendar client chose for the entry it is making, and empty for
// every other creation (offline-sync.md §4.2). The tree answers the entry at that address
// from then on; nothing else reads it, and nothing ever changes it.
CalendarUID string
// Due creates the entry already carrying a due date: the same validation and the same
// records as PUT /items/{id}/due, in the same transaction as the creation - the way an
// explicit assignee reuses the :assign machinery. Nil for none.
Due *domain.DueDate
// LabelIDs creates the entry already carrying labels of its collection, and MemberIDs already
// on a member list. Each goes through the writer that owns the set, with the same guards and
// the same four records as the standalone route, in the same transaction as the creation. Empty
// for none.
LabelIDs []shared.ID
MemberIDs []shared.ID
// CustomFields creates the entry already carrying values, each judged against the definition in
// force for its collection and each written as its own change - the per-key merge rule the
// standalone route exists to keep. Nil for none.
CustomFields map[string]any
// Cover creates the entry already covered: a colour token or an image, with the rules and the
// records of `PUT /items/{id}/cover`, in the same transaction as the creation. Nil for an entry
// with no cover. `ItemID` is filled at the dispatch - the caller of a create does not know the
// identifier yet, which is half of why this field had to be written here rather than left to a
// second request.
Cover *CoverCommand
// BeforeItemID is the sibling the new entry is ranked in front of, and empty puts it at the
// end. It is the move's own anchor, answered by the same neighbours query and the same
// refusal - a sibling that is not at this level is `items.before_item_not_in_level` rather
// than a silent append, because a client that asked for a position and got the end of the list
// has been ignored.
BeforeItemID shared.ID
}
CreateWorkItemCommand is the input, typed.
The fields are the ones this use case owns or dispatches - the assignee, the due date, the labels, the members, the cover and the custom fields each into the writer that owns them. A field the catalogue does not declare is refused rather than accepted and dropped: a request carrying one comes back naming it (usecase.field_unknown) instead of returning a 201 for an item that is not what the caller asked for.
type CustomFieldWriter ¶
type CustomFieldWriter struct {
Fields repository.CustomFields
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
Authorizer Authorizer
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
// Text brings the options and a TEXT value to normal form C on the way in (i18n-l10n.md §5).
Text text.Normalizer
}
CustomFieldWriter is what an edit and a deletion share.
One dependency set held by both, for the reason the label pair holds one: the two are the same walk - read the definition, resolve the scope it lives in, ask the permission there, write under the optimistic lock - and the only thing that differs is what is written at the end.
type DefineCustomField ¶
type DefineCustomField struct {
Fields repository.CustomFields
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
Authorizer Authorizer
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
// Text brings the options and a TEXT value to normal form C on the way in (i18n-l10n.md §5).
Text text.Normalizer
}
DefineCustomField adds a field to a workspace's or a collection's vocabulary.
Two scopes and no more. A field every collection defines separately is a field nobody can filter across; a field only ever defined workspace-wide is one team's vocabulary imposed on every other (domain-model.md §3.5). Which of the two applies is the presence of a collection in the command.
func (DefineCustomField) Descriptor ¶
func (h DefineCustomField) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4).
func (DefineCustomField) Execute ¶
func (h DefineCustomField) Execute( ctx context.Context, actor appshared.ActorContext, cmd DefineCustomFieldCommand, ) (domain.CustomFieldDefinition, error)
Execute defines the field and returns it.
type DefineCustomFieldCommand ¶
type DefineCustomFieldCommand struct {
// CollectionID is the collection the field belongs to, zero for the whole workspace.
CollectionID shared.ID
Key string
Kind domain.CustomFieldKind
Options []string
IsRequired bool
// AppliesTo are the item types that carry the field. Empty means the column's default, which
// is a task alone.
AppliesTo []domain.ItemType
}
DefineCustomFieldCommand is the input, typed.
type DeleteBucket ¶
type DeleteBucket struct {
Writer BucketWriter
}
DeleteBucket takes a column off a board and moves its entries somewhere they still exist.
It is not one of the verbs BucketWriter.change covers, and the difference is the point: this one writes two rows' worth of state - the column and every entry that was in it - and returns what became of them. A verb that only stamped the column would leave the entries pointing at something that is no longer on the board, and the foreign key's ON DELETE SET NULL would take a person's board apart silently instead.
A soft delete. The row stays so that a change log entry can still name it and a restore stays possible (offline-sync.md §7); the partial unique index frees the name at the same moment.
func (DeleteBucket) Descriptor ¶
func (h DeleteBucket) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (DeleteBucket) Execute ¶
func (h DeleteBucket) Execute( ctx context.Context, actor appshared.ActorContext, cmd DeleteBucketCommand, ) (BucketDeletion, error)
Execute deletes the column and returns what became of its entries.
type DeleteBucketCommand ¶
type DeleteBucketCommand struct {
BucketID shared.ID
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read none
// and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
DeleteBucketCommand is the input, typed.
type DeleteComment ¶
type DeleteComment struct {
Writer CommentWriter
}
DeleteComment turns a comment into its tombstone.
func (DeleteComment) Descriptor ¶
func (h DeleteComment) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (DeleteComment) Execute ¶
func (h DeleteComment) Execute( ctx context.Context, actor appshared.ActorContext, cmd ChangeCommentCommand, ) (domain.Comment, error)
Execute writes the tombstone and returns it.
type DeleteCustomField ¶
type DeleteCustomField struct {
Writer CustomFieldWriter
}
DeleteCustomField takes a definition out of use.
func (DeleteCustomField) Descriptor ¶
func (h DeleteCustomField) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (DeleteCustomField) Execute ¶
func (h DeleteCustomField) Execute( ctx context.Context, actor appshared.ActorContext, cmd DeleteCustomFieldCommand, ) error
Execute takes the definition out of use.
A soft delete, and what makes it one is the size of the alternative: rewriting `custom_fields` across every entry in a collection would be an unbounded write from one request. The values stay and stop being visible, and a definition recreated under the same key is a new definition that shows none of what the old one held - which the partial unique index makes true rather than promised.
type DeleteCustomFieldCommand ¶
DeleteCustomFieldCommand is the input, typed.
type DeleteLabel ¶
type DeleteLabel struct {
Writer LabelWriter
}
DeleteLabel takes a label out of a collection's vocabulary.
A soft delete, and the entries that carried it are not rewritten: the read side filters on the label's own stamp, which is what makes the deletion undoable without having to remember who wore it. That is the difference from a deleted column, whose entries have to go somewhere - a chip that is no longer in the vocabulary simply stops being rendered, while an entry in a column that is no longer on the board would be nowhere at all.
func (DeleteLabel) Descriptor ¶
func (h DeleteLabel) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (DeleteLabel) Execute ¶
func (h DeleteLabel) Execute( ctx context.Context, actor appshared.ActorContext, cmd DeleteLabelCommand, ) (domain.Label, error)
Execute deletes the label and returns it as it now stands.
type DeleteLabelCommand ¶
DeleteLabelCommand is the input, typed.
type DeleteReminder ¶
type DeleteReminder struct {
Writer ReminderWriter
}
DeleteReminder removes a reminder.
func (DeleteReminder) Descriptor ¶
func (h DeleteReminder) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (DeleteReminder) Execute ¶
func (h DeleteReminder) Execute( ctx context.Context, actor appshared.ActorContext, cmd ChangeReminderCommand, ) error
Execute removes the reminder.
type DeleteSavedView ¶
type DeleteSavedView struct {
Writer SavedViewWriter
}
DeleteSavedView removes a view. A calendar feed that served it keeps its token and serves nothing, saying why - the reference nulls rather than cascading (migration 0005).
func (DeleteSavedView) Descriptor ¶
func (h DeleteSavedView) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (DeleteSavedView) Execute ¶
func (h DeleteSavedView) Execute( ctx context.Context, actor appshared.ActorContext, cmd SavedViewCommand, ) error
Execute removes the view.
type DeleteTemplate ¶
type DeleteTemplate struct {
Writer TemplateWriter
}
DeleteTemplate removes a template, leaving the trees it has stamped out standing.
func (DeleteTemplate) Descriptor ¶
func (h DeleteTemplate) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (DeleteTemplate) Execute ¶
func (h DeleteTemplate) Execute( ctx context.Context, actor appshared.ActorContext, cmd ChangeTemplateCommand, ) error
Execute removes the template.
type DetachMedia ¶
type DetachMedia struct {
Writer ItemAttachmentWriter
}
DetachMedia takes a file off an entry. The object itself stays - it may serve other entries, and what decides its life is the count rather than this call.
func (DetachMedia) Descriptor ¶
func (h DetachMedia) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (DetachMedia) Execute ¶
func (h DetachMedia) Execute( ctx context.Context, actor appshared.ActorContext, cmd AttachmentCommand, ) (ItemAttachmentSet, error)
Execute takes the file off the entry and returns what it now carries.
type DueDateCommand ¶
type DueDateCommand struct {
ItemID shared.ID
Due *domain.DueDate
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read
// none and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
DueDateCommand is the input of both directions, typed. The due date is nil for a clearing, which is the one field the two do not share.
type DueDateWriter ¶
type DueDateWriter struct {
Items repository.Items
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
// Gate is what the entry's collection offers it: the refusal before a write of what it
// hides, and the mark on the answer (domain-model.md §2).
Gate EntryGate
// Reminders is here because a due date that moves takes the relative reminders with it: the
// reminder lives on the same entry, so its moment is recomputed in the same transaction rather
// than by a job that would leave the two disagreeing until it ran.
Reminders repository.Reminders
// Jobs is where the tenant's next wake-up is asked for, in the transaction that moved the
// date: a reminder that came forward with it needs the schedule to come forward too.
Jobs queue.Queue
Authorizer Authorizer
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
Activity ActivityJournal
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
}
DueDateWriter is what both directions of the due date need.
One dependency set held by both use cases, for the reason AssignmentWriter is one: the two operations are the same walk in opposite directions - the same guards, the same four records - and the only thing that differs is whether a date arrives or leaves. It is also the writer the create and update paths dispatch into: the contract carries the three due fields on their schemas, and serving them through a second implementation would be a second answer to what a due date means.
type DuplicateResult ¶
type DuplicateResult struct {
Item domain.WorkItem
// Copied is the size of the copy, the entry itself included.
Copied int
// DroppedReferences is what could not be carried over, from every entry that lost something.
// Empty rather than nil: a client that iterates the losses should not have to nil-check the
// field.
DroppedReferences []domain.DroppedReference
}
DuplicateResult is what a duplication answers with: the copy of the entry that was asked about, how many entries the copy consists of, and what the destination could not carry over (I-W6).
type DuplicateWorkItem ¶
type DuplicateWorkItem struct {
Quota ItemQuota
Items repository.Items
ItemLabels repository.ItemLabels
ItemMembers repository.ItemMembers
Labels repository.Labels
Buckets repository.Buckets
Fields repository.CustomFields
Containers repository.Containers
Attachments mediarepo.Attachments
Media MediaReferences
Profiles metarepo.CapabilityProfiles
// Gate is what the entry's collection offers it: the refusal before a write of what it
// hides, and the mark on the answer (domain-model.md §2).
Gate EntryGate
Authorizer Authorizer
// Ownership is the same question the create path asks: does the role the actor holds write only
// what is assigned to them. A copy is a create, so it has to ask it too.
Ownership Ownership
Visibility Visibility
// Restrictions answers whether a rule is copying an entry onto a restricted person: the copy is
// made unassigned and the drop reported (data-protection.md §4). Nil restricts nobody.
Restrictions RestrictedAccounts
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
Activity ActivityJournal
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
// Text is what a copied TEXT value is checked with; a value written before the form was
// applied is composed by the copy, exactly as its next edit would compose it.
Text text.Normalizer
}
DuplicateWorkItem copies an entry, and everything below it when asked for.
The copy is a new entry rather than a fork of a conversation: what travels is the description - the title, the notes, the column, the labels, the members, the assignee, the cover, the attachments and the custom field values, as far as each type's capability profile allows - and what does not is the discussion and the history. A comment is an exchange between people about one entry, and there is no sense in which a copy has already had it.
What the destination cannot resolve is reported rather than dropped in silence (I-W6), exactly as a move reports it: a label belongs to one collection's vocabulary, a column to one board, a custom field key to whatever definition that collection gives it, and an account may see one collection and not another. A person who spent an afternoon labelling learns what to redo.
func (DuplicateWorkItem) Descriptor ¶
func (h DuplicateWorkItem) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4) - and copying an entry is an action a template or a rule performs, so the automation door is not a nicety here.
func (DuplicateWorkItem) Execute ¶
func (h DuplicateWorkItem) Execute( ctx context.Context, actor appshared.ActorContext, cmd DuplicateWorkItemCommand, ) (DuplicateResult, error)
Execute copies the entry and returns the copy.
type DuplicateWorkItemCommand ¶
type DuplicateWorkItemCommand struct {
ItemID shared.ID
// IncludeSubtree copies everything below the entry with it. Without it the entry is copied
// alone and its children stay where they are.
IncludeSubtree bool
// TargetParentID is the entry the copy sits under, meaningful only together with ParentGiven:
// the zero value is both "the top level" and "not asked for", and those are different requests.
TargetParentID shared.ID
ParentGiven bool
// TargetCollectionID may be omitted when a parent is given: an entry's collection is the one
// its parent is in, and making a client repeat it is making it possible to contradict.
TargetCollectionID shared.ID
// Title is the copy's title, and empty keeps the original's. A server that invented "Copy of …"
// would be writing display text (ADR-0011).
Title string
}
DuplicateWorkItemCommand is the input, typed.
type EditComment ¶
type EditComment struct {
Writer CommentWriter
}
EditComment rewrites a comment's text.
func (EditComment) Descriptor ¶
func (h EditComment) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (EditComment) Execute ¶
func (h EditComment) Execute( ctx context.Context, actor appshared.ActorContext, cmd ChangeCommentCommand, ) (domain.Comment, error)
Execute rewrites the text and returns the comment.
type EmbedItems ¶
type EmbedItems struct {
Embeddings repository.Embeddings
Providers AiProviders
Semantic SemanticSearch
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
// BatchSize bounds one pass. Zero takes the default below.
BatchSize int
// Kept is whose content stays out of AI: their entries are not embedded, and their vectors
// are forgotten by the pass (data-protection.md §4.1). Nil keeps nobody out.
Kept KeptOutReader
}
EmbedItems brings one workspace's vectors up to date with its entries.
Not a use case, and deliberately not in the catalogue: nobody asks for their entries to be embedded. It is the pass behind a job, in the shape the retention sweep has - a batch, then a decision about whether to come straight back - because what it does is a backlog rather than a question.
**Nothing about it is on the write path.** An embedding cannot be maintained by a trigger the way `search_document` is, because a trigger cannot make a network call, and a write that waited on a provider is precisely what ai-first.md §2 forbids. So an entry whose text just changed is found lexically until the pass reaches it, which is the correct degradation rather than a gap: the entry is searchable the whole time, by the words somebody would use to find something they just wrote.
func (EmbedItems) Execute ¶
func (h EmbedItems) Execute( ctx context.Context, actor appshared.ActorContext, ) (EmbedOutcome, error)
Execute embeds one batch of what the workspace owes.
Three refusals, and all three are "nothing to do" rather than failures: no store, no provider that can embed, and nothing owed. Semantic search is optional at every level - the extension, the provider, the consent - and a pass that treated any of them as an error would fill a dead letter queue with a feature somebody has switched off.
type EmbedOutcome ¶
type EmbedOutcome struct {
// Embedded is how many vectors were written.
Embedded int
// Exhausted is true when the pass filled its batch, which means there is probably more owed.
Exhausted bool
}
EmbedOutcome is what one pass did, for the job that decides whether to come back.
type EmbeddingQueue ¶
type EmbeddingQueue interface {
Enqueue(ctx context.Context, request queue.Request) (shared.ID, error)
}
EmbeddingQueue is the slice of the job queue this subscriber needs. Narrow, so what it can do is one line: ask for work.
type EmergencyNotices ¶
type EmergencyNotices interface {
EmergencyAccessOpened(ctx context.Context, access domain.EmergencyAccess, members []shared.ID) error
}
EmergencyNotices tells a private hub's members that it was opened - the notification service's RecordEmergencyAccess - inside the caller's transaction.
type EntryGate ¶
type EntryGate struct {
Structure StructureResolver
Types repository.ItemTypes
}
EntryGate answers what an entry's collection offers it: the gate every item writer passes, and the mark every item read carries (domain-model.md §2, "Narrowing hides, it never deletes"). One answer for both, so that what a read says is hidden is what a write is refused for (P-05).
A build without it - a test of something else - gates by the installation's profile alone, which is the behaviour from before collections had a structure (CompletionWriter's precedent). Wired, it never falls back: a structure that cannot be read refuses the write.
func (EntryGate) Answer ¶
func (g EntryGate) Answer( ctx context.Context, uow persistence.UnitOfWork, actor appshared.ActorContext, item domain.WorkItem, ) (usecase.Output, error)
Answer is an entry's projection with its mark, as every write to an entry answers.
type ExportItems ¶
type ExportItems struct {
Query QueryItems
ItemLabels repository.ItemLabels
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
}
ExportItems renders a query's result whole, without a saved view: a collection as it is filtered on screen, handed over as a file.
The permission is QueryItems' own: the query is checked against its scope once, under the caller's authorisation, so a refusal is a refusal rather than an empty file. The walk and the cap are the view export's (walkToCap), so the same question exports the same rows whether or not somebody saved it first.
func (ExportItems) Descriptor ¶
func (h ExportItems) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ExportItems) Execute ¶
func (h ExportItems) Execute( ctx context.Context, actor appshared.ActorContext, spec view.Spec, ) (ExportedItems, error)
Execute selects the rows and records the read.
type ExportView ¶
type ExportView struct {
Views viewrepo.SavedViews
Containers repository.Containers
Permits Permitting
// Query is the same use case an ad-hoc query goes through. Held rather than reimplemented:
// an export that selected rows its own way would be a second answer to "what does this view
// contain", and the two would drift.
Query QueryItems
ItemLabels repository.ItemLabels
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
}
ExportView renders a saved view's result whole.
The rows are selected here and rendered by whichever channel asked: a wire format is an adapter's business (project-structure.md §3), and the backend composes no document text of its own (rule 8). What this use case owes is the selection, the cap, and the honest statement that the cap was reached.
The query executes under the **caller's** authorisation, exactly as reading the view does: two people exporting one shared view export two different files, and neither gets a row their role hides (T-04, T-05).
func (ExportView) Descriptor ¶
func (h ExportView) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ExportView) Execute ¶
func (h ExportView) Execute( ctx context.Context, actor appshared.ActorContext, viewID shared.ID, ) (ExportedView, error)
Execute selects the rows.
It walks the query's own pages rather than asking for one large one: the page size the whole product shares is 200, the statements are written and indexed for that, and an export is the one caller that wants all of them - so it asks repeatedly and stops at the cap. One page beyond the cap is what tells it there was more, which is why the walk stops on the first page that crosses it rather than on an exact count nobody asked the database for.
func (ExportView) Select ¶
func (h ExportView) Select( ctx context.Context, actor appshared.ActorContext, viewID shared.ID, ) (ExportedView, error)
Select is the selection without the audit entry, for a caller that polls: the CalDAV calendar fetches a view every quarter of an hour as its owner, exactly as the ICS feed does, and an audit row per poll would bury the entries somebody actually looks for (audit.md §2). The same visibility rule, the same walk, the same cap - only the record differs, which is why this is a method on the export rather than a second selection.
type ExportedItems ¶
type ExportedItems struct {
Items []domain.WorkItem
Labels map[shared.ID][]shared.ID
Truncated bool
GeneratedAt time.Time
}
ExportedItems is what an export without a view answers.
type ExportedView ¶
type ExportedView struct {
View view.SavedView
Items []domain.WorkItem
// Labels is the label identifiers per entry, so that a rendering that shows them does not
// have to ask again.
Labels map[shared.ID][]shared.ID
// Truncated says the result reached view.MaxExportRows and there was more behind it.
Truncated bool
// TimeZone is the zone the selection ran under - the caller's own, or the feed owner's. It
// decides nothing about which rows came back and everything about which day an all-day entry
// falls on when one is rendered (i18n-l10n.md §4).
TimeZone string
GeneratedAt time.Time
}
ExportedView is what an export answers.
type FigureReader ¶
type FigureReader struct {
Figures repository.Figures
Clock clock.Clock
Profiles metarepo.CapabilityProfiles
}
FigureReader counts what counts in the collections a container read answers (UC-WRK-29; the server counts, the client draws - domain-model.md §2). Called after the permission question and after the structures were resolved, so it counts only collections the reader may read, and every entry it counts is one the reader may read too (a role on a container reaches all of it).
Which entries count is the entry gate's question (EntryGate, Offered): the installation's bound is read in the figure's own transaction, so that a figure leaves out exactly what a read marks hidden.
func (FigureReader) Count ¶
func (r FigureReader) Count( ctx context.Context, actor appshared.ActorContext, uow persistence.UnitOfWork, containers []domain.Container, structures map[shared.ID]domain.EffectiveStructure, ) map[shared.ID]domain.CollectionFigure
Count answers the figures of the page, keyed by collection, in a read-only transaction of its own: a statement that fails aborts its transaction, and the container read must not fail with it. A failure answers every requested collection as unavailable rather than an error (P-11).
No figure for a hub, a trashed collection (a collection under a trashed hub is trashed with it), or a collection whose structure counts nothing or not here yet (FLOW).
func (FigureReader) Wired ¶
func (r FigureReader) Wired() bool
Wired reports whether the reader can count. A read built without one answers no figure.
type FireReminders ¶
type FireReminders struct {
Reminders repository.Reminders
Items repository.Items
// Schedule is the scan the two announcements run on: the same repository as Items, under the
// narrow interface that says what this pass may do with it.
Schedule repository.DueAnnouncements
Containers repository.Containers
ItemMembers repository.ItemMembers
// Visibility answers whether a named recipient can still see the entry. A membership revoked
// since the reminder was written is not survived by a reminder that remembers better days -
// the same question the write asked, asked again at the moment it matters.
Visibility Visibility
Notifier ReminderNotifier
// Events is where the two scheduling announcements go: item.due_soon and item.overdue are
// facts about a deadline rather than messages to a person, and what reacts to them is
// automation (domain-model.md §4).
Events outbox.Events
// Changes is how a device learns that a reminder fired. offline-sync.md §8 requires a client
// to reconcile the local notification it scheduled, and it can only do that if the server's
// decision reaches it - so the state the pass writes travels like any other field.
Changes changelog.ChangeLog
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
Signals ReminderSignals
// BatchSize bounds one pass. A pass is one transaction, and a transaction that fired ten
// thousand reminders would hold its locks for as long as that took.
BatchSize int
}
FireReminders turns the reminders whose moment has come into notifications.
Internal, and deliberately absent from the catalogue in domain-model.md §5, for the reason ReconcileMedia is absent from it: the catalogue is what a person, an agent or a rule can ask for, and "fire everybody's reminders now" is not something anybody should be able to ask for - the way to influence when a reminder fires is the reminder.
It runs inside the transaction the job runner opened, which is the whole reliability argument: the guarded transition, the notification records, the delivery jobs and the job's own completion commit together. A process killed halfway leaves none of them and the reminders are still pending when the job is claimed again - nothing is lost and nothing is doubled (test RT-3).
func (FireReminders) Execute ¶
func (h FireReminders) Execute( ctx context.Context, actor appshared.ActorContext, ) (ReminderOutcome, error)
Execute fires what is due for the tenant the actor names.
type FlowGate ¶
type FlowGate struct {
Buckets repository.Buckets
Fields repository.CustomFields
}
FlowGate refuses an entry entering a column past the board's first without its required custom fields, where the collection's structure applies REQUIRED_BEYOND_FIRST_COLUMN (UC-WRK-31 check 2). Every writer that places an entry in a column asks it - an edit of `bucket_id`, a move, a creation - so REST, MCP, an automation action and a device's push meet the same refusal, from the one place that decides it.
Asked inside the writer's transaction with the entry as it would be written, values included, so a creation that carries its fields passes and one that does not is refused whole.
type GetContainer ¶
type GetContainer struct {
Containers repository.Containers
Authorizer Authorizer
UnitOfWork persistence.UnitOfWork
// Structure resolves the container's structure in force for the answer (domain-model.md §2).
Structure StructureResolver
// Figures counts what counts in a collection, after the structure (UC-WRK-29).
Figures FigureReader
// Milestones answers when the container was last finished (UC-WRK-29 check 4). A build
// without it answers none.
Milestones repository.Milestones
}
GetContainer reads one hub or collection.
Read-only throughout, which is not a detail: the transaction may be served by a read replica (multi-tenancy.md §7), and a read that opened a write transaction would pin every list in the product to the primary.
func (GetContainer) Descriptor ¶
func (h GetContainer) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (GetContainer) Execute ¶
func (h GetContainer) Execute( ctx context.Context, actor appshared.ActorContext, query GetContainerQuery, ) (domain.Container, error)
Execute returns the container.
type GetContainerQuery ¶
GetContainerQuery is the input, typed.
type GetRecurrence ¶
type GetRecurrence struct {
Recurrences repository.Recurrences
Items repository.Items
Containers repository.Containers
Authorizer Authorizer
UnitOfWork persistence.UnitOfWork
}
GetRecurrence reads an entry's series.
The right to read the entry is the right to read what it repeats by, for the reason the history draws the same line: a separate right would be one more thing to get wrong for no protection gained (domain-model.md §3.2).
func (GetRecurrence) Descriptor ¶
func (h GetRecurrence) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (GetRecurrence) Execute ¶
func (h GetRecurrence) Execute( ctx context.Context, actor appshared.ActorContext, itemID shared.ID, ) (domain.RecurrenceRule, error)
Execute returns the entry's series, or reports that it has none.
type GetSavedView ¶
type GetSavedView struct {
Views viewrepo.SavedViews
Containers repository.Containers
Permits Permitting
UnitOfWork persistence.UnitOfWork
}
GetSavedView answers one view.
func (GetSavedView) Descriptor ¶
func (h GetSavedView) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (GetSavedView) Execute ¶
func (h GetSavedView) Execute( ctx context.Context, actor appshared.ActorContext, id shared.ID, ) (view.SavedView, error)
Execute returns the view, or that there is none.
A view the caller may not see answers exactly what a missing one answers (T-04): another account's private bookmark, and a shared view over a scope the caller holds nothing on, are both worlds this caller cannot distinguish from empty ones.
type GetTemplate ¶
type GetTemplate struct {
Writer TemplateWriter
}
GetTemplate reads one template.
func (GetTemplate) Descriptor ¶
func (h GetTemplate) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
type GetWorkItem ¶
type GetWorkItem struct {
Items repository.Items
ItemLabels repository.ItemLabels
Containers repository.Containers
Authorizer Authorizer
UnitOfWork persistence.UnitOfWork
// Gate marks what the entry's collection hides of it.
Gate EntryGate
}
GetWorkItem reads one task, work package or activity.
func (GetWorkItem) Descriptor ¶
func (h GetWorkItem) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (GetWorkItem) Execute ¶
func (h GetWorkItem) Execute( ctx context.Context, actor appshared.ActorContext, query GetWorkItemQuery, ) (domain.WorkItem, error)
Execute returns the item.
Two reads before the permission question, not one: the item names its collection, and the collection names the hub. Both are needed because a membership at either applies downwards, and a path that stopped at the collection would refuse somebody who holds the right at the hub (domain-model.md §3.2). They are one round trip apart and inside the same read-only transaction.
type GetWorkItemQuery ¶
type GetWorkItemQuery struct {
ItemID shared.ID
// CalendarUID names the entry by the address a calendar client knows it by instead of by its
// identifier (offline-sync.md §4.2) - the tree's question when a PUT arrives at an address the
// view does not answer. Read when ItemID is empty; the permission is decided the same way,
// because the entry found is the same entry.
CalendarUID string
// ExpandLabels asks for the labels the entry carries.
//
// Asked for rather than always included, which is what `expand` is for (api-guidelines.md §4):
// the labels are a second query, and a client rendering a plain list should not pay for chips
// it is not drawing. Absent rather than empty when it was not asked for - so that a client can
// tell "this entry has no labels" from "I did not ask".
ExpandLabels bool
// ForCalendar answers the entry as a calendar shows it: without a due date its collection
// hides (ExportView.shown), so that a client's write that leaves DUE out clears nothing.
ForCalendar bool
}
GetWorkItemQuery is the input, typed.
type GiveKudos ¶
type GiveKudos struct{ Desk KudosDesk }
GiveKudos acknowledges an entry's completion to whoever completed it.
func (GiveKudos) Descriptor ¶
func (h GiveKudos) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
type InstantiateTemplate ¶
type InstantiateTemplate struct {
Quota ItemQuota
Writer TemplateWriter
Items repository.Items
ItemMembers repository.ItemMembers
// Visibility answers whether a node's fixed assignee can see the collection the tree lands in.
// The same question an assignment asks, asked here because a template written last year
// may name somebody who has left.
Visibility Visibility
// Restrictions answers whether a rule is instantiating for a node's restricted assignee: the
// entry is made unassigned and the drop reported (data-protection.md §4). Nil restricts nobody.
Restrictions RestrictedAccounts
Events outbox.Events
Activity ActivityJournal
}
InstantiateTemplate stamps a template out into a collection.
A sibling of DuplicateWorkItem rather than the same machinery: there is no source tree to read - the shape is a document - but everything else is the same shape of problem, and the answers are the ones the copy already gave. Every entry carries its own records, because a client synchronising the collection has to learn about every row; the references the destination cannot carry are reported rather than dropped silently (I-W6); and the whole act is one audit entry and one announcement.
func (InstantiateTemplate) Descriptor ¶
func (h InstantiateTemplate) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (InstantiateTemplate) Execute ¶
func (h InstantiateTemplate) Execute( ctx context.Context, actor appshared.ActorContext, cmd InstantiateTemplateCommand, ) (InstantiationResult, error)
Execute stamps the template out and returns the tree's root.
type InstantiateTemplateCommand ¶
type InstantiateTemplateCommand struct {
TemplateID shared.ID
CollectionID shared.ID
ParentID shared.ID
// Anchor is the day the relative dates count from, and the zero time for "now". A day rather
// than an instant: "+3 days for a project starting Monday" is about days, and it is read in
// the caller's own zone.
Anchor time.Time
Title string
}
InstantiateTemplateCommand is the input, typed.
type InstantiationResult ¶
type InstantiationResult struct {
TemplateID shared.ID
Root domain.WorkItem
// Created is the size of the tree, the root included.
Created int
// DroppedReferences is what the destination could not carry, from every entry that lost
// something. Empty rather than nil: a client that iterates the losses should not have to
// nil-check the field.
DroppedReferences []domain.DroppedReference
}
InstantiationResult is what an instantiation answers with.
type ItemAttachmentSet ¶
ItemAttachmentSet is what an entry carries, as every channel reports it.
type ItemAttachmentWriter ¶
type ItemAttachmentWriter struct {
Items repository.Items
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
// Gate is what the entry's collection offers it: the refusal before a write of what it
// hides, and the mark on the answer (domain-model.md §2).
Gate EntryGate
Attachments mediarepo.Attachments
Media MediaReferences
Authorizer Authorizer
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
Activity ActivityJournal
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
}
ItemAttachmentWriter is what both directions share.
The tokens are the entry's rather than the media context's, for the reason the label pair's are the entry's: attaching a file to an entry is work on that entry, and somebody who may edit it may attach to it. Staging the file needed `media:write`; putting it on an entry needs the right to change the entry (security.md §5, domain-model.md §3.2).
type ItemLabelSet ¶
ItemLabelSet is the labels an entry carries, as every channel reports them.
type ItemLabelWriter ¶
type ItemLabelWriter struct {
Items repository.Items
ItemLabels repository.ItemLabels
Labels repository.Labels
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
// Gate is what the entry's collection offers it: the refusal before a write of what it
// hides, and the mark on the answer (domain-model.md §2).
Gate EntryGate
Authorizer Authorizer
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
Activity ActivityJournal
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
}
ItemLabelWriter is what both use cases share.
The tokens are the item's rather than the container's, unlike the label itself: defining a collection's vocabulary is structure, and tagging an entry with a word from it is work. Somebody who may edit entries may label them, and somebody who may not must not be able to reach around through this endpoint (security.md §5, domain-model.md §3.2).
type ItemMemberSet ¶
ItemMemberSet is the members an entry carries, as every channel reports them.
type ItemMemberWriter ¶
type ItemMemberWriter struct {
Items repository.Items
ItemMembers repository.ItemMembers
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
// Gate is what the entry's collection offers it: the refusal before a write of what it
// hides, and the mark on the answer (domain-model.md §2).
Gate EntryGate
Authorizer Authorizer
Visibility Visibility
// Accounts answers whether a rule is about to put a restricted person on the entry: no rule
// does (data-protection.md §4). Nil restricts nobody.
Accounts RestrictedAccounts
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
Activity ActivityJournal
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
}
ItemMemberWriter is what both use cases share.
The same shape ItemLabelWriter has, because the member list is the same kind of thing as the label set: an OR-set beside the entry rather than a field on it. What differs is the question asked before the write - a label has to belong to the entry's collection, an account has to be able to see the entry - and the capability that gates it.
type ItemQuota ¶
CreateWorkItem creates a task, a work package, or an activity.
It is the second walking skeleton, and it owes what CreateContainer owes: the permission check before the transaction, the invariants in the domain, and inside one transaction the row, the event, the change log entry for offline clients and the audit entry.
What is new here is that the rules are data. Which type may sit under which, and how deep, come from the capability profiles through the hierarchy service (ADR-0006) - so this file, like that one, names none of the three levels. ItemQuota is the §4 items ceiling, asked before an entry is created. The slice the work package needs of quota.Guard.
type KeptOutReader ¶
type KeptOutReader interface {
Read(ctx context.Context, tenantID shared.ID) (integration.KeptOut, error)
}
KeptOutReader answers whose content stays out of AI, inside the caller's transaction. Declared here because the application layer may not import a sibling service.
type KeptOwnValue ¶
type KeptOwnValue struct {
Container domain.Container
Settings []domain.StructureSetting
}
KeptOwnValue is a collection that follows a hub and keeps its own value of these settings.
type KudosDesk ¶
type KudosDesk struct {
Kudos repository.Kudos
Items repository.Items
Containers repository.Containers
Authorizer Authorizer
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
}
KudosDesk is what the three kudos use cases share (UC-WRK-29 checks 5 and 6).
The right to give kudos is the right to read the entry: kudos change nothing of it - not its version, not its history - so a viewer and a guest on a shared entry may acknowledge a colleague as a member may. Nothing is announced: no event (a webhook or a rule would carry who acknowledged whom out of the product) and no change record.
The trail gets a refusal and nothing else (audit.md §7): a refused give still leaves a trace of misuse, a successful one none an auditor could count.
type LabelCommand ¶
LabelCommand is the input of both directions, typed.
No expected version. A label lives beside the entry rather than on its row, so there is no version of the entry that an addition would be racing - which is also why neither operation spends one. Two devices adding two different labels at once is the case the OR-set exists to serve, and an optimistic lock on the entry would make one of them fail for no reason (offline-sync.md §4.2).
type LabelWriter ¶
type LabelWriter struct {
Labels repository.Labels
Containers repository.Containers
Authorizer Authorizer
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
// Text brings the names people type to normal form C on the way in (i18n-l10n.md §5).
Text text.Normalizer
}
LabelWriter is what both use cases that change an existing label share.
One dependency set rather than two: they read the same label, ask the same permission question of the same collection, and owe the same four writes. What differs is which domain method decides the new state and what the change log calls it.
type LastMember ¶
type LastMember struct {
Hubs repository.OrphanedHubs
Writer ContainerWriter
// Owners tells the workspace's owners. Optional: wired without it the hub still goes to the
// trash, which is the half nobody could do by hand.
Owners OwnersNotice
}
LastMember moves a private hub into the trash once nobody is left in it (ADR-0073 §5).
Nobody else may open a private hub, so one without a member is a hub nobody can reach - not the workspace's owners, who hold nothing in it. Trashed rather than purged: the ordinary grace runs, and the workspace's owners are told that one went, without its name. Taking it over is the emergency access; letting it go is the purge.
Asked by whatever ends a membership - a revocation, an erasure - in that act's own transaction, and by the retention pass for whatever a path missed. A member is an account, neither anonymised nor deleted, holding a role on the hub, on a collection in it or on an entry in one.
func (LastMember) AfterMemberLeft ¶
func (l LastMember) AfterMemberLeft( ctx context.Context, tenantID shared.ID, named []shared.ID, ) (int, error)
AfterMemberLeft trashes those of the hubs the named hubs, collections or entries sit under that have no member left, inside the caller's transaction, and answers how many went. Nothing named asks about nothing.
type LifecycleCommand ¶
type LifecycleCommand struct {
ItemID shared.ID
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read none
// and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
LifecycleCommand is the input every lifecycle verb takes: which entry, and the version the caller read. The same shape for all of them, because they all take exactly that.
type LifecycleWriter ¶
type LifecycleWriter struct {
Items repository.Items
Containers repository.Containers
Authorizer Authorizer
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
Activity ActivityJournal
UnitOfWork persistence.UnitOfWork
// Gate marks the answer with what the entry's collection hides; it gates nothing here, since
// the lifecycle is not a write of what a collection offers.
Gate EntryGate
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
// Queue is where a deletion asks for the tenant's cleanup to be scheduled.
//
// A deletion scheduling its own cleanup is not only convenient, it is the only shape available:
// nothing in this system may enumerate tenants - the `tenant` table is behind row level security
// with no bypass for the application role - so no scheduler can create one sweep job per tenant.
// The job reschedules itself forever afterwards (queue.KindRetentionSweep); provisioning and
// every session opened seed it too, so a deletion is not the only way a workspace gets one.
Queue queue.Queue
}
LifecycleWriter is what every verb that moves an entry between the archive and the trash needs.
One dependency set held by all of them rather than a copy per verb. They are the same walk with a different transition in the middle - read the entry and the collection it is in, ask whether the actor may, apply the transition, write it, and record the three things a change owes - and keeping the machinery in one place is what makes "restoring records what trashing records" true by construction rather than by somebody remembering.
type ListActivity ¶
type ListActivity struct {
History activityrepo.History
Items repository.Items
Containers repository.Containers
Authorizer Authorizer
UnitOfWork persistence.UnitOfWork
}
ListActivity reads one entry's history, newest first.
The right to read the entry is the right to read what happened to it. There is no narrower permission and there should not be: the history says who moved a thing somebody can already see, and a separate right would be one more thing to get wrong for no protection gained (domain-model.md §3.2).
Read-only throughout, which is not a detail: the transaction may be served by a read replica (multi-tenancy.md §7), and a read that opened a write transaction would pin it to the primary.
func (ListActivity) Descriptor ¶
func (h ListActivity) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListActivity) Execute ¶
func (h ListActivity) Execute( ctx context.Context, actor appshared.ActorContext, query ListActivityQuery, ) (activityrepo.EntryPage, error)
Execute returns one page of the entry's history.
Two transactions, and the permission question between them. It has to be between them: a refusal writes an audit entry, and an entry written inside a read-only transaction cannot be written at all - which would turn "you may not read this" into "the database is unavailable" (audit.md §7).
type ListActivityQuery ¶
ListActivityQuery is the input, typed.
type ListBuckets ¶
type ListBuckets struct {
Buckets repository.Buckets
Containers repository.Containers
Authorizer Authorizer
UnitOfWork persistence.UnitOfWork
}
ListBuckets reads a collection's board.
Read-only throughout, which is not a detail: the transaction may be served by a read replica (multi-tenancy.md §7), and a read that opened a write transaction would pin every board in the product to the primary.
func (ListBuckets) Descriptor ¶
func (h ListBuckets) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListBuckets) Execute ¶
func (h ListBuckets) Execute( ctx context.Context, actor appshared.ActorContext, query ListBucketsQuery, ) ([]domain.Bucket, error)
Execute returns the collection's board, left to right.
Not paged, unlike the container and item lists: a board has as many columns as fit on a screen, and the contract returns a plain array (api-guidelines.md §2). A collection that grows thousands of them has a different problem than pagination solves.
type ListBucketsQuery ¶
ListBucketsQuery is the input, typed.
type ListCalendarFeeds ¶
type ListCalendarFeeds struct{ Writer CalendarFeedWriter }
ListCalendarFeeds answers the caller's own feeds.
func (ListCalendarFeeds) Descriptor ¶
func (h ListCalendarFeeds) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListCalendarFeeds) Execute ¶
func (h ListCalendarFeeds) Execute( ctx context.Context, actor appshared.ActorContext, ) ([]domain.CalendarFeed, error)
Execute reads them. Never anybody else's, whatever the role: a feed is a credential its owner holds, and an administrator who could list one could subscribe to it.
type ListComments ¶
type ListComments struct {
Comments repository.Comments
Items repository.Items
Containers repository.Containers
Authorizer Authorizer
UnitOfWork persistence.UnitOfWork
}
ListComments reads one entry's discussion, oldest first - a conversation reads top down.
The right to read the entry is the right to read what was said about it, for the reason the history draws the same line: a separate right would be one more thing to get wrong for no protection gained (domain-model.md §3.2). Tombstones are in the page - that is what keeps a thread with deletions readable - and they travel without their body, which is already gone from the row rather than hidden by this reader.
func (ListComments) Descriptor ¶
func (h ListComments) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListComments) Execute ¶
func (h ListComments) Execute( ctx context.Context, actor appshared.ActorContext, query ListCommentsQuery, ) (repository.CommentPage, error)
Execute returns one page of the entry's discussion.
Two transactions, and the permission question between them, for the reason ListActivity splits there: a refusal writes an audit entry, and an entry written inside a read-only transaction cannot be written at all (audit.md §7).
type ListCommentsQuery ¶
ListCommentsQuery is the input, typed.
type ListContainers ¶
type ListContainers struct {
Containers repository.Containers
Authorizer Authorizer
Reader Reader
UnitOfWork persistence.UnitOfWork
// Structure resolves each container's structure in force, after the page was narrowed to what
// the caller may see.
Structure StructureResolver
// Figures counts what counts in each collection of the page, one statement per kind on it.
Figures FigureReader
// Milestones answers when each container of the page was last finished, in one statement.
Milestones repository.Milestones
}
ListContainers reads one level of the container tree.
func (ListContainers) Descriptor ¶
func (h ListContainers) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListContainers) Execute ¶
func (h ListContainers) Execute( ctx context.Context, actor appshared.ActorContext, query ListContainersQuery, ) (repository.ContainerPage, error)
Execute returns one page of the level.
The permission check has two shapes, because the two levels are different questions.
A level under a named hub is a question about that hub: the client named it, so a refusal is a refusal, and one check covers every row in it - a membership at the hub applies to all its collections. Answering an empty page instead would tell a client that has no access the same thing as one whose hub is empty.
The hub level is anchored to nothing, so there is no single path to check. Checking the tenant scope would refuse everybody whose membership sits on a hub rather than on the workspace, which is most of what hub-scoped memberships are for. So the page is read and then narrowed to the hubs the actor may see: "what can I see" is answered with what they can see, not with a 403.
type ListContainersQuery ¶
type ListContainersQuery struct {
// ParentID is the hub whose collections are wanted. Empty means the hubs, which is the one level
// anchored to nothing.
ParentID shared.ID
// Type narrows the level; empty means both kinds.
Type domain.ContainerType
IncludeArchived bool
Cursor string
Size int
}
ListContainersQuery is the input, typed.
type ListCustomFields ¶
type ListCustomFields struct {
Fields repository.CustomFields
Containers repository.Containers
Authorizer Authorizer
UnitOfWork persistence.UnitOfWork
}
ListCustomFields reads the definitions in force.
func (ListCustomFields) Descriptor ¶
func (h ListCustomFields) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListCustomFields) Execute ¶
func (h ListCustomFields) Execute( ctx context.Context, actor appshared.ActorContext, query ListCustomFieldsQuery, ) ([]domain.CustomFieldDefinition, error)
Execute returns the definitions.
Unpaged, deliberately. A workspace's vocabulary is small and bounded by what a person can fill in on one form; a client renders the whole of it or none of it, and a cursor over a list nobody scrolls would be machinery for its own sake.
type ListCustomFieldsQuery ¶
type ListCustomFieldsQuery struct {
// CollectionID is the collection whose definitions are wanted, together with the
// workspace-wide ones above it. Zero answers the workspace-wide ones alone.
CollectionID shared.ID
}
ListCustomFieldsQuery is the input, typed.
type ListEmergencyNotices ¶
type ListEmergencyNotices struct {
Accesses repository.EmergencyAccesses
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
}
ListEmergencyNotices answers the person's own notices of the last 30 days: each time the workspace's owner opened a private hub they are a member of, from the record written when it was opened, whatever the mail about it did. It is how a person without a mailbox - a child's managed account - or one whose mail failed is told (ADR-0073 §4, identity.md §22). Not a list of notifications: one kind of notice, and nothing to mark read.
func (ListEmergencyNotices) Descriptor ¶
func (h ListEmergencyNotices) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListEmergencyNotices) Execute ¶
func (h ListEmergencyNotices) Execute( ctx context.Context, actor appshared.ActorContext, ) ([]domain.EmergencyAccess, error)
Execute lists them.
type ListJumbleDestinations ¶
type ListJumbleDestinations struct {
Collections repository.Collections
Authorizer Authorizer
Reader Reader
UnitOfWork persistence.UnitOfWork
}
ListJumbleDestinations answers where an arrival may become work for the person asking: the collections that are neither trashed nor archived and where creating an entry is permitted to them, private hubs they are in included, in the order of the tree.
It asks what converting asks first - WRITE_ITEMS at the workspace (domain-model.md §5) - so that a person who could not convert anything is refused rather than handed an empty list. Each collection is then decided as a create there would be, through the authoriser's filter: a collection left out is nobody's refusal, so nothing is recorded per row (audit.md §4). A page can therefore be shorter than asked; the cursor is the scanned boundary, and a client pages on until there is no next page (ListContainers' reasoning).
func (ListJumbleDestinations) Descriptor ¶
func (h ListJumbleDestinations) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListJumbleDestinations) Execute ¶
func (h ListJumbleDestinations) Execute( ctx context.Context, actor appshared.ActorContext, page repository.Page, ) (repository.CollectionPage, error)
Execute answers one page.
type ListLabels ¶
type ListLabels struct {
Labels repository.Labels
Containers repository.Containers
Authorizer Authorizer
UnitOfWork persistence.UnitOfWork
}
ListLabels reads a collection's vocabulary.
Read-only throughout: the transaction may be served by a read replica (multi-tenancy.md §7).
func (ListLabels) Descriptor ¶
func (h ListLabels) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListLabels) Execute ¶
func (h ListLabels) Execute( ctx context.Context, actor appshared.ActorContext, query ListLabelsQuery, ) ([]domain.Label, error)
Execute returns the collection's labels, by name.
Not paged, for the reason a board is not: a vocabulary people have to agree on is as long as a person can hold in their head, and the contract returns a plain array (api-guidelines.md §2).
type ListLabelsQuery ¶
type ListLabelsQuery struct {
CollectionID shared.ID
// IncludeDeleted adds the labels taken out of the vocabulary, for a reader that offers to bring
// one back. The same permission: a deleted label is the same vocabulary, out of use.
IncludeDeleted bool
}
ListLabelsQuery is the input, typed.
type ListPresets ¶
type ListPresets struct {
Profiles metarepo.CapabilityProfiles
// Presets reads the saved ones; a build without it answers Hubtask's four alone.
Presets repository.Presets
// Installation reads the installation's; a build without it answers none.
Installation repository.InstallationPresets
UnitOfWork persistence.UnitOfWork
}
ListPresets answers the presets a person can start from, as this installation offers them: Hubtask's four, the installation's, the workspace's and the person's own (UC-WRK-30 check 2), each with its origin.
Open to anybody signed in to the workspace, like GetAccount: Hubtask's four are held to the installation's capability profiles, which /meta/capabilities answers to anybody at all, and the workspace's are offered to everyone in it. A person's own are answered to them alone. What a credential still bounds is the token scope.
func (ListPresets) Descriptor ¶
func (h ListPresets) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListPresets) Execute ¶
func (h ListPresets) Execute(ctx context.Context, actor appshared.ActorContext) ([]domain.Preset, error)
Execute answers Hubtask's four in a stable order, then the installation's, the workspace's and the person's own, each by name.
type ListPrivateHubAccesses ¶
type ListPrivateHubAccesses struct {
Accesses repository.EmergencyAccesses
Authorizer Authorizer
UnitOfWork persistence.UnitOfWork
}
ListPrivateHubAccesses answers every emergency access to a hub, newest first - the members' record of who opened their hub, when and why (ADR-0073 §4). Whoever reads the hub reads it: its members, and the owner while their access runs; for everybody else the hub is not there.
func (ListPrivateHubAccesses) Descriptor ¶
func (h ListPrivateHubAccesses) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListPrivateHubAccesses) Execute ¶
func (h ListPrivateHubAccesses) Execute( ctx context.Context, actor appshared.ActorContext, hubID shared.ID, ) ([]domain.EmergencyAccess, error)
Execute lists them.
type ListPrivateHubs ¶
type ListPrivateHubs struct {
Hubs repository.PrivateHubs
Policies lifecyclerepo.Policies
Authorizer Authorizer
UnitOfWork persistence.UnitOfWork
// Accesses, Owners and Clock answer each row's emergency access for the caller (ADR-0073
// §4): whether they may open the hub now, and until when theirs runs. Without them every row
// says it is not offered - the safe answer for a control that must not be drawn where the
// server would refuse it (P-05).
Accesses repository.EmergencyAccesses
Owners Permitting
Clock clock.Clock
}
ListPrivateHubs answers the administrators what ADR-0073 §3 lets them know of the workspace's private hubs: that they exist, whose they are, how much they hold, and when a trashed one goes - no name, no description, no content.
MANAGE_MEMBERS rather than READ_CONFIGURATION: the list is for running the workspace - a person leaving, the storage they leave behind - which is membership management, and an auditor reads how the workspace is configured, not the people's hubs.
func (ListPrivateHubs) Descriptor ¶
func (h ListPrivateHubs) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListPrivateHubs) Execute ¶
func (h ListPrivateHubs) Execute(ctx context.Context, actor appshared.ActorContext) ([]PrivateHubRow, error)
Execute lists them.
type ListReminders ¶
type ListReminders struct {
Reminders repository.Reminders
Items repository.Items
Containers repository.Containers
Authorizer Authorizer
UnitOfWork persistence.UnitOfWork
}
ListReminders reads one entry's reminders, oldest first.
The right to read the entry is the right to read what it reminds about, for the reason the discussion draws the same line: a separate right would be one more thing to get wrong for no protection gained (domain-model.md §3.2). Unpaged, because what one entry may carry is bounded where reminders are written.
func (ListReminders) Descriptor ¶
func (h ListReminders) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListReminders) Execute ¶
func (h ListReminders) Execute( ctx context.Context, actor appshared.ActorContext, query ListRemindersQuery, ) ([]domain.Reminder, error)
Execute returns the entry's reminders.
Two transactions, and the permission question between them, for the reason ListComments splits there: a refusal writes an audit entry, and an entry written inside a read-only transaction cannot be written at all (audit.md §7).
type ListRemindersQuery ¶
ListRemindersQuery is the input, typed.
type ListSavedViews ¶
type ListSavedViews struct {
Views viewrepo.SavedViews
Containers repository.Containers
Authorizer Authorizer
UnitOfWork persistence.UnitOfWork
}
ListSavedViews answers the views the caller may see.
func (ListSavedViews) Descriptor ¶
func (h ListSavedViews) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListSavedViews) Execute ¶
func (h ListSavedViews) Execute( ctx context.Context, actor appshared.ActorContext, query ListSavedViewsQuery, ) ([]view.SavedView, error)
Execute returns the views.
Unpaged, deliberately, the way the custom field list is: a shelf of bookmarks is small and bounded by what a person saves, and a cursor over a list nobody scrolls would be machinery for its own sake. What is shared along the path is computed in the query, never filtered after a page (domain-model.md §3.2).
type ListSavedViewsQuery ¶
type ListSavedViewsQuery struct {
// ContainerID widens the answer to what is shared along that container's path. Zero answers
// the caller's own views alone.
ContainerID shared.ID
}
ListSavedViewsQuery is the input, typed.
type ListTemplates ¶
type ListTemplates struct {
Templates repository.Templates
Containers repository.Containers
Authorizer Authorizer
UnitOfWork persistence.UnitOfWork
}
ListTemplates reads the templates reachable from a container's path.
func (ListTemplates) Descriptor ¶
func (h ListTemplates) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListTemplates) Execute ¶
func (h ListTemplates) Execute( ctx context.Context, actor appshared.ActorContext, query ListTemplatesQuery, ) (repository.TemplatePage, error)
Execute returns one page of what the caller may use.
type ListTemplatesQuery ¶
ListTemplatesQuery is the input, typed.
type ListTrash ¶
type ListTrash struct {
Trash repository.Trash
Reader Reader
UnitOfWork persistence.UnitOfWork
// Policies and TombstoneWindow are the two numbers the retention pass removes by - the trash
// period it reads and the installation's offline window the Purger is given - so that the date
// each entry carries is the pass's own.
Policies lifecyclerepo.Policies
TombstoneWindow time.Duration
// Holds and Contributors are what says whether a legal hold keeps a deletion: its rows are
// judged as the retention pass judges them (lifecycle.Holds.Blocking), so `held` is the pass's
// own answer rather than a second rule.
Holds lifecyclerepo.HoldList
Contributors lifecyclerepo.ContributorReader
// Restrictions is whose processing is restricted: the automatic pass keeps what such a person
// contributed as it keeps a held account's (data-retention.md §4 item 2), so the listing says so.
Restrictions privacyrepo.Restrictions
}
ListTrash reads what is in the trash, newest deletion first.
One entry per deletion rather than per deleted row. A hub with two hundred entries under it went in as one act and comes back as one act (I-C2), so what is listed is the root of each deletion - the thing somebody deleted - and the batch beside it is what took the rest.
Read-only throughout, which is not a detail: the transaction may be served by a read replica (multi-tenancy.md §7), and a read that opened a write transaction would pin it to the primary.
func (ListTrash) Descriptor ¶
func (h ListTrash) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListTrash) Execute ¶
func (h ListTrash) Execute( ctx context.Context, actor appshared.ActorContext, query ListTrashQuery, ) (TrashListing, error)
Execute returns one page of the trash.
The permission check has the shape the hub level has, and for the same reason: the trash is anchored to nothing. It spans hubs, so there is no single path to check - and checking the tenant scope would refuse everybody whose membership sits on a hub rather than on the workspace, which is most of what hub-scoped memberships are for. So the page is read and then narrowed to what the actor may see: "what did I delete" is answered with what they may see, not with a 403.
type ListTrashQuery ¶
ListTrashQuery is the input, typed.
type ListWorkItems ¶
type ListWorkItems struct {
Items repository.Items
ItemLabels repository.ItemLabels
Containers repository.Containers
Authorizer Anchored
UnitOfWork persistence.UnitOfWork
// Gate marks what the collection hides of each entry.
Gate EntryGate
}
ListWorkItems reads one level of one collection.
func (ListWorkItems) Descriptor ¶
func (h ListWorkItems) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ListWorkItems) Execute ¶
func (h ListWorkItems) Execute( ctx context.Context, actor appshared.ActorContext, query ListWorkItemsQuery, ) (repository.ItemPage, error)
Execute returns one page of the level.
One permission check for the whole page, unlike the hub level of ListContainers. This list is anchored: the client named a collection, so there is a single path to ask about, and the answer covers every row in it - a membership at the collection, or at its hub, applies to everything inside. A refusal is therefore a refusal rather than an empty page, which is the honest answer when the client named the container it cannot read.
The one caller for whom that single answer is not the whole truth is somebody who holds no role on the collection and a membership on entries inside it. Their level is those entries, and the restriction goes into the query rather than filtering the page afterwards: filtered afterwards, a page would come back short and its cursor would skip.
type ListWorkItemsQuery ¶
type ListWorkItemsQuery struct {
// CollectionID is required. A list of every item in a tenant is an unindexed scan, and every
// filter beyond one level is the query DSL's.
CollectionID shared.ID
// ParentID is the item whose children are wanted. Empty means the items directly in the
// collection.
ParentID shared.ID
IncludeArchived bool
Cursor string
Size int
}
ListWorkItemsQuery is the input, typed.
type MaterializationOutcome ¶
type MaterializationOutcome struct {
// Created counts the occurrences that came into being, Considered the series the pass looked
// at - which is what tells the handler whether its batch filled.
Created int
Considered int
// NextAt is when the tenant next owes an occurrence, and nil when it owes none for now. It is
// the moment the horizon reaches the next occurrence, not the occurrence itself: a series with
// a ninety-day window owes tomorrow's entry ninety days before it is due.
NextAt *time.Time
}
MaterializationOutcome is what one pass did and what it leaves behind.
type MaterializeOccurrences ¶
type MaterializeOccurrences struct {
Recurrences repository.Recurrences
Items repository.Items
Containers repository.Containers
// Copy is DuplicateWorkItem, reused rather than rebuilt: an occurrence is a copy of the
// template with its subtree, and everything that makes a copy correct - the vocabulary of the
// destination, the references it cannot carry (I-W6), the records each entry owes - is already
// decided there.
Copy DuplicateWorkItem
Expander recurrenceport.Expander
Events outbox.Events
Clock clock.Clock
IDs clock.IDGenerator
Signals OccurrenceSignals
// RuleBatch bounds how many series one pass looks at, OccurrenceBatch how many entries one
// series may produce in it. A pass is one transaction, and a transaction that materialised a
// year of everything would hold its locks for as long as that took.
RuleBatch int
OccurrenceBatch int
}
MaterializeOccurrences turns the series of one tenant into the entries their rolling windows owe (arc42 §6.3).
Internal, and deliberately absent from the catalogue in domain-model.md §5 for the reason ReconcileMedia and FireReminders are absent from it: the catalogue is what a person, an agent or a rule can ask for, and "materialise everybody's series now" is not something anybody should be able to ask for. The way to influence what a series produces is the rule, and the way to skip one occurrence is SkipOccurrence.
It runs inside the transaction the job runner opened, which is what makes it exactly-once: the occurrences, their events, the watermark's compare-and-set and the job's own completion commit together. Two leaders that both wake up cannot mint the same morning twice - the second finds the watermark moved, fails the update, and rolls back the entries it wrote with it.
func (MaterializeOccurrences) Execute ¶
func (h MaterializeOccurrences) Execute( ctx context.Context, actor appshared.ActorContext, ) (MaterializationOutcome, error)
Execute materialises what the tenant's series owe.
type MediaReferences ¶
type MediaReferences interface {
Find(ctx context.Context, id shared.ID) (media.Object, error)
AdjustRefCount(ctx context.Context, id shared.ID, delta int) error
}
MediaReferences is the slice of the media record store this package needs: read one object, and move its reference counter.
Narrow rather than the whole port, for the reason Visibility is narrow: a use case declaring a dependency it does not use is a use case whose test has to satisfy it anyway. The counter is the only thing this package writes about a media object - what the object is, and whether it may be used at all, is the media domain's answer (media.Object.Attachable).
type MemberCommand ¶
MemberCommand is the input of both directions, typed.
No expected version, for the reason LabelCommand spends none: a member lives beside the entry rather than on its row, so there is no version of the entry an addition would be racing. Two devices adding two different people at once is the case the OR-set exists to serve, and an optimistic lock on the entry would make one of them fail for no reason (offline-sync.md §4.2).
type MintedFeed ¶
type MintedFeed struct {
Feed domain.CalendarFeed
Token domain.FeedToken
}
MintedFeed is what a minting answers: the feed as it will be listed, and the credential that will never be readable again.
type Moderation ¶
type Moderation interface {
RoleAlong(
ctx context.Context, actor appshared.ActorContext, path []identity.Scope,
) (identity.Role, bool, error)
}
Moderation is the slice of the authorisation service the comment changes need beyond Authorizer: the actor's own effective role, for the rule the permission matrix cannot express - only the author or an administrator may change a comment (domain-model.md §3.5).
type MoveContainer ¶
type MoveContainer struct {
Writer ContainerWriter
// Revocations tells the devices of whoever read the collection through the hub it left, and
// cannot read it where it now sits, that they lost it.
Revocations MoveRevoker
// Structure resolves what the collection inherits from the hub it moves into, which offline
// clients are told as its structure in force (offline-sync.md §4.2). A build without it records
// the placement alone.
Structure StructureResolver
}
MoveContainer moves a collection into another hub, and ranks it there.
A hub is refused: it sits in the tenant and in nothing else (I-C1), so there is no destination to name. Naming the hub a collection already sits in reorders it there, which is the same operation with the same event - a drag within a level and a drag between hubs are one gesture to a person.
Nothing under the collection is written. A container tree is two deep, so a collection has no containers below it whose placement would have to follow, and the items it holds reference their collection rather than a path through the hubs.
func (MoveContainer) Descriptor ¶
func (h MoveContainer) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (MoveContainer) Execute ¶
func (h MoveContainer) Execute( ctx context.Context, actor appshared.ActorContext, cmd MoveContainerCommand, ) (domain.Container, error)
Execute moves the collection and returns it as it now stands.
type MoveContainerCommand ¶
type MoveContainerCommand struct {
ContainerID shared.ID
// TargetParentID is the hub to move into. Required, and not nullable: there is no level above a
// hub for a collection to be moved to.
TargetParentID shared.ID
// BeforeContainerID is the sibling to land in front of at the destination. Empty appends.
BeforeContainerID shared.ID
ExpectedVersion int
}
MoveContainerCommand is the input, typed.
type MoveResult ¶
type MoveResult struct {
Item domain.WorkItem
// SubtreeSize is how many rows moved, the item included. The event reports it so that a client knows how
// much of its own copy to rewrite.
SubtreeSize int
// DroppedReferences is what the destination collection could not resolve: the labels of the
// vocabulary the subtree left, and the columns of the board it was on (I-W6). Empty rather than
// nil, and empty for every move within one collection - nothing there has to be resolved again.
//
// The members and the custom fields join it with the use cases that own them.
DroppedReferences []domain.DroppedReference
}
MoveResult is what a move answers with: the item, and the references a change of collection could not carry over (I-W6).
type MoveRevoker ¶
type MoveRevoker interface {
AfterContainerMoved(ctx context.Context, from, to domain.Container) (access.Loss, error)
Announce(ctx context.Context, tenantID shared.ID, losses ...access.Loss) error
}
MoveRevoker is the slice of access.Revocations a move needs: who may have lost the collection, and the ACCESS_REVOKED record for each of them who did.
type MoveWorkItem ¶
type MoveWorkItem struct {
Placement PlacementWriter
}
MoveWorkItem moves an item, and its subtree with it, to a new parent or a new collection.
func (MoveWorkItem) Descriptor ¶
func (h MoveWorkItem) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4) - and a move is the action a kanban rule performs, so the automation door is not a nicety here.
func (MoveWorkItem) Execute ¶
func (h MoveWorkItem) Execute( ctx context.Context, actor appshared.ActorContext, cmd MoveWorkItemCommand, ) (MoveResult, error)
Execute moves the item and returns it.
type MoveWorkItemCommand ¶
type MoveWorkItemCommand struct {
ItemID shared.ID
// TargetParentID is the item to move under. Meaningful only together with ParentGiven: the zero value is
// both "the top level" and "not asked for", and those are different requests.
TargetParentID shared.ID
// ParentGiven says the caller named a parent, null included. Without it, a request that omitted the field
// entirely would be read as "move to the top level" - which is how an item ends up somewhere nobody asked
// for.
ParentGiven bool
// TargetCollectionID may be omitted when a parent is given: an item's collection is the one its parent is
// in, and making a client repeat it is making it possible to contradict.
TargetCollectionID shared.ID
// BeforeItemID is the sibling to land in front of at the destination. Empty appends.
BeforeItemID shared.ID
// OrderKey is the rank a device computed itself, between the neighbours it holds, in place of
// naming one (offline-sync.md §4.2: the position is a key between neighbours, not an integer).
// Empty means the server computes the key from BeforeItemID. Both is a contradiction.
OrderKey string
// TargetBucketID is the column of the destination's board to land in, meaningful only together
// with BucketGiven: the zero value is both "no column" and "not asked for".
TargetBucketID shared.ID
BucketGiven bool
ExpectedVersion int
}
MoveWorkItemCommand is the input, typed.
type Narrowing ¶
type Narrowing string
Narrowing is what a structure change does with the entries it hides by their level.
const ( // NarrowingHide keeps them where they are, stored and marked hidden: the default, undone by // switching back (domain-model.md §2, "Narrowing hides, it never deletes"). NarrowingHide Narrowing = "HIDE" // NarrowingFlatten moves them to the top level (UC-WRK-24 check 6); switching back does not // undo it. NarrowingFlatten Narrowing = "FLATTEN" )
type OccurrenceSignals ¶
type OccurrenceSignals interface {
OccurrenceMaterialized(ctx context.Context, mode string, lagSeconds float64)
}
OccurrenceSignals is the slice of the metrics adapter the materialisation reports through (observability-reliability.md §3.2, ADR-0008's promised occurrence lag).
One measurement: how late an occurrence was created against the moment it is for. A series whose occurrences appear the morning they are due is working; one whose lag grows is a scheduler that has stopped, and the entries a person expects to find are not there.
type OpenPrivateHub ¶
type OpenPrivateHub struct {
Accesses repository.EmergencyAccesses
Hubs identityrepository.HubLocks
Notices EmergencyNotices
Authorizer Authorizer
// StepUp judges the fresh proof. Nil refuses rather than letting the act through unproven.
StepUp stepup.Verifier
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
}
OpenPrivateHub is the workspace owner's emergency access to a private hub (ADR-0073 §4): a fresh proof, a stated reason, one hour, not renewable - and every member of the hub told in the same transaction that opens it, so that nobody reads the hub before its members are told.
Only the workspace's owner - a person, never a service account or a rule - and only for a private hub they hold nothing in: a shared hub is theirs to read already, and one they hold a role in is open to them. A second access while one runs is refused, naming its end; one after it ended is a new act, told again. What it opens, and for whom, is the authoriser's (identity.md §22).
func (OpenPrivateHub) Descriptor ¶
func (h OpenPrivateHub) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (OpenPrivateHub) Execute ¶
func (h OpenPrivateHub) Execute( ctx context.Context, actor appshared.ActorContext, cmd OpenPrivateHubCommand, ) (domain.EmergencyAccess, error)
Execute opens the hub and answers the access.
type OpenPrivateHubCommand ¶
OpenPrivateHubCommand is the input, typed.
type OwnHubs ¶
type OwnHubs interface {
AuthorizeOwnPrivateHub(ctx context.Context, actor appshared.ActorContext, request access.Request) error
}
OwnHubs is the authorisation question a private hub of one's own asks instead of STRUCTURE (ADR-0073 §2).
type OwnersNotice ¶
type OwnersNotice interface {
PrivateHubTrashed(ctx context.Context, tenantID shared.ID, recipients []shared.ID) error
}
OwnersNotice tells the workspace's owners that a private hub went to the trash, naming nothing - the notification context's recorder.
type Ownership ¶
type Ownership interface {
WritesOnlyWhatIsAssigned(
ctx context.Context, actor appshared.ActorContext, path []identity.Scope,
) (bool, error)
}
Ownership is the slice of the authorisation service the create path needs beyond Authorizer: whether the role the actor holds writes only what is assigned to them.
Its own interface rather than a method on Authorizer, for the reason that split Visibility off: a use case declaring a dependency it does not use is a use case whose test has to satisfy it anyway, and creation is the only write that has to know this.
type Permitting ¶
type Permitting interface {
Permits(ctx context.Context, actor appshared.ActorContext, request access.Request) (bool, error)
}
Permitting is the slice of the authorisation service the view reads need beyond Authorizer: the same question, answered without an audited refusal - a view the actor may not see is not found, and a DENIED entry for every invisible bookmark would make the trail unreadable (T-04).
type PlacementWriter ¶
type PlacementWriter struct {
Items repository.Items
Buckets repository.Buckets
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
// Gate is what the entry's collection offers it: the refusal before a write of what it
// hides, and the mark on the answer (domain-model.md §2).
Gate EntryGate
// Flow refuses an entry moved into a column past the first without its required fields, where
// the destination applies that (FlowGate).
Flow FlowGate
Authorizer Authorizer
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
Activity ActivityJournal
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
}
PlacementWriter is what moving and reordering share.
One dependency set held by both use cases, on the reasoning that keeps them one event type: a reorder is a move that keeps its parent. What differs is how much has to be rewritten, and that is one branch rather than two implementations.
type PresetDesk ¶
type PresetDesk struct {
Presets repository.Presets
Structure StructureResolver
Authorizer Authorizer
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
Text text.Normalizer
}
PresetDesk is what the saved presets' writers share (UC-WRK-30 checks 1, 3): the presets, the structure a preset is copied from, the rights.
type PreviewContainerStructure ¶
type PreviewContainerStructure struct {
Set SetContainerStructure
Shapes repository.EntryShapes
}
PreviewContainerStructure answers what a change of a hub's or a collection's own structure would stop showing, before it is made (UC-WRK-24 checks 2 and 7). It decides the change exactly as SetContainerStructure does, and so refuses it as that would, then writes nothing.
func (PreviewContainerStructure) Descriptor ¶
func (h PreviewContainerStructure) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (PreviewContainerStructure) Execute ¶
func (h PreviewContainerStructure) Execute( ctx context.Context, actor appshared.ActorContext, cmd SetContainerStructureCommand, ) (StructurePreview, error)
Execute decides the change and counts what it would hide, in a read-only transaction.
type PrivacyRevocations ¶
type PrivacyRevocations interface {
AfterHubMadePrivate(ctx context.Context, hub domain.Container) (access.Loss, error)
Announce(ctx context.Context, tenantID shared.ID, losses ...access.Loss) error
}
PrivacyRevocations is the slice of access.Revocations a hub turning private needs: who held it through the workspace, and the record telling their devices (offline-sync.md §6).
type PrivateHubRow ¶
type PrivateHubRow struct {
repository.PrivateHubSummary
// PurgeOn is the day a trashed hub goes for good, by the workspace's trash period.
PurgeOn string
// EmergencyOffered is whether the caller may open the hub through the emergency access now:
// the workspace's owner, a person, holding nothing in it, with no access of theirs running -
// the full condition OpenPrivateHub asks, so the control is absent wherever it would refuse.
EmergencyOffered bool
// OpenUntil is the end of the caller's running access, nil where none runs.
OpenUntil *time.Time
}
PrivateHubRow is one row of the answer.
type PrivateHubsRule ¶
type PrivateHubsRule interface {
PrivateHubsAllowed(ctx context.Context, tenantID shared.ID) (bool, error)
}
PrivateHubsRule answers whether the workspace allows new private hubs now: *Private hubs allowed* at the workspace, the plan and the installation, resolved (ADR-0073 §6).
type QueryItems ¶
type QueryItems struct {
Items repository.Items
ItemLabels repository.ItemLabels
// Tallies counts what each entry holds, for `expand=counts`. Absent where the use case is
// composed into something that never asks (an export, a view's preview).
Tallies repository.ItemTallies
Containers repository.Containers
Authorizer Authorizer
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
// Gate marks what each entry's collection hides of it.
Gate EntryGate
}
QueryItems answers the query language: one filter over one anchored scope, sorted, paged, and optionally grouped into the columns a board draws (api-guidelines.md §3).
It lives in this package rather than in a `view` one of its own, although the catalogue groups it with the saved views. What it reads is work items, and it answers them through this package's projection, its authorisation path, its page size and its label expansion - four things a second package would have to import or copy. `view` is for SavedView, which stores a query rather than running one; the *grammar* is already in core/domain/model/view, where project-structure.md §1 puts it.
func (QueryItems) Descriptor ¶
func (h QueryItems) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
The filter, the ordering and the grouping are declared as documents rather than as fields of their own, because they are a grammar: what may go in them is answered by `GET /meta/capabilities` under `query_fields`, which is a list this installation computes and not one a schema could pin. An agent reads that manifest for the same reason a client does (ai-first.md §1.1).
func (QueryItems) Execute ¶
func (h QueryItems) Execute( ctx context.Context, actor appshared.ActorContext, spec view.Spec, ) (repository.ItemQueryResult, error)
Execute answers the query.
Three steps, in this order. The placeholders are resolved first because it costs nothing and a query that cannot be resolved is one nobody should read a row for. The anchor is then read and the permission asked about it - once, for the whole result, exactly as ListWorkItems asks about the collection it was given: the client named the scope, so a refusal is a refusal rather than an empty page - and somebody who holds only individual shares inside it queries those. Only then does the compiled statement run.
type QueryMeaning ¶
type QueryMeaning interface {
// Of embeds the query and names the model that did. An empty vector and a nil error is
// "search lexically", which is the answer for every reason a provider might not be reachable
// - a search must not fail because somebody else's machine is slow. The model travels because
// the vector is compared only with rows of the same model.
Of(ctx context.Context, actor appshared.ActorContext, words string) (vector []float32, model string, err error)
}
QueryMeaning turns what somebody typed into a vector, or says it cannot.
A seam of its own rather than the provider, because the interesting part is not the call but the *policy around it*: whether this installation has a store at all, whether the workspace has consented, and what to do when the provider is slow. All of that is decided in one place, and what this use case receives is a vector or nothing.
type ReadCalendarFeed ¶
type ReadCalendarFeed struct {
Feeds feedrepo.CalendarFeeds
Accounts identityrepo.Accounts
// Export is the selection the view export performs, without its audit entry: the same rows,
// the same cap, the same visibility question. Shared rather than reimplemented, so a feed and
// an export of one view can never disagree about what is in it.
Export ExportView
UnitOfWork persistence.UnitOfWork
}
ReadCalendarFeed answers what one subscription contains.
It is not a use case and is deliberately not in the catalogue, for MediaContent's reason: a catalogue entry is something a person, an agent or a rule may ask for, and this is a route answering a credential nobody in the system holds. What it shares with a use case is the rule that matters - the tenant comes from the credential and from nowhere else (multi-tenancy.md §2.2) - and the rule that matters more: every permission question is asked here, in the application layer, and the adapter only ever parses a string (ADR-0005).
The feed reads as its **owner**, evaluated now rather than when the token was minted. An owner who has lost access to half the view sees the feed narrow with them, a disabled account's feed stops answering, and a revoked membership is not survived by a token that remembers better days.
func (ReadCalendarFeed) Execute ¶
func (h ReadCalendarFeed) Execute( ctx context.Context, token domain.FeedToken, ) (ExportedView, error)
Execute answers the feed's rows, or that there is no such feed.
Every refusal is the same one. An unknown token, a revoked feed, a feed whose view was deleted, an owner who no longer exists and an owner who may no longer see the view all answer not found, with the same body: distinguishing them would answer questions for whoever is trying tokens (T-04's shape, and T-21's requirement).
type ReadKudos ¶
type ReadKudos struct{ Desk KudosDesk }
ReadKudos answers what the reader may know of an entry's kudos.
func (ReadKudos) Descriptor ¶
func (h ReadKudos) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
type Reader ¶
type Reader interface {
Permitted(
ctx context.Context, actor appshared.ActorContext, request access.Request,
paths [][]identity.Scope,
) ([]bool, error)
}
Reader is the slice of the authorisation service the read side needs beyond Authorizer: the same question asked of a whole page at once.
Two interfaces rather than one with two methods, because the two reads need different halves - a single Get asks about one path, and only the unanchored list needs the plural form. A use case declaring a dependency it does not use is a use case whose test has to satisfy it anyway.
type RebuildOutcome ¶
type RebuildOutcome struct {
// Rewritten is how many rows this pass rewrote.
Rewritten int64
// Remaining is how many were still stale after it, which is what the job reports as progress.
Remaining int64
}
RebuildOutcome is what one pass did.
type RebuildSearchIndex ¶
type RebuildSearchIndex struct {
Index repository.SearchIndex
UnitOfWork persistence.UnitOfWork
// BatchSize bounds one pass. Zero takes the default below.
BatchSize int
}
RebuildSearchIndex is the pass behind the job: one batch of stale rows rewritten in its own transaction, and a decision about whether to come straight back.
Not a use case and not in the catalogue: nobody asks for a batch, they ask for the index to be current (ReindexSearch). The shape is the embedding pass's - a batch, then the outcome says whether there is more - with no provider anywhere near it: the rewrite is one statement over rows this PostgreSQL holds.
func (RebuildSearchIndex) Execute ¶
func (h RebuildSearchIndex) Execute( ctx context.Context, actor appshared.ActorContext, ) (RebuildOutcome, error)
Execute rewrites one batch and counts what is left.
type RecurrenceWriter ¶
type RecurrenceWriter struct {
Recurrences repository.Recurrences
Items repository.Items
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
// Gate is what the entry's collection offers it: the refusal before a write of what it
// hides, and the mark on the answer (domain-model.md §2).
Gate EntryGate
Authorizer Authorizer
// Expander is the library behind ADR-0008's decision, as a port: it is what turns "is this a
// rule, and is it a sane one" from an opinion into an answer, and it is asked before anything
// is stored rather than by the scheduler afterwards (T-17, R-07).
Expander recurrenceport.Expander
Changes changelog.ChangeLog
Audit audit.Sink
Activity ActivityJournal
// Jobs is where the series asks to be materialised. The write that made something owed seeds
// it, because nothing may enumerate tenants (multi-tenancy.md §2.1) - the same shape the
// reminder's wake-up has.
Jobs queue.Queue
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
}
RecurrenceWriter is what both directions of a series need: the same reads, the same permission question, the same records - and the port that decides whether the text is a rule at all.
type ReindexAccepted ¶
ReindexAccepted is what a 202 hands back: the job to watch, and how many rows it will rewrite.
type ReindexSearch ¶
type ReindexSearch struct {
Index repository.SearchIndex
Jobs Jobs
Authorizer Authorizer
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
}
ReindexSearch brings a workspace's search documents current with the configurations its PostgreSQL has (ADR-0034).
ADR-0034 left one case open: an installation that *gains* a text search configuration after the entries were written - an upgrade, or an operator installing one - keeps searching those entries as `simple` until each happens to be edited. The rows now record which configuration built them, so the stale ones can be found; this counts them, queues the job that rewrites exactly those in batches, and answers the count, which is the operation's report. Tenant-scoped and an administrator's, because nothing may enumerate tenants (multi-tenancy.md §2.1).
func (ReindexSearch) Descriptor ¶
func (h ReindexSearch) Descriptor() usecase.Descriptor
Descriptor registers the reindex in all three channels.
func (ReindexSearch) Execute ¶
func (h ReindexSearch) Execute( ctx context.Context, actor appshared.ActorContext, ) (ReindexAccepted, error)
Execute counts, queues and records.
type ReminderNotifier ¶
type ReminderNotifier interface {
Execute(ctx context.Context, tenantID, itemID shared.ID, recipients []shared.ID) error
}
ReminderNotifier is the slice of the Notification context this pass needs: somebody is to be told about an entry, now.
A slice rather than the package, for the reason the notification consumer declares slices of the work repositories: what the firing pass can do to the notification context is one line - it can ask for a record to be written - and it cannot read, suppress or send anything.
type ReminderOutcome ¶
type ReminderOutcome struct {
// Fired counts the reminders that became notifications, Cancelled the ones whose entry no
// longer warrants one, and Announced the deadlines the pass told automation about.
Fired int
Cancelled int
Announced int
// NextAt is when the tenant next owes a reminder, and nil when it owes none - which is what
// lets the job finish instead of idling forever.
NextAt *time.Time
}
ReminderOutcome is what one pass did and what it leaves behind.
func (ReminderOutcome) FilledBatch ¶
func (o ReminderOutcome) FilledBatch(batch int) bool
FilledBatch reports whether the pass ran into its own bound, in which case there is known work left and the job comes straight back for it rather than sleeping until the next moment.
type ReminderSignals ¶
type ReminderSignals interface {
ReminderFired(ctx context.Context, channel string, delaySeconds float64)
}
ReminderSignals is the slice of the metrics adapter this pass reports through (observability-reliability.md §3.2, SLO-5).
One measurement, and it is the one the service level objective is written about: how late a reminder was against the moment it promised. The channel is the label because that is where the difference will be once there is more than one - an email that waits for a mail server is a different story from a push that does not.
type ReminderWriter ¶
type ReminderWriter struct {
Reminders repository.Reminders
Items repository.Items
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
// Gate is what the entry's collection offers it: the refusal before a write of what it
// hides, and the mark on the answer (domain-model.md §2).
Gate EntryGate
Authorizer Authorizer
// Visibility answers the one question the permission cannot: whether the account somebody
// named can see the entry at all. The same question an assignment asks.
Visibility Visibility
Changes changelog.ChangeLog
Audit audit.Sink
// Jobs is where the tenant's next wake-up is asked for. The write that makes something due is
// what seeds it, because nothing may enumerate tenants (multi-tenancy.md §2.1) - the same
// shape the retention sweep and the media reconciliation already have.
Jobs queue.Queue
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
}
ReminderWriter is what every use case that writes a reminder shares: the same reads, the same permission question, and the same two records - the change log entry for offline clients, and the audit entry.
Two records rather than four. There is no reminder event in the §4 catalogue and none is invented here: what consumers react to is the entry moving (`item.due_changed`) and the reminder firing, not somebody writing one down. And there is no step of the entry's history either: a reminder is a person's own arrangement with the clock rather than a change to the work - the history says what happened to the entry, and "Anna set herself a reminder" is not that. Both decisions are recorded in the pull request, because the next reader will ask.
type RemoveLabel ¶
type RemoveLabel struct {
Writer ItemLabelWriter
}
RemoveLabel takes a label off an entry.
func (RemoveLabel) Descriptor ¶
func (h RemoveLabel) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (RemoveLabel) Execute ¶
func (h RemoveLabel) Execute( ctx context.Context, actor appshared.ActorContext, cmd LabelCommand, ) (ItemLabelSet, error)
Execute takes the label off the entry and returns the labels it now carries.
type RemoveMember ¶
type RemoveMember struct {
Writer ItemMemberWriter
}
RemoveMember takes an account off an entry's member list.
func (RemoveMember) Descriptor ¶
func (h RemoveMember) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (RemoveMember) Execute ¶
func (h RemoveMember) Execute( ctx context.Context, actor appshared.ActorContext, cmd MemberCommand, ) (ItemMemberSet, error)
Execute takes the account off the entry and returns the members it now carries.
type RemoveRecurrence ¶
type RemoveRecurrence struct {
Writer RecurrenceWriter
}
RemoveRecurrence takes the series off an entry, leaving every occurrence it produced standing.
func (RemoveRecurrence) Descriptor ¶
func (h RemoveRecurrence) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (RemoveRecurrence) Execute ¶
func (h RemoveRecurrence) Execute( ctx context.Context, actor appshared.ActorContext, cmd RemoveRecurrenceCommand, ) error
Execute takes the series off the entry.
type RemoveRecurrenceCommand ¶
RemoveRecurrenceCommand is the input, typed.
type RenameContainer ¶
type RenameContainer struct {
Writer ContainerWriter
}
RenameContainer changes a hub or a collection's own descriptive fields.
What it does not change: how the collection works (UpdateContainerPolicies), where it sits (MoveContainer), and whether it is writable at all (ArchiveContainer). A single endpoint writing all of them would need one audit entry covering everything and one event nobody could subscribe to narrowly.
func (RenameContainer) Descriptor ¶
func (h RenameContainer) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4).
func (RenameContainer) Execute ¶
func (h RenameContainer) Execute( ctx context.Context, actor appshared.ActorContext, cmd RenameContainerCommand, ) (domain.Container, error)
Execute renames the container and returns it as it now stands.
type RenameContainerCommand ¶
type RenameContainerCommand struct {
ContainerID shared.ID
// Attributes carries a pointer per field, so that "set it to nothing" and "do not touch it" stay
// two different requests all the way down from the merge patch that expressed them.
Attributes domain.ContainerAttributes
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read none
// and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
RenameContainerCommand is the input, typed.
type ReopenWorkItem ¶
type ReopenWorkItem struct {
Completion CompletionWriter
}
ReopenWorkItem marks a completed item open again, and reopens the completed items above it where the policy rolls up.
func (ReopenWorkItem) Descriptor ¶
func (h ReopenWorkItem) Descriptor() usecase.Descriptor
func (ReopenWorkItem) Execute ¶
func (h ReopenWorkItem) Execute( ctx context.Context, actor appshared.ActorContext, cmd CompletionCommand, ) (domain.WorkItem, error)
Execute reopens the item and returns it.
type ReorderBucket ¶
type ReorderBucket struct {
Writer BucketWriter
}
ReorderBucket moves a column along its board.
func (ReorderBucket) Descriptor ¶
func (h ReorderBucket) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ReorderBucket) Execute ¶
func (h ReorderBucket) Execute( ctx context.Context, actor appshared.ActorContext, cmd ReorderBucketCommand, ) (domain.Bucket, error)
Execute moves the column and returns it as it now stands.
type ReorderBucketCommand ¶
type ReorderBucketCommand struct {
BucketID shared.ID
// BeforeBucketID is the column this one goes to the left of. Empty is the right hand end, which
// is a position like any other rather than a special case.
BeforeBucketID shared.ID
ExpectedVersion int
}
ReorderBucketCommand is the input, typed.
type ReorderContainer ¶
type ReorderContainer struct {
Writer ContainerWriter
}
ReorderContainer ranks a container within the level it already sits in.
It exists because MoveContainer cannot express the case that matters most in a sidebar. A move requires a `target_parent_id` and ranks the collection there as a consequence - naming the hub a collection is already in reorders it, which the operation says itself. A **hub** sits in the tenant and in nothing else (I-C1), so it has no parent to name, while `domain-model.md` §3.3 defines `order_key` as "ordering within the parent context" and every Container answers one. The rank of a hub was therefore a field the API reported and nothing could change, and a sidebar listed hubs in an order their owner could not touch.
The audit action and the event are MoveContainer's rather than new ones. `container.moved` is already documented as covering "a change of hub and a reorder within one", and ContainerMoved already says a consumer recognises a reorder by `from_parent_id` matching `parent_id`. A second name for the same fact would be a second thing for every consumer to learn and for the change log to keep in step.
func (ReorderContainer) Descriptor ¶
func (h ReorderContainer) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ReorderContainer) Execute ¶
func (h ReorderContainer) Execute( ctx context.Context, actor appshared.ActorContext, cmd ReorderContainerCommand, ) (domain.Container, error)
Execute ranks the container and returns it as it now stands.
type ReorderContainerCommand ¶
type ReorderContainerCommand struct {
ContainerID shared.ID
// BeforeContainerID is the sibling to land in front of. Empty appends to the end of the level.
BeforeContainerID shared.ID
ExpectedVersion int
}
ReorderContainerCommand is the input, typed.
type ReorderWorkItem ¶
type ReorderWorkItem struct {
Placement PlacementWriter
}
ReorderWorkItem changes an item's rank among the items it already sits with.
func (ReorderWorkItem) Descriptor ¶
func (h ReorderWorkItem) Descriptor() usecase.Descriptor
func (ReorderWorkItem) Execute ¶
func (h ReorderWorkItem) Execute( ctx context.Context, actor appshared.ActorContext, cmd ReorderWorkItemCommand, ) (domain.WorkItem, error)
Execute reorders the item and returns it.
A reorder is a move that keeps its parent, and it is expressed as exactly that: the same plan, with the destination read off the item rather than from the request. What it does not do is rewrite the subtree - nothing moved, so no path changed, and a statement over the subtree would spend a version on every descendant for no reason.
type ReorderWorkItemCommand ¶
type ReorderWorkItemCommand struct {
ItemID shared.ID
BeforeItemID shared.ID
// OrderKey is the rank the caller computed, MoveWorkItemCommand's field.
OrderKey string
ExpectedVersion int
}
ReorderWorkItemCommand is the input, typed.
type RestoreContainer ¶
type RestoreContainer struct {
Writer ContainerWriter
}
RestoreContainer takes one container deletion back out of the trash, whole.
The same permission as the deletion. Whoever owns the decision to delete a hub owns its reversal: an administrator who may not delete one may not bring one back either, or an owner's deletion would be undoable by somebody the matrix deliberately excluded from making it.
func (RestoreContainer) Descriptor ¶
func (h RestoreContainer) Descriptor() usecase.Descriptor
func (RestoreContainer) Execute ¶
func (h RestoreContainer) Execute( ctx context.Context, actor appshared.ActorContext, cmd ContainerLifecycleCommand, ) (domain.Container, error)
Execute restores the deletion the container belongs to and returns it as it now stands.
type RestoreLabel ¶
type RestoreLabel struct {
Writer LabelWriter
}
RestoreLabel brings a deleted label back into a collection's vocabulary.
The deletion's inverse, and possible only because the deletion rewrote nothing: every entry that carried the label still does, so bringing the label back brings the chips back with it. That is what lets a client offer *Undo* on a deletion and a list of deleted labels to bring back.
func (RestoreLabel) Descriptor ¶
func (h RestoreLabel) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (RestoreLabel) Execute ¶
func (h RestoreLabel) Execute( ctx context.Context, actor appshared.ActorContext, cmd RestoreLabelCommand, ) (domain.Label, error)
Execute brings the label back and returns it as it now stands.
type RestoreLabelCommand ¶
RestoreLabelCommand is the input, typed. No version: a restore's precondition is the deletion, and a retry after a lost answer must not meet the version its own first attempt moved on.
type RestoreWorkItem ¶
type RestoreWorkItem struct {
Lifecycle LifecycleWriter
}
RestoreWorkItem takes one deletion back out of the trash, whole.
func (RestoreWorkItem) Descriptor ¶
func (h RestoreWorkItem) Descriptor() usecase.Descriptor
func (RestoreWorkItem) Execute ¶
func (h RestoreWorkItem) Execute( ctx context.Context, actor appshared.ActorContext, cmd LifecycleCommand, ) (domain.WorkItem, error)
Execute restores the deletion the entry belongs to and returns the entry as it now stands.
type RestrictedAccounts ¶
type RestrictedAccounts interface {
Restricted(ctx context.Context, accountIDs []shared.ID) (map[shared.ID]bool, error)
}
RestrictedAccounts answers which of the accounts named may not be processed automatically - the question automatic assignment already asks of its pool.
type RevokeCalendarFeed ¶
type RevokeCalendarFeed struct{ Writer CalendarFeedWriter }
RevokeCalendarFeed stops a token.
func (RevokeCalendarFeed) Descriptor ¶
func (h RevokeCalendarFeed) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (RevokeCalendarFeed) Execute ¶
func (h RevokeCalendarFeed) Execute( ctx context.Context, actor appshared.ActorContext, feedID shared.ID, ) error
Execute revokes the feed, and is idempotent: revoking twice is somebody making sure.
The permission is the same read the minting asked for, and deliberately no more. Revocation must never be harder than minting - the moment somebody needs it is the moment a URL has gone somewhere it should not have, and a person who cannot stop their own leak because their role changed is a security problem rather than a policy.
type SavePreset ¶
type SavePreset struct{ Desk PresetDesk }
SavePreset saves a hub's or collection's structure in force as a preset of the person's own or of the workspace (UC-WRK-30 check 1).
func (SavePreset) Descriptor ¶
func (h SavePreset) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (SavePreset) Execute ¶
func (h SavePreset) Execute( ctx context.Context, actor appshared.ActorContext, cmd SavePresetCommand, ) (domain.Preset, error)
Execute saves the preset.
type SavePresetCommand ¶
type SavePresetCommand struct {
Name string
Origin domain.PresetOrigin
ContainerID shared.ID
}
SavePresetCommand is the input, typed.
type SavedViewCommand ¶
type SavedViewCommand struct {
ViewID shared.ID
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read
// none and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
SavedViewCommand is the shared input of the three: which view, and the version the caller read.
type SavedViewWriter ¶
type SavedViewWriter struct {
Views viewrepo.SavedViews
Containers repository.Containers
Authorizer Authorizer
Permits Permitting
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
// Text brings the name to normal form C on the way in (i18n-l10n.md §5).
Text text.Normalizer
}
SavedViewWriter is what the three changes to an existing view need.
One dependency set, for the reason CustomFieldWriter is one: the same find, the same visibility, the same ownership question, and only what is written at the end differs.
type SearchItems ¶
type SearchItems struct {
Items repository.Items
Containers repository.Containers
Authorizer Authorizer
Anchored Anchored
Reader Reader
UnitOfWork persistence.UnitOfWork
// Meaning is the semantic half (ADR-0050). Optional at every level - no store, no
// provider, no consent, or a provider that does not answer - and every one of those is a
// lexical search rather than a failure.
Meaning QueryMeaning
// Clock is what `@today` is resolved against, as it is for the query (rule 4, arc42 §8.13).
// A filter arrives here since ADR-0064, and a filter may carry a placeholder.
Clock clock.Clock
// Gate marks what each entry's collection hides of it.
Gate EntryGate
}
SearchItems answers "where is this".
The one read of this product that is not anchored to a place in the workspace. A query is anchored because an unanchored one is a question authorisation cannot answer in one step (view.Scope); a search is the question "anywhere I can see", so it is answered the way the trash and the hub level are - read, then narrowed to what the actor may see rather than refused (ListTrash, ReadContainers.Execute).
Which makes the narrowing the security-critical half of this use case rather than a detail of it. A search that returned a title from a collection the actor cannot open is T-04 with a different verb, and no scope check upstream can prevent it: the rows come from everywhere at once. It is asked here, in the application layer, like every other permission (rule 2, ADR-0005).
func (SearchItems) Descriptor ¶
func (h SearchItems) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (SearchItems) Execute ¶
func (h SearchItems) Execute( ctx context.Context, actor appshared.ActorContext, query SearchItemsQuery, ) (repository.ItemHitPage, error)
Execute answers one page of hits, in the order the database ranked them.
type SearchItemsQuery ¶
type SearchItemsQuery struct {
Words string
// ContainerID narrows the search to one hub or collection. Empty searches everywhere.
ContainerID shared.ID
// Language is the tag the words are read under. Empty takes the actor's locale.
Language string
IncludeArchived bool
IncludeTrashed bool
Cursor string
Size int
// Mode is AUTO or LEXICAL, and empty is AUTO.
Mode string
// Filter and Sort are the raw request, parsed here with the query's own parsers - one grammar,
// two readers (ADR-0064). Nil is no narrowing and no ordering asked for.
Filter any
Sort any
}
SearchItemsQuery is the input, typed.
type SearchMeaning ¶
type SearchMeaning struct {
Providers AiProviders
Semantic SemanticSearch
// UnitOfWork is needed for the one read: whether this installation has a store at all.
UnitOfWork persistence.UnitOfWork
// Timeout bounds the provider call. Zero takes the default below.
Timeout time.Duration
// Kept is whose content stays out of AI. A person kept out searches by words: what they type
// is theirs, and no AI action is theirs (UC-PRV-05 check 7). Nil keeps nobody out.
Kept KeptOutReader
}
SearchMeaning embeds what somebody typed, where that is possible at all (ADR-0050).
**Its whole design is the answer to "and if it is not".** Semantic search is optional four times over - the extension may be absent, the workspace may have configured no provider, it may have configured one and not consented, and the provider may simply not answer today - and every one of those has the same right answer: search lexically. So this returns a vector or nothing, and never an error a caller has to interpret.
That is also why the timeout is here and short. A search is something somebody is waiting for; a provider that takes four seconds has, for the purpose of this feature, not answered. The lexical half is complete on its own, so the cost of giving up early is a search that finds slightly less, and the cost of not giving up is a search that hangs.
type SeedEmbedding ¶
type SeedEmbedding struct {
Jobs EmbeddingQueue
// Delay is how long after the write the pass runs. Short, but not zero: a burst of edits to
// one workspace should become one pass rather than one per keystroke, and the deduplication
// key only collapses jobs that are still pending.
Delay time.Duration
Clock clock.Clock
}
SeedEmbedding asks for a workspace's vectors to be brought up to date when one of its entries changes.
It is a *seed*, not the work: what it writes is one job saying "this workspace owes embeddings", deduplicated per tenant, and the pass behind that job works out which entries actually owe one. That is why an event that changed no text is harmless - the pass compares fingerprints and finds nothing.
Seeded by the write rather than by a scheduler, like every per-tenant duty in this system: nothing may enumerate tenants (multi-tenancy.md §2.1), so nothing can create one job per workspace on a timer. A workspace nobody writes in owes nothing and costs nothing.
It runs inside the dispatcher's transaction, which is the reliability argument (ADR-0007): the job and the event's consumption commit together, so an event that was seen is an event whose job exists.
func (SeedEmbedding) Wants ¶
func (s SeedEmbedding) Wants(eventType event.Type) bool
Wants reports whether this event could have changed an entry's text.
The two that carry a title or notes. A deletion is deliberately absent: the store's rows go with the entry by cascade, so nothing has to be asked for. So is everything else an entry can have happen to it - a label, an assignment, a move - because none of them changes the words the index is built from, and asking for a pass after each would be a provider call for a card somebody dragged.
type SemanticSearch ¶
SemanticSearch reports whether this installation has an embedding store at all (ADR-0050).
type SetContainerStructure ¶
type SetContainerStructure struct {
Writer ContainerWriter
Structure StructureResolver
// Epochs is advanced instead of a fan-out past structureFanOutCeiling.
Epochs changelog.Epochs
// Labels answers whether a category the write names is a label of the collection.
Labels repository.Labels
// Flatten moves what a change hides by level to the top level, where the caller asks for it.
Flatten StructureFlattener
}
SetContainerStructure changes how much structure a hub or a collection has (UC-WRK-23 checks 2, 5, 8): a preset chosen, settings changed one by one, settings reset to the layer above.
Not destructive, and declared so: a structure says what is offered, never what is kept - every value an entry holds stays stored, and comes back when the setting does (domain-model.md §2, "Narrowing hides, it never deletes").
func (SetContainerStructure) Descriptor ¶
func (h SetContainerStructure) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (SetContainerStructure) Execute ¶
func (h SetContainerStructure) Execute( ctx context.Context, actor appshared.ActorContext, cmd SetContainerStructureCommand, ) (StructureResult, error)
Execute changes the container's own layer and returns it with the structure in force.
type SetContainerStructureCommand ¶
type SetContainerStructureCommand struct {
ContainerID shared.ID
// Preset is the preset to copy into the container's own layer, replacing its own values; empty
// for none.
Preset domain.PresetKey
// Settings are set on top, each on its own; only the ones decided here change.
Settings domain.StructureValues
// Reset are the settings that go back to the layer above.
Reset []domain.StructureSetting
// ExpectedVersion is from If-Match; zero accepts whatever is stored.
ExpectedVersion int
// Narrowing is what happens to the entries the change hides by their level; empty is HIDE.
Narrowing Narrowing
}
SetContainerStructureCommand is the input, typed.
type SetCover ¶
type SetCover struct {
Cover CoverWriter
}
SetCover puts a cover on an entry: a colour token, or an image.
Only a type whose profile carries COVER has one, which by default is a task and neither a work package nor an activity (domain-model.md §2, §3.4). Refused rather than quietly ignored: a client that covered a work package and received a 200 would believe the picture is there.
Replacing a cover is this call again. The displaced image loses a reference and is left to the reconciliation job, which is the whole of the deletion path here - a request has no business waiting on a bucket for a picture somebody just swapped out.
func (SetCover) Descriptor ¶
func (h SetCover) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4).
type SetCustomField ¶
type SetCustomField struct {
Items repository.Items
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
// Gate is what the entry's collection offers it: the refusal before a write of what it
// hides, and the mark on the answer (domain-model.md §2).
Gate EntryGate
Fields repository.CustomFields
Authorizer Authorizer
// Visibility answers the one question a USER value asks: can the account named reach the
// entry at all. The same question an assignment asks, and the same refusal.
Visibility Visibility
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
Activity ActivityJournal
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
// Text brings the options and a TEXT value to normal form C on the way in (i18n-l10n.md §5).
Text text.Normalizer
}
SetCustomField writes one custom field on one entry.
One key per call, and that is the merge rule made unavoidable rather than a convenience. The values live in one jsonb document on the entry, and merging that document as one scalar is exactly the loss the per-field rule exists to prevent: two devices setting two different keys would resolve to whichever wrote later, and one of the two values would quietly not be there (offline-sync.md §4.2). A call that could write the whole document would be writing keys it never read, so there is no such call.
The value is judged against the definition in force for the entry's collection: the collection's own, or the workspace-wide one. Judged rather than coerced - a NUMBER arriving as a string is refused, because a client that sent "3" meant a string (domain-model.md §6).
func (SetCustomField) Descriptor ¶
func (h SetCustomField) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (SetCustomField) Execute ¶
func (h SetCustomField) Execute( ctx context.Context, actor appshared.ActorContext, cmd SetCustomFieldCommand, ) (domain.WorkItem, error)
Execute writes the field and returns the entry as it now stands.
type SetCustomFieldCommand ¶
type SetCustomFieldCommand struct {
ItemID shared.ID
Key string
// Value is what the field shall hold, shaped by the definition's kind. Nil clears the key.
Value any
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read
// none and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
SetCustomFieldCommand is the input, typed.
type SetDueDate ¶
type SetDueDate struct {
Due DueDateWriter
}
SetDueDate puts a due date on an entry: the instant, the all-day flag and the IANA zone, written together because the three describe one date (i18n-l10n.md §4).
Moving a due date is this use case again: the trio is a scalar family, so it is one decision, one version and one step of the history.
func (SetDueDate) Descriptor ¶
func (h SetDueDate) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4).
func (SetDueDate) Execute ¶
func (h SetDueDate) Execute( ctx context.Context, actor appshared.ActorContext, cmd DueDateCommand, ) (domain.WorkItem, error)
Execute puts the due date on the entry and returns it.
type SetHubPrivacy ¶
type SetHubPrivacy struct {
Writer ContainerWriter
// Hubs takes the hub's row before anything is decided, as granting a group does: a group
// cannot slip in while the hub turns private (identity.md §22).
Hubs identityrepository.HubLocks
Revocations PrivacyRevocations
// PrivateHubs is the workspace's switch: read when a shared hub is to turn private, never when
// a private one is shared again (ADR-0073 §6).
PrivateHubs PrivateHubsRule
}
SetHubPrivacy makes an existing hub private, or shared again (ADR-0073 §2).
Marking needs both the right to create hubs - STRUCTURE on the workspace - and an OWNER membership on the hub itself; a role held on the workspace does not make anybody the owner of a hub, so an administrator cannot take a shared hub away from everybody else. Unmarking needs an OWNER on the hub. A person without STRUCTURE has a private hub by creating one.
func (SetHubPrivacy) Descriptor ¶
func (h SetHubPrivacy) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (SetHubPrivacy) Execute ¶
func (h SetHubPrivacy) Execute( ctx context.Context, actor appshared.ActorContext, cmd SetHubPrivacyCommand, ) (domain.Container, error)
Execute changes the hub's privacy and returns it as it now stands.
type SetHubPrivacyCommand ¶
SetHubPrivacyCommand is the input, typed.
type SetRecurrence ¶
type SetRecurrence struct {
Writer RecurrenceWriter
}
SetRecurrence puts a series on an entry, or changes the one it has.
One use case for both, which is the one place this task departs from the catalogue's wording: §5 names `SetRecurrence` and `UpdateRecurrence`, and they are the same call here for the reason the route is one PUT - a series is one document, "every Monday in Berlin, on completion, ninety days ahead" is a sentence rather than six settings, and a caller sending it does not know or care whether the entry already had one. What differs between the two is the trail: the audit entry and the history verb say which of the two happened, decided from what was there.
func (SetRecurrence) Descriptor ¶
func (h SetRecurrence) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4).
func (SetRecurrence) Execute ¶
func (h SetRecurrence) Execute( ctx context.Context, actor appshared.ActorContext, cmd SetRecurrenceCommand, ) (domain.RecurrenceRule, error)
Execute stores the series and returns it.
type SetRecurrenceCommand ¶
type SetRecurrenceCommand struct {
ItemID shared.ID
Spec domain.RecurrenceSpec
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read
// none and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
SetRecurrenceCommand is the input, typed.
type SetWorkspaceStructure ¶
type SetWorkspaceStructure struct {
Structure StructureResolver
Authorizer Authorizer
Changes changelog.ChangeLog
Epochs changelog.Epochs
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
HLC clock.HLCSource
Text text.Normalizer
}
SetWorkspaceStructure changes the workspace's own structure (UC-WRK-23 checks 2, 8; the lock writer UX-10's check 7 needs): values every hub and collection inherits unless it has its own, locks that bind them all, and the preset a new hub starts from.
No step-up: a structure narrows what is offered and never who gets in (identity.md §5 asks one for the sign-in rule alone). Not destructive, for SetContainerStructure's reason.
func (SetWorkspaceStructure) Descriptor ¶
func (h SetWorkspaceStructure) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (SetWorkspaceStructure) Execute ¶
func (h SetWorkspaceStructure) Execute( ctx context.Context, actor appshared.ActorContext, cmd SetWorkspaceStructureCommand, ) (WorkspaceStructureResult, error)
Execute changes the workspace's structure.
type SetWorkspaceStructureCommand ¶
type SetWorkspaceStructureCommand struct {
Preset domain.PresetKey
Settings domain.StructureValues
Reset []domain.StructureSetting
// Locks is the whole set of locks after the change; nil leaves them as they are.
Locks []string
// DefaultPreset sets the preset a new hub starts from; ClearDefaultPreset removes it.
DefaultPreset domain.PresetKey
ClearDefaultPreset bool
ExpectedVersion int
}
SetWorkspaceStructureCommand is the input, typed.
type ShareSavedView ¶
type ShareSavedView struct {
}
ShareSavedView decides who sees a view: the owner alone, or everyone who may read its scope.
func (ShareSavedView) Descriptor ¶
func (h ShareSavedView) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (ShareSavedView) Execute ¶
func (h ShareSavedView) Execute( ctx context.Context, actor appshared.ActorContext, cmd SavedViewCommand, sharing view.Sharing, ) (view.SavedView, error)
Execute decides the sharing and returns the view.
Publishing into the scope asks STRUCTURE on the scope's path, whoever asks - a view over a collection cannot be pushed onto people by somebody who may not shape that collection, the owner included. Taking a view back to PRIVATE is the owner's right without any permission, and a STRUCTURE holder's for a view that was published.
type SkipOccurrence ¶
type SkipOccurrence struct {
Writer RecurrenceWriter
}
SkipOccurrence moves a series past its next unmade occurrence.
The one user-facing half of the materialisation, and the shape of it is what the bookkeeping already is: the watermark says how far the series has been dealt with, so skipping is moving it past one moment without creating anything. Nothing that already exists is touched - a materialised occurrence is an ordinary entry, and skipping it would be deleting somebody's work.
func (SkipOccurrence) Descriptor ¶
func (h SkipOccurrence) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (SkipOccurrence) Execute ¶
func (h SkipOccurrence) Execute( ctx context.Context, actor appshared.ActorContext, cmd SkipOccurrenceCommand, ) (domain.RecurrenceRule, error)
Execute skips the next occurrence and answers the rule.
type SkipOccurrenceCommand ¶
SkipOccurrenceCommand is the input, typed.
type Starter ¶
type Starter struct {
Starts repository.Starter
Permits access.Permitter
Authorizer Authorizer
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
}
Starter is the workspace's first start (UC-WRK-30 check 5): whom a client asks, once, what the workspace is used for. The starter set itself is the client's - hubs and collections with translated names, created through CreateContainer - because rule 8 keeps names out of the backend; the server holds only that the question was asked.
func (Starter) StarterOffered ¶
StarterOffered reports whether a client asks this reader: nobody answered yet, nobody made a hub, collection or entry beyond what the workspace was created with - so a restored or long-used workspace is never asked -, and the reader may change the workspace's shape. Asked silently: a reader who may not is simply not asked.
type StructureFlattener ¶
type StructureFlattener struct {
Placement PlacementWriter
Labels ItemLabelWriter
NewLabels CreateLabel
Shapes repository.EntryShapes
Flat repository.ItemFlattener
}
StructureFlattener moves the entries a structure hides by their level to the top level of their collection: each as an entry of the top level's type, labelled with its former parent's title, its history recording the move. Deepest first, so that each one is a leaf when it moves and only a trashed entry's path has to follow it.
func (StructureFlattener) Wired ¶
func (f StructureFlattener) Wired() bool
Wired reports whether the flattener has what it needs.
type StructurePreview ¶
type StructurePreview struct {
Narrowing domain.Narrowing
Keeping []KeptOwnValue
// Flatten is how many entries narrowing FLATTEN would move to the top level.
Flatten int
}
StructurePreview is the answer: what would no longer be shown, and for a hub the collections that keep their own value of a setting the change moves.
type StructureResolver ¶
type StructureResolver struct {
Containers repository.Containers
Structures repository.Structures
Profiles metarepo.CapabilityProfiles
// Presets reads the saved presets a layer can be copied from. A build without it offers
// Hubtask's presets alone.
Presets repository.Presets
// Installation reads the installation's presets: offered to everyone, and its default's hold
// on every workspace. A build without it has neither.
Installation repository.InstallationPresets
}
StructureResolver answers the structure in force for a container: its own layer, its hub's, the workspace's, held inside the installation's bound (domain-model.md §2). Every reader and writer of a structure resolves through it, so a value read on one door is the value written on another.
func (StructureResolver) Resolve ¶
func (r StructureResolver) Resolve( ctx context.Context, actor appshared.ActorContext, uow persistence.UnitOfWork, containers []domain.Container, ) (map[shared.ID]domain.EffectiveStructure, error)
Resolve resolves a page of containers in one read-only transaction of its own. Called after the permission question was answered, so it resolves only what the caller may see.
func (StructureResolver) Wired ¶
func (r StructureResolver) Wired() bool
Wired reports whether the resolver has what it needs. A handler built without one - in a test of something else - answers containers without their structure rather than failing.
type StructureResult ¶
type StructureResult struct {
Container domain.Container
Structure domain.EffectiveStructure
KeptByLock []domain.LockKey
}
StructureResult is a structure write's answer: the container, its structure in force, and the locks that kept a value a chosen preset would have changed.
type TakeBackKudos ¶
type TakeBackKudos struct{ Desk KudosDesk }
TakeBackKudos takes back the reader's kudos on an entry's current completion.
func (TakeBackKudos) Descriptor ¶
func (h TakeBackKudos) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
type TemplateWriter ¶
type TemplateWriter struct {
Templates repository.Templates
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
// Gate is what the entry's collection offers it: the refusal before a write of what it
// hides, and the mark on the answer (domain-model.md §2).
Gate EntryGate
Authorizer Authorizer
Changes changelog.ChangeLog
Audit audit.Sink
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
// Text brings a template's name, its description and every node's title and notes to normal
// form C on the way in, and the entries an instantiation writes with them (i18n-l10n.md §5).
Text text.Normalizer
// Holds, Removals and TombstoneWindow are the deletion's (data-retention.md §4): a template no
// legal hold covers is removed at once, with the journal entry and the tombstone every removal
// leaves; one a hold covers is only marked deleted, for the retention pass to remove later.
Holds lifecyclerepo.LegalHolds
Removals lifecyclerepo.Removals
TombstoneWindow time.Duration
}
TemplateWriter is what every use case that writes a template shares: the same scope resolution, the same permission question, and the same records.
The permission is STRUCTURE rather than WRITE_ITEMS, and that is the decision the backlog left open ("who may define at which scope is the application layer's question"): a template is vocabulary for a whole workspace or a whole collection, like a label or a board column, and defining one is shaping how everybody there works. Which scope somebody may shape follows the role matrix at that scope - so a workspace-wide template needs STRUCTURE at the tenant, which is an administrator, and a collection's needs it there.
type TranslateCommand ¶
type TranslateCommand struct {
ItemID shared.ID
// TargetLocale is BCP 47. Empty takes the caller's own.
TargetLocale string
}
TranslateCommand is the input, typed.
type Translation ¶
type Translation struct {
TargetLocale string
SourceLanguage string
Title string
Notes string
Model string
PromptID string
PromptVersion string
ProducedAt time.Time
}
Translation is the answer: the two texts and the provenance every AI output carries.
type TrashContainer ¶
type TrashContainer struct {
Writer ContainerWriter
}
TrashContainer moves a hub or a collection and everything under it into the trash.
The whole subtree, under one batch: a hub takes its collections, and every collection takes its entries (I-C2). That is what makes the way back a single decision rather than a walk that has to guess what belonged together - and it is why this is the one operation an administrator may not perform. Deleting a container takes work nobody was asked about with it, so it is the owner's decision (domain-model.md §3.2).
func (TrashContainer) Descriptor ¶
func (h TrashContainer) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (TrashContainer) Execute ¶
func (h TrashContainer) Execute( ctx context.Context, actor appshared.ActorContext, cmd ContainerLifecycleCommand, ) (domain.Container, error)
Execute puts the container and its subtree into the trash and returns it as it now stands.
type TrashListing ¶
type TrashListing struct {
repository.TrashPage
// contains filtered or unexported fields
}
TrashListing is one page of the trash, when the automatic pass may take each entry, and which a legal hold keeps.
func (TrashListing) Held ¶
func (l TrashListing) Held(entry domain.TrashEntry) bool
Held reports whether a legal hold or a restriction keeps the entry's deletion: one blocks one of its rows, and the pass keeps a root while anything below it stays (data-retention.md §4 item 6).
func (TrashListing) HeldBy ¶
func (l TrashListing) HeldBy(entry domain.TrashEntry) string
HeldBy is what keeps it - `legal_hold` before `restriction`, §4's order - and empty when nothing does.
func (TrashListing) PurgeAfter ¶
func (l TrashListing) PurgeAfter(entry domain.TrashEntry) time.Time
PurgeAfter is the moment after which the automatic pass may remove the entry, unless a legal hold keeps it.
type TrashWorkItem ¶
type TrashWorkItem struct {
Lifecycle LifecycleWriter
}
TrashWorkItem moves an entry and everything under it into the trash.
A soft delete: the rows stay, stamped with when they went and with the batch that names this one deletion (I-C2). What removes them for good is the retention job, once the tenant's period has run - and until then a restore brings back exactly this batch and nothing else.
The subtree goes with it because a deletion that left an entry's children behind would leave them unreachable rather than deleted: their parent is gone from every list they would be read through.
func (TrashWorkItem) Descriptor ¶
func (h TrashWorkItem) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (TrashWorkItem) Execute ¶
func (h TrashWorkItem) Execute( ctx context.Context, actor appshared.ActorContext, cmd LifecycleCommand, ) (domain.WorkItem, error)
Execute puts the entry and its subtree into the trash and returns the entry as it now stands.
type UnarchiveContainer ¶
type UnarchiveContainer struct {
Writer ContainerWriter
}
UnarchiveContainer makes a hub or a collection writable again.
func (UnarchiveContainer) Descriptor ¶
func (h UnarchiveContainer) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (UnarchiveContainer) Execute ¶
func (h UnarchiveContainer) Execute( ctx context.Context, actor appshared.ActorContext, cmd ArchiveContainerCommand, ) (domain.Container, error)
Execute unarchives the container and returns it as it now stands.
type UnarchiveWorkItem ¶
type UnarchiveWorkItem struct {
Lifecycle LifecycleWriter
}
UnarchiveWorkItem makes an archived entry writable again.
func (UnarchiveWorkItem) Descriptor ¶
func (h UnarchiveWorkItem) Descriptor() usecase.Descriptor
func (UnarchiveWorkItem) Execute ¶
func (h UnarchiveWorkItem) Execute( ctx context.Context, actor appshared.ActorContext, cmd LifecycleCommand, ) (domain.WorkItem, error)
Execute unarchives the entry and returns it as it now stands.
type UnassignWorkItem ¶
type UnassignWorkItem struct {
Assignment AssignmentWriter
}
UnassignWorkItem takes the assignee off an entry.
func (UnassignWorkItem) Descriptor ¶
func (h UnassignWorkItem) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (UnassignWorkItem) Execute ¶
func (h UnassignWorkItem) Execute( ctx context.Context, actor appshared.ActorContext, cmd AssignmentCommand, ) (domain.WorkItem, error)
Execute takes the assignee off the entry and returns it.
type UpdateBucket ¶
type UpdateBucket struct {
Writer BucketWriter
}
UpdateBucket changes a column's own fields.
What it does not change: where the column sits, which is ReorderBucket. A single endpoint writing both would need one audit entry covering everything and one event nobody could subscribe to narrowly.
func (UpdateBucket) Descriptor ¶
func (h UpdateBucket) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (UpdateBucket) Execute ¶
func (h UpdateBucket) Execute( ctx context.Context, actor appshared.ActorContext, cmd UpdateBucketCommand, ) (domain.Bucket, error)
Execute changes the column and returns it as it now stands.
type UpdateBucketCommand ¶
type UpdateBucketCommand struct {
BucketID shared.ID
// Attributes carries a pointer per field, so that "set it to nothing" and "do not touch it" stay
// two different requests all the way down from the merge patch that expressed them.
Attributes domain.BucketAttributes
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read none
// and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
UpdateBucketCommand is the input, typed.
type UpdateCommand ¶
type UpdateCommand struct {
ItemID shared.ID
// Attributes carries a pointer per field, so that "set it to nothing" and "do not touch it" stay
// two different requests all the way down from the merge patch that expressed them.
Attributes domain.ItemAttributes
// Due is the patch's touch on the due trio, nil when it left the trio alone. Raw rather than
// resolved, because the target is only decidable against the stored trio, which is read
// inside the transaction.
Due *domain.DuePatch
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read none
// and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
UpdateCommand is the input, typed.
type UpdateContainerPolicies ¶
type UpdateContainerPolicies struct {
Writer ContainerWriter
}
UpdateContainerPolicies replaces a collection's policies.
func (UpdateContainerPolicies) Descriptor ¶
func (h UpdateContainerPolicies) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (UpdateContainerPolicies) Execute ¶
func (h UpdateContainerPolicies) Execute( ctx context.Context, actor appshared.ActorContext, cmd UpdateContainerPoliciesCommand, ) (domain.Container, error)
Execute replaces the policies and returns the collection as it now stands.
type UpdateContainerPoliciesCommand ¶
type UpdateContainerPoliciesCommand struct {
ContainerID shared.ID
// Policies is the whole document rather than the keys that were sent. This is a PUT: a key the
// caller omitted is the default, not what happens to be stored.
Policies domain.ContainerPolicies
ExpectedVersion int
}
UpdateContainerPoliciesCommand is the input, typed.
type UpdateCustomField ¶
type UpdateCustomField struct {
Writer CustomFieldWriter
}
UpdateCustomField changes what a definition permits.
func (UpdateCustomField) Descriptor ¶
func (h UpdateCustomField) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (UpdateCustomField) Execute ¶
func (h UpdateCustomField) Execute( ctx context.Context, actor appshared.ActorContext, cmd UpdateCustomFieldCommand, ) (domain.CustomFieldDefinition, error)
Execute applies the change and returns the definition as it now stands.
type UpdateCustomFieldCommand ¶
type UpdateCustomFieldCommand struct {
FieldID shared.ID
Attributes domain.CustomFieldAttributes
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read
// none and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
UpdateCustomFieldCommand is the input, typed. A pointer per field, so that "set it to nothing" and "do not touch it" stay two different requests all the way down from the merge patch that expressed them.
type UpdateLabel ¶
type UpdateLabel struct {
Writer LabelWriter
}
UpdateLabel changes a label's own fields.
func (UpdateLabel) Descriptor ¶
func (h UpdateLabel) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (UpdateLabel) Execute ¶
func (h UpdateLabel) Execute( ctx context.Context, actor appshared.ActorContext, cmd UpdateLabelCommand, ) (domain.Label, error)
Execute changes the label and returns it as it now stands.
type UpdateLabelCommand ¶
type UpdateLabelCommand struct {
LabelID shared.ID
// Attributes carries a pointer per field, so that "set it to nothing" and "do not touch it"
// stay two different requests all the way down from the merge patch that expressed them.
Attributes domain.LabelAttributes
// ExpectedVersion is the version the caller read, from If-Match. Zero means the caller read
// none and accepts whatever is there (api-guidelines.md §5).
ExpectedVersion int
}
UpdateLabelCommand is the input, typed.
type UpdateReminder ¶
type UpdateReminder struct {
Writer ReminderWriter
}
UpdateReminder changes a reminder that has not fired yet.
func (UpdateReminder) Descriptor ¶
func (h UpdateReminder) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (UpdateReminder) Execute ¶
func (h UpdateReminder) Execute( ctx context.Context, actor appshared.ActorContext, cmd ChangeReminderCommand, ) (domain.Reminder, error)
Execute changes the reminder and returns it.
type UpdateSavedView ¶
type UpdateSavedView struct {
Writer SavedViewWriter
}
UpdateSavedView changes a view's own fields: its name, its layout, its query, its hints.
func (UpdateSavedView) Descriptor ¶
func (h UpdateSavedView) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4).
func (UpdateSavedView) Execute ¶
func (h UpdateSavedView) Execute( ctx context.Context, actor appshared.ActorContext, cmd SavedViewCommand, attributes view.ViewAttributes, ) (view.SavedView, error)
Execute applies the update and returns the view as it now stands.
type UpdateTemplate ¶
type UpdateTemplate struct {
Writer TemplateWriter
}
UpdateTemplate changes a template's name, description or tree.
func (UpdateTemplate) Descriptor ¶
func (h UpdateTemplate) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
func (UpdateTemplate) Execute ¶
func (h UpdateTemplate) Execute( ctx context.Context, actor appshared.ActorContext, cmd ChangeTemplateCommand, ) (domain.Template, error)
Execute changes the template and returns it.
type UpdateWorkItem ¶
type UpdateWorkItem struct {
Items repository.Items
Buckets repository.Buckets
Containers repository.Containers
Profiles metarepo.CapabilityProfiles
// Gate is what the entry's collection offers it: the refusal before a write of what it
// hides, and the mark on the answer (domain-model.md §2).
Gate EntryGate
Authorizer Authorizer
Events outbox.Events
Changes changelog.ChangeLog
Audit audit.Sink
Activity ActivityJournal
UnitOfWork persistence.UnitOfWork
Clock clock.Clock
IDs clock.IDGenerator
HLC clock.HLCSource
// DueDates is the writer the patch dispatches the due trio into.
DueDates DueDateWriter
// Text brings a changed title or notes to normal form C on the way in (i18n-l10n.md §5).
Text text.Normalizer
// Flow refuses an entry entering a column past the first without its required fields, where
// the collection applies that (FlowGate).
Flow FlowGate
// Structure resolves the collection's structure in force, which decides whether it offers an
// amount (ensureAmountOffered).
Structure StructureResolver
}
UpdateWorkItem changes an item's own fields: what it is called, and what is noted on it.
The capability profile is the gate. Notes on an ACTIVITY come back as `capability_not_supported` naming the type and the capability rather than being quietly dropped - silent ignoring is how a client comes to believe it stored something (domain-model.md §2, ADR-0006).
What it does not change: where the item sits (MoveWorkItem), whether it is done (CompleteWorkItem), which labels it carries (AddLabel - a set is not a field), and the assignee, which is an action route because two ways to write that column would be two places deciding whether the person may see the entry. The due date is not on that list, in a particular way: the contract carries the three due fields on this schema, so the patch serves them - by dispatching into the writer the SetDueDate pair owns, which is how the fields keep one validation, one event and one history whichever door they arrive through.
func (UpdateWorkItem) Descriptor ¶
func (h UpdateWorkItem) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry. Registering it is what makes the use case reachable through REST, MCP and automation at once (arc42 §4).
func (UpdateWorkItem) Execute ¶
func (h UpdateWorkItem) Execute( ctx context.Context, actor appshared.ActorContext, cmd UpdateCommand, ) (domain.WorkItem, error)
Execute applies the update and returns the item as it now stands.
type Visibility ¶
type Visibility interface {
CanSee(
ctx context.Context, actor appshared.ActorContext, accountID shared.ID,
path []identity.Scope,
) (bool, error)
}
Visibility is the slice of the authorisation service this package needs beyond Authorizer: the same membership question asked about somebody other than the actor.
Its own interface rather than a second method on Authorizer, on the reasoning that split Reader off: a use case declaring a dependency it does not use is a use case whose test has to satisfy it anyway, and most writers here never ask about a second person.
type WithdrawPreset ¶
type WithdrawPreset struct{ Desk PresetDesk }
WithdrawPreset withdraws a saved preset (UC-WRK-30 check 3): offered no longer, while what was made from it keeps its settings. The workspace's default is refused until another is chosen.
func (WithdrawPreset) Descriptor ¶
func (h WithdrawPreset) Descriptor() usecase.Descriptor
Descriptor is the catalogue entry.
type WorkspaceReader ¶
WorkspaceReader reads the workspace the transaction is bound to, for the language it declared.
type WorkspaceStructureAnswer ¶
type WorkspaceStructureAnswer struct {
Resolver StructureResolver
UnitOfWork persistence.UnitOfWork
}
WorkspaceStructureAnswer answers the workspace's own structure for `GET /tenant`, in its own read-only transaction: what every hub and collection inherits, the locks, and the default preset.
func (WorkspaceStructureAnswer) WorkspaceStructure ¶
func (a WorkspaceStructureAnswer) WorkspaceStructure( ctx context.Context, actor appshared.ActorContext, ) (map[string]any, error)
WorkspaceStructure is the WorkspaceStructure schema.
type WorkspaceStructureResult ¶
type WorkspaceStructureResult struct {
Workspace domain.WorkspaceStructure
Structure domain.EffectiveStructure
Version int
}
WorkspaceStructureResult is the workspace's structure after the change, with the tenant row's version for the next If-Match.
Source Files
¶
- Activity.go
- AddComment.go
- AddLabel.go
- AddMember.go
- AmountGate.go
- ArchiveContainer.go
- ArchiveWorkItem.go
- AssignWorkItem.go
- AttachMedia.go
- AutoAssignWorkItem.go
- BulkUpdateWorkItems.go
- CalendarFeed.go
- ChangeComment.go
- ChangeCustomField.go
- ChangePreset.go
- ChangeReminder.go
- ChangeSavedView.go
- ChangeTemplate.go
- CloseStarter.go
- CompleteWorkItem.go
- CreateBucket.go
- CreateContainer.go
- CreateLabel.go
- CreateWorkItem.go
- DefineCustomField.go
- DeleteBucket.go
- DuplicateWorkItem.go
- Embedding.go
- EmbeddingSeed.go
- EmergencyAccesses.go
- EntryGate.go
- EntryGateWrites.go
- ExportItems.go
- ExportView.go
- FigureReader.go
- FireReminders.go
- FlowGate.go
- InstantiateTemplate.go
- Items.go
- JumbleDestinations.go
- Kudos.go
- LastMember.go
- ListComments.go
- ListPresets.go
- ListPrivateHubs.go
- MaterializeOccurrences.go
- MoveContainer.go
- MoveWorkItem.go
- OpenPrivateHub.go
- Paging.go
- Path.go
- PreviewContainerStructure.go
- QueryItems.go
- ReadActivity.go
- ReadCalendarFeed.go
- ReadContainers.go
- ReadTrash.go
- ReadWorkItems.go
- Recurrence.go
- ReindexSearch.go
- Reminder.go
- ReorderContainer.go
- Restricted.go
- SavePreset.go
- SavedPresets.go
- SavedView.go
- SearchItems.go
- SearchMeaning.go
- SetContainerStructure.go
- SetCover.go
- SetCustomField.go
- SetDueDate.go
- SetHubPrivacy.go
- SetWorkspaceStructure.go
- Snapshots.go
- StructureCategories.go
- StructureDocuments.go
- StructureFlatten.go
- StructureInput.go
- StructureOutput.go
- StructureResolver.go
- StructureWrites.go
- Template.go
- Translate.go
- TrashContainer.go
- TrashWorkItem.go
- UpdateBucket.go
- UpdateContainer.go
- UpdateLabel.go
- UpdateWorkItem.go