| 1 |
<?php |
| 2 |
/** |
| 3 |
* Capability_Seed — registers the core capability set at bootstrap (spec 053). |
| 4 |
* |
| 5 |
* Called from Plugin.php on `plugins_loaded`, immediately before |
| 6 |
* Modules_Manager::boot(), so every module can gate on these keys during its |
| 7 |
* own boot. Registration stores metadata only — no probe runs here (FR-006). |
| 8 |
* |
| 9 |
* The WordPress 7.1 keys are registered VERSION-ONLY on purpose (spec 053 |
| 10 |
* Clarifications, 2026-08-02): the code must be testable on a 7.1 release |
| 11 |
* candidate as soon as one exists; each probe is added when the final 7.1 |
| 12 |
* documentation confirms the symbol. Every key here has a matching ledger row |
| 13 |
* in .ai/context/wp-feature-watch.md (FR-024) — keep the two in sync. |
| 14 |
* |
| 15 |
* PHP 7.2 SYNTAX ONLY (FR-038). |
| 16 |
* |
| 17 |
* @package Templately |
| 18 |
*/ |
| 19 |
|
| 20 |
namespace Templately\Core; |
| 21 |
|
| 22 |
class Capability_Seed { |
| 23 |
/** |
| 24 |
* Register the seed set on the shared registry. |
| 25 |
* |
| 26 |
* @return void |
| 27 |
*/ |
| 28 |
public static function register_all() { |
| 29 |
$capabilities = Capabilities::get_instance(); |
| 30 |
|
| 31 |
// The block-pattern catalog's host gate. The version INPUT still flows |
| 32 |
// through the legacy `templately_block_patterns_wp_version` filter so |
| 33 |
// third-party/test code holding that seam keeps working (FR-033); the |
| 34 |
// boolean override `templately_capability_pattern-file-path` is the |
| 35 |
// documented path going forward. |
| 36 |
$capabilities->register( 'pattern-file-path', [ |
| 37 |
'probe' => function () { |
| 38 |
global $wp_version; |
| 39 |
$version = apply_filters( 'templately_block_patterns_wp_version', $wp_version ); |
| 40 |
return version_compare( (string) $version, '6.5', '>=' ); |
| 41 |
}, |
| 42 |
'wp' => '6.5', |
| 43 |
'posture' => 'hard', |
| 44 |
'note' => 'Lazy pattern content via filePath registration (WP 6.5); the block-patterns module is absent without it.', |
| 45 |
] ); |
| 46 |
|
| 47 |
$capabilities->register( 'http-parallel-requests', [ |
| 48 |
'probe' => function () { |
| 49 |
return class_exists( 'WpOrg\\Requests\\Requests' ) && is_callable( [ 'WpOrg\\Requests\\Requests', 'request_multiple' ] ); |
| 50 |
}, |
| 51 |
'wp' => '6.2', |
| 52 |
'posture' => 'degrade', |
| 53 |
'note' => 'Namespaced Requests library (WP 6.2) with request_multiple(); full-site-import fetches attachments a few at a time with it and falls back to one request per file without it.', |
| 54 |
] ); |
| 55 |
|
| 56 |
$capabilities->register( 'abilities-api', [ |
| 57 |
'probe' => function () { |
| 58 |
return function_exists( 'wp_register_ability' ); |
| 59 |
}, |
| 60 |
'wp' => '6.9', |
| 61 |
'posture' => 'degrade', |
| 62 |
'note' => 'WordPress core Abilities API; the abilities bridge detaches without it (built-in MCP server still serves).', |
| 63 |
] ); |
| 64 |
|
| 65 |
$capabilities->register( 'mcp-adapter-plugin', [ |
| 66 |
'probe' => function () { |
| 67 |
return class_exists( 'WP\\MCP\\Core\\McpAdapter' ); |
| 68 |
}, |
| 69 |
'posture' => 'degrade', |
| 70 |
'note' => 'The WordPress/mcp-adapter companion plugin; the adapter bridge detaches without it and Settings offers the install.', |
| 71 |
] ); |
| 72 |
|
| 73 |
// ---- WordPress 7.1 wave: version-only until the 7.1 Field Guide |
| 74 |
// ---- confirms each probe symbol (Clarifications 2026-08-02). |
| 75 |
|
| 76 |
// Probed, not merely versioned, as of 2026-08-02: the mechanism was read |
| 77 |
// off a real 7.1-beta4 host, so the symbol is known rather than assumed. |
| 78 |
// `WP_View_Config_Data` is what the filter hands every callback, so its |
| 79 |
// presence is the same fact the feature actually depends on. |
| 80 |
$capabilities->register( 'site-editor-view-config', [ |
| 81 |
'probe' => function () { |
| 82 |
return class_exists( 'WP_View_Config_Data' ); |
| 83 |
}, |
| 84 |
'wp' => '7.1', |
| 85 |
'posture' => 'degrade', |
| 86 |
'note' => 'Site Editor screen view-config filters (get_entity_view_config_{kind}_{name}); contribute additively via WP_View_Config_Data::merge( $patch, $version ).', |
| 87 |
] ); |
| 88 |
|
| 89 |
$capabilities->register( 'abilities-selective-fields', [ |
| 90 |
'wp' => '7.1', |
| 91 |
'posture' => 'degrade', |
| 92 |
'note' => 'Abilities API selective field responses / validation hooks. Probe TBD at 7.1 Field Guide.', |
| 93 |
] ); |
| 94 |
|
| 95 |
$capabilities->register( 'wp-knowledge-cpt', [ |
| 96 |
'probe' => function () { |
| 97 |
return post_type_exists( 'wp_knowledge' ); |
| 98 |
}, |
| 99 |
'wp' => '7.1', |
| 100 |
'stage' => 'init', |
| 101 |
'posture' => 'degrade', |
| 102 |
'note' => 'wp_knowledge post type (/wp/v2/knowledge). Probe valid only after init registers post types.', |
| 103 |
] ); |
| 104 |
|
| 105 |
$capabilities->register( 'block-bindings-nested-richtext', [ |
| 106 |
'wp' => '7.1', |
| 107 |
'posture' => 'degrade', |
| 108 |
'note' => 'Bound rich text preserves nested inner blocks (behavioral — likely stays version-only).', |
| 109 |
] ); |
| 110 |
|
| 111 |
$capabilities->register( 'icons-api', [ |
| 112 |
'probe' => function () { |
| 113 |
return function_exists( 'register_icon' ); |
| 114 |
}, |
| 115 |
'wp' => '7.1', |
| 116 |
'posture' => 'degrade', |
| 117 |
'note' => 'Icons API (register_icon(), wp/v2/icon-collections).', |
| 118 |
] ); |
| 119 |
|
| 120 |
// Read on the CLIENT, not here: `@wordpress/components` is host-provided, |
| 121 |
// so the site's WordPress decides whether its form controls still want the |
| 122 |
// `__next40pxDefaultSize` opt-in (below 7.1) or warn about it (7.1+, where |
| 123 |
// 40px is unconditional and the prop is a no-op). There is no server-side |
| 124 |
// symbol to probe — the evidence lives in a JS package — so this key stays |
| 125 |
// version-only by nature, not as a stopgap. See |
| 126 |
// `react-src/utils/componentSizing.ts`, its only consumer. |
| 127 |
$capabilities->register( 'components-40px-default', [ |
| 128 |
'wp' => '7.1', |
| 129 |
'posture' => 'degrade', |
| 130 |
'note' => '@wordpress/components form controls default to 40px; __next40pxDefaultSize hard-deprecated to a no-op.', |
| 131 |
] ); |
| 132 |
} |
| 133 |
} |
| 134 |
|