From 7e734fec66772438c7686581b67334953f2afa44 Mon Sep 17 00:00:00 2001 From: magdev Date: Sun, 3 May 2026 13:19:58 +0200 Subject: [PATCH] healthz: depend on Publisher to force bundle deep-load (perfsmoke gap) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v0.1.0 shipped two bugs that left /healthz returning 200 against a half-loaded bundle: the path-repo symlink dangling at runtime in the AppImage (vendor/php-qml/bridge → nonexistent), and the writable cache-dir bug (Symfony couldn't create var/cache/prod). HealthController returned a static {status:"ok"} without ever touching any BridgeBundle service, so perfsmoke + the connection-state probe both passed even when the bundle's autoload was broken — first sign of trouble was a 500 from /api/todos under real load. Inject Publisher (the bundle's Mercure-publish wrapper) via constructor and reference its FQN in the response body. Two effects: - Symfony's container resolves Publisher when the controller is instantiated; if the bundle's autoload is broken, the controller can't even construct, /healthz returns 500. - The response now includes `bundle: "PhpQml\Bridge\Publisher"` — proves to perfsmoke + dev console that the canary is live, not a cached static response. Connection-state probe semantics unchanged: still 200 = Online, non-200 = Reconnecting/Offline. Probe interval is 5s — Publisher's construction is constant-time, no perf concern. No new public API: /healthz response gained a `bundle` field (additive, JSON parsers ignore unknown keys); 200 vs 500 boundary is preserved. No existing consumer broken. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../php/src/Controller/HealthController.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/framework/php/src/Controller/HealthController.php b/framework/php/src/Controller/HealthController.php index b989cbf..9d9e79a 100644 --- a/framework/php/src/Controller/HealthController.php +++ b/framework/php/src/Controller/HealthController.php @@ -4,18 +4,33 @@ declare(strict_types=1); namespace PhpQml\Bridge\Controller; +use PhpQml\Bridge\Publisher; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Attribute\Route; /** * Readiness probe used by the Qt host to detect when the backend is up. * See PLAN.md §3 (*Startup*, step 4). + * + * Publisher is injected purely as a deep-health canary: if the bridge + * bundle's autoload or container wiring is broken (e.g. a packaging build + * with a dangling vendor path-repo symlink), this controller can't even + * be constructed, so /healthz fails 500 instead of misleadingly returning + * 200 against a half-loaded bundle. */ final class HealthController { + public function __construct( + private readonly Publisher $publisher, + ) { + } + #[Route('/healthz', name: 'php_qml_bridge_healthz', methods: ['GET'])] public function __invoke(): JsonResponse { - return new JsonResponse(['status' => 'ok']); + return new JsonResponse([ + 'status' => 'ok', + 'bundle' => $this->publisher::class, + ]); } }