From ac55dc96a436db6e901c5a3bfc05de1a86ef6a34 Mon Sep 17 00:00:00 2001 From: Sylvain Duchesne Date: Sun, 16 Aug 2026 22:06:52 +0200 Subject: [PATCH] A refreshed data-layer package never reaches a running dev server `link:polyfill` overlays the package into node_modules and keeps it current. That is all it does. A `bun run dev` already running goes on serving what it loaded at startup, however many times the overlay is rewritten underneath it: node_modules is excluded by file watchers, so the change lands in the one place nothing is looking. The script promised the opposite -- that `bun --hot` would reload the copied file live. That promise is now removed, and replaced by the instruction to restart. It cost about an hour. A defect had been fixed upstream, the overlay refreshed, and a probe on a freshly launched server confirmed the fix 3 runs out of 3. The same sequence by hand reproduced the defect at once. The two observations looked irreconcilable and the hunt went to the wallet, to prior state, to timing. The dev server had been up for six days. It predated the package rename and the whole migration; the browser was running a different application from the one under test. Nothing warned: a stale server looks exactly like a current one, and the script's own header ruled out the true cause for anyone who trusted it. The reflex is written down with the leaf: when a fix seems not to take, or when a hand-run and an automated run disagree, check how long the server has been up before anything else. One command, cheapest hypothesis first. --- .../data-layer/contract_polyfill-surface.md | 2 +- ...at_polyfill-overlay-needs-a-dev-restart.md | 31 +++++++++++++++++++ scripts/link-polyfill.ts | 16 +++++++--- 3 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 .project/concepts/tech-stack/caveat_polyfill-overlay-needs-a-dev-restart.md diff --git a/.project/concepts/data-layer/contract_polyfill-surface.md b/.project/concepts/data-layer/contract_polyfill-surface.md index 075fb68..68d9606 100644 --- a/.project/concepts/data-layer/contract_polyfill-surface.md +++ b/.project/concepts/data-layer/contract_polyfill-surface.md @@ -2,7 +2,7 @@ type: contract summary: The API @ng-eventually/polyfill exposes to an application — signatures, guaranteed behaviour, and what it does not offer pulled_from: https://gitea.reconnexion.apps.gueraud.net/Reconnexion/ng-eventually.git/.project/concepts/app-contract/contract_polyfill-surface.md -pulled_version: 1ecf511e9d8de8e0feb007f3a88f2c0d56ce455a +pulled_version: a33fb8a21464194227668fd703edd35f685bb3c1 pulled_at: 2026-08-16 --- diff --git a/.project/concepts/tech-stack/caveat_polyfill-overlay-needs-a-dev-restart.md b/.project/concepts/tech-stack/caveat_polyfill-overlay-needs-a-dev-restart.md new file mode 100644 index 0000000..efef6ff --- /dev/null +++ b/.project/concepts/tech-stack/caveat_polyfill-overlay-needs-a-dev-restart.md @@ -0,0 +1,31 @@ +--- +type: caveat +summary: A running `bun run dev` never picks up a refreshed data-layer package — the overlay lives in node_modules, which watchers exclude, so the server keeps serving whatever it loaded at startup. Restart after every `link:polyfill`, and suspect a stale server before suspecting the code. +last_checked: 2026-08-16 +--- + +# Pitfall: refreshing the data-layer package does not reach a running dev server + +`pnpm run link:polyfill` overlays the local package into `node_modules/@ng-eventually/polyfill/` as real files, and keeps them current. **That is all it does.** A `bun run dev` already running goes on serving the package it loaded at startup, however many times the overlay is rewritten underneath it — `node_modules` is excluded by file watchers as a matter of convention, so the change happens in the one place nothing is looking. + +**So: restart `bun run dev` after every refresh of the package.** There is no signal that you needed to; a stale server looks exactly like a current one. + +## Why this is worth a leaf + +VERIFIED 2026-08-16, and it cost about an hour. A defect had been fixed on the provider's side, the overlay was refreshed, and an automated probe on a freshly launched server confirmed the fix — 3 runs out of 3, clean. The same sequence performed by hand in a browser reproduced the defect immediately. The two observations looked irreconcilable, and the search went to the wallet, to prior state, to timing. + +The dev server had been running for **six days**. It predated the package rename and the whole migration, and it was serving code from before the fix. The browser was running a different application from the one under test. + +Two things made it hard to see. The failure mode is **silence** — nothing warns that the served code is old. And `scripts/link-polyfill.ts` explicitly promised the opposite, that `bun --hot` would reload the copied file live; that claim is now corrected in the script, but a reader who trusted it would rule out the true cause first, which is exactly what happened. + +## The reflex to build + +When a fix does not appear to take effect, or when a hand-run and an automated run disagree, **check how long the server has been up before anything else**. It is one command, and it eliminates the cheapest hypothesis first: + +```bash +ps -o lstart= -p $(pgrep -f 'bun --hot src/index.ts' | head -1) +``` + +The same reasoning applies to anything else served out of `node_modules` — the trap is the location, not this package. + +Related: [[cookbook_live-probe]] (bdd-testing) — a probe answers only for the code the server actually holds, so a stale server invalidates the probe's conclusion, not the product's behaviour. diff --git a/scripts/link-polyfill.ts b/scripts/link-polyfill.ts index faaa1ef..e7552b8 100644 --- a/scripts/link-polyfill.ts +++ b/scripts/link-polyfill.ts @@ -18,13 +18,20 @@ * directory holding the local polyfill's package.json + src (NO node_modules). * 2. Asserts the single-instance invariant (same @ng-org/web realpath from Festipod and * from the overlay) — aborts if it would break. - * 3. Watches the local polyfill src and copies each change into the overlay, so - * `bun --hot` (bun run dev) reloads the edited file live. + * 3. Watches the local polyfill src and copies each change into the overlay. + * + * ⚠️ RESTART `bun run dev` AFTER THIS SCRIPT WRITES. The overlay lives inside + * node_modules, which file watchers exclude by convention — so a running dev + * server keeps serving the polyfill it loaded at startup, however many times + * this script rewrites the files underneath it. VERIFIED the hard way: a dev + * server six days old served pre-fix code while the overlay on disk was current, + * and an hour went into hunting a defect that had already been fixed. Watching + * copies the files; it does not make anything reload them. * * USAGE (reactive dev): * Terminal 1: pnpm run link:polyfill # overlays local source, then watches * Terminal 2: bun run dev # portless festipod bun --hot src/index.ts - * Edit files under packages/polyfill/src → they land in node_modules → bun --hot reloads. + * Edit files under packages/polyfill/src → they land in node_modules → RESTART dev to pick them up. * * pnpm run link:polyfill --once # overlay + verify, no watch (CI / one-shot) * Return to the committed git-installed dependency: pnpm install @@ -85,7 +92,8 @@ if (ONCE) { process.exit(0); } -// 3. Watch and copy on change so `bun --hot` sees live edits. +// 3. Watch and copy on change. This keeps the overlay CURRENT; it does not make a +// running dev server notice (node_modules is outside the watcher) — restart it. console.log(`👀 watching ${SRC_LOCAL} → ${SRC_TARGET} (Ctrl-C to stop)`); watch(SRC_LOCAL, { recursive: true }, (_event, filename) => { if (!filename) return;