fix(cache-bust): close FE-024 gaps so every JS/CSS gets ?v=<sha>
Some checks failed
CI / ruff (push) Successful in 17s
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / pytest (push) Has been cancelled

The 2026-05-18 cache-busting system was only catching a fraction of
includes because:

1. FE-024 anti-pattern only matched `'<module>_static'` mount names
   (e.g. `'core_static'`, `'billing_static'`). The bare `'static'`
   mount — which is what every persona base.html uses for shared JS,
   CSS, and Tailwind output — never matched.
2. The rule explicitly excluded `base.html` files, which are exactly
   where most of the shared JS/CSS includes live.

User reported only a handful of files had `?v=` in their Network tab.
Swept 5 persona base.html + 15 standalone templates (login/register/
forgot/reset password, error pages, onboarding, invitation-accept,
admin module-info/config, etc.) — 53 url_for('static', ...) refs for
.js/.css converted to static_v(request, 'static', ...).

Then tightened FE-024:
- Added an anti-pattern for the bare `'static'` mount.
- Dropped `base.html` from exceptions (kept `partials/`).

Re-running the validator: same 126-warning baseline, 0 FE-024 hits.

Now every deploy flips the `?v=<sha>` query string on every shared
asset; browsers refetch automatically without a hard refresh.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-29 21:01:44 +02:00
parent 5f359283bc
commit 3ce9468397
21 changed files with 83 additions and 83 deletions

View File

@@ -39,7 +39,7 @@
</style>
{# Tailwind CSS v4 (built locally via standalone CLI) #}
<link rel="stylesheet" href="{{ url_for('static', path='storefront/css/tailwind.output.css') }}">
<link rel="stylesheet" href="{{ static_v(request, 'static', path='storefront/css/tailwind.output.css') }}">
</head>
<body>
<div class="flex items-center min-h-screen p-6 bg-gray-50 dark:bg-gray-900" x-cloak>

View File

@@ -38,7 +38,7 @@
</style>
{# Tailwind CSS v4 (built locally via standalone CLI) #}
<link rel="stylesheet" href="{{ url_for('static', path='storefront/css/tailwind.output.css') }}">
<link rel="stylesheet" href="{{ static_v(request, 'static', path='storefront/css/tailwind.output.css') }}">
</head>
<body>
<div class="flex items-center min-h-screen p-6 bg-gray-50 dark:bg-gray-900" x-cloak>
@@ -182,7 +182,7 @@
</div>
<!-- Icons (registers $icon magic helper for Alpine) -->
<script defer src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/icons.js') }}"></script>
<!-- Alpine.js v3 -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.0/dist/cdn.min.js"></script>

View File

@@ -39,7 +39,7 @@
</style>
{# Tailwind CSS v4 (built locally via standalone CLI) #}
<link rel="stylesheet" href="{{ url_for('static', path='storefront/css/tailwind.output.css') }}">
<link rel="stylesheet" href="{{ static_v(request, 'static', path='storefront/css/tailwind.output.css') }}">
</head>
<body>
<div class="flex items-center min-h-screen p-6 bg-gray-50 dark:bg-gray-900" x-cloak>

View File

@@ -39,7 +39,7 @@
</style>
{# Tailwind CSS v4 (built locally via standalone CLI) #}
<link rel="stylesheet" href="{{ url_for('static', path='storefront/css/tailwind.output.css') }}">
<link rel="stylesheet" href="{{ static_v(request, 'static', path='storefront/css/tailwind.output.css') }}">
</head>
<body>
<div class="flex items-center min-h-screen p-6 bg-gray-50 dark:bg-gray-900" x-cloak>

View File

@@ -8,7 +8,7 @@
<title>Welcome to Orion - Setup Your Account</title>
<link href="{{ static_v(request, 'static', path='shared/fonts/inter.css') }}" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="{{ url_for('static', path='store/css/tailwind.output.css') }}" />
<link rel="stylesheet" href="{{ static_v(request, 'static', path='store/css/tailwind.output.css') }}" />
<style>
[x-cloak] { display: none !important; }
</style>
@@ -373,10 +373,10 @@
</div>
<!-- Scripts -->
<script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>
<script defer src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
<script defer src="{{ url_for('static', path='shared/js/utils.js') }}"></script>
<script defer src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/log-config.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/icons.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/utils.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/api-client.js') }}"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.0/dist/cdn.min.js"></script>
<script defer src="{{ static_v(request, 'marketplace_static', path='store/js/onboarding.js') }}"></script>
</body>

View File

@@ -8,12 +8,12 @@
<!-- Fonts: Local fallback + Google Fonts -->
<link href="{{ static_v(request, 'static', path='shared/fonts/inter.css') }}" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="{{ url_for('static', path='admin/css/tailwind.output.css') }}" />
<link rel="stylesheet" href="{{ static_v(request, 'static', path='admin/css/tailwind.output.css') }}" />
<!-- Flag Icons CSS (for language selector) with local fallback -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/flag-icons@7.2.3/css/flag-icons.min.css"
onerror="this.onerror=null; this.href='{{ url_for('static', path='shared/css/store/flag-icons.min.css') }}';"
onerror="this.onerror=null; this.href='{{ static_v(request, 'static', path='shared/css/store/flag-icons.min.css') }}';"
/>
<style>
[x-cloak] { display: none !important; }
@@ -181,16 +181,16 @@
<script>window.FRONTEND_TYPE = '{{ frontend_type | default("admin") }}';</script>
<!-- 1. Log Configuration -->
<script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/log-config.js') }}"></script>
<!-- 2. Icons -->
<script defer src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/icons.js') }}"></script>
<!-- 3. Utils -->
<script defer src="{{ url_for('static', path='shared/js/utils.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/utils.js') }}"></script>
<!-- 4. API Client -->
<script defer src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/api-client.js') }}"></script>
<!-- 5. Alpine.js v3 with CDN fallback -->
<script>

View File

@@ -148,5 +148,5 @@
{% endblock %}
{% block extra_scripts %}
<script defer src="{{ url_for('static', path='admin/js/module-config.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='admin/js/module-config.js') }}"></script>
{% endblock %}

View File

@@ -270,5 +270,5 @@
{% endblock %}
{% block extra_scripts %}
<script defer src="{{ url_for('static', path='admin/js/module-info.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='admin/js/module-info.js') }}"></script>
{% endblock %}

View File

@@ -8,7 +8,7 @@
<title>Select Platform - Admin Panel</title>
<link href="{{ static_v(request, 'static', path='shared/fonts/inter.css') }}" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="{{ url_for('static', path='admin/css/tailwind.output.css') }}" />
<link rel="stylesheet" href="{{ static_v(request, 'static', path='admin/css/tailwind.output.css') }}" />
<style>[x-cloak] { display: none !important; }</style>
</head>
<body x-cloak>
@@ -173,10 +173,10 @@
</div>
<!-- Scripts -->
<script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>
<script defer src="{{ url_for('static', path='shared/js/utils.js') }}"></script>
<script defer src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
<script defer src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/log-config.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/utils.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/api-client.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/icons.js') }}"></script>
<script defer src="{{ static_v(request, 'tenancy_static', path='admin/js/select-platform.js') }}"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.13.3/dist/cdn.min.js"></script>
</body>

View File

@@ -9,12 +9,12 @@
<!-- Fonts: Local fallback + Google Fonts -->
<link href="{{ static_v(request, 'static', path='shared/fonts/inter.css') }}" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="{{ url_for('static', path='merchant/css/tailwind.output.css') }}" />
<link rel="stylesheet" href="{{ static_v(request, 'static', path='merchant/css/tailwind.output.css') }}" />
<!-- Flag Icons CSS (for language selector) with local fallback -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/flag-icons@7.2.3/css/flag-icons.min.css"
onerror="this.onerror=null; this.href='{{ url_for('static', path='shared/css/store/flag-icons.min.css') }}';"
onerror="this.onerror=null; this.href='{{ static_v(request, 'static', path='shared/css/store/flag-icons.min.css') }}';"
/>
<style>
[x-cloak] { display: none !important; }
@@ -182,16 +182,16 @@
<script>window.FRONTEND_TYPE = '{{ frontend_type | default("merchant") }}';</script>
<!-- 1. Log Configuration -->
<script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/log-config.js') }}"></script>
<!-- 2. Icons -->
<script defer src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/icons.js') }}"></script>
<!-- 3. Utils -->
<script defer src="{{ url_for('static', path='shared/js/utils.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/utils.js') }}"></script>
<!-- 4. API Client -->
<script defer src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/api-client.js') }}"></script>
<!-- 5. Alpine.js v3 with CDN fallback -->
<script>

View File

@@ -8,11 +8,11 @@
<title>Accept Invitation - {{ invitation.store_name if invitation else 'Store' }}</title>
<link href="{{ static_v(request, 'static', path='shared/fonts/inter.css') }}" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="{{ url_for('static', path='store/css/tailwind.output.css') }}" />
<link rel="stylesheet" href="{{ static_v(request, 'static', path='store/css/tailwind.output.css') }}" />
<style>
[x-cloak] { display: none !important; }
</style>
<script src="{{ url_for('static', path='shared/js/dev-toolbar.js') }}"></script>
<script src="{{ static_v(request, 'static', path='shared/js/dev-toolbar.js') }}"></script>
</head>
<body>
<div class="flex items-center min-h-screen p-6 bg-gray-50 dark:bg-gray-900" x-cloak>
@@ -140,9 +140,9 @@
<!-- Scripts -->
<script>window.FRONTEND_TYPE = '{{ frontend_type | default("store") }}';</script>
<script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>
<script defer src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
<script defer src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/log-config.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/icons.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/api-client.js') }}"></script>
<script>
(function() {
var script = document.createElement('script');

View File

@@ -8,13 +8,13 @@
<!-- Fonts: Local fallback + Google Fonts -->
<link href="{{ static_v(request, 'static', path='shared/fonts/inter.css') }}" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="{{ url_for('static', path='store/css/tailwind.output.css') }}" />
<link rel="stylesheet" href="{{ static_v(request, 'static', path='store/css/tailwind.output.css') }}" />
<style>
[x-cloak] { display: none !important; }
</style>
<!-- Dev debug toolbar (dev only — auto-hides in production, must load early for interceptors) -->
<script src="{{ url_for('static', path='shared/js/dev-toolbar.js') }}"></script>
<script src="{{ static_v(request, 'static', path='shared/js/dev-toolbar.js') }}"></script>
</head>
<body>
<div class="flex items-center min-h-screen p-6 bg-gray-50 dark:bg-gray-900" x-cloak>
@@ -212,16 +212,16 @@
<script>window.FRONTEND_TYPE = '{{ frontend_type | default("store") }}';</script>
<!-- 1. Log Configuration -->
<script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/log-config.js') }}"></script>
<!-- 2. Icons -->
<script defer src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/icons.js') }}"></script>
<!-- 3. Utils -->
<script defer src="{{ url_for('static', path='shared/js/utils.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/utils.js') }}"></script>
<!-- 4. API Client -->
<script defer src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
<script defer src="{{ static_v(request, 'static', path='shared/js/api-client.js') }}"></script>
<!-- 5. Alpine.js v3 with CDN fallback -->
<script>