feat(t208): close candidate decision feedback loop
This commit is contained in:
@@ -72,6 +72,7 @@ func NewDeviceRouteRegistrar(
|
||||
routes.POST("/api/v1/tasks/:id/events", handler.appendEvents)
|
||||
routes.POST("/api/v1/tasks/:id/evidence", handler.uploadEvidence)
|
||||
routes.POST("/api/v1/tasks/:id/candidates", handler.storeCandidates)
|
||||
routes.POST("/api/v1/tasks/:id/human-reviews", handler.storeHumanReview)
|
||||
routes.POST("/api/v1/tasks/:id/complete", handler.completeTask)
|
||||
routes.POST("/api/v1/tasks/:id/fail", handler.failTask)
|
||||
return nil
|
||||
@@ -500,6 +501,53 @@ func (handler *deviceHandlers) storeCandidates(ctx *gin.Context) {
|
||||
ctx.JSON(http.StatusOK, gin.H{"replayed": replayed})
|
||||
}
|
||||
|
||||
func (handler *deviceHandlers) storeHumanReview(ctx *gin.Context) {
|
||||
principal, ok := devicePrincipal(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var request struct {
|
||||
ExecutionID string `json:"execution_id"`
|
||||
ClaimGeneration int64 `json:"claim_generation"`
|
||||
TaskContentSHA256 string `json:"task_content_sha256"`
|
||||
ReasonSchemaVersion int `json:"reason_schema_version"`
|
||||
Outcome string `json:"outcome"`
|
||||
SelectedCandidateOrdinal *int `json:"selected_candidate_ordinal"`
|
||||
PrimaryReasonCode string `json:"primary_reason_code"`
|
||||
Note string `json:"note"`
|
||||
SupersedesReviewID *string `json:"supersedes_review_id"`
|
||||
Items []usecase.CandidateHumanReviewItemInput `json:"items"`
|
||||
}
|
||||
if !decodeDeviceJSON(ctx, &request) {
|
||||
return
|
||||
}
|
||||
result, err := handler.services.Results.StoreHumanReview(
|
||||
ctx.Request.Context(),
|
||||
usecase.StoreCandidateHumanReviewCommand{
|
||||
Identity: handler.executionResultIdentity(
|
||||
ctx, principal, request.ExecutionID, request.ClaimGeneration,
|
||||
),
|
||||
TaskContentSHA256: request.TaskContentSHA256,
|
||||
ReasonSchemaVersion: request.ReasonSchemaVersion,
|
||||
Outcome: request.Outcome,
|
||||
SelectedCandidateOrdinal: request.SelectedCandidateOrdinal,
|
||||
PrimaryReasonCode: request.PrimaryReasonCode,
|
||||
Note: request.Note,
|
||||
SupersedesReviewID: request.SupersedesReviewID,
|
||||
Items: request.Items,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
writeUsecaseError(ctx, err)
|
||||
return
|
||||
}
|
||||
ctx.Header("Cache-Control", "no-store")
|
||||
ctx.JSON(http.StatusOK, gin.H{
|
||||
"review": candidateHumanReviewResponse(result.Review),
|
||||
"replayed": result.Replayed,
|
||||
})
|
||||
}
|
||||
|
||||
func (handler *deviceHandlers) completeTask(ctx *gin.Context) {
|
||||
principal, ok := devicePrincipal(ctx)
|
||||
if !ok {
|
||||
@@ -624,6 +672,37 @@ func deviceEvidenceResponse(evidence domain.ExecutionEvidenceAsset) gin.H {
|
||||
}
|
||||
}
|
||||
|
||||
func candidateHumanReviewResponse(review domain.CandidateHumanReview) gin.H {
|
||||
items := make([]gin.H, 0, len(review.Items))
|
||||
for _, item := range review.Items {
|
||||
items = append(items, gin.H{
|
||||
"candidate_ordinal": item.CandidateOrdinal,
|
||||
"label": item.Label,
|
||||
"primary_reason_code": item.PrimaryReasonCode,
|
||||
"reason_codes": item.ReasonCodes,
|
||||
"note": item.Note,
|
||||
})
|
||||
}
|
||||
return gin.H{
|
||||
"id": review.ID,
|
||||
"task_id": review.TaskID,
|
||||
"execution_id": review.ExecutionID,
|
||||
"task_content_sha256": review.TaskContentSHA256,
|
||||
"version": review.Version,
|
||||
"reason_schema_version": review.ReasonSchemaVersion,
|
||||
"outcome": review.Outcome,
|
||||
"selected_candidate_ordinal": review.SelectedCandidateOrdinal,
|
||||
"primary_reason_code": review.PrimaryReasonCode,
|
||||
"note": review.Note,
|
||||
"supersedes_review_id": review.SupersedesReviewID,
|
||||
"actor_user_id": review.ActorUserID,
|
||||
"actor_device_id": review.ActorDeviceID,
|
||||
"created_at": formatTime(review.CreatedAt),
|
||||
"received_after_execution_expiry": review.ReceivedAfterExecutionExpiry,
|
||||
"items": items,
|
||||
}
|
||||
}
|
||||
|
||||
type lifecycleTransitionRequest struct {
|
||||
DeviceID string `json:"device_id"`
|
||||
ClaimGeneration int64 `json:"claim_generation"`
|
||||
|
||||
Reference in New Issue
Block a user