nextVersionNumber($policy); $version = PolicyVersion::create([ 'tenant_id' => $policy->tenant_id, 'policy_id' => $policy->id, 'version_number' => $versionNumber, 'policy_type' => $policy->policy_type, 'platform' => $policy->platform, 'created_by' => $createdBy, 'captured_at' => CarbonImmutable::now(), 'snapshot' => $payload, 'metadata' => $metadata, ]); $this->auditLogger->log( tenant: $policy->tenant, action: 'policy.versioned', context: [ 'metadata' => [ 'policy_id' => $policy->id, 'version_number' => $versionNumber, ], ], actorEmail: $createdBy, resourceType: 'policy', resourceId: (string) $policy->id ); return $version; } public function captureFromGraph( Tenant $tenant, Policy $policy, ?string $createdBy = null, array $metadata = [], ): PolicyVersion { $snapshot = $this->snapshotService->fetch($tenant, $policy, $createdBy); if (isset($snapshot['failure'])) { $reason = $snapshot['failure']['reason'] ?? 'Unable to fetch policy snapshot'; throw new \RuntimeException($reason); } $metadata = array_merge(['source' => 'version_capture'], $metadata); return $this->captureVersion( policy: $policy, payload: $snapshot['payload'], createdBy: $createdBy, metadata: $metadata, ); } private function nextVersionNumber(Policy $policy): int { $current = PolicyVersion::query() ->where('policy_id', $policy->id) ->max('version_number'); return (int) ($current ?? 0) + 1; } }