Skip to content

chore: CodeRabbit follow-ups across PRs #52-61 - #62

Merged
zackkatz merged 2 commits into
developfrom
chore/coderabbit-followups
Jul 23, 2026
Merged

chore: CodeRabbit follow-ups across PRs #52-61#62
zackkatz merged 2 commits into
developfrom
chore/coderabbit-followups

Conversation

@zackkatz

@zackkatz zackkatz commented Jul 23, 2026

Copy link
Copy Markdown
Member

Summary

CodeRabbit review feedback across the 8 merged PRs (#52-#61) — 31 findings, verified individually against current develop (not each PR's original diff, since later PRs changed a lot of the flagged code). Every finding below is either fixed, or explicitly skipped with a one-line reason.

26 fixed, 5 skipped (2 confirmed stale, 3 judgment-call skips with documented reasoning).

Fixed — genuine code-quality / correctness

PR File:line Fix
53 README.md Documented include_supports as opt-in (default false)
53 src/tools/discovery.ts list_block_types rejects a non-boolean include_supports (e.g. the truthy string "false") instead of silently forwarding it
55 class-rest-controller.php Added missing @since 2.2.0 to get_binding_sources()
57 class-rest-controller.php Added missing @since 2.2.0 to create_pattern()
57 src/tools/patterns.ts create_pattern's inputSchema now declares oneOf(blocks, content) — the XOR contract is enforced structurally, not just at runtime
57 src/tools/patterns.ts create_pattern validates sync_status/status against their enums before casting
57 src/client.ts + src/coerce.ts Extracted isNonEmptyArray()/isNonEmptyString() — the single shared definition of "empty" for blocks/content, used by both the MCP dispatch layer and the client layer instead of two independent (if currently identical) copies
60 class-template-manager.php update_template()'s rollback path now checks its own wp_delete_post() return and reports a distinct rollback_failed error (with the orphaned post ID) when cleanup itself also fails, instead of returning the original error as if cleanup succeeded
60 class-template-manager.php Named the is_wp_error() predicates before branching in the touched rollback code, per the repo's coding standard
61 class-template-manager.php Same named-predicate treatment for get_templates()'s hybrid-theme note condition
61 class-template-manager.php update_template()/reset_template(): classic_theme vs not_found on an unresolved id now also checks whether the theme has any real template/part, not just wp_is_block_theme() — a hybrid theme with content elsewhere gets not_found (this id is simply wrong); a genuinely classic theme keeps classic_theme (nothing exists at all)
61 src/tools/templates.ts list_templates' description corrected: wp_id is present whenever a template is database-backed (an override or a fully custom template with no theme file), not only when an override "shadows" a theme file
61 tests/Templates/TemplateManagerWriteTest.php (test file, PHPStan) Narrowed $result before calling WP_Error methods so PHPStan's own (broader) pass doesn't flag array|WP_Error

Fixed — test quality

  • BlockTypesTest.php: contract docblocks on every test; allowed_blocks assertion skipped on WordPress < 6.5 — WP_Block_Type::$allowed_blocks was added in 6.5.0, the plugin's floor is 6.0, so this was a genuine portability gap in the test, not just a style nit (bundled test WP is 6.9.4, so the guard is a no-op in this repo's own CI).
  • PatternsCategoryTest.php: contract docblocks; pattern-registry cleanup moved to try/finally so an assertion failure doesn't leak fixtures into later tests.
  • BindingSourcesTest.php: contract docblocks; the two tests that assumed WordPress 6.5+ now branch on the real function_exists() capability and assert the 6.0-6.4 fallback shape too; added a negative case so uses_context presence is proven conditional (only for a source that declares it), not unconditional.
  • CreatePatternTest.php: the "real text plus whitespace" success case now wraps the text in markup too (<em>Real Title</em>), so over-aggressive sanitization would be caught, not just under-aggressive; review-tool attribution stripped from section-header comments.
  • TemplateManagerTest.php / TemplateManagerWriteTest.php: comments trimmed to state current contracts rather than implementation/review history ("the old X short-circuit" / "Codex review" / "Unchanged regression... proves" framing).

Skipped, with reason

PR File:line Reason
52 class-template-manager.php:226 Stale. format_template_summary()'s wp_id comment already names update_template/reset_template as real, implemented tools — not speculative future work (BLOCK-33 implemented them after this finding was raised).
57 tools.manifest.json:1192 Stale. create_pattern's manifest permission: "create_pattern" already routes through check_create_pattern_permissions in Abilities_Registry::check_tool_permission() (BLOCK-35), matching the REST route's cap check exactly.
55 src/tools/discovery.ts:214 (enrich list_binding_sources) The codebase's existing enrichment (enrichBlockTypes/enrichPatternList) groups/summarizes server-computed classification data (tier, score) it already has. Binding sources carry no such classification — "guidance" here would mean inventing and maintaining hardcoded prose describing named third-party sources, a heavier and fundamentally different kind of enrichment than the established pattern, not a small clean addition.
57 src/client.ts:404 (@since on createPattern) This repo's @since convention is PHP-only (per project standard); zero TS docblocks anywhere in the codebase carry one — adding it here would be the first and only instance.
55 BindingSourcesTest.php:51 ("add a controllable capability-check seam") Would require adding a test-only filter/seam to production code (Block_Registry::get_binding_sources()) purely so a modern (6.9.4) test environment can force the pre-6.5 branch. CodeRabbit's own severity tag flags this as a "Heavy lift". The existing test already has a docblock explaining exactly why it pins the fallback's literal shape/message instead of exercising it live (the bundled WP version can't flip function_exists() false). Its sibling asks in the same finding — docblocks, branching the other two tests on the real capability, the negative uses_context case — are fixed.

Verification

Strict TDD: RED (3912710) → GREEN (fad0d6d), unsquashed. Every behavioral change (schema XOR, include_supports/sync_status/status validation, rollback-failure handling, hybrid-vs-classic not_found) was proven RED against pre-fix code via a targeted git stash of just the implementation files, tests re-run, restored, re-run GREEN — not merely "wrote a test that happens to pass."

composer test    → 1434 + 28 + 11 + 5 tests, 0 failures (was 1430 before this PR, +4 new PHP)
composer lint     → 0 errors, 0 warnings
composer analyze  → [OK] No errors (PHPStan)
npm test          → 55 files / 864 tests passed (was 859, +5 new TS)
npx tsc --noEmit  → clean
manifest drift    → tool count unchanged (33); create_pattern schema/description + list_templates description changed
npm run build     → bundle regenerated + copied into wordpress-plugin/gk-block-mcp/assets/mcp-server/index.cjs

No Linear issue for this batch (CodeRabbit follow-ups, not a tracked feature/bug).

Claude-Session: https://claude.ai/code/session_013YcSbKroBJjPanX3okQrT3

Summary by CodeRabbit

  • New Features

    • Added stricter validation for pattern creation, including allowed status values and requiring exactly one content format.
    • Added validation for the optional block type support details setting.
    • Improved template handling for hybrid themes and clarified editable template identification.
    • Added safer cleanup when template updates fail.
  • Documentation

    • Clarified block type support details and template identifier behavior.
    • Expanded documentation of block types, pattern categories, and REST endpoint behavior.
  • Bug Fixes

    • Improved error reporting for missing templates and failed template cleanup.

💾 Build file (fad0d6d).

zackkatz added 2 commits July 22, 2026 22:15
… [red]

Adds/updates test coverage for the confirmed-genuine CodeRabbit findings
that require a behavior change, plus test-quality fixes (docblocks,
cleanup, portability guards) for the ones that don't. Verified red
against pre-fix code via targeted stash of the implementation changes:

- discovery.ts: list_block_types rejects a non-boolean include_supports
  (e.g. the truthy string "false") instead of silently forwarding it.
- patterns.ts: create_pattern rejects an invalid sync_status/status
  instead of silently defaulting; declares oneOf(blocks, content) in its
  inputSchema so a schema-validating client rejects both-or-neither
  before dispatch, not just at runtime.
- class-template-manager.php: update_template's term-assignment rollback
  reports rollback_failed (distinct from the original cause) when its own
  wp_delete_post() also fails, instead of silently returning the original
  error as if cleanup succeeded.
- class-template-manager.php: a hybrid theme (real content elsewhere) with
  a bad id returns not_found, not classic_theme — classic_theme is now
  reserved for a genuinely classic theme (regression-pinned for both
  update_template and reset_template).

Test-quality only (no corresponding production-code change):
- BlockTypesTest.php: contract docblocks on every test; allowed_blocks
  assertion skipped on WordPress < 6.5 (the property was added in 6.5,
  the plugin's floor is 6.0 — a real portability gap in the test, not
  just a style nit).
- PatternsCategoryTest.php: contract docblocks; pattern-registry cleanup
  moved to try/finally so an assertion failure doesn't leak fixtures into
  later tests.
- BindingSourcesTest.php: contract docblocks; the two tests that assumed
  WordPress 6.5+ now branch on the real function_exists() capability and
  assert the 6.0-6.4 fallback shape too; a negative case added so
  uses_context presence is proven conditional, not unconditional.
- CreatePatternTest.php: the "real text plus whitespace" success case now
  wraps the text in markup too, so over-aggressive sanitization would be
  caught, not just under-aggressive; review-tool attribution stripped
  from section-header comments.
- TemplateManagerTest.php / TemplateManagerWriteTest.php: comments
  trimmed to state current contracts rather than implementation history
  ("the old X" / "Codex review" / "regression"/"proves" framing).

Claude-Session: https://claude.ai/code/session_013YcSbKroBJjPanX3okQrT3
…61 [green]

- discovery.ts: list_block_types validates include_supports is a boolean
  before forwarding, rejecting a non-boolean instead of letting a truthy
  non-boolean (e.g. the string "false") silently flip to true downstream.
- patterns.ts: create_pattern validates sync_status/status against their
  declared enums before casting; declares oneOf(blocks, content) in its
  inputSchema so the "exactly one" contract is enforced structurally, not
  only at runtime.
- coerce.ts: adds isNonEmptyArray()/isNonEmptyString(), the single shared
  definition of "present" for a list/string field. patterns.ts and
  client.ts's independent (if currently identical) copies of the same
  blocks/content emptiness check now both call it, so they can't
  silently drift apart.
- README.md: documents include_supports as opt-in (default false).
- class-rest-controller.php: adds the missing @SInCE 2.2.0 to
  get_binding_sources() and create_pattern() — both new in this range.
- class-template-manager.php:
  - update_template()'s rollback helper (used by both term-assignment
    failures and a content-write failure) now checks its own
    wp_delete_post() return value and reports a distinct rollback_failed
    error — including the orphaned post's ID — when cleanup itself also
    fails, instead of returning the original error as if cleanup
    succeeded.
  - the classic_theme/not_found choice on an unresolved id now also
    checks whether the theme has ANY real template/part, not just
    wp_is_block_theme(): a hybrid theme with content elsewhere gets
    not_found (this id is simply wrong); a genuinely classic theme keeps
    classic_theme (there's nothing here at all). Applies to both
    update_template and reset_template.
  - is_wp_error() checks in the touched code assign a named boolean
    before branching, per the repo's coding standard.
- src/tools/templates.ts: list_templates' description corrected —
  wp_id is present whenever a template is database-backed (an override
  OR a fully custom template with no theme file), not only when an
  override "shadows" a theme file.
- Manifest/bundle regenerated (tool count unchanged at 33) for the
  create_pattern schema/description and list_templates description
  changes.

## Skipped findings (verified against current code)

- PR52 class-template-manager.php:226 — stale. format_template_summary()'s
  wp_id comment already names update_template/reset_template as real,
  implemented tools, not speculative future work.
- PR57 tools.manifest.json:1192 — stale. create_pattern's manifest
  permission already routes through check_create_pattern_permissions
  (BLOCK-35), matching the REST route's cap check exactly.
- PR55 discovery.ts:214 (list_binding_sources enrichment) — the
  codebase's existing enrichment (enrichBlockTypes/enrichPatternList)
  groups/summarizes server-computed classification data (tier, score) it
  already has; binding sources carry no such classification, so
  "guidance" here would mean inventing and maintaining hardcoded prose
  about named third-party sources — a different, heavier kind of
  enrichment than the established pattern, not a small clean addition.
- PR57 src/client.ts:404 (@SInCE on createPattern) — this repo's @SInCE
  convention is PHP-only; zero TS docblocks anywhere in the codebase
  carry one.
- PR55 BindingSourcesTest.php:51 ("add a controllable capability-check
  seam") — would require adding a test-only filter/seam to production
  code (Block_Registry::get_binding_sources()) purely so a modern (6.9)
  test environment can force the pre-6.5 branch; CodeRabbit's own
  severity tag flags this as a "Heavy lift". The existing test already
  documents why it pins the fallback's literal shape instead of
  exercising it live. Its sibling asks (docblocks, branch-on-real-
  capability for the other two tests, negative uses_context case) are
  fixed.

Claude-Session: https://claude.ai/code/session_013YcSbKroBJjPanX3okQrT3
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The change tightens MCP tool validation, clarifies tool documentation, improves template error classification and rollback handling, and expands REST and template regression coverage.

Changes

MCP tool contracts and validation

Layer / File(s) Summary
Pattern input validation
src/coerce.ts, src/client.ts, src/tools/patterns.ts, wordpress-plugin/gk-block-mcp/assets/mcp-server/index.cjs, wordpress-plugin/gk-block-mcp/includes/abilities/tools.manifest.json, src/__tests__/tools/patterns/*
create_pattern now enforces exclusive non-empty blocks or content inputs and validates sync_status and status values.
Discovery and template tool contracts
src/tools/discovery.ts, src/tools/templates.ts, README.md, wordpress-plugin/gk-block-mcp/assets/mcp-server/index.cjs, wordpress-plugin/gk-block-mcp/includes/abilities/tools.manifest.json, src/__tests__/tools/discovery/*
list_block_types validates boolean include_supports, and tool descriptions clarify supports opt-in behavior and database-backed template identifiers.

Template classification and rollback

Layer / File(s) Summary
Template resolution and override rollback
wordpress-plugin/gk-block-mcp/includes/class-template-manager.php
Template update and reset paths distinguish classic themes from hybrid themes and centralize rollback of newly created overrides, including rollback failure reporting.
Template regression coverage
wordpress-plugin/gk-block-mcp/tests/Templates/*
Tests cover hybrid-theme not-found responses, classic-theme errors, taxonomy rollback, rollback failures, and fixture cache refresh behavior.

REST contract test maintenance

Layer / File(s) Summary
REST behavior contracts and compatibility
wordpress-plugin/gk-block-mcp/includes/class-rest-controller.php, wordpress-plugin/gk-block-mcp/tests/REST/*
REST tests document block-type, binding-source, pattern, and category contracts, add compatibility handling, strengthen cleanup, and verify sanitized pattern titles.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TemplateManager
  participant WordPressTaxonomy
  participant WordPressPostAPI
  participant OverridePost
  TemplateManager->>WordPressTaxonomy: assign required override terms
  WordPressTaxonomy-->>TemplateManager: return assignment result
  TemplateManager->>WordPressPostAPI: write override content or blocks
  WordPressPostAPI-->>TemplateManager: return write result
  TemplateManager->>OverridePost: delete newly-created override on failure
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 84.31% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the changeset: it describes CodeRabbit follow-up fixes spanning multiple prior PRs.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/coderabbit-followups

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
wordpress-plugin/gk-block-mcp/tests/REST/BlockTypesTest.php (1)

43-49: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the historical regression narrative.

Keep the current styles contract, but remove “an earlier version” language from Lines 45-48.

As per coding guidelines, comments and docblocks must document present behavior and hard contracts, not development history.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wordpress-plugin/gk-block-mcp/tests/REST/BlockTypesTest.php` around lines 43
- 49, Update the docblock near the styles contract to describe only the current
behavior: styles must include every block.json-declared variation and correctly
flag the default with is_default. Remove the historical regression and
failure-mode narrative referencing earlier versions and missing styles.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@wordpress-plugin/gk-block-mcp/includes/abilities/tools.manifest.json`:
- Line 2119: The `description` for the template-listing tool incorrectly
presents non-null `wp_id` as required for editing. Update that wording to
describe `wp_id` only as indicating existing database backing, while preserving
that templates with null `wp_id` can still be edited through `update_template`
by creating an override and aligning with the `get_template` contract.

In `@wordpress-plugin/gk-block-mcp/tests/Templates/TemplateManagerWriteTest.php`:
- Around line 516-520: Add a `@var` \WP_Error $result narrowing annotation
immediately after each assertInstanceOf(\WP_Error::class, $result) in
TemplateManagerWriteTest.php at lines 516-520, 602-605, 619-622, and 653-656,
before invoking get_error_code(), get_error_message(), or get_error_data().
- Around line 494-514: Wrap the update_template() invocation and both filter
removals in a try/finally block so pre_insert_term and pre_delete_post are
always removed, including when update_template() throws. Keep the existing
filter setup and result assignment unchanged, and perform cleanup in the finally
block.

---

Nitpick comments:
In `@wordpress-plugin/gk-block-mcp/tests/REST/BlockTypesTest.php`:
- Around line 43-49: Update the docblock near the styles contract to describe
only the current behavior: styles must include every block.json-declared
variation and correctly flag the default with is_default. Remove the historical
regression and failure-mode narrative referencing earlier versions and missing
styles.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 72c1f0bf-eb88-4ff2-ba48-c0582dba192a

📥 Commits

Reviewing files that changed from the base of the PR and between 4554dbb and fad0d6d.

📒 Files selected for processing (18)
  • README.md
  • src/__tests__/tools/discovery/list_block_types.test.ts
  • src/__tests__/tools/patterns/create_pattern.test.ts
  • src/client.ts
  • src/coerce.ts
  • src/tools/discovery.ts
  • src/tools/patterns.ts
  • src/tools/templates.ts
  • wordpress-plugin/gk-block-mcp/assets/mcp-server/index.cjs
  • wordpress-plugin/gk-block-mcp/includes/abilities/tools.manifest.json
  • wordpress-plugin/gk-block-mcp/includes/class-rest-controller.php
  • wordpress-plugin/gk-block-mcp/includes/class-template-manager.php
  • wordpress-plugin/gk-block-mcp/tests/REST/BindingSourcesTest.php
  • wordpress-plugin/gk-block-mcp/tests/REST/BlockTypesTest.php
  • wordpress-plugin/gk-block-mcp/tests/REST/CreatePatternTest.php
  • wordpress-plugin/gk-block-mcp/tests/REST/PatternsCategoryTest.php
  • wordpress-plugin/gk-block-mcp/tests/Templates/TemplateManagerTest.php
  • wordpress-plugin/gk-block-mcp/tests/Templates/TemplateManagerWriteTest.php

"ability": "gk-block-mcp/list-templates",
"label": "List templates",
"description": "List the active theme's templates (page layouts like \"single\", \"archive\") or template parts (reusable regions like \"header\", \"footer\") — works on a theme without a full block-theme structure too, as long as it has real templates/parts. Each row includes `wp_id` — non-null only when a database override shadows the theme file, which is what makes a template editable via update_template. Returns an empty list with a `note` only when there is truly nothing to list.",
"description": "List the active theme's templates (page layouts like \"single\", \"archive\") or template parts (reusable regions like \"header\", \"footer\") — works on a theme without a full block-theme structure too, as long as it has real templates/parts. Each row includes `wp_id` — present (non-null) whenever the template is backed by a database post, whether that's an override shadowing a theme file or a fully custom template with none; that's what makes it editable via update_template. Returns an empty list with a `note` only when there is truly nothing to list.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not imply that wp_id is required for editability.

Line 2119 says a non-null wp_id is what makes a template editable, but Line 2203 documents that update_template can create an override when wp_id is null. Align this wording with the get_template contract at Line 2168 and describe wp_id as existing database backing, not an editability prerequisite.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wordpress-plugin/gk-block-mcp/includes/abilities/tools.manifest.json` at line
2119, The `description` for the template-listing tool incorrectly presents
non-null `wp_id` as required for editing. Update that wording to describe
`wp_id` only as indicating existing database backing, while preserving that
templates with null `wp_id` can still be edited through `update_template` by
creating an override and aligning with the `get_template` contract.

Comment on lines +494 to +514
$term_filter = static function ( $term, $taxonomy ) {
if ( 'wp_theme' === $taxonomy ) {
return new \WP_Error( 'term_insert_failed', 'Simulated taxonomy failure.' );
}
return $term;
};
add_filter( 'pre_insert_term', $term_filter, 10, 2 );

$delete_filter = static function () {
return false; // Force wp_delete_post() to short-circuit and fail.
};
add_filter( 'pre_delete_post', $delete_filter );

$result = $this->tm->update_template(
$this->theme . '//index',
'wp_template',
array( 'content' => '<!-- wp:paragraph --><p>x</p><!-- /wp:paragraph -->' )
);

remove_filter( 'pre_insert_term', $term_filter, 10 );
remove_filter( 'pre_delete_post', $delete_filter );

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Always remove test filters.

An unexpected exception from update_template() skips both removals and contaminates later tests. Use try/finally.

Proposed fix
 add_filter( 'pre_insert_term', $term_filter, 10, 2 );
-
 add_filter( 'pre_delete_post', $delete_filter );
 
- $result = $this->tm->update_template(
-     $this->theme . '//index',
-     'wp_template',
-     array( 'content' => '<!-- wp:paragraph --><p>x</p><!-- /wp:paragraph -->' )
- );
-
- remove_filter( 'pre_insert_term', $term_filter, 10 );
- remove_filter( 'pre_delete_post', $delete_filter );
+ try {
+     $result = $this->tm->update_template(
+         $this->theme . '//index',
+         'wp_template',
+         array( 'content' => '<!-- wp:paragraph --><p>x</p><!-- /wp:paragraph -->' )
+     );
+ } finally {
+     remove_filter( 'pre_insert_term', $term_filter, 10 );
+     remove_filter( 'pre_delete_post', $delete_filter );
+ }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$term_filter = static function ( $term, $taxonomy ) {
if ( 'wp_theme' === $taxonomy ) {
return new \WP_Error( 'term_insert_failed', 'Simulated taxonomy failure.' );
}
return $term;
};
add_filter( 'pre_insert_term', $term_filter, 10, 2 );
$delete_filter = static function () {
return false; // Force wp_delete_post() to short-circuit and fail.
};
add_filter( 'pre_delete_post', $delete_filter );
$result = $this->tm->update_template(
$this->theme . '//index',
'wp_template',
array( 'content' => '<!-- wp:paragraph --><p>x</p><!-- /wp:paragraph -->' )
);
remove_filter( 'pre_insert_term', $term_filter, 10 );
remove_filter( 'pre_delete_post', $delete_filter );
$term_filter = static function ( $term, $taxonomy ) {
if ( 'wp_theme' === $taxonomy ) {
return new \WP_Error( 'term_insert_failed', 'Simulated taxonomy failure.' );
}
return $term;
};
add_filter( 'pre_insert_term', $term_filter, 10, 2 );
$delete_filter = static function () {
return false; // Force wp_delete_post() to short-circuit and fail.
};
add_filter( 'pre_delete_post', $delete_filter );
try {
$result = $this->tm->update_template(
$this->theme . '//index',
'wp_template',
array( 'content' => '<!-- wp:paragraph --><p>x</p><!-- /wp:paragraph -->' )
);
} finally {
remove_filter( 'pre_insert_term', $term_filter, 10 );
remove_filter( 'pre_delete_post', $delete_filter );
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wordpress-plugin/gk-block-mcp/tests/Templates/TemplateManagerWriteTest.php`
around lines 494 - 514, Wrap the update_template() invocation and both filter
removals in a try/finally block so pre_insert_term and pre_delete_post are
always removed, including when update_template() throws. Keep the existing
filter setup and result assignment unchanged, and perform cleanup in the finally
block.

Comment on lines +516 to +520
$this->assertInstanceOf( \WP_Error::class, $result );
$this->assertSame( 'rollback_failed', $result->get_error_code() );
$this->assertStringContainsString( 'Simulated taxonomy failure', $result->get_error_message() );
$data = $result->get_error_data();
$this->assertSame( 500, $data['status'] );

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Narrow WP_Error before calling its methods.

PHPStan reports each result as array|WP_Error, so these method calls fail static analysis despite the preceding assertion.

  • wordpress-plugin/gk-block-mcp/tests/Templates/TemplateManagerWriteTest.php#L516-L520: add a @var \WP_Error $result assertion after assertInstanceOf().
  • wordpress-plugin/gk-block-mcp/tests/Templates/TemplateManagerWriteTest.php#L602-L605: add the same narrowing annotation.
  • wordpress-plugin/gk-block-mcp/tests/Templates/TemplateManagerWriteTest.php#L619-L622: add the same narrowing annotation.
  • wordpress-plugin/gk-block-mcp/tests/Templates/TemplateManagerWriteTest.php#L653-L656: add the same narrowing annotation.
🧰 Tools
🪛 PHPStan (2.2.5)

[error] 517-517: Cannot call method get_error_code() on array|WP_Error.

(method.nonObject)


[error] 518-518: Cannot call method get_error_message() on array|WP_Error.

(method.nonObject)


[error] 519-519: Cannot call method get_error_data() on array|WP_Error.

(method.nonObject)

📍 Affects 1 file
  • wordpress-plugin/gk-block-mcp/tests/Templates/TemplateManagerWriteTest.php#L516-L520 (this comment)
  • wordpress-plugin/gk-block-mcp/tests/Templates/TemplateManagerWriteTest.php#L602-L605
  • wordpress-plugin/gk-block-mcp/tests/Templates/TemplateManagerWriteTest.php#L619-L622
  • wordpress-plugin/gk-block-mcp/tests/Templates/TemplateManagerWriteTest.php#L653-L656
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wordpress-plugin/gk-block-mcp/tests/Templates/TemplateManagerWriteTest.php`
around lines 516 - 520, Add a `@var` \WP_Error $result narrowing annotation
immediately after each assertInstanceOf(\WP_Error::class, $result) in
TemplateManagerWriteTest.php at lines 516-520, 602-605, 619-622, and 653-656,
before invoking get_error_code(), get_error_message(), or get_error_data().

Sources: Coding guidelines, Linters/SAST tools

@zackkatz
zackkatz merged commit 53118bd into develop Jul 23, 2026
9 checks passed
@zackkatz
zackkatz deleted the chore/coderabbit-followups branch July 23, 2026 02:23
zackkatz added a commit that referenced this pull request Jul 23, 2026
Three findings from CodeRabbit's re-review of PR #62:

1. TemplateManagerWriteTest.php: a real test-isolation bug —
   test_update_template_reports_rollback_failure_when_delete_also_fails()
   added pre_insert_term/pre_delete_post filters and only removed them
   after update_template() returned. An unexpected exception from that
   call would skip both remove_filter() calls, leaking the filters into
   every later test in the same PHP process (any subsequent
   wp_insert_term()/wp_delete_post() call would be affected). Wrapped the
   call in try/finally so cleanup always runs.

2. Same file: added `@var \WP_Error $result` narrowing docblocks after
   four assertInstanceOf(\WP_Error::class, $result) calls, immediately
   before each result's get_error_code()/get_error_data() calls.
   composer analyze is already clean on develop (tests/* is excluded
   from this repo's own PHPStan paths) — applied because it's a trivial,
   readability-neutral-to-positive one-liner per site, not because our
   gate required it.

3. src/tools/templates.ts: list_templates' description said a non-null
   wp_id "is what makes a template editable via update_template" —
   false. update_template()'s own description (and behavior) already
   documents that a wp_id:null template is still editable: calling it
   creates a new override. Reworded to describe wp_id as existing
   database backing, matching get_template's accurate framing, without
   implying editability requires it.

Manifest/bundle regenerated for the description change (tool count
unchanged at 33).

This is the last dispatch on the CodeRabbit-follow-up thread.

Claude-Session: https://claude.ai/code/session_013YcSbKroBJjPanX3okQrT3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant