Let the panel reach the card by its hashed name, not by the one address without a version

The sidebar page could serve a previous card for hours. It imported the
card through the stable facade, `./houseplan-card.js` — a relative
specifier, and relative resolution does not inherit a query. A dashboard
reaches the same file as `houseplan-card.js?v=1.74.0`, so an upgrade
changes its URL and the browser must refetch. The panel always asked for
the same address, and entries are served with no Cache-Control at all —
only ETag and Last-Modified — so the browser applies heuristic freshness
and may answer from cache without asking. A stale 1164-byte loader names
a stale chunk, chunks are immutable for a year, and the panel then ran a
previous card against the current backend without a single error. The
version banner was telling the truth; reloading could not help, because
the address never changed.

Rollup already emits the right edge: the panel's side-effect import
points straight at the shared implementation. The rewrite in
entryFallbackPlugin replaced it with the facade for a fallback that the
hashed name gives anyway — and better: a chunk the manifest no longer
serves now raises the panel's own "House Plan was updated" notice
instead of silently working on old code.

Two #486 assertions change meaning and are rewritten, not adjusted: the
panel no longer routes through the facade, and its initial graph no
longer contains it. The invariant they defended — the panel reuses the
exact card graph, never a second copy — is now stated over the
implementation, and a new test pins that no built entry reaches the card
by an address without a version.

Issue: #535
User-Visible: yes
This commit is contained in:
Claude
2026-09-11 22:12:11 +00:00
committed by claude[bot]
parent a52dcab77a
commit 063de0eef4
57 changed files with 536 additions and 428 deletions
+27 -6
View File
@@ -3100,23 +3100,44 @@ const MUTANT_DEFINITIONS = [
file: 'scripts/bundle-manifest.mjs',
find: ' panelEntry.code = panelEntry.code.replace(\n'
+ ' panelPattern,\n'
+ ' `try{await import("./${CARD_ENTRY_FILE}")}`\n'
+ ' `try{await import("${cardAsset}")}`\n'
+ ' + `catch(e){${fallbackDefinition(panelContract)}`\n'
+ " + 'console.error(\"[houseplan] stale houseplan-panel.js: the card entry is unavailable\",e)}',\n"
+ " + 'console.error(\"[houseplan] stale houseplan-panel.js: the card implementation '\n"
+ " + 'chunk is unavailable\",e)}',\n"
+ ' );',
replace: ' void panelEntry; // mutant: cached panel keeps its static shared-chunk import',
}],
},
{
id: 'panel-imports-unversioned-card-entry',
guard: 'node --test --test-name-pattern="#486 both stable entries" '
+ 'test/bundle-assets.test.mjs',
because: 'the stable card facade is the one address in the distribution without a version '
+ 'in it, and entries are served without Cache-Control: routing the panel through it let a '
+ 'browser keep a previous card for hours and run it against the current backend in silence '
+ '(#535 AC1/AC2)',
patches: [{
file: 'scripts/bundle-manifest.mjs',
find: ' `try{await import("${cardAsset}")}`\n'
+ ' + `catch(e){${fallbackDefinition(panelContract)}`\n'
+ " + 'console.error(\"[houseplan] stale houseplan-panel.js: the card implementation '",
replace: ' `try{await import("./${CARD_ENTRY_FILE}")}`\n'
+ ' + `catch(e){${fallbackDefinition(panelContract)}`\n'
+ " + 'console.error(\"[houseplan] stale houseplan-panel.js: the card implementation '",
}],
},
{
id: 'panel-entry-bypasses-card-graph',
guard: 'node --test --test-name-pattern="#486 both stable entries" '
+ 'test/bundle-assets.test.mjs',
because: 'routing panel startup straight to a hashed implementation bypasses the stable '
+ 'card root and breaks the required exact card-graph subset (#486 AC5)',
because: 'the manifest edge must name what the panel actually imports. Since #535 that is '
+ 'the hashed implementation, not the stable facade; an edge naming the facade would make '
+ 'initialPanelFiles describe a graph the panel never loads, and the "panel reuses the '
+ 'exact card graph" check would then pass on a fiction (#486 AC5, #535 AC4)',
patches: [{
file: 'scripts/bundle-manifest.mjs',
find: ' panelEntry.imports = [CARD_ENTRY_FILE];',
replace: ' panelEntry.imports = [cardAsset.slice(2)]; // mutant: direct hashed edge',
find: " panelEntry.imports = [cardAsset.replace(/^\\.\\//, '')];",
replace: ' panelEntry.imports = [CARD_ENTRY_FILE]; // mutant: edge names the facade',
}],
},
{