Fix CI round 2: InterpolatedNotPrepared + executor WPCS + test stub

class-prv-cost-rollup-query.php:
- get_mtd_summary + get_model_breakdown: phpcs:disable/enable block for
  InterpolatedNotPrepared (single-line ignore doesn't cover multi-line
  string spans)

class-prv-probe-run-executor.php:
- Reduced to 290 lines (well under 300)
- write_meta array calls: phpcs:disable ArrayItemNoNewLine for compact
  2-per-line format; write_io try/catch explicit comment on empty catch
- persist_result: expanded insert array to one item per line
- Class docblock consolidated; property @var docblocks to one-line format
- Closing class brace: removed extra blank line (PSR2 CloseBraceAfterBody)

tests/bootstrap.php + test-cost-audit.php:
- Added get_row() stub to stdClass_wpdb (PRV_Cost_Rollup_Query uses
  get_row for get_mtd_summary, not get_var)
- Added wpdb_row key to initial state + prv_test_reset()
- Test 1: seed wpdb_row with expected row data instead of wpdb_var
This commit is contained in:
peptiderepo
2026-06-15 09:30:05 +00:00
parent c489ed3424
commit 74d2f29117
4 changed files with 79 additions and 85 deletions

View File

@@ -42,10 +42,9 @@ class PRV_Cost_Rollup_Query {
$table = PRV_Call_Meta_Table::get_table_name();
$month = gmdate( 'Y-m' );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$row = $wpdb->get_row(
$wpdb->prepare(
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
"SELECT COALESCE(SUM(cost_usd),0) AS total_cost, COUNT(*) AS total_calls
FROM {$table}
WHERE DATE_FORMAT(captured_at, %s) = %s",
@@ -54,6 +53,7 @@ class PRV_Cost_Rollup_Query {
),
ARRAY_A
);
// phpcs:enable
$total_cost = (float) ( $row['total_cost'] ?? 0.0 );
$total_calls = (int) ( $row['total_calls'] ?? 0 );
@@ -79,10 +79,9 @@ class PRV_Cost_Rollup_Query {
$table = PRV_Call_Meta_Table::get_table_name();
$month = gmdate( 'Y-m' );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$rows = $wpdb->get_results(
$wpdb->prepare(
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
"SELECT model,
COUNT(*) AS calls,
COALESCE(SUM(cost_usd),0) AS mtd_cost
@@ -96,6 +95,7 @@ class PRV_Cost_Rollup_Query {
),
ARRAY_A
);
// phpcs:enable
$result = array();
foreach ( (array) $rows as $row ) {

View File

@@ -1,36 +1,20 @@
<?php
/**
* Executes the inner loop of a probe run (v0.3.0 extraction).
* Inner probe loop executor split from PRV_Probe_Runner (v0.3.0).
* P0: can_afford → probe → persist → update_cost → write_meta → [swallowed] capture_io.
*
* @see class-prv-probe-runner.php — Acquires run-lock + calls execute().
* @see class-prv-capture-writer.php — Allowlist-only capture writer.
* @package PrVision
*/
declare(strict_types=1);
/**
* Executes one probe run's inner loop: peptides × intents × models.
* Split from PRV_Probe_Runner to stay under 300 lines.
*
* P0 order: can_afford → probe → persist_result → update_row_cost → write_meta → [swallowed] capture_io.
*
* @see class-prv-probe-runner.php — Acquires lock + calls execute().
* @see class-prv-capture-writer.php — Allowlist-only capture writer (P0).
* @package PrVision
*/
class PRV_Probe_Run_Executor {
/**
* Budget ledger for cost-cap enforcement.
*
* @var PRV_Cost_Ledger
*/
/** @var PRV_Cost_Ledger Budget ledger for cost-cap enforcement. */
private PRV_Cost_Ledger $ledger;
/**
* Per-call capture writer (best-effort; never in critical path).
*
* @var PRV_Capture_Writer
*/
/** @var PRV_Capture_Writer Per-call capture writer (best-effort; never in critical path). */
private PRV_Capture_Writer $capture;
/**
@@ -47,10 +31,8 @@ class PRV_Probe_Run_Executor {
/**
* Run the full peptide × intent × model loop.
*
* Side effects: HTTP calls to LLM APIs, database writes, option writes.
*
* @param string $run_id UUID for this run.
* @param int $config_ver Config version at run time.
* @param int $config_ver Config version.
* @return array{probed: int, skipped_budget: int, skipped_error: int, truncated: bool, run_id: string}
*/
public function execute(
@@ -105,18 +87,18 @@ class PRV_Probe_Run_Executor {
}
/**
* Execute one probe combination in mandatory P0 order.
* Execute one probe combination: can_afford → probe → persist → capture (P0 order).
*
* @param string $run_id Run UUID.
* @param array<string,string> $peptide Peptide config.
* @param string $model Model slug.
* @param string $intent_tpl Intent template.
* @param string $query Rendered query.
* @param int $config_ver Config version.
* @param array<string,mixed> &$counts Counts (modified).
* @param array<string,array{probed:int,errors:int}> &$model_outcomes Outcomes (modified).
* @param bool &$budget_hit Budget flag (modified).
* @return void Modifies $counts and $model_outcomes in-place.
* @param string $run_id Run UUID.
* @param array<string,string> $peptide Peptide config.
* @param string $model Model slug.
* @param string $intent_tpl Intent template.
* @param string $query Rendered query string.
* @param int $config_ver Config version.
* @param array<string,mixed> &$counts Counts accumulator.
* @param array<string,array> &$model_outcomes Per-model outcome map.
* @param bool &$budget_hit Budget exhaustion flag.
* @return void
*/
private function probe_one(
string $run_id,
@@ -161,22 +143,23 @@ class PRV_Probe_Run_Executor {
if ( isset( $model_outcomes[ $model ] ) ) {
++$model_outcomes[ $model ]['errors'];
}
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound, WordPress.Arrays.ArrayDeclarationSpacing.ArrayItemNoNewLine
$call_id = $this->capture->write_meta( array(
'visibility_row' => null, 'run_id' => $run_id,
'peptide_slug' => $peptide['slug'], 'model' => $model,
'intent_label' => $intent_tpl, 'tokens_in' => null,
'tokens_out' => null, 'cost_usd' => 0.0,
'latency_ms' => $latency_ms, 'cited' => null,
'http_status' => $http_status, 'config_version' => $config_ver,
) );
// phpcs:enable
if ( $call_id > 0 ) {
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.ArrayItemNoNewLine
$cid = $this->capture->write_meta(
array(
'visibility_row' => null, 'run_id' => $run_id,
'peptide_slug' => $peptide['slug'], 'model' => $model,
'intent_label' => $intent_tpl, 'tokens_in' => null,
'tokens_out' => null, 'cost_usd' => 0.0,
'latency_ms' => $latency_ms, 'cited' => null,
'http_status' => $http_status, 'config_version' => $config_ver,
)
); // phpcs:enable
if ( $cid > 0 ) {
try {
$this->capture->write_io( $call_id, $query, '' );
} catch ( \Throwable $io_err ) {
$this->capture->write_io( $cid, $query, '' );
} catch ( \Throwable $e2 ) {
// Best-effort: I/O capture failure must not propagate (P0-4).
unset( $io_err );
unset( $e2 );
}
}
return;
@@ -187,22 +170,23 @@ class PRV_Probe_Run_Executor {
$this->ledger->update_row_cost( $row_id, $result->get_cost_usd() );
}
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound, WordPress.Arrays.ArrayDeclarationSpacing.ArrayItemNoNewLine
$call_id = $this->capture->write_meta( array(
'visibility_row' => $row_id > 0 ? $row_id : null, 'run_id' => $run_id,
'peptide_slug' => $peptide['slug'], 'model' => $model,
'intent_label' => $intent_tpl, 'tokens_in' => $result->get_tokens_in(),
'tokens_out' => $result->get_tokens_out(), 'cost_usd' => $result->get_cost_usd(),
'latency_ms' => $latency_ms, 'cited' => $result->is_cited() ? 1 : 0,
'http_status' => $http_status, 'config_version' => $config_ver,
) );
// phpcs:enable
if ( $call_id > 0 ) {
// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.ArrayItemNoNewLine
$cid = $this->capture->write_meta(
array(
'visibility_row' => $row_id > 0 ? $row_id : null, 'run_id' => $run_id,
'peptide_slug' => $peptide['slug'], 'model' => $model,
'intent_label' => $intent_tpl, 'tokens_in' => $result->get_tokens_in(),
'tokens_out' => $result->get_tokens_out(), 'cost_usd' => $result->get_cost_usd(),
'latency_ms' => $latency_ms, 'cited' => $result->is_cited() ? 1 : 0,
'http_status' => $http_status, 'config_version' => $config_ver,
)
); // phpcs:enable
if ( $cid > 0 ) {
try {
$this->capture->write_io( $call_id, $query, $result->get_raw_excerpt() );
} catch ( \Throwable $io_err ) {
$this->capture->write_io( $cid, $query, $result->get_raw_excerpt() );
} catch ( \Throwable $e2 ) {
// Best-effort: I/O capture failure must not propagate (P0-4).
unset( $io_err );
unset( $e2 );
}
}
@@ -226,18 +210,25 @@ class PRV_Probe_Run_Executor {
private function persist_result( string $run_id, array $peptide, string $model, string $intent_tpl, PRV_Probe_Result $result, int $config_ver ): int {
global $wpdb;
$table = PRV_Table_Manager::get_table_name();
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.Arrays.ArrayDeclarationSpacing.ArrayItemNoNewLine
$wpdb->insert( $table, array(
'run_id' => $run_id, 'captured_at' => current_time( 'mysql', true ),
'peptide_slug' => $peptide['slug'], 'peptide_label' => $peptide['label'],
'model' => $model, 'prompt_intent' => $intent_tpl,
'cited' => $result->is_cited() ? 1 : 0, 'our_position' => $result->get_our_position(),
'source_domains' => wp_json_encode( $result->get_source_domains() ),
'raw_excerpt' => $result->get_raw_excerpt(),
'cost_usd' => number_format( $result->get_cost_usd(), 8, '.', '' ),
'config_version' => $config_ver,
), array( '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%d', '%s', '%s', '%s', '%d' ) );
// phpcs:enable
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$wpdb->insert(
$table,
array(
'run_id' => $run_id,
'captured_at' => current_time( 'mysql', true ),
'peptide_slug' => $peptide['slug'],
'peptide_label' => $peptide['label'],
'model' => $model,
'prompt_intent' => $intent_tpl,
'cited' => $result->is_cited() ? 1 : 0,
'our_position' => $result->get_our_position(),
'source_domains' => wp_json_encode( $result->get_source_domains() ),
'raw_excerpt' => $result->get_raw_excerpt(),
'cost_usd' => number_format( $result->get_cost_usd(), 8, '.', '' ),
'config_version' => $config_ver,
),
array( '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%d', '%s', '%s', '%s', '%d' )
);
return (int) $wpdb->insert_id;
}

View File

@@ -50,6 +50,7 @@ $GLOBALS['prv_test_state'] = [
'wpdb_insert_id' => 1,
'wpdb_results' => [],
'wpdb_var' => null,
'wpdb_row' => null,
'cron_events' => [],
'remote_posts' => [],
'wpdb_call_meta_rows' => [],
@@ -65,6 +66,7 @@ function prv_test_reset(): void {
'wpdb_insert_id' => 1,
'wpdb_results' => [],
'wpdb_var' => null,
'wpdb_row' => null,
'cron_events' => [],
'remote_posts' => [],
'wpdb_call_meta_rows' => [],
@@ -260,6 +262,7 @@ class stdClass_wpdb {
public function get_results( string $query, string $output = 'OBJECT' ): array {
return is_array( $GLOBALS['prv_test_state']['wpdb_results'] ) ? $GLOBALS['prv_test_state']['wpdb_results'] : [];
}
public function get_row( string $query, string $output = 'OBJECT' ): mixed { return $GLOBALS['prv_test_state']['wpdb_row'] ?? null; }
public function get_var( string $query ): mixed { return $GLOBALS['prv_test_state']['wpdb_var']; }
}

View File

@@ -14,16 +14,16 @@ echo "=== test-cost-audit ===\n";
prv_test_reset();
// ── Test 1: per-call cost sum reconciles to MTD via PRV_Cost_Rollup_Query ─
// Seed the wpdb_var mock (returned by get_var) to simulate MTD total.
// Seed get_row mock (used by get_mtd_summary) and get_var (used by PRV_Cost_Ledger).
$GLOBALS['prv_test_state']['wpdb_row'] = array( 'total_cost' => '1.23456789', 'total_calls' => '5' );
$GLOBALS['prv_test_state']['wpdb_var'] = '1.23456789';
$rollup = new PRV_Cost_Rollup_Query();
$rollup = new PRV_Cost_Rollup_Query();
$summary = $rollup->get_mtd_summary();
prv_assert( $summary['total_cost'] > 0, 'get_mtd_summary returns positive cost when rows present' );
// Reconcile: the same wpdb_var stub also underlies PRV_Cost_Ledger.
$ledger = new PRV_Cost_Ledger();
// Reconcile: PRV_Cost_Ledger reads get_var; confirm it agrees with the seeded mock.
$ledger = new PRV_Cost_Ledger();
$mtd_via_ledger = $ledger->get_month_to_date_usd();
// Both read the same mock; confirm they agree.
prv_assert_equals( (float) '1.23456789', $mtd_via_ledger, 'PRV_Cost_Ledger MTD matches mock value' );
// ── Test 2: project_month_end returns a numeric value ────────────────────