Skip to content

Release: develop -> main - #1245

Merged
TaprootFreak merged 1 commit into
mainfrom
develop
Aug 3, 2026
Merged

Release: develop -> main#1245
TaprootFreak merged 1 commit into
mainfrom
develop

Conversation

@github-actions

@github-actions github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

Automatic Release PR

This PR was automatically created after changes were pushed to develop.

Commits: 1 new commit(s)

Checklist

  • Review all changes
  • Verify CI passes
  • Approve and merge when ready for production

…1244)

* Stop a wrong asset response from outliving the deploy that caused it

A missing /static/* asset used to be answered by the SPA fallback with the app
shell, and the path-based header rule stamped that HTML with the lifetime meant
for fingerprinted assets: public, max-age=31536000, immutable. Because immutable
forbids revalidation, the wrong answer was pinned for a year in every cache that
had fetched it. Customers hit an error screen instead of the buy page; one case
stayed stuck for roughly 40 minutes on a fixed-line connection, and the edge had
to be purged by hand to clear it.

Missing assets already answer 404. This adds the parts that keep a single
mistake from becoming a year-long outage:

- drop immutable from /static/*, so a user-triggered reload can revalidate and
  correct a poisoned entry. The long max-age stays; shared caches are unaffected
  and still need purging, which the comment now says plainly.
- route /favicon.ico and /logo.png through the content check as well. They carried
  the same path-not-content assumption with a 24h lifetime and never reached the
  function.
- treat a missing content-type on an ok response as the fallback case instead of
  defaulting it away, and compare case-insensitively.
- verify after every production deploy that an absent asset really answers a bare
  404 with the exact headers the function emits. A run-specific marker is polled
  first so the check runs against a live deploy; the step states plainly what it
  can and cannot prove, since marker and asset are separate requests.
- pin the route list in a test, so dropping an entry cannot pass silently.

* Close the same gap on the remaining non-fingerprinted paths, and tighten the checks

Review follow-ups on the same failure class:

- robots.txt, manifest.json and asset-manifest.json go through the content
  check too. no-cache still stores the response — it only forces revalidation
  before reuse — so a later 304 can confirm a wrongly stored app shell under
  those URLs. / and /index.html stay out: they are the fallback.
- the route-list test now pins include and exclude exactly, so an entry cannot
  be added or dropped unnoticed, and reads the file as a typed import like the
  rest of the suite.
- compare the media type before the parameters instead of scanning the whole
  header, so a content type that merely mentions text/html in a parameter is not
  mistaken for the fallback.
- separate the transport-failure conclusion from the wrong-answer one in the
  verify step, and qualify three comment claims that promised more than HTTP
  caching guarantees.

* Cover version.json too, and fail closed on an empty content type

- version.json goes through the content check as well. It is written by the
  build and an e2e test expects real JSON from it, so a missing file would have
  been served as the app shell under that URL — the same failure class the rest
  of this change closes. It gets the same revalidation rule as the other
  manifests.
- an empty content-type is now treated like a missing one. The claim was that
  the check fails closed; it only did so for an absent header.
- the verify step tracks whether any response was received at all, so an
  aborted final attempt is no longer reported as 'never got a response' after
  eleven wrong-but-received ones, and a non-matching response is no longer
  asserted to be the incident's failure mode when it could be an unrelated error.

The empty-content-type test sets the header after construction on purpose:
passing '' in the init is discarded and the platform fills in text/plain, so
the constructor route cannot produce the case the test is named after.

* Say only what the checks actually establish

Two diagnostic texts promised more than the code observes:

- the marker loop reported 'new deploy did not become active' whenever no
  matching marker body came back, though all it establishes is that no marker
  matching this run was seen — the deploy may well be live with the marker
  endpoint misbehaving.
- the version.json comment claimed a new commit hash on every build. The hash
  only moves when HEAD does; a rebuild of the same commit reuses it, and only
  the build time is always fresh. It is also written by the main-app build,
  not by the widget build.

* Note that the version hash can be unknown

generate-version.js falls back to the literal 'unknown' when git metadata is
unavailable, so the comment describing what version.json holds should not
promise a commit hash unconditionally.
@TaprootFreak
TaprootFreak merged commit 799c510 into main Aug 3, 2026
6 checks passed
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.

1 participant