Files
peptide-repo-core/qa-reviews/peptide-repo-core/2026-04-22-874f93b.md
peptiderepo f086b89e2d feat(cpt): consolidate peptide CPT with PSA; drop pr_peptide (v0.2.0) (#1)
Consolidates the `peptide` CPT under PR Core ownership, dropping the `pr_peptide` slug-collision that was 404ing all 89 peptide detail pages.

- POST_TYPE: pr_peptide -> peptide
- TAX_CATEGORY: pr_peptide_category -> peptide_category
- TAX_FAMILY + pr_peptide_family taxonomy removed
- Defensive post_type_exists/taxonomy_exists guards on both registrations
- Args harmonized as superset of PSA + prior PR Core (adds thumbnail support)
- Activation hook + init:999 version-change flush handler
- Uninstall scoped to _pr_core_authored flag (PSA-authored posts survive)
- Plugin version 0.1.1 -> 0.2.0, CHANGELOG in same commit
- ARCHITECTURE.md + CONVENTIONS.md updated with CPT ownership rule

QA gate verdict: PASS (qa-reviews/peptide-repo-core/2026-04-22-874f93b.md)
Sibling release: peptiderepo/peptide-search-ai#15 (PSA v4.5.0)

Known follow-up: manage_peptide_content capability is not granted to any role as of v0.2.0. No current UI/theme reads or writes PR Core-defined meta keys, so no regression. Follow-up thread to decide role assignment.
2026-04-22 16:44:04 +07:00

8.0 KiB
Executable File

QA Gate — PR #1 (feature/v0.2.0-cpt-consolidation)

  • Head SHA: 874f93b (feature/v0.2.0-cpt-consolidation, rebased onto origin/main @ c70352e)
  • Commits in PR:
    • 850279e — feat(cpt): consolidate peptide CPT with PSA; drop pr_peptide (v0.2.0) (originally reviewed as 283b7ea pre-rebase; diff identical)
    • e0ce324 — fix(tests): drop floatval() stub (PHP builtin; redeclaration fatal on 8.3)
    • 874f93b — fix(tests): define PR_CORE_PLUGIN_DIR + trailingslashit in bootstrap
  • Reviewer: independent QA subagent (with two post-hoc CI-failure follow-up fixes applied by the author, both test-harness-only, no runtime impact — PR Core code unchanged)
  • Date: 2026-04-22
  • Verdict: PASS

Contract compliance

  • [PASS] CPT slug is peptide (not pr_peptide) — PR_Core_Peptide_CPT::POST_TYPE = 'peptide' at includes/cpt/class-pr-core-peptide-cpt.php:24
  • [PASS] Taxonomy is peptide_category (not pr_peptide_category) — TAX_CATEGORY = 'peptide_category' at includes/cpt/class-pr-core-peptide-cpt.php:27
  • [PASS] TAX_FAMILY constant and pr_peptide_family taxonomy are removed — no TAX_FAMILY constant exists, register_taxonomies() only registers peptide_category at includes/cpt/class-pr-core-peptide-cpt.php:215-233, verified by unit test at tests/unit/test-peptide-cpt.php:103-105
  • [PASS] Registration is guarded with post_type_exists() / taxonomy_exists() and truly no-ops when they return true — post_type_exists( self::POST_TYPE ) at line 148 returns early, taxonomy_exists( self::TAX_CATEGORY ) at line 216 returns early; unit tests verify this at tests/unit/test-peptide-cpt.php:44-50 and 84-90
  • [PASS] Activation hook: flush_rewrite_rules() runs on activate (line 44 of includes/class-pr-core-activator.php:44), and version-change flush handler wired at init:999 via PR_Core_Activator::maybe_flush_on_version_change() registered in includes/class-pr-core.php:37; handler compares get_option('pr_core_version') to PR_CORE_VERSION and flushes on mismatch at includes/class-pr-core-activator.php:64-77
  • [PASS] Args payload is the harmonized superset at includes/cpt/class-pr-core-peptide-cpt.php:177-195: supports includes thumbnail (line 194), revisions, custom-fields; rewrite.slug='peptides' with with_front=false (line 193); has_archive=true (line 192); show_in_rest=true (line 184); rest_base='peptides' (line 185); rest_namespace='pr-core/v1' (line 186); map_meta_cap=true (line 190); verified by unit tests tests/unit/test-peptide-cpt.php:64-79
  • [PASS] uninstall.php only deletes peptide posts tagged with _pr_core_authored='1' (scoped teardown) — SQL at lines 45-54 uses INNER JOIN {$wpdb->postmeta} on meta_key _pr_core_authored and meta_value '1', and checks post_type = 'peptide' at line 53; 89 canonical PSA-authored posts (which don't carry this flag) will survive uninstall
  • [PASS] uninstall.php does NOT delete peptide_category taxonomy terms (shared-ownership taxonomy) — lines 61-70 explicitly state terms are preserved, and no DELETE statement targets wp_terms or wp_term_taxonomy
  • [PASS] Version bumped to 0.2.0 in peptide-repo-core.php (Version header at line 6 and PR_CORE_VERSION constant at line 25) AND CHANGELOG entry exists for 0.2.0 at CHANGELOG.md:6-26 in the same commit — verified by git show 283b7ea --name-status showing both CHANGELOG.md and peptide-repo-core.php modified in the same commit

Scrub scope (narrow — msg 02 says "tight")

  • [PASS] Renamed identifiers limited to CPT-slug-derived strings. Preserved identifiers confirmed:
    • Class names: PR_Core_Peptide_CPT, PR_Core_Peptide_DTO, PR_Core_Peptide_Repository, PR_Core_Peptide_Metaboxes, PR_Core_Admin_Columns remain with PR_Core_ prefix
    • File names: class-pr-core-peptide-*.php pattern preserved
    • Plugin constants: PR_CORE_VERSION, PR_CORE_PLUGIN_FILE, PR_CORE_PLUGIN_DIR, PR_CORE_PLUGIN_URL, PR_CORE_TARGET_SCHEMA_VERSION unchanged in peptide-repo-core.php:25-29
    • Option keys: pr_core_version at includes/class-pr-core-activator.php:21
    • REST namespace: pr-core/v1 at includes/cpt/class-pr-core-peptide-cpt.php:186
    • Hook names: pr_core_* filters preserved in includes/class-pr-core.php:71-73
    • No stray pr_peptide strings outside comments/tests: grep shows only test assertions (lines 103-104 of test file) and comments verifying removal

Project invariants

  • [PASS] No PHP file exceeds 300 lines (exclude tests/, vendor/) — verified by bash check: find includes -name "*.php" -exec wc -l {} \; shows no file over 300 lines
  • [PASS] Version+CHANGELOG-in-same-commit — git show --stat 283b7ea lists both peptide-repo-core.php and CHANGELOG.md as modified in the single commit 283b7ea
  • [PASS] Git author on head commit is peptiderepo <peptiderepo@users.noreply.github.com> — verified by git show 283b7ea | head -10
  • [PASS] Tests exist at tests/unit/test-peptide-cpt.php covering:
    • Constants match contract (POST_TYPE, TAX_CATEGORY, CAPABILITY) — lines 34-39
    • TAX_FAMILY removal — line 39
    • Guard no-op behavior when post_type_exists/taxonomy_exists return true — lines 44-50, 84-90
    • Payload shape (supports includes thumbnail, rewrite slug, REST namespace) — lines 64-79, 111-115
    • Taxonomy registration (peptide_category only, no pr_peptide_family) — lines 99-109
  • [PASS] ARCHITECTURE.md updated with CPT ownership model (Key Decision #7 at line 187-188) and §2.9 Uninstall specification (lines 190-198); CONVENTIONS.md updated with "CPT Ownership" section (lines 65-71) and _pr_core_authored flag contract (lines 72-74)

CI

  • Workflow: .github/workflows/ci.yml
    • PHP syntax check on 8.1, 8.2, 8.3
    • Lightweight unit tests (no PHPUnit): runs tests/unit/test-*.php
    • WordPress Coding Standards (PHPCS)
    • 300-line file-size gate
  • Observed on head 874f93b: all 5 checks green
    • PHP Lint (8.1): success
    • PHP Lint (8.2): success
    • PHP Lint (8.3): success
    • WordPress Coding Standards: success
    • 300-line file check: success
  • Earlier CI on 850279e failed PHP Lint 8.3 (Cannot redeclare floatval() in bootstrap) and on e0ce324 failed PHP Lint 8.2 (Undefined constant PR_CORE_PLUGIN_DIR in autoloader when invoked from plain-php test runner). Both are test-harness bugs; 874f93b addresses them. No PR Core runtime code was modified by the follow-ups, so the substantive review above stands.

Hunted risks

  • Uninstall data-loss: SAFE. The SQL query at uninstall.php:45-54 uses INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = %s AND pm.meta_value = %s with meta_key='_pr_core_authored' and meta_value='1', and filters WHERE p.post_type = %s with 'peptide'. This ensures only posts carrying the _pr_core_authored flag are deleted. The 89 canonical PSA-authored peptide posts will not carry this flag and will survive uninstall. No blanket DELETE FROM posts WHERE post_type='peptide' exists.

  • Guard correctness: SAFE. The CPT guard checks post_type_exists( self::POST_TYPE ) where self::POST_TYPE = 'peptide' (correct slug, not pr_peptide). Taxonomy guard checks taxonomy_exists( self::TAX_CATEGORY ) where self::TAX_CATEGORY = 'peptide_category' (correct slug, not pr_peptide_category). Both guards are early returns (lines 148-150, 216-218) that prevent further execution, so the deploy-order-safety claim is sound.

  • Version-change flush: SAFE. The activator stores the activated version in pr_core_version option at includes/class-pr-core-activator.php:49. The init:999 handler at line 64-77 retrieves the option via get_option( self::VERSION_OPTION, '' ) (line 69, where VERSION_OPTION = 'pr_core_version' per line 21), compares to PR_CORE_VERSION constant, and if they don't match, flushes rewrite rules and updates the option. This handles in-place updates where the activation hook never fires (e.g., SCP deploys or file copies). The init:999 priority ensures all CPTs/taxonomies are already registered before the flush.

  • REST namespace: SAFE. The CPT args at `includes/cpt/class-pr-core-peptide-c