createdAt = new \DateTimeImmutable(); } // Factory Methods public static function builder(int $taskId, string $executor, AssigneeType $executorType): TaskResultBuilder { return new TaskResultBuilder($taskId, $executor, $executorType); } // Getters - Only for necessary access public function id(): ?int { return $this->id; } public function taskId(): int { return $this->taskId; } public function assignmentId(): ?int { return $this->assignmentId; } public function executor(): string { return $this->executor; } public function executorType(): AssigneeType { return $this->executorType; } public function modelName(): ?string { return $this->modelName; } public function request(): ?string { return $this->request; } public function response(): ?string { return $this->response; } public function duration(): ExecutionDuration { return $this->duration; } public function metrics(): ExecutionMetrics { return $this->metrics; } public function status(): ResultStatus { return $this->status; } public function errorMessage(): ?string { return $this->errorMessage; } public function createdAt(): \DateTimeImmutable { return $this->createdAt; } // Setter only for infrastructure (persistence) public function setId(int $id): void { $this->id = $id; } // Domain Logic public function isSuccess(): bool { return $this->status === ResultStatus::SUCCESS; } public function hasError(): bool { return $this->status === ResultStatus::ERROR; } public function hasModel(): bool { return $this->modelName !== null; } public function summary(): string { $parts = [ $this->executor, $this->status->label(), ]; if ($this->modelName !== null) { $parts[] = "({$this->modelName})"; } return implode(' - ', $parts); } // Legacy getters for backward compatibility (deprecated) public function getId(): ?int { return $this->id; } public function getTaskId(): int { return $this->taskId; } public function getAssignmentId(): ?int { return $this->assignmentId; } public function getExecutor(): string { return $this->executor; } public function getExecutorType(): AssigneeType { return $this->executorType; } public function getModelName(): ?string { return $this->modelName; } public function getRequest(): ?string { return $this->request; } public function getResponse(): ?string { return $this->response; } public function getRequestTimestamp(): \DateTimeImmutable { return $this->duration->requestedAt(); } public function getResponseTimestamp(): ?\DateTimeImmutable { return $this->duration->respondedAt(); } public function getDurationMs(): ?int { return $this->duration->durationMs(); } public function getTokensInput(): ?int { return $this->metrics->tokens()->input(); } public function getTokensOutput(): ?int { return $this->metrics->tokens()->output(); } public function getTokensTotal(): ?int { return $this->metrics->tokens()->total(); } public function getCostUsd(): ?float { return $this->metrics->costUsd(); } public function getStatus(): string { return $this->status->value; } public function getErrorMessage(): ?string { return $this->errorMessage; } public function getCreatedAt(): \DateTimeImmutable { return $this->createdAt; } // Serialization public function toArray(): array { $durationData = $this->duration->toArray(); $metricsData = $this->metrics->toArray(); return [ 'id' => $this->id, 'task_id' => $this->taskId, 'assignment_id' => $this->assignmentId, 'executor' => $this->executor, 'executor_type' => $this->executorType->value, 'model_name' => $this->modelName, 'request' => $this->request, 'response' => $this->response, 'request_timestamp' => $durationData['requested_at'], 'response_timestamp' => $durationData['responded_at'], 'duration_ms' => $durationData['duration_ms'], 'tokens_input' => $metricsData['tokens_input'], 'tokens_output' => $metricsData['tokens_output'], 'tokens_total' => $metricsData['tokens_total'], 'cost_usd' => $metricsData['cost_usd'], 'status' => $this->status->value, 'error_message' => $this->errorMessage, 'created_at' => $this->createdAt->format('Y-m-d H:i:s.u'), ]; } public static function fromArray(array $data): self { $executorType = AssigneeType::from($data['executor_type']); $status = isset($data['status']) ? ResultStatus::from($data['status']) : ResultStatus::SUCCESS; $requestTimestamp = isset($data['request_timestamp']) ? new \DateTimeImmutable($data['request_timestamp']) : new \DateTimeImmutable(); $responseTimestamp = isset($data['response_timestamp']) ? new \DateTimeImmutable($data['response_timestamp']) : null; $duration = $responseTimestamp !== null ? ExecutionDuration::completed($requestTimestamp, $responseTimestamp) : ExecutionDuration::start($requestTimestamp); $tokens = TokenCount::fromNullable( $data['tokens_input'] ?? null, $data['tokens_output'] ?? null ); $metrics = ExecutionMetrics::create( $tokens, isset($data['cost_usd']) ? (float) $data['cost_usd'] : null, isset($data['duration_ms']) ? (int) $data['duration_ms'] : null ); $result = new self( (int) $data['task_id'], isset($data['assignment_id']) ? (int) $data['assignment_id'] : null, $data['executor'], $executorType, $data['model_name'] ?? null, $data['request'] ?? null, $data['response'] ?? null, $duration, $metrics, $status, $data['error_message'] ?? null ); if (isset($data['id'])) { $result->setId((int) $data['id']); } if (isset($data['created_at'])) { $result->createdAt = new \DateTimeImmutable($data['created_at']); } return $result; } }