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.
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 as283b7eapre-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(notpr_peptide) —PR_Core_Peptide_CPT::POST_TYPE = 'peptide'atincludes/cpt/class-pr-core-peptide-cpt.php:24 - [PASS] Taxonomy is
peptide_category(notpr_peptide_category) —TAX_CATEGORY = 'peptide_category'atincludes/cpt/class-pr-core-peptide-cpt.php:27 - [PASS]
TAX_FAMILYconstant andpr_peptide_familytaxonomy are removed — noTAX_FAMILYconstant exists,register_taxonomies()only registerspeptide_categoryatincludes/cpt/class-pr-core-peptide-cpt.php:215-233, verified by unit test attests/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 attests/unit/test-peptide-cpt.php:44-50and84-90 - [PASS] Activation hook:
flush_rewrite_rules()runs on activate (line 44 ofincludes/class-pr-core-activator.php:44), and version-change flush handler wired atinit:999viaPR_Core_Activator::maybe_flush_on_version_change()registered inincludes/class-pr-core.php:37; handler comparesget_option('pr_core_version')toPR_CORE_VERSIONand flushes on mismatch atincludes/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:supportsincludesthumbnail(line 194),revisions,custom-fields;rewrite.slug='peptides'withwith_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 teststests/unit/test-peptide-cpt.php:64-79 - [PASS]
uninstall.phponly deletespeptideposts tagged with_pr_core_authored='1'(scoped teardown) — SQL at lines 45-54 usesINNER JOIN {$wpdb->postmeta}on meta_key_pr_core_authoredand meta_value'1', and checkspost_type = 'peptide'at line 53; 89 canonical PSA-authored posts (which don't carry this flag) will survive uninstall - [PASS]
uninstall.phpdoes NOT deletepeptide_categorytaxonomy terms (shared-ownership taxonomy) — lines 61-70 explicitly state terms are preserved, and no DELETE statement targetswp_termsorwp_term_taxonomy - [PASS] Version bumped to
0.2.0inpeptide-repo-core.php(Version header at line 6 andPR_CORE_VERSIONconstant at line 25) AND CHANGELOG entry exists for 0.2.0 atCHANGELOG.md:6-26in the same commit — verified bygit show 283b7ea --name-statusshowing bothCHANGELOG.mdandpeptide-repo-core.phpmodified 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_Columnsremain withPR_Core_prefix - File names:
class-pr-core-peptide-*.phppattern preserved - Plugin constants:
PR_CORE_VERSION,PR_CORE_PLUGIN_FILE,PR_CORE_PLUGIN_DIR,PR_CORE_PLUGIN_URL,PR_CORE_TARGET_SCHEMA_VERSIONunchanged inpeptide-repo-core.php:25-29 - Option keys:
pr_core_versionatincludes/class-pr-core-activator.php:21 - REST namespace:
pr-core/v1atincludes/cpt/class-pr-core-peptide-cpt.php:186 - Hook names:
pr_core_*filters preserved inincludes/class-pr-core.php:71-73 - No stray
pr_peptidestrings outside comments/tests: grep shows only test assertions (lines 103-104 of test file) and comments verifying removal
- Class names:
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 283b7ealists bothpeptide-repo-core.phpandCHANGELOG.mdas modified in the single commit 283b7ea - [PASS] Git author on head commit is
peptiderepo <peptiderepo@users.noreply.github.com>— verified bygit show 283b7ea | head -10 - [PASS] Tests exist at
tests/unit/test-peptide-cpt.phpcovering:- 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.mdupdated with CPT ownership model (Key Decision #7 at line 187-188) and §2.9 Uninstall specification (lines 190-198);CONVENTIONS.mdupdated with "CPT Ownership" section (lines 65-71) and_pr_core_authoredflag 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
850279efailed PHP Lint 8.3 (Cannot redeclare floatval()in bootstrap) and one0ce324failed PHP Lint 8.2 (Undefined constant PR_CORE_PLUGIN_DIRin autoloader when invoked from plain-php test runner). Both are test-harness bugs;874f93baddresses 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-54usesINNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = %s AND pm.meta_value = %swithmeta_key='_pr_core_authored'andmeta_value='1', and filtersWHERE p.post_type = %swith'peptide'. This ensures only posts carrying the_pr_core_authoredflag are deleted. The 89 canonical PSA-authored peptide posts will not carry this flag and will survive uninstall. No blanketDELETE FROM posts WHERE post_type='peptide'exists. -
Guard correctness: SAFE. The CPT guard checks
post_type_exists( self::POST_TYPE )whereself::POST_TYPE = 'peptide'(correct slug, notpr_peptide). Taxonomy guard checkstaxonomy_exists( self::TAX_CATEGORY )whereself::TAX_CATEGORY = 'peptide_category'(correct slug, notpr_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_versionoption atincludes/class-pr-core-activator.php:49. The init:999 handler at line 64-77 retrieves the option viaget_option( self::VERSION_OPTION, '' )(line 69, whereVERSION_OPTION = 'pr_core_version'per line 21), compares toPR_CORE_VERSIONconstant, 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