docs(qa): PR #1 QA gate verdict — PASS

Independent QA subagent verdict for v0.2.0 at head 874f93b.
All 10 contract items, 7 project invariants, and 5 hunted risks PASS.
No blockers, no non-blocking notes. CI all 5 checks green.
This commit is contained in:
peptiderepo
2026-04-22 08:42:25 +00:00
parent 874f93b252
commit 56e7d0c32d

View File

@@ -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 <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