ci: adopt estate reusable CI + split 300-line violations (v0.22.0)

- ci.yml → thin caller of peptide-e2e reusable CI (tests: brain-monkey)
  + keep check-constants as parallel job; workflow_call trigger enabled
- deploy.yml: retire standalone validate job; gate on ci.yml via workflow_call
- composer.json: add 'test' script (vendor/bin/phpunit --testdox)
- Version bumped to 0.22.0

300-line splits (9 files over limit → all under 300):
- class-keyword-extractor.php: shared tokeniser from Idea_Scorer + Semantic_Dedup
- class-image-aspect-ratio.php: aspect-ratio snap from OR provider + OR batch
- class-settings-sanitizer.php: option sanitization from Admin_Page
- class-settings-fields-images.php: image fields from Settings_Fields_Extended
- class-runware-image-validator.php: credential check from Runware_Image_Provider
- Runware batch: 15 phpcs:disable lines → 1 grouped suppression

Agent-Session: claude/adopt-ci-20260616
This commit is contained in:
peptiderepo
2026-06-16 14:00:48 +00:00
parent d770e8a86a
commit 3890485dcb
18 changed files with 559 additions and 765 deletions

View File

@@ -11,9 +11,10 @@ declare(strict_types=1);
* API is unavailable.
*
* Triggered by: PRAutoBlogger_Pipeline_Runner (step 3).
* Dependencies: Semantic_Dedup, WP_Query.
* Dependencies: Semantic_Dedup, Keyword_Extractor, WP_Query.
*
* @see core/class-semantic-dedup.php — Embedding + keyword dedup engine.
* @see core/class-semantic-dedup.php — Embedding + keyword dedup engine.
* @see core/class-keyword-extractor.php — Keyword tokeniser (fallback dedup).
* @see core/class-content-analyzer.php — Produces the analysis results we consume.
* @see core/class-content-generator.php — Consumes the ranked ideas we produce.
* @see ARCHITECTURE.md — Data flow step 3.
@@ -67,7 +68,7 @@ class PRAutoBlogger_Idea_Scorer {
if ( ! $this->skip_dedup ) {
$topic = $result->get_topic();
$topic_keywords = $this->extract_meaningful_keywords( $topic );
$topic_keywords = PRAutoBlogger_Keyword_Extractor::extract( $topic );
if ( $dedup->is_batch_duplicate( $topic, $topic_keywords, $accepted_keyword_sets ) ) {
++$deduped_count;
@@ -165,133 +166,6 @@ class PRAutoBlogger_Idea_Scorer {
return false;
}
/**
* Extract meaningful keywords from a text string (for fallback dedup).
*
* @param string $text Input text.
* @return string[] Unique lowercase keywords.
*/
private function extract_meaningful_keywords( string $text ): array {
static $stopwords = array(
'a',
'an',
'the',
'and',
'or',
'but',
'is',
'are',
'was',
'were',
'in',
'on',
'at',
'to',
'for',
'of',
'with',
'by',
'from',
'as',
'it',
'its',
'this',
'that',
'your',
'you',
'how',
'what',
'when',
'why',
'where',
'which',
'who',
'do',
'does',
'did',
'not',
'no',
'can',
'will',
'should',
'could',
'would',
'may',
'might',
'have',
'has',
'had',
'be',
'been',
'being',
'about',
'into',
'through',
'during',
'before',
'after',
'above',
'below',
'between',
'same',
'up',
'down',
'out',
'off',
'over',
'under',
'again',
'then',
'here',
'there',
'all',
'each',
'every',
'both',
'few',
'more',
'most',
'other',
'some',
'such',
'than',
'too',
'very',
'just',
'also',
'only',
'own',
'so',
'if',
'while',
'because',
'until',
'vs',
'versus',
'guide',
'complete',
'ultimate',
'best',
'top',
'new',
'first',
'need',
'know',
'everything',
);
$text = strtolower( $text );
$words = preg_split( '/[^a-z0-9-]+/', $text, -1, PREG_SPLIT_NO_EMPTY );
$words = array_filter(
$words,
static function ( string $w ) use ( $stopwords ): bool {
return strlen( $w ) >= 2 && ! in_array( $w, $stopwords, true );
}
);
return array_values( array_unique( $words ) );
}
/**
* Map analysis type to a reader-friendly article type.
*

View File

@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/**
* phpcs:ignore WordPress.Files.FileName.InvalidClassFileName -- class naming convention differs from WordPress standard
*
* Shared keyword-extraction utility for the scoring and dedup pipeline.
*
* Extracts meaningful lowercase tokens from free text by stripping stop-words.
* Used by both PRAutoBlogger_Idea_Scorer and PRAutoBlogger_Semantic_Dedup as the
* fallback path when embedding similarity is unavailable.
*
* What: Static utility; no state.
* Who triggers: PRAutoBlogger_Idea_Scorer, PRAutoBlogger_Semantic_Dedup.
* Dependencies: none.
*
* @see core/class-idea-scorer.php -- Primary consumer (scoring pipeline).
* @see core/class-semantic-dedup.php -- Secondary consumer (embedding fallback).
*/
class PRAutoBlogger_Keyword_Extractor {
/**
* Common English stop-words filtered out before keyword matching.
*
* @var string[]
*/
private const STOPWORDS = array(
'a', 'an', 'the', 'and', 'or', 'but', 'is', 'are', 'was', 'were',
'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by', 'from', 'as',
'it', 'its', 'this', 'that', 'your', 'you', 'how', 'what', 'when',
'why', 'where', 'which', 'who', 'do', 'does', 'did', 'not', 'no',
'can', 'will', 'should', 'could', 'would', 'may', 'might', 'have',
'has', 'had', 'be', 'been', 'being', 'about', 'into', 'through',
'during', 'before', 'after', 'above', 'below', 'between', 'same',
'up', 'down', 'out', 'off', 'over', 'under', 'again', 'then', 'here',
'there', 'all', 'each', 'every', 'both', 'few', 'more', 'most',
'other', 'some', 'such', 'than', 'too', 'very', 'just', 'also',
'only', 'own', 'so', 'if', 'while', 'because', 'until', 'vs',
'versus', 'guide', 'complete', 'ultimate', 'best', 'top', 'new',
'first', 'need', 'know', 'everything',
);
/**
* Extract meaningful keywords from text.
*
* Lowercases input, splits on non-alphanumeric characters, filters
* stop-words and single-character tokens, and de-duplicates.
*
* @param string $text Input text.
* @return string[] Unique lowercase keyword tokens.
*/
public static function extract( string $text ): array {
$text = strtolower( $text );
$words = preg_split( '/[^a-z0-9-]+/', $text, -1, PREG_SPLIT_NO_EMPTY );
$words = array_filter(
$words,
static function ( string $w ): bool {
return strlen( $w ) >= 2 && ! in_array( $w, self::STOPWORDS, true );
}
);
return array_values( array_unique( $words ) );
}
}

View File

@@ -204,7 +204,7 @@ class PRAutoBlogger_Semantic_Dedup {
return false;
}
foreach ( $this->recent_titles as $title ) {
$title_keywords = $this->extract_meaningful_keywords( $title );
$title_keywords = PRAutoBlogger_Keyword_Extractor::extract( $title );
$overlap = count( array_intersect( $topic_keywords, $title_keywords ) );
if ( $overlap / count( $topic_keywords ) >= 0.6 ) {
return true;
@@ -241,131 +241,4 @@ class PRAutoBlogger_Semantic_Dedup {
}
return $titles;
}
/**
* Extract meaningful keywords from text (fallback dedup).
*
* @param string $text Input text.
* @return string[] Unique lowercase keywords.
*/
private function extract_meaningful_keywords( string $text ): array {
static $stopwords = array(
'a',
'an',
'the',
'and',
'or',
'but',
'is',
'are',
'was',
'were',
'in',
'on',
'at',
'to',
'for',
'of',
'with',
'by',
'from',
'as',
'it',
'its',
'this',
'that',
'your',
'you',
'how',
'what',
'when',
'why',
'where',
'which',
'who',
'do',
'does',
'did',
'not',
'no',
'can',
'will',
'should',
'could',
'would',
'may',
'might',
'have',
'has',
'had',
'be',
'been',
'being',
'about',
'into',
'through',
'during',
'before',
'after',
'above',
'below',
'between',
'same',
'up',
'down',
'out',
'off',
'over',
'under',
'again',
'then',
'here',
'there',
'all',
'each',
'every',
'both',
'few',
'more',
'most',
'other',
'some',
'such',
'than',
'too',
'very',
'just',
'also',
'only',
'own',
'so',
'if',
'while',
'because',
'until',
'vs',
'versus',
'guide',
'complete',
'ultimate',
'best',
'top',
'new',
'first',
'need',
'know',
'everything',
);
$text = strtolower( $text );
$words = preg_split( '/[^a-z0-9-]+/', $text, -1, PREG_SPLIT_NO_EMPTY );
$words = array_filter(
$words,
static function ( string $w ) use ( $stopwords ): bool {
return strlen( $w ) >= 2 && ! in_array( $w, $stopwords, true );
}
);
return array_values( array_unique( $words ) );
}
}