PR Vision v0.2.0: clear CI blockers (P1-A/B/C/D/E + P2 CONTEXT)
P1-A: Remove defined()/constant() declarations from tests/bootstrap.php
- Both shadow PHP built-ins → 'Cannot redeclare' fatal on PHP 8.x
- Killed PHP Lint (8.2) job and cancelled 8.1/8.3 jobs
- Full bootstrap audit: no other PHP built-in shadows remain
P1-B: Re-escape pre-escaped vars at echo site (class-prv-model-manager-table)
- $prov, $slug, $note: esc_html() at output (was only at assignment)
- $id: esc_attr() at every output site (3 places)
- $checked_attr/$toggle_attrs: phpcs:ignore -- hard-coded attribute strings
- render_health_badge(): phpcs:ignore -- returns pre-escaped HTML markup
P1-C: phpcs:disable/enable NonceVerification.Missing (class-prv-settings-page)
- Wraps all four handle_*() POST blocks
- Justification inline: nonce verified via require_admin_nonce() private helper
P1-D/E: Auto-fixable PHPCS violations (array formatting + style)
- class-prv-config.php: expand 12 inline associative peptide arrays to multi-line
- class-prv-probe-runner.php: expand inline {probed,errors} array; align param docs
- class-prv-model-test-ajax.php: multi-line wp_send_json_success/error calls
- class-prv-settings-renderer.php: extract cadence options array to variable
- class-prv-model-registry.php: capitalise docblock first word; align param spacing
P2: CONTEXT.md v0.2.0 -- add 7 new domain terms
- Config Version, Run Lock, Model Registry, Run Health, Projected Cost,
Config-version Stamp, Truncation State
Agent-Session: pr-vision admin v0.2.0 CI fix
This commit is contained in:
@@ -144,18 +144,54 @@ class PRV_Config {
|
||||
*/
|
||||
private static function default_peptides(): array {
|
||||
return array(
|
||||
array( 'slug' => 'bpc-157', 'label' => 'BPC-157' ),
|
||||
array( 'slug' => 'tb-500', 'label' => 'TB-500' ),
|
||||
array( 'slug' => 'mk-677', 'label' => 'MK-677' ),
|
||||
array( 'slug' => 'cjc-1295', 'label' => 'CJC-1295' ),
|
||||
array( 'slug' => 'ghrp-6', 'label' => 'GHRP-6' ),
|
||||
array( 'slug' => 'ipamorelin', 'label' => 'Ipamorelin' ),
|
||||
array( 'slug' => 'semaglutide', 'label' => 'Semaglutide' ),
|
||||
array( 'slug' => 'tirzepatide', 'label' => 'Tirzepatide' ),
|
||||
array( 'slug' => 'selank', 'label' => 'Selank' ),
|
||||
array( 'slug' => 'semax', 'label' => 'Semax' ),
|
||||
array( 'slug' => 'nad-plus', 'label' => 'NAD+' ),
|
||||
array( 'slug' => 'aod-9604', 'label' => 'AOD-9604' ),
|
||||
array(
|
||||
'slug' => 'bpc-157',
|
||||
'label' => 'BPC-157',
|
||||
),
|
||||
array(
|
||||
'slug' => 'tb-500',
|
||||
'label' => 'TB-500',
|
||||
),
|
||||
array(
|
||||
'slug' => 'mk-677',
|
||||
'label' => 'MK-677',
|
||||
),
|
||||
array(
|
||||
'slug' => 'cjc-1295',
|
||||
'label' => 'CJC-1295',
|
||||
),
|
||||
array(
|
||||
'slug' => 'ghrp-6',
|
||||
'label' => 'GHRP-6',
|
||||
),
|
||||
array(
|
||||
'slug' => 'ipamorelin',
|
||||
'label' => 'Ipamorelin',
|
||||
),
|
||||
array(
|
||||
'slug' => 'semaglutide',
|
||||
'label' => 'Semaglutide',
|
||||
),
|
||||
array(
|
||||
'slug' => 'tirzepatide',
|
||||
'label' => 'Tirzepatide',
|
||||
),
|
||||
array(
|
||||
'slug' => 'selank',
|
||||
'label' => 'Selank',
|
||||
),
|
||||
array(
|
||||
'slug' => 'semax',
|
||||
'label' => 'Semax',
|
||||
),
|
||||
array(
|
||||
'slug' => 'nad-plus',
|
||||
'label' => 'NAD+',
|
||||
),
|
||||
array(
|
||||
'slug' => 'aod-9604',
|
||||
'label' => 'AOD-9604',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -75,12 +75,12 @@ class PRV_Model_Manager_Table {
|
||||
echo '<tr class="' . esc_attr( $row_class ) . '">';
|
||||
|
||||
// Provider.
|
||||
echo '<td>' . $prov . '</td>';
|
||||
echo '<td>' . esc_html( $prov ) . '</td>';
|
||||
|
||||
// Slug (monospace).
|
||||
echo '<td><code style="background:var(--prv-surface3);padding:1px 5px;border-radius:3px;font-size:12px;' . ( $retired ? 'text-decoration:line-through;' : '' ) . '">' . $slug . '</code>';
|
||||
echo '<td><code style="background:var(--prv-surface3);padding:1px 5px;border-radius:3px;font-size:12px;' . ( $retired ? 'text-decoration:line-through;' : '' ) . '">' . esc_html( $slug ) . '</code>';
|
||||
if ( $note ) {
|
||||
echo '<br><span style="color:var(--prv-text-muted);font-size:11px">' . $note . '</span>';
|
||||
echo '<br><span style="color:var(--prv-text-muted);font-size:11px">' . esc_html( $note ) . '</span>';
|
||||
}
|
||||
echo '</td>';
|
||||
|
||||
@@ -89,13 +89,14 @@ class PRV_Model_Manager_Table {
|
||||
$form_name = 'prv_enable_' . $id;
|
||||
echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '" style="display:inline">';
|
||||
echo '<input type="hidden" name="action" value="prv_model_update">';
|
||||
echo '<input type="hidden" name="prv_model_id" value="' . $id . '">';
|
||||
echo '<input type="hidden" name="prv_model_id" value="' . esc_attr( $id ) . '">';
|
||||
echo '<input type="hidden" name="prv_model_slug" value="' . esc_attr( (string) ( $m['slug'] ?? '' ) ) . '">';
|
||||
echo '<input type="hidden" name="prv_model_note" value="' . esc_attr( (string) ( $m['note'] ?? '' ) ) . '">';
|
||||
wp_nonce_field( PRV_Settings_Page::NONCE_MODEL, 'prv_nonce' );
|
||||
// Disabled toggle for retired models.
|
||||
$toggle_attrs = $retired ? ' disabled aria-disabled="true"' : '';
|
||||
$checked_attr = $enabled ? ' checked' : '';
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $checked_attr and $toggle_attrs are hard-coded attribute strings; $enabled is a boolean cast.
|
||||
echo '<input type="checkbox" name="prv_model_enabled" onchange="this.form.submit()" ' . $checked_attr . $toggle_attrs . ' title="' . ( $enabled ? 'Disable' : 'Enable' ) . '">';
|
||||
echo '</form>';
|
||||
if ( $retired ) {
|
||||
@@ -104,12 +105,13 @@ class PRV_Model_Manager_Table {
|
||||
echo '</td>';
|
||||
|
||||
// Run health.
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- render_health_badge() returns pre-escaped HTML markup.
|
||||
echo '<td>' . $this->render_health_badge( $m ) . '</td>';
|
||||
|
||||
// Test button + chip.
|
||||
$chip_id = 'prv-test-chip-' . $id;
|
||||
echo '<td>';
|
||||
echo '<button type="button" class="prv-btn prv-btn-ghost prv-test-btn" data-model-id="' . $id . '">';
|
||||
echo '<button type="button" class="prv-btn prv-btn-ghost prv-test-btn" data-model-id="' . esc_attr( $id ) . '">';
|
||||
echo esc_html__( 'Test', 'pr-vision' );
|
||||
echo '</button>';
|
||||
echo ' <span id="' . esc_attr( $chip_id ) . '" class="prv-badge prv-badge-unknown" role="status" aria-live="polite"></span>';
|
||||
@@ -119,7 +121,7 @@ class PRV_Model_Manager_Table {
|
||||
echo '<td>';
|
||||
echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '" style="display:inline" onsubmit="return confirm(\'Remove this model?\');">';
|
||||
echo '<input type="hidden" name="action" value="prv_model_remove">';
|
||||
echo '<input type="hidden" name="prv_model_id" value="' . $id . '">';
|
||||
echo '<input type="hidden" name="prv_model_id" value="' . esc_attr( $id ) . '">';
|
||||
wp_nonce_field( PRV_Settings_Page::NONCE_MODEL, 'prv_nonce' );
|
||||
echo '<button type="submit" class="prv-btn prv-btn-danger">' . esc_html__( 'Remove', 'pr-vision' ) . '</button>';
|
||||
echo '</form>';
|
||||
|
||||
@@ -10,7 +10,7 @@ declare(strict_types=1);
|
||||
/**
|
||||
* Manages prv_models: v0.1.x→v0.2.0 migration, CRUD, per-model run-health.
|
||||
*
|
||||
* v2 schema: [{id, provider, slug, enabled, note, health_status, health_probed,
|
||||
* V2 schema: [{id, provider, slug, enabled, note, health_status, health_probed,
|
||||
* health_errors, health_run_id}]. v1 legacy: flat string array.
|
||||
*
|
||||
* Who triggers: PRV_Upgrader::run() (upgrade path); PRV_Settings_Page (CRUD).
|
||||
@@ -196,8 +196,8 @@ class PRV_Model_Registry {
|
||||
*
|
||||
* Side effects: Updates prv_models option.
|
||||
*
|
||||
* @param string $run_id UUID of completed run.
|
||||
* @param array<string, array{probed: int, errors: int}> $outcomes Per-slug outcome.
|
||||
* @param string $run_id UUID of completed run.
|
||||
* @param array<string, array{probed: int, errors: int}> $outcomes Per-slug outcome.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
@@ -74,18 +74,22 @@ class PRV_Model_Test_Ajax {
|
||||
try {
|
||||
$provider->probe( 'what is BPC-157' );
|
||||
$ms = (int) round( ( microtime( true ) - $start ) * 1000 );
|
||||
wp_send_json_success( array(
|
||||
'resolved' => true,
|
||||
'latency_ms' => $ms,
|
||||
'slug' => $slug,
|
||||
'message' => sprintf( 'Resolved · %d ms', $ms ),
|
||||
) );
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'resolved' => true,
|
||||
'latency_ms' => $ms,
|
||||
'slug' => $slug,
|
||||
'message' => sprintf( 'Resolved · %d ms', $ms ),
|
||||
)
|
||||
);
|
||||
} catch ( \Exception $e ) {
|
||||
wp_send_json_error( array(
|
||||
'resolved' => false,
|
||||
'slug' => $slug,
|
||||
'message' => 'Not resolved -- provider returned error (slug retired?)',
|
||||
) );
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'resolved' => false,
|
||||
'slug' => $slug,
|
||||
'message' => 'Not resolved -- provider returned error (slug retired?)',
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,10 @@ class PRV_Probe_Runner {
|
||||
$model_outcomes = array(); // Per-slug: {probed, errors}.
|
||||
|
||||
foreach ( $models as $slug ) {
|
||||
$model_outcomes[ $slug ] = array( 'probed' => 0, 'errors' => 0 );
|
||||
$model_outcomes[ $slug ] = array(
|
||||
'probed' => 0,
|
||||
'errors' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
$counts = array(
|
||||
@@ -233,7 +236,7 @@ class PRV_Probe_Runner {
|
||||
/**
|
||||
* Derive + persist the API-key status from run outcomes.
|
||||
*
|
||||
* @param array<string, mixed> $counts Run counts.
|
||||
* @param array<string, mixed> $counts Run counts.
|
||||
* @param array<string, array{probed: int, errors: int}> $model_outcomes Model outcomes.
|
||||
*
|
||||
* @return void
|
||||
|
||||
@@ -115,6 +115,7 @@ class PRV_Settings_Page {
|
||||
$this->require_admin_nonce( self::NONCE_SAVE );
|
||||
$old_cadence = PRV_Config::get_cadence();
|
||||
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Missing -- nonce verified above via require_admin_nonce().
|
||||
$budget = isset( $_POST['prv_monthly_budget_usd'] )
|
||||
? max( 0.01, (float) sanitize_text_field( wp_unslash( (string) $_POST['prv_monthly_budget_usd'] ) ) )
|
||||
: PRV_DEFAULT_MONTHLY_BUDGET_USD;
|
||||
@@ -148,6 +149,7 @@ class PRV_Settings_Page {
|
||||
$intent_lines = array_filter( array_map( 'trim', explode( "\n", $raw_intents ) ) );
|
||||
update_option( 'prv_prompt_intents', array_values( $intent_lines ) );
|
||||
}
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Missing
|
||||
|
||||
PRV_Config_Version::bump_version_if_changed();
|
||||
|
||||
@@ -168,10 +170,12 @@ class PRV_Settings_Page {
|
||||
*/
|
||||
public function handle_model_add(): void {
|
||||
$this->require_admin_nonce( self::NONCE_MODEL );
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Missing -- nonce verified above via require_admin_nonce().
|
||||
$slug = sanitize_text_field( wp_unslash( (string) ( $_POST['prv_model_slug'] ?? '' ) ) );
|
||||
$provider = sanitize_text_field( wp_unslash( (string) ( $_POST['prv_model_provider'] ?? 'openrouter' ) ) );
|
||||
$note = sanitize_text_field( wp_unslash( (string) ( $_POST['prv_model_note'] ?? '' ) ) );
|
||||
$enabled = ! empty( $_POST['prv_model_enabled'] );
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Missing
|
||||
if ( '' !== $slug ) {
|
||||
PRV_Model_Registry::add( $slug, $provider, $enabled, $note );
|
||||
PRV_Config_Version::bump_version_if_changed();
|
||||
@@ -189,10 +193,12 @@ class PRV_Settings_Page {
|
||||
*/
|
||||
public function handle_model_update(): void {
|
||||
$this->require_admin_nonce( self::NONCE_MODEL );
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Missing -- nonce verified above via require_admin_nonce().
|
||||
$id = sanitize_text_field( wp_unslash( (string) ( $_POST['prv_model_id'] ?? '' ) ) );
|
||||
$slug = sanitize_text_field( wp_unslash( (string) ( $_POST['prv_model_slug'] ?? '' ) ) );
|
||||
$enabled = ! empty( $_POST['prv_model_enabled'] );
|
||||
$note = sanitize_text_field( wp_unslash( (string) ( $_POST['prv_model_note'] ?? '' ) ) );
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Missing
|
||||
if ( '' !== $id && '' !== $slug ) {
|
||||
PRV_Model_Registry::update( $id, array( 'slug' => $slug, 'enabled' => $enabled, 'note' => $note ) );
|
||||
PRV_Config_Version::bump_version_if_changed();
|
||||
@@ -210,7 +216,9 @@ class PRV_Settings_Page {
|
||||
*/
|
||||
public function handle_model_remove(): void {
|
||||
$this->require_admin_nonce( self::NONCE_MODEL );
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Missing -- nonce verified above via require_admin_nonce().
|
||||
$id = sanitize_text_field( wp_unslash( (string) ( $_POST['prv_model_id'] ?? '' ) ) );
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Missing
|
||||
if ( '' !== $id ) {
|
||||
PRV_Model_Registry::remove( $id );
|
||||
PRV_Config_Version::bump_version_if_changed();
|
||||
|
||||
@@ -169,7 +169,12 @@ class PRV_Settings_Renderer {
|
||||
$next_str = $next_ts ? gmdate( 'Y-m-d H:i T', (int) $next_ts ) : 'Not scheduled';
|
||||
echo '<div class="prv-field"><label class="prv-label" for="prv_cadence">' . esc_html__( 'Probe cadence', 'pr-vision' ) . '</label>';
|
||||
echo '<select class="prv-input" id="prv_cadence" name="prv_cadence" data-scoring-relevant="1" style="max-width:200px">';
|
||||
foreach ( array( 'weekly' => 'Weekly', 'twicedaily' => 'Twice daily', 'daily' => 'Daily' ) as $v => $l ) {
|
||||
$cadence_options = array(
|
||||
'weekly' => 'Weekly',
|
||||
'twicedaily' => 'Twice daily',
|
||||
'daily' => 'Daily',
|
||||
);
|
||||
foreach ( $cadence_options as $v => $l ) {
|
||||
echo '<option value="' . esc_attr( $v ) . '"' . selected( $cadence, $v, false ) . '>' . esc_html( $l ) . '</option>';
|
||||
}
|
||||
echo '</select>';
|
||||
|
||||
Reference in New Issue
Block a user