*/ public static function profiles(): array { return [ self::VERIFIED_PROFILE => [ 'status' => self::STATUS_KNOWN_GOOD, 'note' => 'Fully audited baseline: docs/audit/fluentcart-contracts.md, Contracts.php.', ], '1.5.3' => [ 'status' => self::STATUS_KNOWN_GOOD, 'note' => 'Real source comparison against FluentCart 1.5.3 + Pro 1.5.3 and Breakdance 2.8.1: ' . 'docs/audit/fc-1.5.3-source-comparison.md. Every contracted shortcode/method/model-field/' . 'asset-loader-method/capability re-verified file:line against the new source; only real ' . 'behaviour change found is fluent_cart_single_product now being booted (Contracts.php notes ' . 'updated, OR-fallback kept for 1.3.27 compat). Breakdance 2.8.1 comparison found no ' . 'differences at all.', ], '1.5.5' => [ 'status' => self::STATUS_KNOWN_GOOD, 'note' => 'Profile audit against FluentCart 1.5.5 core (Pro still 1.5.3) and Breakdance 2.8.1: ' . 'docs/audit/fc-1.5.5-source-comparison.md. contract-check reports 0 missing; all 50 elements ' . 'render through Breakdance\'s own Element::ssr() with warnings treated as failures; every JS ' . 'dataset key, selector and event was re-read in the 1.5.5 bundles rather than trusted from the ' . '1.5.3 audit. No contracted interface changed. A full 1.5.3 to 1.5.5 source diff was not done, ' . 'see the "not verified" section of that document.', ], ]; } /** * Highest version in the profile registry with a known-good status. The * "is this newer than what we audited" question has to be asked against * this, not against VERIFIED_PROFILE: that constant is the ORIGINAL * baseline and never moves, so comparing against it would keep naming * 1.3.27 as "the last audited profile" no matter how many newer profiles * are added, and would leave an audited newer release reported as * unknown. VERIFIED_PROFILE stays the right reference for the * older-unsupported branch, where the oldest supported release is what * matters. */ public static function newestAuditedProfile(): string { $newest = self::VERIFIED_PROFILE; foreach (self::profiles() as $version => $profile) { if (($profile['status'] ?? '') !== self::STATUS_KNOWN_GOOD) { continue; } if (version_compare((string) $version, $newest, '>')) { $newest = (string) $version; } } return $newest; } /** * Evaluate an active FluentCart version against the profile registry. * * @param string|null $activeVersion Defaults to the FLUENTCART_VERSION * constant when omitted. * @return array{version: string|null, status: string, message: string} */ public static function evaluate(?string $activeVersion = null): array { if ($activeVersion === null) { $activeVersion = defined('FLUENTCART_VERSION') ? (string) FLUENTCART_VERSION : null; } if ($activeVersion === null || $activeVersion === '') { return [ 'version' => null, 'status' => self::STATUS_UNDETECTED, 'message' => 'FLUENTCART_VERSION is not defined; FluentCart does not appear to be active.', ]; } $profiles = self::profiles(); if (isset($profiles[$activeVersion])) { return [ 'version' => $activeVersion, 'status' => $profiles[$activeVersion]['status'], 'message' => sprintf( 'FluentCart %s matches an audited profile (%s).', $activeVersion, $profiles[$activeVersion]['note'] ), ]; } $newestAudited = self::newestAuditedProfile(); if (version_compare($activeVersion, $newestAudited, '>')) { return [ 'version' => $activeVersion, 'status' => self::STATUS_UNKNOWN_NEWER, 'message' => sprintf( 'FluentCart %s is newer than the last audited profile (%s). Sentinel geel: nothing is blocked ' . 'blindly and nothing fatals; each contract still decides per capability whether it is safe ' . 'to use. Run tools/contract-check.php against this install and add a new profile after a ' . 'real source comparison.', $activeVersion, $newestAudited ), ]; } return [ 'version' => $activeVersion, 'status' => self::STATUS_OLDER_UNSUPPORTED, 'message' => sprintf( 'FluentCart %s is older than the last audited profile (%s). Contracts that are missing on this ' . 'version are an expected limitation of running an older release, not necessarily a plugin bug; ' . 'upgrading FluentCart is recommended.', $activeVersion, self::VERIFIED_PROFILE ), ]; } /** * Convenience boolean for callers that only need a yes/no "is this a * fully audited, known-good version" answer (e.g. an admin banner). * Never used to gate whether a feature is allowed to run: that decision * always belongs to the per-contract capability check. */ public static function isKnownGood(?string $activeVersion = null): bool { return self::evaluate($activeVersion)['status'] === self::STATUS_KNOWN_GOOD; } /** * G1K kernel-commit (ELITE-PLAN.md par. 182-201, Golf 1): capability keys * that WP3/WP4/WP5 gate behavior on instead of a bare version compare. * Each key corresponds 1:1 to a Contracts::all() entry id of the same * name; the value here is only a short human label for an admin/Health * display, never itself used to decide whether a feature runs (that * decision always belongs to Contracts::verify()'s per-entry check, per * the capability-first principle documented on Contracts::verify()). * * Full evidence and analysis for every key: docs/audit/g1k-probes.md. * * @return array */ public static function capabilities(): array { return [ 'cap.bd.share_props_with_ssr_children' => 'Breakdance SSR parent-property propagation (Element::ssr() 2nd arg, settings()[\'sharePropsWithSSRChildren\']).', 'cap.bd.template_type_registration' => 'Breakdance\\Themeless\\registerTemplateType()/registerCondition() public API.', 'cap.bd.template_include_priority' => 'Breakdance template_include takeover callback registered at priority 1000000.', 'cap.bd.loop_controller' => 'Breakdance\\DynamicData\\LoopController per-id loop-item singleton (getInstance/set/get/reset).', 'cap.fc.shop_page_setting' => 'FluentCart StoreSettings::getShopPageId()/getShopPage() designated virtual shop page.', 'cap.fc.product_archive' => 'FluentCart fluent-products CPT registered with a real has_archive WordPress archive.', 'cap.fc.json_ld_absent' => 'Documented finding: FluentCart emits no product/offer JSON-LD anywhere; WP3 must emit its own.', 'cap.fc.disable_auto_single_product_page' => 'FluentCart fluent_cart/disable_auto_single_product_page filter check sites (TemplateActions).', 'cap.fc.theme_support_gate' => 'Documented finding: the disable filter is only ever consulted when the active theme is "unsupported" (TemplateLoader::hasThemeSupport() false).', ]; } }