Skip to content

feat: import plugin v2 from features-plugin-skeleton - #2

Merged
AnuragVasanwala merged 70 commits into
mainfrom
feat/import-v2
Oct 1, 2026
Merged

AnuragVasanwala merged 70 commits into
mainfrom
feat/import-v2

Conversation

@aryanjasala

@aryanjasala aryanjasala commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Merge after rtCamp/wp-primitives#98 and rtCamp/wp-primitives#100, once the repository is renamed to rtCamp/wp-primitives and v2.0.0 is tagged: composer.json requires rtcamp/wp-primitives ^2.0 from that repository, so CI fails until then.

History

  • chore: move the v1 plugin brings in features-plugin-skeleton at 92b8c7b, the v1 state the v2 work branched from, as one commit. The full v1 history stays in that repository.
  • The 53 v2 commits follow on top with their original authors, dates and merge commits.

Changes on top of v2

  • The rename from #759: package rtcamp/wp-primitives, namespace rtCamp\WPPrimitives\, repository and docs names plugin-elementary.
  • The interactive example block uses the plugin's text domain and store namespace, so npm run init renames them and the render.php and view.js stores match.
  • The starter JS entries drop their console.log demos.
  • The editor script and style are registered only when the build has them.
  • .wp-env.json uses the latest stable WordPress instead of master, CI adds WordPress 7.1, and the plugin header says Tested up to: 7.1.
  • The Tailwind feature installs @rtcamp/tailwind-config from wp-tooling's split branch; ^0.1.0 is not on npm.
  • Guides, links and the docs workflow point at main. The docs workflow only runs in rtCamp/plugin-elementary, because generated plugins inherit it.

Testing

  • PHPUnit: 109 tests passing on PHP 8.2 / WP 6.7, PHP 8.4 / WP 7.1 and the latest stable release
  • PHP line coverage: 62.38%; Jest line coverage: 87.12%
  • PHPCS, PHPStan, ESLint (src/), stylelint and npm run build:prod: clean
  • gitleaks over every commit in this PR: no findings

After merge

  • Make the repository public and tag v2.0.0.

aryanjasala and others added 30 commits October 1, 2026 20:32
Brings in rtCamp/features-plugin-skeleton at 92b8c7b, the v1 state the v2 work branched from, as a single commit. The full v1 history stays in that repository. The v2 commits follow on top with their original authors.
refactor: PSR-4 migration + adopt rtcamp/wp-framework
Single inc/ PSR-4 root; drop hand-rolled spl_autoload_register.
Framework primitives (Singleton, Container, abstracts, traits)
consumed from vendor/rtcamp/wp-framework.
- Main loads Modules only; each *Module extends AbstractModule
- Worked examples per primitive: PostTypes, Taxonomies, Blocks,
  REST, Settings, Shortcodes, Roles, Cron, CLI
- ConditionallyRegistrable gates CLI under WP-CLI
- Core/Assets uses AssetLoaderTrait; filemtime boilerplate gone
- custom-functions.php → Helpers\Util static class
…668)

Convert the asset-loading mixin into a concrete, injectable class so it
has an instance identity that can be passed around and shared.
Plugin components render through framework hierarchy (overridable
by parent then child theme) and enqueue assets by default. Plugin
supplies only context + shared AssetLoader.
- Core/Components extends ComponentLoader, implements Shareable;
  get_context() → project-name-features
- Core/Assets marked Shareable; registered in Main
- Util::component() / get_component() helpers via shared loader
- Bootstrap uses Autoloader::class / Main::class
Skeleton consumes shared review rules from rtcamp/wp-framework via
npm run sync-ai (chained from npm run init). No Composer hooks →
no install-order coupling.
Migrate off removed TemplateLoaderTrait onto concrete TemplateLoader,
matching Assets / Components pattern.
…Encryptor (#676)

Wire plugin into instance-based Encryptor — same consumer pattern
as Assets / Components / Templates: context-owned Core service
shared via container, Util wrappers as call surface.
Overhaul local dev build: HMR for block editor, BrowserSync live
reload for frontend, per-site configurable dev-server ports via
.env.local, recursive entry collection from src/js + src/css.
- Rename npm run commands
- Update docs in webpack configs
- Read BS enable/disable from .env.local
- Default HMR port → 8888; default BS port updated
- Add docs/hmr.md
Replace per-handle constants (FRONTEND/ADMIN/EDITOR) and private
PREFIX with public HANDLE_PREFIX override + $this->handle( name )
calls. One naming convention, no growing const list. Internal —
nothing outside Assets.php referenced old constants.
…ne (#681)

* refactor(assets): name handles via AssetLoader::handle()

Replace the per-handle constants (FRONTEND/ADMIN/EDITOR) and the private
PREFIX with a public HANDLE_PREFIX override + $this->handle( name ) calls.
One naming convention, no growing const list.

* feat(scaffold): delegate plugin setup to the shared engine

Replace the bespoke bin/init.js with a thin wrapper that hands bin/scaffold.config.js to the rtcamp/wp-framework scaffold engine.

Adopt Husky via the engine and remove the hand-rolled bin/husky.sh and bin/pre-commit-hook.sh plus their package.json scripts. Add @rtcamp/wp-tooling as a dev dependency, add a prepare -> sync-ai step, and persist identity to .wp-scaffold.json.

* feat(scaffold): declare feature config (manage mode ready)

Add featuresDir + an empty features array so the plugin supports manage mode.

* refactor(scaffold): full source identity + namespace/package templates

Declare the placeholder as a full identity (name + explicit namespace/package) plus namespace()/package() templates. Lets the shared engine edit any identity field and rename an already-initialized plugin from .wp-scaffold.json.

* fix(scaffold): declare review/edit fields explicitly

Replace details() with a fields list (key + label) so the review table and the editor stay in sync. Plugin shows seven fields (no CSS Prefix), which the editor no longer offers.

* docs: drop removed husky/pre-commit script references

* refactor(init): delegate init to @rtcamp/wp-tooling/init

The wrapper previously loaded the scaffold engine from vendor/rtcamp/wp-framework/bin/scaffold.js. The engine's permanent repository is wp-tooling (wp-tooling#32 moves it there as the ./init export), so point the wrapper at @rtcamp/wp-tooling/init and drop the Composer vendor check and the now-unused fs require. The @rtcamp/wp-tooling ^0.1.0 pin is already in package.json. Folds in #709, which can close as redundant.

* refactor: Update `husky` to `hooks`

Refer:
rtCamp/wp-tooling@cb6f53d

* feat: enqueue Tailwind CSS, gated on a constant (#704)

Ship PROJECT_NAME_FEATURES_ENABLE_TAILWIND (default false, overridable in
wp-config) in the main plugin file, resolve it once into a property, and
enqueue css/tailwind off it via handle('tailwind'). No per-request file_exists;
the scaffold's Tailwind feature flips the constant.

* chore(npm): resolve @rtCamp scope from private GitHub Packages

@rtcamp/wp-tooling is served from GitHub Packages (restricted) during the pilot, not public npm. Adds the registry mapping and token-via-env auth so org users and CI can install it. Includes a TODO to drop this once the package ships on public npm.

* feat: add Tailwind CSS as an opt-in scaffold feature (#706)

* feat: enqueue Tailwind CSS, gated on a constant

Ship PROJECT_NAME_FEATURES_ENABLE_TAILWIND (default false, overridable in
wp-config) in the main plugin file, resolve it once into a property, and
enqueue css/tailwind off it via handle('tailwind'). No per-request file_exists;
the scaffold's Tailwind feature flips the constant.

* feat(scaffold): add Tailwind CSS opt-in feature

Toggleable in manage mode: copies the entry CSS + PostCSS config and adds
the tailwindcss / @tailwindcss/postcss devDeps; detected via the entry
file. The enqueue side lives in inc/Core/Assets.php.

* fix(tailwind): resolve PostCSS config from @rtcamp/tailwind-config

The feature's postcss.config.js required @rtcamp/wp-tooling/tailwind-config/postcss, a subpath wp-tooling does not export. The PostCSS config is the standalone @rtcamp/tailwind-config package (exports ./postcss). Point the require there and add @rtcamp/tailwind-config to the feature's apply.devDependencies so enabling Tailwind installs it. Without this, enabling the feature broke the build at postcss load time.

* feat: toggle HMR (BrowserSync) via ENABLE_HMR (#707)

* feat(scaffold): add HMR enable/disable feature

HMR (BrowserSync live reload) is now a toggleable feature, gated on a
single ENABLE_HMR flag in .env.local that both the build and PHP read:

- webpack only starts the BrowserSync server when ENABLE_HMR is not off
- Assets.php only enqueues the client under the same flag
- the scaffold feature flips ENABLE_HMR in .env.local on enable/disable

Default is on, so existing setups are unaffected. browser-sync deps stay
installed (they are dev-only), so the toggle is a fast local switch.
DISABLE_BS still works for the finer client-only case. Toggle from
`npm run init` (manage mode) or by editing .env.local directly.

* docs(assets): note ENABLE_HMR in enqueue_browser_sync docblock

The method now also gates on the ENABLE_HMR master switch, not just DISABLE_BS.

* test(assets): cover ENABLE_HMR / DISABLE_BS gating

* feat(scaffold): mark example sets for selective removal (#708)

Wrap each module's example registrations in wp:example markers and group them in the scaffold config so init can drop selected sets while keeping the rest.

---------

Co-authored-by: AnuragVasanwala <75766877+AnuragVasanwala@users.noreply.github.com>

* docs: add internal testing guide for v2 init (npm link)

---------

Co-authored-by: AnuragVasanwala <75766877+AnuragVasanwala@users.noreply.github.com>
…#716)

Make AGENTS.md the single source of truth; CLAUDE.md and copilot-instructions become thin pointers. Rewrite README for the npm run init flow, real inc/ structure, AI onboarding, and private packages. Add .claude/skills: a new init skill plus scaffold and setup synced from @rtcamp/wp-tooling.
eslint was bumped to 10.4.0 (via Dependabot) ahead of WordPress eslint stack support. @wordpress/eslint-plugin@25.3.0 and every plugin it bundles (eslint-plugin-react, -jest, -import, -jsdoc, -jsx-a11y, @babel/eslint-parser) peer-accept only eslint "^7 || ^8 || ^9", not 10. So every `npm install` logged a wall of ERESOLVE "overriding peer dependency" warnings and npm resolved an inconsistent eslint tree.

Pin eslint to 9.39.4 — the highest release the entire @WordPress build stack supports — which removes all ERESOLVE peer warnings with no --legacy-peer-deps, overrides, or other patches (verified on a clean install). eslint 9 fully supports the flat config (eslint.config.mjs) this project already uses, so no config change is needed. We stay on 9 until @wordpress/eslint-plugin ships eslint 10 support upstream; revert this pin to ^10 only once that lands.
- deps: consume @rtcamp/wp-tooling as a git dependency
  (git+https://github.com/rtCamp/wp-tooling.git) instead of the GitHub Packages
  spec it was never published under (npm ci 404'd, failing all jobs). .npmrc drops
  the private registry and adds legacy-peer-deps (wp-tooling's eslint ^10 peer is
  advisory here; redundant once rtCamp/wp-tooling#38 marks its peers optional).
- ci: authenticate git for the private clone before every npm ci (rewrite github
  https/ssh/scp URLs to an HTTPS token URL via WP_FRAMEWORK_REPO_TEMP_TOKEN), since
  the lockfile pins a git+ssh URL.
- ci hardening (Copilot): add a least-privilege top-level permissions block (FP-4);
  quote github.base_ref in the fetch step (FP-6); replace deprecated ::set-output
  with $GITHUB_OUTPUT.

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

The import contains a path-traversal risk and several functional, testing, tooling, and documentation defects.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity · 2 Low severity

Open (4)
Resolved since last review (1)
Previously missed (11)

In code that hasn't changed since last review

Medium severity Comment out placeholder SSL settings

.env.local.example:29

The example activates both SSL variables with nonexistent placeholder paths. Because the webpack config enables HTTPS whenever both values are non-empty, copying this file for a normal HTTP setup makes BrowserSync try to read missing certificates and fail; leave these optional settings commented out.

Medium severity Fix dead editor stylesheet selector

src/​blocks/​example-block-interactive/​editor.scss:12

This selector uses the old create-block namespace and targets an input, while the registered block class is wp-block-project-name-features-example-block-interactive and Edit renders only a paragraph. Consequently the entire editor stylesheet is dead; target the actual block markup or remove it.

Medium severity Add canonical block wrapper attributes

templates/​block-templates/​example-block-dynamic.php:15

This dynamic block omits get_block_wrapper_attributes(), so its rendered markup lacks the canonical block class and the stylesheet selector .wp-block-project-name-features-example-block-dynamic never matches. Include the wrapper attributes on the root element.

Medium severity Test the Encryption subclass fallback

tests/​php/​EncryptionTest.php:44

This test bypasses the plugin's Encryption subclass and exercises the framework Encryptor directly. It would remain green if Encryption::key() stopped falling back correctly, so instantiate Encryption here to cover the behavior described by the test.

Medium severity Verify PostTypes hook registration specifically

tests/​php/​PostTypesTest.php:120

has_action( 'init' ) is already true in a booted WordPress test environment, so this assertion passes even if PostTypes::register_hooks() does nothing. Verify the callbacks added by this module or an isolated observable registration outcome instead.

Medium severity Verify Taxonomies hook registration specifically

tests/​php/​TaxonomiesTest.php:110

has_action( 'init' ) is already true in a booted WordPress test environment, so this assertion passes even if Taxonomies::register_hooks() does nothing. Verify the callbacks added by this module or an isolated observable registration outcome instead.

Low severity Clarify when --name is required

.github/​prompts/​init.prompt.md:61

This statement is too broad and conflicts with the valid manage-mode commands immediately above it, which use --yes without --name. Clarify that --name is required with --yes only during initial setup so the prompt does not reject supported feature toggles.

Low severity Update outdated wp-tooling authentication notes

.npmrc:8

These comments still describe wp-tooling as private and requiring a token, but this PR consumes its public repository and the setup guide explicitly says no token is needed. Update the note so developers are not sent through unnecessary authentication troubleshooting.

Low severity Remove redundant browser-sync port instruction

docs/​hmr.md:103

PHP now reads BS_PORT from the same .env.local, so changing the port does not require a matching wp-config constant. This instruction duplicates the setting and contradicts Assets::get_browser_sync_port(); reserve the constant for overriding the full URL in proxy/remote setups.

Low severity Add missing @​since tags to docblocks

inc/​Main.php:17

The repository's PHP rules require @since on every class/trait docblock, but the imported classes consistently omit it (including this bootstrap class). Add the tag across the new production and test class docblocks before merging.

Low severity Align Graphify installation guidance with AGENTS.md

scripts/​graphify/​README.md:7

This says the skill cannot install Graphify, but AGENTS.md explicitly directs assistants—with consent—to run scripts/graphify/install.sh and then verify.sh. Align this README with the source-of-truth workflow so users are not told they must perform a step the agent is designed to handle.

Comment thread inc/Helpers/Util.php
Comment thread scripts/graphify/verify.sh
Comment thread docs/hmr.md Outdated
Comment thread docs/tailwind.md Outdated
Copilot AI balanced review requested due to automatic review settings October 1, 2026 15:36

Copilot AI 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.

Copilot review overview

🔵 Needs a closer look

Uncached remote failures, ineffective tests, missing wrapper coverage, and a broken block style selector should be addressed before merge.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity · 2 Low severity

Open (4)
Previously missed (6)

In code that hasn't changed since last review

Medium severity Cache failed responses to prevent repeated slow retries

inc/​Modules/​Transients/​ExampleTransients.php:68

Failed or non-200 responses are returned without populating the transient, so an upstream outage makes every call retry the three-second request. This contradicts the method’s “fetched at most once per hour” contract and can amplify latency; cache the negative result briefly before returning.

Medium severity Test the plugin Encryption subclass instead of framework Encryptor

tests/​php/​EncryptionTest.php:44

This test exercises the framework Encryptor directly rather than the plugin’s Encryption subclass. It will still pass if Encryption::key() stops delegating correctly when the plugin constant is absent, so the behavior described by the test remains uncovered.

Medium severity Assert the taxonomy-specific hook registration

tests/​php/​TaxonomiesTest.php:109

has_action( 'init' ) only proves that some callback is registered on this heavily used WordPress hook; the bootstrapped plugin already adds several, so this assertion passes even if Taxonomies::register_hooks() is broken. Assert the module-specific callback/effect instead so the test covers the wiring it claims to verify.

Medium severity Add tests for dev-server compatibility wrapper paths and ports

webpack.blocks.config.js:44

The new dev-server compatibility wrapper has no Jest coverage for port validation or for its single-config versus array-config export paths. Because it mutates an upstream configuration object and exists specifically to bridge incompatible schemas, add focused tests with the WordPress config mocked so dependency updates cannot silently break start:blocks.

Low severity Update stale comment about the publishing branch

.github/​workflows/​documentation.yml:5

The workflow triggers and deploy conditions use main, not a v2 branch, so this maintainer comment is stale and misstates the publishing source.

Low severity Fix selector and styles for the actual block markup

src/​blocks/​example-block-interactive/​editor.scss:7

This selector uses the old create-block namespace and targets a text input, while this block’s generated class is .wp-block-project-name-features-example-block-interactive and Edit renders only a paragraph. Consequently none of these editor-only styles can ever apply; target the actual block markup or remove the unused stylesheet/import.

The slug was interpolated into a require path, so ../../some-file could load any PHP file the process can read. Slugs are now limited to letters, digits, hyphens and underscores, and anything else returns the default without loading a file. UtilTest covers a normal load, a missing file and six unsafe slugs (traversal, nested traversal, backslash, absolute path, null byte, empty).
…notes

- verify.sh looked for the interpreter through uv tool run, which installs graphify on demand, so it could report success on a machine without it. It now checks the installed uv tool environment only.
- docs/hmr.md no longer tells wp-env users to change a port the example no longer sets to 8888.
- docs/tailwind.md drops the Stylelint failure note; the config accepts the Tailwind at-rules.
Copilot AI balanced review requested due to automatic review settings October 1, 2026 16:56
BS_PORT and BLOCKS_DEV_SERVER_PORT now have to be all digits: parseInt took 8888foo as 8888, and Number would have accepted hex. Matches the starter theme.

Copilot AI 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.

Copilot review overview

🔵 Needs a closer look

Initialization and block-scaffolding paths contain reproducible failures and destructive edge cases.

Review effort: Balanced
Findings: None

Resolved since last review (4)
Previously missed (3)

In code that hasn't changed since last review

Medium severity Fix selector namespace for interactive block styles

src/​blocks/​example-block-interactive/​editor.scss:7

This selector still uses the create-block namespace, while the block name is project-name-features/example-block-interactive. As a result, these editor-only styles never match the block.

Medium severity Use consistent whole-number port validation

webpack.config.js:45

parseInt() accepts partially numeric values such as 123abc, so BrowserSync binds to port 123 while the PHP client parser and block dev-server wrapper reject the same value and fall back. Parse with Number() so all three consumers apply the documented whole-port validation consistently.

Low severity Avoid caching unused Components service as shared

inc/​Core/​Components.php:25

Components is cached as Shareable, but no production caller retrieves it from the container; the only get_shared( Components::class ) calls are in ComponentsTest. Under the framework contract, this should remain a plain loaded service unless a real consumer needs shared retrieval, otherwise every request retains an unnecessary instance solely to satisfy the test.

Copilot AI balanced review requested due to automatic review settings October 1, 2026 17:05

Copilot AI 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.

Copilot review overview

🔵 Needs a closer look

The review found functional installer, styling, caching, translation-timing, test-coverage, and repository-rule inconsistencies.

Review effort: Balanced
Findings: None

Previously missed (9)

In code that hasn't changed since last review

Medium severity Missing-dependency fallback translates too early

inc/​Autoloader.php:46

This fallback executes while the main plugin file is loading, before the init textdomain hook. On WordPress 6.7+, attempting this translation can emit an additional “translation loading was triggered too early” warning precisely when dependencies are missing. Build the translated admin-notice text after init, while keeping any immediate diagnostic free of early translation calls.

Medium severity Test does not exercise the plugin encryption override

inc/​Core/​Encryption.php:40

test_missing_key_throws() instantiates the framework Encryptor, so it never executes this plugin override or verifies its fallback. Exercise Encryption (in an isolated process so the key constant cannot leak between tests) to cover the missing/empty-key path.

Medium severity CLI command resolution triggers translations too early

inc/​Modules/​CLI/​Healthcheck.php:32

CLI::get_commands() calls this during plugin bootstrap, before the plugin's init textdomain hook. On the supported WordPress 6.7+ versions, this can trigger the “translation loading was triggered too early” warning whenever WP-CLI loads the plugin. Defer command registration/description resolution until init (or another appropriately timed WP-CLI hook).

Medium severity Failed requests are not negatively cached

inc/​Modules/​Transients/​ExampleTransients.php:67

The method promises the remote request runs at most once per hour, but failures are returned without any transient. During an outage or non-200 response, every call repeats the three-second request, which can amplify latency and load. Negative-cache failures (typically with a shorter TTL) or revise the stated caching contract.

Medium severity Tests omit remote-fetch cache and error scenarios

inc/​Modules/​Transients/​ExampleTransients.php:69

The tests explicitly pre-seed the transient and never exercise this cache-miss branch. Mock the WordPress HTTP response to cover success/caching, WP_Error, non-200, and invalid JSON so the new remote-fetch behaviour is protected.

Medium severity Gemini upgrade does not install the optional extra

scripts/​graphify/​install.ps1:73

-Gemini -Upgrade logs that it is upgrading the Gemini spec, but both the uv branch here and the pipx branch on line 83 upgrade only $Package. If an existing installation lacks the optional extra, a successful upgrade never adds it. Reinstall $Spec when -Gemini is requested, and make the matching correction in the shell installer.

Low severity Imported classes lack required @​since docblocks

.github/​instructions/​primitives-php.instructions.md:26

This newly added mandatory rule is not satisfied by the imported implementation: the class docblocks throughout inc/ omit @since (and keep @package only in file headers), for example inc/Main.php:15-17 and inc/Core/Encryption.php:15-25. Bring the imported classes into compliance before making this the repository's source-of-truth rule.

Low severity Imported code violates the required static references rule

.github/​instructions/​primitives-php.instructions.md:28

The imported production code does not follow this newly declared invariant: self:: remains in multiple classes, including Main::CLASSES, Autoloader::class, and the module constants. Either migrate those references to the required static:: form or reconcile the rule before publishing these instructions as canonical.

Low severity Editor styles target an obsolete selector

src/​blocks/​example-block-interactive/​editor.scss:7

This selector is left over from the create-block namespace, and the editor component renders a <p> rather than an <input>. As a result, none of these editor-only declarations ever match the interactive block. Target the block's actual wrapper class (or remove this unused stylesheet).

AnuragVasanwala
AnuragVasanwala previously approved these changes Oct 1, 2026
composer.json already required rtcamp/wp-primitives ^2.0, but the lock still pinned rtcamp/wp-framework v1.0.1, so composer install refused it and the Lint PHP and PHPUnit jobs failed. Locked to wp-primitives dev-main (cfa5b99, the current main) until v2.0.0 is tagged; no other package changed.
Copilot AI balanced review requested due to automatic review settings October 1, 2026 17:40
AnuragVasanwala
AnuragVasanwala previously approved these changes Oct 1, 2026

Copilot AI 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.

Copilot review overview

🔵 Needs a closer look

Independent capability removal can leave fatal taxonomy references, and several test, architecture, selector, and documentation issues remain unresolved.

Review effort: Balanced
Findings: None

Previously missed (6)

In code that hasn't changed since last review

Medium severity Test the plugin Encryption override instead of framework Encryptor

tests/​php/​EncryptionTest.php:44

This test constructs the framework Encryptor, so it never exercises the plugin's Encryption::key() override described by the test. A regression that stops the plugin service from falling back on a missing constant would still pass; invoke Encryption here instead.

Medium severity Assert taxonomy-specific hook registration

tests/​php/​TaxonomiesTest.php:109

This assertion is already true before register_hooks() because the bootstrapped plugin registers other init callbacks (for example Assets::register_blocks() at inc/Core/Assets.php:65). The test therefore passes even if taxonomy hook wiring breaks; assert a module-specific callback or observable taxonomy registration instead.

Low severity Clarify --yes validity for first-run versus manage mode

.github/​prompts/​init.prompt.md:60

This statement applies only to first-run setup. In manage mode, --yes is valid with --features, --enable, or --disable, as the examples above demonstrate. Qualify the rule here and in the matching Claude skill so the two instructions remain aligned.

Low severity Update comment to reflect main branch documentation deployment

.github/​workflows/​documentation.yml:5

This comment still says documentation publishes from a v2 branch, while the workflow triggers and deploys from main. Update the comment so it describes the actual workflow.

Low severity Remove or consume the unused shared Components service

inc/​Core/​Components.php:25

No production code retrieves Components from the container; only this PR's tests call get_shared( Components::class ). Under the framework contract, Shareable is reserved for services with a real later retrieval need. Either add the intended runtime consumer/accessor or stop registering this unused shared service.

Low severity Use the block's actual namespace in the selector

src/​blocks/​example-block-interactive/​editor.scss:7

This selector still uses the old create-block namespace, so it cannot match the wrapper generated for project-name-features/example-block-interactive. Update it to the block's actual namespace.

Copilot AI balanced review requested due to automatic review settings October 1, 2026 17:49

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

PHPStan currently exits before analysis, with additional unresolved runtime, test-coverage, and documentation defects.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Previously missed (16)

In code that hasn't changed since last review

Medium severity Add integration tests for Assets Registrable behavior

inc/​Core/​Assets.php:65

AssetsHmrTest covers only the two environment gates; the new Registrable's hook wiring, static block registration, asset/module registration, and conditional editor assets have no matching tests. Add an Assets integration test that exercises these callbacks against WordPress's registries.

Medium severity Test HTTP success, failure, invalid JSON, and transient caching

inc/​Modules/​Transients/​ExampleTransients.php:65

The matching test explicitly avoids this cache-miss branch, so successful responses, HTTP failures, invalid JSON, and writing the fetched payload to the transient are unverified. Mock the HTTP request with WordPress's pre_http_request filter and cover these outcomes.

Medium severity Cache HTTP failures to prevent repeated requests

inc/​Modules/​Transients/​ExampleTransients.php:68

HTTP errors are returned without caching, despite this method promising to fetch at most once per hour. During an outage every call repeats the remote request; cache the empty fallback (or another negative-cache marker) before returning.

Medium severity Use the actual block namespace in the editor styles selector

src/​blocks/​example-block-interactive/​editor.scss:12

This selector still uses the create-block placeholder namespace, while the block wrapper is wp-block-project-name-features-example-block-interactive. The editor-only styles therefore never match the rendered block.

Medium severity Test the plugin Encryption class instead of the framework base

tests/​php/​EncryptionTest.php:44

This test instantiates the framework base Encryptor, so it still passes if the plugin's Encryption::key() override stops rejecting a missing key. Exercise the class under test instead.

Low severity Align the workflow description with the main branch

.github/​workflows/​documentation.yml:4

This comment says Pages publishes from a v2 branch, but every trigger and deployment condition below targets main. Keep the workflow description aligned with its actual branch.

Low severity Update framework links to the locked v2.0.0 documentation

DEVELOPMENT.md:7

This guide and its later API links still target wp-primitives v1.0.1 even though the imported plugin requires ^2.0. Update all framework links in this file to the locked v2.0.0 tag so the documented contracts match the installed dependency.

Low severity Update the wp-primitives link to v2.0.0 documentation

README.md:27

The plugin now requires wp-primitives v2, but this link still sends developers to the v1.0.1 API documentation. Point it at the locked v2.0.0 docs used elsewhere in this PR.

Low severity Update AssetLoader and AbstractBlock links to v2.0.0

docs/​blocks-and-assets.md:3

The AssetLoader link here and the AbstractBlock link later on the page still point to wp-primitives v1.0.1. Update both to v2.0.0 to match the dependency imported by this PR.

Low severity Update framework references to v2.0.0 documentation

docs/​features.md:13

The framework references throughout this page still target v1.0.1, while the plugin requires wp-primitives v2 and the docs index already links v2.0.0. Update all framework links on this page to the locked v2.0.0 tag.

Low severity Correct the workflow trigger branch from master to main

docs/​features.md:44

The workflow runs on pushes to main, not master; this usage guide currently tells maintainers the wrong trigger branch.

Low severity Document the generated primitives-php instructions file path

docs/​initialization.md:78

sync-ai generates primitives-php.instructions.md, as shown by the added file and Copilot instructions; framework-php.instructions.md does not exist. Update the documented output path.

Low severity Reference primitives-php.instructions.md in the ownership table

docs/​internal/​maintenance.md:14

The generated review-rules file is named primitives-php.instructions.md, not framework-php.instructions.md. This ownership table currently directs maintainers to a nonexistent file.

Low severity Update the contributor link to the v2.0.0 guide

docs/​internal/​maintenance.md:107

This contributor link still targets the v1.0.1 framework guide even though the repository now consumes wp-primitives v2. Point it at the locked v2.0.0 guide; keep the dated v1 validation record later in this document unchanged.

Low severity Update wp-primitives links to v2.0.0 documentation

docs/​scaffolding.md:112

Both wp-primitives links on this page still target v1.0.1 although this plugin now consumes v2. Update them to the locked v2.0.0 documentation so generated-code guidance reflects the current contracts.

Low severity Add @​since tags to imported class and trait docblocks

inc/​Main.php:17

The imported PHP classes systematically omit the required @since tag from their class docblocks (this bootstrap class is one example). Add the version metadata to each new class/trait docblock so the skeleton itself demonstrates its documented PHP contract.

Comment thread phpstan.neon.dist
@AnuragVasanwala
AnuragVasanwala merged commit c394054 into main Oct 1, 2026
31 checks passed
@AnuragVasanwala
AnuragVasanwala deleted the feat/import-v2 branch October 1, 2026 18:06
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.

7 participants