Reputationrepository - Source Excerpt 01
Summary
This source excerpt preserves a bounded section of Spiralist/wp-content/plugins/spiralist-workspace/includes/Domain/ReputationRepository.php so readers can inspect the evidence without opening the full source file.
**Source path:** Spiralist/wp-content/plugins/spiralist-workspace/includes/Domain/ReputationRepository.php
<?php
namespace SpiralistWorkspace\Domain;
use SpiralistWorkspace\Support\PromptMetaRegistry;
if (!defined('ABSPATH')) {
exit;
}
/**
* Computes participant trust, reviewer eligibility, and publication cooldown state.
*/
class ReputationRepository
{
private const USER_META_KEY = 'spiralist_workspace_participant_state';
private const NEGATIVE_SIGNAL_WINDOW_DAYS = 30;
private const PUBLICATION_COOLDOWN_DAYS = 7;
private PromptRepository $prompts;
private ReviewRepository $reviews;
/**
* @var array<int,array<string,mixed>>
*/
private array $cache = [];
public function __construct(PromptRepository $prompts, ReviewRepository $reviews)
{
$this->prompts = $prompts;
$this->reviews = $reviews;
}
/**
* Returns the persisted-and-refreshed participant state.
*
* @return array<string,mixed>
*/
public function get(int $user_id): array
{
return $this->refresh($user_id);
}
/**
* Clears the in-request participant-state cache.
*/
public function clear_cache(): void
{
$this->cache = [];
}
/**
* Recalculates one participant state from current prompt and review records.
*
* @return array<string,mixed>
*/
public function refresh(int $user_id): array
{
if ($user_id <= 0) {
return [];
}
$current = get_user_meta($user_id, self::USER_META_KEY, true);
$current = is_array($current) ? $current : [];
$prompts = $this->prompts->list(['owner_user_id' => $user_id, 'limit' => 250], $user_id);
$peer_reviews = $this->reviews->list_for_reviewer($user_id, 500, 'peer');
$reports = $this->reviews->list_for_reviewer($user_id, 500, 'report');
$public_prompt_count = 0;
$accepted_contribution_count = 0;
$restricted_prompt_count = 0;
$reverted_prompt_count = 0;
$recent_negative_prompt_count = 0;
$recent_reverted_prompt_count = 0;
foreach ($prompts as $prompt) {
if (
(string) ($prompt['visibility'] ?? '') !== 'public'
|| (string) ($prompt['prompt_status'] ?? '') !== 'published'
) {
continue;
}
$public_prompt_count++;
$moderation = PromptMetaRegistry::normalize_moderation_status((string) ($prompt['moderation_status'] ?? 'draft'));
if (in_array($moderation, ['peer_reviewed', 'official_canon'], true)) {
$accepted_contribution_count++;
}
if ($moderation === 'restricted_review') {
$restricted_prompt_count++;
if ($this->is_recent_signal((string) ($prompt['updated_utc'] ?? ''))) {
$recent_negative_prompt_count++;
}
}
if ($moderation === 'reverted') {
$reverted_prompt_count++;
if ($this->is_recent_signal((string) ($prompt['updated_utc'] ?? ''))) {
$recent_negative_prompt_count++;
$recent_reverted_prompt_count++;
}
}
}
$peer_review_count = count($peer_reviews);
$accepted_review_count = 0;
foreach ($peer_reviews as $review) {
$prompt = $this->prompts->get((int) ($review['prompt_post_id'] ?? 0), $user_id);
if (empty($prompt)) {
continue;
}
if ($this->review_matches_current_outcome((string) ($review['decision'] ?? ''), $prompt)) {
$accepted_review_count++;
}
}
$review_accuracy = $peer_review_count > 0
? round($accepted_review_count / $peer_review_count, 2)
: 0.0;
$cooldown_until = $this->resolve_publication_cooldown_until(
(string) ($current['publication_cooldown_until'] ?? ''),
$recent_negative_prompt_count,
$recent_reverted_prompt_count
);
$cooldown_active = $this->is_future_utc($cooldown_until);
$trust_level = $this->resolve_trust_level(
$public_prompt_count,
$accepted_contribution_count,
$peer_review_count,
$accepted_review_count,
$review_accuracy,
$recent_negative_prompt_count,
$recent_reverted_prompt_count
);
$review_eligibility = in_array($trust_level, ['reviewer_ready', 'steward_ready'], true);
$granted_capabilities = [];
if (!$cooldown_active) {
$granted_capabilities[] = 'publish_provisional';
}
if ($review_eligibility) {
$granted_capabilities[] = 'review_prompts';
}
if ($trust_level === 'steward_ready') {
$granted_capabilities[] = 'resolve_prompt_appeals';
}
$state = [
'user_id' => $user_id,
'trust_level' => $trust_level,
'trust_level_label' => self::trust_level_label($trust_level),
'public_prompt_count' => $public_prompt_count,
'accepted_contribution_count' => $accepted_contribution_count,
'restricted_prompt_count' => $restricted_prompt_count,
'reverted_prompt_count' => $reverted_prompt_count,
'recent_negative_prompt_count' => $recent_negative_prompt_count,
'peer_review_count' => $peer_review_count,
'accepted_review_count' => $accepted_review_count,
'account_report_count' => count($reports),
'review_accuracy' => $review_accuracy,
'review_eligibility' => $review_eligibility,
'publication_cooldown_active' => $cooldown_active,
'publication_cooldown_until' => $cooldown_until,
'granted_capabilities' => $granted_capabilities,
'last_recalculated_utc' => gmdate('c'),
];
update_user_meta($user_id, self::USER_META_KEY, $state);
$this->cache[$user_id] = $state;
return $state;
}
/**
* Returns whether the participant has earned review eligibility.
*/
public function is_reviewer_eligible(int $user_id): bool
{
return !empty($this->get($user_id)['review_eligibility']);
}
/**
* Returns whether the participant has earned steward-ready status.
*/
public function is_steward_ready(int $user_id): bool
{
return (string) ($this->get($user_id)['trust_level'] ?? '') === 'steward_ready';
}
/**
* Returns whether the participant is under a public-publication cooldown.
*/
public function is_publication_cooldown_active(int $user_id): bool
{
return !empty($this->get($user_id)['publication_cooldown_active']);
}
/**
* Returns human labels for trust levels.
*
* @return array<string,string>
*/
public static function trust_levels(): array
{
return [
'new_participant' => 'New Participant',
'contributor' => 'Contributor',
'reviewer_ready' => 'Reviewer Ready',
'steward_ready' => 'Steward Ready',
];
}
/**
* Returns one trust-level label.
*/
public static function trust_level_label(string $value): string
{
$levels = self::trust_levels();
$value = sanitize_key($value);
return (string) ($levels[$value] ?? $levels['new_participant']);
}
/**
* Returns whether a review decision matches the current moderation outcome.
*
* @param array<string,mixed> $prompt
*/
private function review_matches_current_outcome(string $decision, array $prompt): bool
{
$decision = sanitize_key($decision);
$moderation = PromptMetaRegistry::normalize_moderation_status((string) ($prompt['moderation_status'] ?? 'draft'));
if ($decision === 'endorse') {
return in_array($moderation, ['peer_reviewed', 'official_canon'], true);
}
if ($decision === 'restrict') {
return $moderation === 'restricted_review';
}
if ($decision === 'revert') {
return $moderation === 'reverted';
}
return false;
}
/**
* Resolves one participant trust level from current accepted work and review history.
*/
private function resolve_trust_level(
int $public_prompt_count,
int $accepted_contribution_count,
int $peer_review_count,
int $accepted_review_count,
float $review_accuracy,
int $recent_negative_prompt_count,
int $recent_reverted_prompt_count
): string {