Interface AnnotationSetService
- All Known Implementing Classes:
AnnotationSetServiceImpl
AnnotationSet rows. Drives the
/datasets/{id}/annotation-sets + /annotation-sets/{id}
REST endpoints and the /preboarded/{id}/annotation-sets
pre-load surface.
attach(Investigation, AnnotationSetRole, AnnotationSetSource, AgentCurationKind, String, String, String, String, Date, String, AnnotationSet) is idempotent on
(investigation, role, runId): a retry with the same triple
returns the existing row instead of creating a duplicate. The REST
layer reports the existing row as 200 OK rather than 201 Created in
that case.
-
Nested Class Summary
Nested ClassesModifier and TypeInterfaceDescriptionstatic classstatic classWho produced an annotation set, and from which build. -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringRun-id prefix on aSNAPSHOTtaken by a curation commit to keep the state it displaced, as opposed to one a caller asked for throughPOST /datasets/{id}/annotation-sets/snapshot. -
Method Summary
Modifier and TypeMethodDescriptionattach(Investigation investigation, AnnotationSetRole role, AnnotationSetSource source, AgentCurationKind kind, String runId, String createdBy, String agentVersion, String model, Date ranAt, String payloadJson, AnnotationSet parent) Attach (or return existing) an annotation set to the given investigation.attach(Investigation investigation, AnnotationSetRole role, AnnotationSetSource source, AgentCurationKind kind, String runId, String createdBy, AnnotationSetService.RunProvenance runProvenance, String payloadJson, AnnotationSet parent) Asattach(Investigation, AnnotationSetRole, AnnotationSetSource, AgentCurationKind, String, String, String, String, Date, String, AnnotationSet), but carrying the full run provenance rather than theagentVersion/model/ranAttriple.longcountByInvestigation(Investigation investigation, AnnotationSetRole roleFilter) countByRoleSince(Date since) longcountDistinctRunIdsSince(Date since, AnnotationSetRole roleFilter) longcountSince(Date since, AnnotationSetRole roleFilter) longcountSummaries(AnnotationSetRole roleFilter, AnnotationSetSource sourceFilter, String createdByFilter, AgentCurationKind kindFilter, String statusFilter, List<Long> investigationIds) booleanDelete the row with the given id.finalizeSet(Long id, String finalizedBy, String notes) StampfinalizedAt+finalizedByon the row, marking a DRAFT as "done editing" or a SNAPSHOT as the polished canonical view.findByInvestigation(Investigation investigation, AnnotationSetRole roleFilter) findByInvestigationAndRoleAndRunId(Investigation investigation, AnnotationSetRole role, String runId) findLatestByInvestigation(Investigation investigation, AnnotationSetRole roleFilter) findLatestCreatedAt(AnnotationSetRole roleFilter) findSummariesByInvestigation(Investigation investigation, AnnotationSetRole roleFilter) Thin metadata projection.listSummaries(AnnotationSetRole roleFilter, AnnotationSetSource sourceFilter, String createdByFilter, AgentCurationKind kindFilter, String statusFilter, List<Long> investigationIds, int offset, int limit, AnnotationSetDao.SummarySort sort, boolean descending) intrebindInvestigation(Investigation from, Investigation to) ClearfinalizedAt+finalizedBy+finalizedNoteson the row, reopening a finalized DRAFT or unblessing a polished SNAPSHOT.updateProvenance(Long id, AnnotationSetService.RunProvenance provenance) Correct the run-provenance envelope on an existing row in place, so a mis-stampedagentVersion/model/agentName/runSha/ranAtis fixed without the delete-and-recreate that mints a new id.updateStatus(AnnotationSet annotationSet, String status) Record where a proposal stands with its reviewer, and return the updated set.upsertDraft(Investigation investigation, String createdBy, String payloadJson, String parkedElements, AnnotationSet parent) Convenience overload for the common DRAFT-upsert path used by the curation-UI: ensures one DRAFT per(investigation, curator)by derivingrunIdas"draft-{createdBy}".
-
Field Details
-
PRE_COMMIT_SNAPSHOT_RUN_ID_PREFIX
Run-id prefix on aSNAPSHOTtaken by a curation commit to keep the state it displaced, as opposed to one a caller asked for throughPOST /datasets/{id}/annotation-sets/snapshot. Both are ordinary snapshots and restore the same way; the prefix is what tells them apart in a list.The rest of the id is a UUID, as for any snapshot — a commit-taken backup names no run of its own.
- See Also:
-
-
Method Details
-
attach
AnnotationSetService.AttachedAnnotationSet attach(Investigation investigation, AnnotationSetRole role, AnnotationSetSource source, @Nullable AgentCurationKind kind, @Nullable String runId, @Nullable String createdBy, @Nullable String agentVersion, @Nullable String model, @Nullable Date ranAt, @Nullable String payloadJson, @Nullable AnnotationSet parent) Attach (or return existing) an annotation set to the given investigation. TherunIdsemantic depends on role and is the caller's responsibility:PROPOSAL— pass the agent runner's unique id.DRAFT— pass"draft-{createdBy}"(or the service derives it if blank; seeupsertDraft(Investigation, String, String, String, AnnotationSet)).SNAPSHOT— pass a generated UUID, or let the service generate one whenrunIdis null/blank.
- Returns:
- the persisted row plus a flag noting whether it was created (true) or returned as existing (false).
-
attach
AnnotationSetService.AttachedAnnotationSet attach(Investigation investigation, AnnotationSetRole role, AnnotationSetSource source, @Nullable AgentCurationKind kind, @Nullable String runId, @Nullable String createdBy, @Nullable AnnotationSetService.RunProvenance runProvenance, @Nullable String payloadJson, @Nullable AnnotationSet parent) Asattach(Investigation, AnnotationSetRole, AnnotationSetSource, AgentCurationKind, String, String, String, String, Date, String, AnnotationSet), but carrying the full run provenance rather than theagentVersion/model/ranAttriple.An overload rather than two more parameters on the method above: that signature already takes eleven arguments, six of which would then be adjacent nullable strings meaning different things (
runId,createdBy,agentVersion,model,runSha,agentName). Transposing two of those compiles silently and mis-files provenance in a way nothing would catch.AnnotationSetService.RunProvenancemakes that impossible and leaves the existing callers untouched. -
upsertDraft
AnnotationSet upsertDraft(Investigation investigation, String createdBy, String payloadJson, @Nullable String parkedElements, @Nullable AnnotationSet parent) Convenience overload for the common DRAFT-upsert path used by the curation-UI: ensures one DRAFT per(investigation, curator)by derivingrunIdas"draft-{createdBy}". If a row already exists, itspayloadJson/parkedElements/updatedAtare updated in place; otherwise a new row is created.- Parameters:
parent- optionalPROPOSALthis draft was seeded from (forms the lineage edge for diff-derived dispositions).
-
findByInvestigation
List<AnnotationSet> findByInvestigation(Investigation investigation, @Nullable AnnotationSetRole roleFilter) - Returns:
- all sets attached to the given investigation matching the role filter (or all roles if null), newest first.
-
findSummariesByInvestigation
List<AnnotationSetSummaryValueObject> findSummariesByInvestigation(Investigation investigation, @Nullable AnnotationSetRole roleFilter) Thin metadata projection. -
findLatestByInvestigation
@Nullable AnnotationSet findLatestByInvestigation(Investigation investigation, @Nullable AnnotationSetRole roleFilter) -
load
-
findByInvestigationAndRoleAndRunId
@Nullable AnnotationSet findByInvestigationAndRoleAndRunId(Investigation investigation, AnnotationSetRole role, String runId) -
countByInvestigation
-
rebindInvestigation
-
listSummaries
List<AnnotationSetSummaryValueObject> listSummaries(@Nullable AnnotationSetRole roleFilter, @Nullable AnnotationSetSource sourceFilter, @Nullable String createdByFilter, @Nullable AgentCurationKind kindFilter, @Nullable String statusFilter, @Nullable List<Long> investigationIds, int offset, int limit, @Nullable AnnotationSetDao.SummarySort sort, boolean descending) -
countSummaries
long countSummaries(@Nullable AnnotationSetRole roleFilter, @Nullable AnnotationSetSource sourceFilter, @Nullable String createdByFilter, @Nullable AgentCurationKind kindFilter, @Nullable String statusFilter, @Nullable List<Long> investigationIds) -
finalizeSet
StampfinalizedAt+finalizedByon the row, marking a DRAFT as "done editing" or a SNAPSHOT as the polished canonical view. Idempotent: a row already finalized returns unchanged (no re-stamp).- Parameters:
notes- the curator's closing note, ornull. Trimmed, and truncated to fit rather than rejected — losing the tail of an explanation is a smaller harm than refusing a closure the curator has already decided on. A blank note is stored asnull.🛑 The one thing this is NOT idempotent about. A set that is already finalized is still re-stamped with a non-blank
notes, because dropping the sentence and answering 200 is the exact failure the parameter exists to fix; a caller cannot tell that from a successful write. Everything else about the row stays as it was.
-
reopenSet
ClearfinalizedAt+finalizedBy+finalizedNoteson the row, reopening a finalized DRAFT or unblessing a polished SNAPSHOT. Idempotent: a row already not finalized returns unchanged.The note goes with the closure it explains. Carrying it across a reopen would attach one closure's words to the next one, and the UI pre-fills the re-close box from the value it read BEFORE reopening.
-
updateProvenance
Correct the run-provenance envelope on an existing row in place, so a mis-stampedagentVersion/model/agentName/runSha/ranAtis fixed without the delete-and-recreate that mints a new id. Only the envelope moves; the set's content (payloadJson,parkedElements), its identity (role,source,runId,investigation,parent) and its finalized status are untouched.Per field: null leaves the stored value alone, and a blank string clears it.
ranAthas no blank form, so it can be corrected but not cleared.- Returns:
- the updated row, or null if no row has that id
-
delete
Delete the row with the given id. Returns true if a row was removed, false if no such row existed. Cascades on parent edges are governed byON DELETE SET NULL: descendants survive, theirparentlink is cleared. -
countSince
-
countByRoleSince
-
countDistinctRunIdsSince
-
findLatestCreatedAt
-
updateStatus
Record where a proposal stands with its reviewer, and return the updated set.🛑 The value is stored as given. There is deliberately no vocabulary check here — Paul, 2026-09-04: "don't lock us into any kind of enums. if we settle down on this we might formalize it."
pending | needs_changes | accepted | rejectedare the values in use, not the values permitted, and a caller sending a fifth gets it stored rather than rejected. The only gate is structural: non-blank, and short enough for the column.- Parameters:
annotationSet- the set to rule onstatus- the status, already trimmed and lowercased by the caller- Returns:
- the updated set
-