Skip to content

routine 2026-10-13: #heading lands on it, README images keep their size, stalls that say why, marked 18.1.1 - #24

Merged
physics515 merged 13 commits into
mainfrom
routine/site-2026-10-13
Oct 14, 2026
Merged

physics515 merged 13 commits into
mainfrom
routine/site-2026-10-13

Conversation

@physics515

@physics515 physics515 commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Nightly routine, 2026-10-13 (21:10 CDT → past 01:10). Eleven increments plus the snapshot. In order:

1. marked 18.1.1, on a byte diff — f3c2003

Roadmap: the held marked 18.1.1 item. Ticked.
Files: package.json, package-lock.json, ROADMAP.md.

marked 18.1.1 changes rendered output: blockquote lazy lines, HTML blocks interrupting paragraphs, backslashes in code spans, and tab stops. So it went in only on a byte diff.

  • The diff: all 8 READMEs (prepared the way the site prepares them) and all 98 posts (94 copied from the live content volume, plus the 4 fixtures) went through renderMarkdown on 18.1.0 and on 18.1.1. The output is byte-identical.
  • The diff can see a change: 5 of 8 probes built from the release notes render differently between the two versions.

Results: typecheck clean; 276/276 tests; build green; audit:runtime clean. Every route answered as below; check 22 pages / 79 URLs clean. Screenshots: home, Skidbladnir and Nisaba at 1280 and 390, looked at.

2. vitest 5.0.4; Vue 3.5.44 held — 8449f84

Roadmap: new [ ] for Vue 3.5.44.
Files: package.json, package-lock.json, ROADMAP.md.

  • vitest is dev-only. Typecheck, 276 tests and build all green; every route code is identical to increment 1's.
  • Vue 3.5.44 is held a night. It was published 2026-10-13 07:45 UTC with 24 fixes in SSR, hydration and the scheduler. That matches how marked and Nuxt 4.6 were held. The item carries a diff plan.

3. A cold load of #heading lands on it — 1ec2ac7

Roadmap: the cold-load miss from #23. Ticked.
Files: app/plugins/restore-jump.client.ts, scripts/check-navigation.mjs, scripts/lib/browser.mjs, ROADMAP.md.

Cause: two scrolls raced.

  • HTML's "scroll to the fragment" uses behavior auto, so the site's scroll-behavior: smooth made Chromium's own fragment scroll an animation from the top.
  • vue-router jumped to the same heading as the app started.
  • When the animation's first step landed after the jump, it was added on top of it.
  • Evidence: with vue-router's jump held back, the browser's first step was 4 or 18 px on 12/12 loads, and every miss was exactly +4 or +18.

Fix: restore-jump.client.ts now holds scroll-behavior: auto on a fresh load that carries a fragment, as it already did for reloads and Back/Forward.

Nanna at 390 px, #api-key-invalid, no instrumentation:

full speed CPU 6×
before 4/20 off 19/20 off (one 21 px behind the masthead)
after 0/20 0/20

The check now sees it. Before, it couldn't: a +4 miss leaves the text 12 px clear, inside the 0–200 band.

  • It now wants the heading's box on scroll-padding-top's line, within 2 px.
  • It takes the cold load at 6× CPU in Chromium, through a new page.throttle().
  • Main's build: fails it on 9 cold loads across 5 pages.
  • This build: 160/160 in Chromium and Firefox.

A first try compared the heading's text line and failed every heading that has space above its text inside the box; that was fixed to the box before landing.

Results:

  • typecheck clean; 276/276 tests; build green.
  • check 22 pages / 79 URLs clean; a11y 23 pages clean.
  • a11y:browser and a11y:firefox: 23 pages × 3 widths, clean.
  • Screenshots looked at. A chromium --screenshot of the #fragment URL came out blank (2.7 KB), so it is not used as evidence.

4. A request that never answers fails in 180 s — 4ea706c

Roadmap: new sub-item under the cold-load item.
Files: scripts/lib/cdp.mjs, scripts/lib/bidi.mjs, ROADMAP.md.

The CDP and BiDi clients had no request timeout, so a hung page held CI's step for 15 minutes. Every request now rejects after 180 s, naming its method and its expression or URL.

  • Proved with a never-settling evaluate in both engines: rejected at 180 s, and the next call answered in milliseconds.
  • Every browser run after it was green, with no false fires.

5. README images keep the snapshot's size when a probe misses — 0eddd79

Roadmap: new [x].
Files: shared/markdown/image-size.ts, server/utils/readmeImages.ts, server/utils/github.ts, test/image-size.test.ts, ROADMAP.md.

Problem: a missed size probe isn't cached, so the README goes out with that image at 0 × 0 until the next refresh. Cold starts, i.e. every deploy, are where misses land.

  • img.shields.io answered in 3.0 s to 1 request in 9 from this host, past the server's 2 s budget.
  • A local server tonight served splimes with four badges unsized.

Fix: statedSizes() reads the snapshot's sizes for the same README and sizeReadmeImages falls back to them. A live answer still wins.

Proof: cold start behind a CONNECT proxy that refuses img.shields.io.

  • main's build: 18 images unsized across 5 about pages
  • this build: 0

Results: 279 tests (3 new); typecheck clean; build green; routes unchanged; check clean.

The badges were a suspect for a 31 px reload drift seen once (open item), but they move the page 2.3 px, so they don't explain it.

6. …and npm run sync doesn't write a miss into the snapshot — 17b45bb

Files: scripts/fetch-projects.mjs, ROADMAP.md.

A miss during sync wrote the image unsized into the snapshot, which would take away the fallback from increment 5. Sync now keeps the previous snapshot's size and logs it.

Proof: behind the same proxy, main's script wrote 22 unsized images; this one wrote 0, keeping 22 sizes across 6 READMEs. The snapshot was restored afterwards.

7. /healthz counts how README images were sized — 2780a4d

Files: shared/types/health.ts, server/utils/health.ts, server/utils/readmeImages.ts, app/pages/status.vue, README.md, ROADMAP.md.

readmeImages: { measured, kept, unsized } now appears on /healthz and as a status-page row. The fallback hides misses from the page; this shows whether production has any.

  • shields.io refused: 6 measured / 22 kept / 0 unsized
  • normal: 28 / 0 / 0

Results: typecheck, tests and build green; routes unchanged; status page looked at, 390 and 1280.

8. A minimal Chromium repro of the fragment race — bda7343

Files: docs/repro/smooth-fragment-overshoot.html (1.4 KB), ROADMAP.md.

Chromium 152 at 390 px, 30 cold loads each:

case result
instant jump 10 ms after load 4 or 13 px past on 30/30
jump at 20 ms 13 or 24 px past on 30/30
scroll-behavior: auto on target 60/60
no jump on target 30/30

CSSOM View says a new scroll aborts an ongoing smooth one; this is not that. Reporting it is an owner [ ].

9. The Firefox masthead: likely broken, with the fix written down — b02913a

Roadmap: the "look in a real Firefox" item, sharpened. Still [ ].

What I could and couldn't check: headless Firefox draws no backdrop-filter at all: not the site's chain, not a plain blur(2.5px), not blur(8px), with any gfx.webrender.* pref. So the routine can't see it.

Why it's likely broken:

  • Mozilla's open bug 1961378 says backdrop-filter is dropped entirely when an SVG url() filter falls back to a blob image.
  • A duplicate, 1972363, names feImage and feDisplacementMap, which the masthead filters use (4 feDisplacementMap, 2 feImage).

The fix, not shipped because nobody has looked: and (not (-moz-appearance: none)) on the @supports. That query is true in Firefox 155 and false in Chromium 152.

Needs a person with a real Firefox: look before and after. Alternatively, a headless compositor (cage or sway, a pacman install) would let the routine look for itself.

10–11. The check:navigation stall, caught and narrowed — a3c43bb, fb656bb

Files: scripts/check-navigation.mjs, scripts/lib/browser.mjs, ROADMAP.md.

Caught twice tonight (#23's stall), thanks to increment 4:

  1. Nanna at 1280 px, 1 of 3 runs: arrive's evaluate went 180 s unanswered.
  2. In 9 hunt runs, once more: Skidbladnir at 1280 px, the first page of the pass, right after clicking the blog tab. Renderer idle, 0.00 CPU s in 1 s.

My first theory (Nanna's length, an old tab) is wrong: the second stall was a different page in a fresh tab, so neither fits.

What a stalled page does now:

  • It fails with the last 4 calls made of it, the busiest renderer's CPU time over 1 s, and whether a plain evaluate still answers.
  • The run then continues in a fresh tab.

Shown on a stand-in site:

  • a spinning page: "busy (0.99–1.00 CPU s); a plain evaluate got no answer in 5 s"
  • an idle one: "idle; answered: visible, complete, …"

The real check passes 160/160 in both engines with this in place. Still open: the next stall's answer to the plain-evaluate question.

Snapshot — a1200ea

npm run sync: Skidbladnir v1.4.4 (released 2026-10-13), downloads and push times. Every README image is sized.

Verification of the shipping tree

The final tree, snapshot included, built and served, all green:

  • typecheck clean
  • 279/279 tests; build green
  • check: 22 pages / 79 URLs
  • a11y: 23 pages
  • a11y:browser and a11y:firefox: 23 pages × 1280 / 390 / 320
  • check:navigation: 160/160 in Chromium and in Firefox
  • /healthz: ok, live, readmeImages 116 measured / 0 kept / 0 unsized
  • Screenshots: home, Skidbladnir and Nisaba at 1280 and 390, looked at. The flat single background is intact, the masthead glass is unchanged, and nothing overflows.

Server routes (built server, real codes):

  • 200: /healthz, /, /projects, every /projects/<slug>/about and /blog, /api/projects, /api/projects/onyums, /sitemap.xml, /news, /robots.txt, /releases.xml.
  • 301: /projects/<slug>, to /about.
  • 404: /projects/no-such-project, /no-such-page, /api/projects/nope, and the admin routes.
  • 503: /onion-frame, as expected locally without a gateway.

These are unchanged from increment 1 through the final build.

Bugs found, not fixed

  1. A reload on splimes at 1280 px landed 31 px past the reader, once, on main's build. The trace shows the fold restoring what pagehide saved, so the page moved between reading and unload. Not the badges (2.3 px). Not seen in 15 complete Chromium runs on this branch. [ ] with next steps.
  2. The navigation stall above: diagnosed further, not fixed.
  3. Firefox masthead blur, likely missing in real Firefox and Tor Browser (item 9).
  4. A README resource failing mid-check (hunt run 9, during CI): ERR_CONNECTION_CLOSED on Skidbladnir at 390 px, with a client navigation to #editions landing the box 27 px under the aim (43 vs 16). The console error fails the check on its own, so the run was red either way. My guess is an external README image failing during the scroll and changing size above the heading; the error carries no URL, so that isn't shown. A [ ] for next run: log the failed URL, and find out what a broken README image does to the layout.

Research

Done vs open

Done: increments 1–11; snapshot refreshed.

Onion probe: 4 scheduled runs in ~31 h, one first-try connect timeout, three first-try reaches in 11–13 s. Logged in the roadmap; too few to weigh C Tor.

Glance: glance.basicautomation.io now resolves publicly through Cloudflare and answers 401, so it is behind auth. The roadmap item was already ticked.

New [ ]:

  • Vue 3.5.44
  • report the Chromium repro (owner)
  • the splimes reload drift
  • the stall's next answer

Still owner-gated or upstream: unchanged from #23, plus the Firefox look.

STOP REASON: 4 h cutoff (21:10 → past 01:10 CDT). For the last hour the frontier was thin: what remained was owner-gated, upstream-blocked, held for soak, or waiting on a stall to recur.

Recommended next step

  1. Vue 3.5.44, with the SSR body diff.
  2. Owner: open the site in a real Firefox, scrolled, to judge the masthead blur. If the text is crisp behind the pane, ship the one-line @supports change.
  3. The next navigation stall's plain-evaluate answer.
  4. Owner: whether to report the Chromium repro and the Nuxt reload-to-top.

Shipped

  • CI: green (build 26 m 8 s, image builds, onion gateway). Squash-merged as 7606f2c, with main's tree identical to the verified branch.
  • Release: prerelease nightly-2026-10-13.
  • Deploy:
    • Before the build, the running image (636d5e5dab10) was tagged basicautomation-site:previous.
    • The new :local (b82154695c85), built from merged main, reported healthy in about 6 s.
  • Live gate:
    • / 200, <title> "basic automation — software for the productive"; /projects/skidbladnir/about 200; /projects 200; /status 200.
    • /healthz 200: ok, data live, onion launched, readmeImages 28 measured / 0 kept / 0 unsized.
  • Live behaviour: 15 cold loads of https://basicautomation.io/projects/nanna/about#api-key-invalid at 390 px, all with the heading exactly 16 px under the masthead.
  • No rollback was needed.

🤖 Generated with Claude Code

physics515 and others added 13 commits October 13, 2026 21:13
marked 18.1.1 changes rendered output (blockquote lazy lines, html blocks
interrupting paragraphs, backslashes in code spans, tab stops), so it was
held a night. All 8 READMEs and all 98 posts (94 from the live content
volume, 4 fixtures) render byte-identical through renderMarkdown on 18.1.0
and 18.1.1; five of eight probes taken from the release notes do differ
between the two, so the diff could see a change had there been one.

package.json, package-lock.json, ROADMAP.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
vitest is dev-only and nothing it touches reaches the build: typecheck
clean, 276/276 tests, build green, every route's status code identical to
the previous build's. Vue 3.5.44 shipped this morning with SSR, hydration
and scheduler changes, so it goes on the roadmap with a diff plan instead.

package.json, package-lock.json, ROADMAP.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two scrolls raced on a fresh load with a fragment. HTML's scroll to the
fragment uses behavior auto, so the stylesheet's scroll-behavior: smooth
made Chromium's own fragment scroll an animation from the top, while
vue-router jumped to the same heading as the app started; when the
animation's first step (always 4 or 18 px here) landed after the jump it
was added on top. restore-jump.client.ts now holds scroll-behavior: auto
for a fresh load with a hash too, as it did for reloads and Back/Forward.

Nanna at 390 px, #api-key-invalid, uninstrumented: 4/20 cold loads off
at full speed and 19/20 at 6x CPU before; 0/20 and 0/20 after.

check:navigation could not see a +4 miss (text still 12 px clear of the
masthead), so it now also wants the heading's box on scroll-padding-top's
line within 2 px, and takes the cold load at 6x CPU in Chromium via a new
page.throttle() in scripts/lib/browser.mjs (a no-op in Firefox). Main's
build fails it on 9 cold loads across 5 pages; this build passes 160/160
in Chromium and Firefox.

app/plugins/restore-jump.client.ts, scripts/check-navigation.mjs,
scripts/lib/browser.mjs, ROADMAP.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The CDP and BiDi clients sent requests with no timeout, so a page whose
renderer stopped answering held a check until CI's 15-minute step limit
with nothing to say what it waited for (check:navigation on Nanna at
390 px, 2026-10-12). Every request now rejects after REQUEST_TIMEOUT_MS
(180 s) with its method and the expression or URL it carried.

Proved with an evaluate of a promise that never settles: rejected at
180 s in Chromium (Runtime.evaluate) and Firefox (script.evaluate), and
the page answered the next call in milliseconds. check:navigation and
a11y:browser in both engines ran green under it, with no false fires.

scripts/lib/cdp.mjs, scripts/lib/bidi.mjs, ROADMAP.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A missed size probe is not cached, so the README is served with that
image at 0x0 until the next refresh, and cold starts (every deploy) are
where misses land. img.shields.io answered in 3.0 s to 1 request in 9
from this host, past the 2 s budget; a local server tonight served
splimes with four badges unsized.

statedSizes() reads the sizes a rendered README states, and
sizeReadmeImages() now takes the snapshot's copy of the same README's
sizes as a fallback for any image the live probe misses. A live answer
still wins.

Cold-started behind a CONNECT proxy refusing img.shields.io: main's
build served 18 README images unsized across 5 about pages, this one 0.
279 tests, typecheck clean, build green, routes unchanged, check clean.

shared/markdown/image-size.ts, server/utils/readmeImages.ts,
server/utils/github.ts, test/image-size.test.ts, ROADMAP.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
npm run sync wrote a README image unsized into the snapshot whenever its
size probe missed, which takes away the very fallback the server now
uses for its own misses. withImageSizes() keeps the previous snapshot's
size for such an image (statedSizes) and logs how many it kept.

Behind a CONNECT proxy refusing img.shields.io: main's script wrote 22
unsized images into data/projects.generated.json; this one wrote 0,
keeping 22 sizes across six READMEs. (Snapshot restored afterwards; the
refresh for this run is a separate commit.)

scripts/fetch-projects.mjs, ROADMAP.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/healthz and the status page carry readmeImages: per image per render,
measured by a probe (or the day's cached answer), kept at the snapshot's
size because the probe missed, or unsized. The fallback landed earlier
tonight hides misses from the page; this says whether production has any.

With img.shields.io refused from cold: 6 measured, 22 kept, 0 unsized;
without: 28 measured. Typecheck clean, 279 tests, build green, routes
unchanged; status page checked at 390 and 1280 px.

shared/types/health.ts, server/utils/health.ts,
server/utils/readmeImages.ts, app/pages/status.vue, README.md, ROADMAP.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 1.4 KB page that reproduces, outside the site, the race fixed in
restore-jump.client.ts: scroll-behavior: smooth, a #target 16,000 px
down, and one instant scrollTo to the same place shortly after load.
Chromium 152.0.7977.82 at 390 px, 30 cold loads per case: a jump 10 ms
after load lands 4 or 13 px past on 30/30, 20 ms after lands 13 or 24 px
past on 30/30; with scroll-behavior: auto, on target 60/60; with no
jump, on target 30/30. Reporting it upstream is the owner's call.

docs/repro/smooth-fragment-overshoot.html, ROADMAP.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the fix

Headless Firefox draws no backdrop-filter at all, plain blur included,
so the routine cannot settle what a real Firefox shows. But Mozilla bug
1961378 (open) says backdrop-filter is ignored entirely when an SVG url()
filter falls back to a blob image, and its duplicate 1972363 names
feImage and feDisplacementMap, which the masthead's two filters use.
Firefox passes the @supports that hands it that chain. The item now
carries the one-condition fix (not (-moz-appearance: none)) to ship once
someone has looked in a real Firefox, before and after.

ROADMAP.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The stall last seen 2026-10-12 recurred tonight in 1 of 3 Chromium
check:navigation runs: arrive()'s evaluate on Nanna at 1280 px went 180 s
unanswered (the new request timeout caught it; before, CI's 15-minute
step did). Both stalls were on Nanna, on the last page of a width's pass.

A page that throws or stalls is now a failure of that page, not the end
of the run: it reports the last four calls made of it and, in Chromium,
the busiest renderer's CPU time over one second (busy(), from
SystemInfo.getProcessInfo), then the run continues in a fresh tab. On a
stand-in page that spins after load: "stalled after goto → goto →
evaluate; renderer busy (0.99 CPU s in 1 s)", and the next page checked.
The real check: 160/160 in Chromium and in Firefox.

scripts/check-navigation.mjs, scripts/lib/browser.mjs, ROADMAP.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Skidbladnir v1.4.4 (released 2026-10-13) and its README, crate download
counts, push times. Every README image in it carries its size.

data/projects.generated.json

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Caught one more check:navigation stall tonight with the new report: on
Skidbladnir at 1280 px, the first page of the pass in a fresh tab, on
arrive(blog) right after clicking the blog tab, renderer idle (0.00 CPU s
in 1 s). That rules out the longest README and an old tab, and says the
page was waiting, not computing.

The report now also tries a plain evaluate (visibilityState, readyState,
path) with 5 s to answer: on a stand-in that spins, no answer; on an idle
one, "visible, complete, /projects/y/about". The next real stall will say
whether only the stuck promise's timers were held or the page was blocked.

scripts/check-navigation.mjs, ROADMAP.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
15 complete Chromium check:navigation runs on this branch without the
splimes reload drift; the stall recurred once in the seven hunt runs.

ROADMAP.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@physics515
physics515 merged commit 7606f2c into main Oct 14, 2026
3 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