PR Vision v0.1.1: rename from peptide-geo-monitor + QA P2 fixes (#2)

Rename to PR Vision (PRV_/pr-vision); fix www-strip char-mask in competitor display (str_starts_with); make PHPCS a CI gate; remove dead in-run cache.

QA: APPROVE — qa-reviews/pr-vision/2026-06-13-c633fdb9e1541386a713a6de12d2ee2ae4239640.md

Agent-Session: pr-vision rename v0.1.1 merge
This commit is contained in:
peptiderepo
2026-06-13 17:17:52 +07:00
committed by GitHub
parent 46fda89e18
commit 401077fe45
40 changed files with 1277 additions and 1056 deletions

View File

@@ -1,8 +1,14 @@
<?php
/**
* AI-visibility dashboard panel renderer.
*
* @package PrVision
*/
declare(strict_types=1);
/**
* AI-visibility dashboard panel — renders the GEO Monitor admin page.
* AI-visibility dashboard panel — renders the PR Vision admin page.
*
* Renders four sections:
* 1. Proxy-metric disclaimer note.
@@ -10,31 +16,31 @@ declare(strict_types=1);
* 3. Per-peptide standings table (cited, position, top competitor domains).
* 4. MTD cost vs cap + last-run time.
*
* All output is escaped. Chart.js is enqueued via PGM_Admin_Page.
* All output is escaped. Chart.js is enqueued via PRV_Admin_Page.
*
* Who triggers: PGM_Admin_Page::render_page() via PGM_Collector_Registry.
* Dependencies: PGM_Ai_Visibility_Collector (data format contract).
* Who triggers: PRV_Admin_Page::render_page() via PRV_Collector_Registry.
* Dependencies: PRV_Ai_Visibility_Collector (data format contract).
*
* @see interface-pgm-dashboard-panel.php — Interface this implements.
* @see class-pgm-ai-visibility-collector.php — Produces the $data array.
* @see class-pgm-admin-page.php — Enqueues Chart.js; calls render().
* @package PeptideGeoMonitor
* @see interface-prv-dashboard-panel.php — Interface this implements.
* @see class-prv-ai-visibility-collector.php — Produces the $data array.
* @see class-prv-admin-page.php — Enqueues Chart.js; calls render().
* @package PrVision
*/
class PGM_Ai_Visibility_Panel implements PGM_Dashboard_Panel {
class PRV_Ai_Visibility_Panel implements PRV_Dashboard_Panel {
/**
* {@inheritDoc}
*
* @param array<string, mixed> $data Payload from PGM_Ai_Visibility_Collector.
* @param array<string, mixed> $data Payload from PRV_Ai_Visibility_Collector.
*
* @return void
*/
public function render( array $data ): void {
$trendline = is_array( $data['trendline'] ?? null ) ? $data['trendline'] : array();
$standings = is_array( $data['standings'] ?? null ) ? $data['standings'] : array();
$last_run_at = isset( $data['last_run_at'] ) && $data['last_run_at'] ? esc_html( (string) $data['last_run_at'] ) : esc_html__( 'Never', 'peptide-geo-monitor' );
$mtd_cost = isset( $data['mtd_cost_usd'] ) ? (float) $data['mtd_cost_usd'] : 0.0;
$cap = isset( $data['monthly_cap_usd'] ) ? (float) $data['monthly_cap_usd'] : PGM_DEFAULT_MONTHLY_BUDGET_USD;
$trendline = is_array( $data['trendline'] ?? null ) ? $data['trendline'] : array();
$standings = is_array( $data['standings'] ?? null ) ? $data['standings'] : array();
$last_run_at = isset( $data['last_run_at'] ) && $data['last_run_at'] ? esc_html( (string) $data['last_run_at'] ) : esc_html__( 'Never', 'pr-vision' );
$mtd_cost = isset( $data['mtd_cost_usd'] ) ? (float) $data['mtd_cost_usd'] : 0.0;
$cap = isset( $data['monthly_cap_usd'] ) ? (float) $data['monthly_cap_usd'] : PRV_DEFAULT_MONTHLY_BUDGET_USD;
$this->render_proxy_note();
$this->render_meta_bar( $last_run_at, $mtd_cost, $cap );
@@ -46,7 +52,7 @@ class PGM_Ai_Visibility_Panel implements PGM_Dashboard_Panel {
* {@inheritDoc}
*/
public function get_title(): string {
return __( 'AI Visibility', 'peptide-geo-monitor' );
return __( 'AI Visibility', 'pr-vision' );
}
/**
@@ -55,9 +61,9 @@ class PGM_Ai_Visibility_Panel implements PGM_Dashboard_Panel {
* @return void
*/
private function render_proxy_note(): void {
echo '<div class="notice notice-info pgm-proxy-note"><p>';
echo '<strong>' . esc_html__( 'Note:', 'peptide-geo-monitor' ) . '</strong> ';
echo esc_html__( 'Scores are measured via API probes — a directional proxy, not the consumer ChatGPT/Gemini apps (different retrieval, personalisation, and system prompts).', 'peptide-geo-monitor' );
echo '<div class="notice notice-info prv-proxy-note"><p>';
echo '<strong>' . esc_html__( 'Note:', 'pr-vision' ) . '</strong> ';
echo esc_html__( 'Scores are measured via API probes — a directional proxy, not the consumer ChatGPT/Gemini apps (different retrieval, personalisation, and system prompts).', 'pr-vision' );
echo '</p></div>';
}
@@ -72,40 +78,42 @@ class PGM_Ai_Visibility_Panel implements PGM_Dashboard_Panel {
*/
private function render_meta_bar( string $last_run_at, float $mtd_cost, float $cap ): void {
$pct = $cap > 0 ? min( 100.0, round( $mtd_cost / $cap * 100, 1 ) ) : 0;
echo '<div class="pgm-meta-bar">';
echo '<span class="pgm-meta-item"><strong>' . esc_html__( 'Last run:', 'peptide-geo-monitor' ) . '</strong> ' . esc_html( $last_run_at ) . '</span>';
echo '<span class="pgm-meta-item"><strong>' . esc_html__( 'MTD cost:', 'peptide-geo-monitor' ) . '</strong> $' . esc_html( number_format( $mtd_cost, 4 ) ) . ' / $' . esc_html( number_format( $cap, 2 ) ) . ' (' . esc_html( $pct ) . '% of cap)</span>';
echo '<div class="prv-meta-bar">';
echo '<span class="prv-meta-item"><strong>' . esc_html__( 'Last run:', 'pr-vision' ) . '</strong> ' . esc_html( $last_run_at ) . '</span>';
echo '<span class="prv-meta-item"><strong>' . esc_html__( 'MTD cost:', 'pr-vision' ) . '</strong> $' . esc_html( number_format( $mtd_cost, 4 ) ) . ' / $' . esc_html( number_format( $cap, 2 ) ) . ' (' . esc_html( $pct ) . '% of cap)</span>';
echo '</div>';
}
/**
* Render the Chart.js visibility score trendline.
*
* @param array<int, array{run_id: string, captured_at: string, score: float}> $trendline
* @param array<int, array{run_id: string, captured_at: string, score: float}> $trendline Trendline data points.
*
* @return void
*/
private function render_trendline( array $trendline ): void {
echo '<div class="pgm-card"><h2>' . esc_html__( 'Visibility Score — Trend', 'peptide-geo-monitor' ) . '</h2>';
echo '<div class="prv-card"><h2>' . esc_html__( 'Visibility Score — Trend', 'pr-vision' ) . '</h2>';
if ( empty( $trendline ) ) {
echo '<p>' . esc_html__( 'No probe runs recorded yet. Click "Run now" to collect the first data point.', 'peptide-geo-monitor' ) . '</p></div>';
echo '<p>' . esc_html__( 'No probe runs recorded yet. Click "Run now" to collect the first data point.', 'pr-vision' ) . '</p></div>';
return;
}
$labels = array();
$scores = array();
foreach ( $trendline as $point ) {
$labels[] = esc_js( date( 'M j', strtotime( $point['captured_at'] ) ) );
$labels[] = esc_js( gmdate( 'M j', (int) strtotime( $point['captured_at'] ) ) );
$scores[] = (float) $point['score'];
}
$labels_json = wp_json_encode( $labels );
$scores_json = wp_json_encode( $scores );
echo '<canvas id="pgm-trendline-chart" height="120"></canvas>';
echo '<canvas id="prv-trendline-chart" height="120"></canvas>';
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<script>document.addEventListener("DOMContentLoaded",function(){';
echo 'var ctx=document.getElementById("pgm-trendline-chart").getContext("2d");';
echo 'var ctx=document.getElementById("prv-trendline-chart").getContext("2d");';
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo 'new Chart(ctx,{type:"line",data:{labels:' . $labels_json . ',datasets:[{label:"Visibility Score",data:' . $scores_json . ',borderColor:"#2271b1",tension:0.3,fill:false}]},options:{scales:{y:{min:0,max:1,title:{display:true,text:"Score"}}},plugins:{legend:{display:false}}}});';
echo '});</script>';
echo '</div>';
@@ -114,24 +122,24 @@ class PGM_Ai_Visibility_Panel implements PGM_Dashboard_Panel {
/**
* Render the per-peptide current standings table.
*
* @param array<string, array{label: string, cited: bool, our_position: int|null, top_domains: string[], model_count: int}> $standings
* @param array<string, array{label: string, cited: bool, our_position: int|null, top_domains: string[], model_count: int}> $standings Per-peptide standing data.
*
* @return void
*/
private function render_standings( array $standings ): void {
echo '<div class="pgm-card"><h2>' . esc_html__( 'Current Standings', 'peptide-geo-monitor' ) . '</h2>';
echo '<div class="prv-card"><h2>' . esc_html__( 'Current Standings', 'pr-vision' ) . '</h2>';
if ( empty( $standings ) ) {
echo '<p>' . esc_html__( 'No standings data yet.', 'peptide-geo-monitor' ) . '</p></div>';
echo '<p>' . esc_html__( 'No standings data yet.', 'pr-vision' ) . '</p></div>';
return;
}
echo '<table class="wp-list-table widefat fixed striped pgm-standings">';
echo '<table class="wp-list-table widefat fixed striped prv-standings">';
echo '<thead><tr>';
echo '<th>' . esc_html__( 'Peptide', 'peptide-geo-monitor' ) . '</th>';
echo '<th>' . esc_html__( 'Cited?', 'peptide-geo-monitor' ) . '</th>';
echo '<th>' . esc_html__( 'Our Position', 'peptide-geo-monitor' ) . '</th>';
echo '<th>' . esc_html__( 'Top Competing Domains', 'peptide-geo-monitor' ) . '</th>';
echo '<th>' . esc_html__( 'Peptide', 'pr-vision' ) . '</th>';
echo '<th>' . esc_html__( 'Cited?', 'pr-vision' ) . '</th>';
echo '<th>' . esc_html__( 'Our Position', 'pr-vision' ) . '</th>';
echo '<th>' . esc_html__( 'Top Competing Domains', 'pr-vision' ) . '</th>';
echo '</tr></thead><tbody>';
foreach ( $standings as $row ) {

View File

@@ -1,22 +1,28 @@
<?php
/**
* Contract for all PR Vision dashboard panels.
*
* @package PrVision
*/
declare(strict_types=1);
/**
* Seam: contract for all GEO-monitor dashboard panels.
* Seam: contract for all PR Vision dashboard panels.
*
* Each panel renders one data category within the GEO Monitor admin page.
* v1 ships only PGM_Ai_Visibility_Panel. Future panels (keyword rankings,
* schema coverage…) implement this and register with PGM_Collector_Registry.
* Each panel renders one data category within the PR Vision admin page.
* v1 ships only PRV_Ai_Visibility_Panel. Future panels (keyword rankings,
* schema coverage…) implement this and register with PRV_Collector_Registry.
*
* Who triggers: PGM_Admin_Page when building the GEO Monitor page.
* Who triggers: PRV_Admin_Page when building the PR Vision page.
* Dependencies: None — pure interface.
*
* @see class-pgm-ai-visibility-panel.php — v1 implementation.
* @see class-pgm-collector-registry.php — Registration hub.
* @see class-prv-ai-visibility-panel.php — v1 implementation.
* @see class-prv-collector-registry.php — Registration hub.
* @see ARCHITECTURE.md — §Collector/Panel seam.
* @package PeptideGeoMonitor
* @package PrVision
*/
interface PGM_Dashboard_Panel {
interface PRV_Dashboard_Panel {
/**
* Render the panel HTML into the current output buffer.