diff --git a/qa-reviews/peptide-repo-core/2026-04-22-874f93b.md b/qa-reviews/peptide-repo-core/2026-04-22-874f93b.md new file mode 100755 index 0000000..e7dd08e --- /dev/null +++ b/qa-reviews/peptide-repo-core/2026-04-22-874f93b.md @@ -0,0 +1,71 @@ +# 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 ` — 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 \ No newline at end of file