Skip to content

Size every image served from public/, and check link fragments in the audit - #1135

Open
Iamfle4ka wants to merge 11 commits into
mainfrom
claude/serene-dirac-o1hc1u
Open

Iamfle4ka wants to merge 11 commits into
mainfrom
claude/serene-dirac-o1hc1u

Conversation

@Iamfle4ka

@Iamfle4ka Iamfle4ka commented Sep 21, 2026 •

Copy link
Copy Markdown
Collaborator

Jira issue(s): n/a — follow-up to #1132, closing what Michal Jeřábek's front-end audit left open. Tracked as PRDCT-691.

Rewritten 23 Sep — this PR is now half the size it was. It used to also fix 18 dangling anchors. #1112 (PRDCT-679) landed the same fixes independently while this sat in review, so they are gone from the diff and main's versions stand. See What used to be here at the bottom. The bot's needs_human review was written against the older, larger head.

Changes:

  • Image dimensions — 784 → 1,971 of 1,990 <img> sized. 1,193 tags shipped with no width/height, so the browser had no aspect ratio to reserve space with. The split was not arbitrary: Astro sizes what it processes, but never touches public/, and AGENTS.md prescribes referencing screenshots as [Alt](/section/page/image.png) — which lands in public/. A remark plugin reads the intrinsic size from the file header and attaches it via hProperties.
  • audit-phase2.mjs now checks link fragments. It verified that a link's path resolves but never that its #fragment names an anchor that exists — the gap a rename falls through, and the class of bug review caught in Fix the Starlight overrides that match nothing, port three dev-docs pages, add robots/llms #1132.

Image dimensions

Starlight already ships max-width: 100%; height: auto for content images, which is exactly the pairing that makes intrinsic attributes correct — the ratio comes from the attributes, CSS still controls displayed size. No CSS changes, and nothing about how images render changes.

Header parsing is synchronous and hand-rolled for PNG, JPEG, GIF, WebP and SVG rather than calling sharp, so the remark pipeline stays synchronous like every other plugin here and no dependency is added. Results are cached per build. JPEG retries on the full file, since its frame header can sit past the first 64KB.

beacon-transforms' transformFigureCaptions builds its <img> by hand, so it had to be taught to carry the dimensions over — otherwise figures would be the one place still shipping an unsized image. The new plugin runs first for that reason.

The 19 still unsized are 6 external images, which cannot be measured, and 13 hand-written tags in the dbt page's bespoke comparison table that already carry an explicit display width (width="226px"). Changing those is a layout decision, not a mechanical fix, so they are left alone.

A missing file warns rather than throws — audit-phase2.mjs is what fails on a genuinely missing image, and a hard error here would block editing a page whose screenshot has not landed yet. Say the word if you would rather it were fatal; it is a one-line change.

The fragment check

Two details are what make it trustworthy rather than noise:

  • It follows redirect_from's meta-refresh stubs before looking for ids. Without that, every link through a redirect reports as broken — a false-positive class that would make the check worth ignoring.
  • It covers same-page #fragment links, which the scan previously skipped wholesale. When this PR still carried the anchor fixes, 7 of the 18 breakages it found were same-page ones.

It is now also an independent check on #1112: on the merged head it reports 0 broken fragments across the whole site, so that PR's anchor work resolves correctly everywhere.

What used to be here

This PR originally fixed 18 dangling anchors. #1112 fixed the same ones. Eleven of thirteen came out identical on both sides, which is reassuring about both. Three files conflicted on merge and main's side was taken in all three — twice because it is simply better:

This branch had main has Why main's
functions/index.md same-page #function-contexts the absolute path equivalent in a browser, more explicit
incremental/index.md /management/limits/#… /management/project/limits/#… canonical page; this branch went through a redirect stub
table-export.md repointed at /storage/files/#file-links kept the importer page, dropped the dangling fragment this was flagged as an owner's call; merging #1112 answered it

I still think the importer page is the wrong home for a sentence promising download instructions — but the defect (an anchor resolving nowhere) is gone either way, and that is a content question for a separate change, not something to re-open here.


Verified on the merged head: build clean at 371 pages. audit-phase2 0 broken links, 0 broken fragments, 0 missing images; total issues 75 → 70, the drop coming from main.

Not in this PR

  • Meta descriptions — 327 of 364 pages have none. A writing pass, not a diff; tracked in PRDCT-691.
  • lastUpdated — attempted here and reverted (dd612bc), because the preview showed no "Updated ‹date›" line. The mechanism works locally (366/366 pages, 33 distinct dates); what is unknown is whether Vercel's build runs prebuild at all. A build log searched for [unshallow] settles it, and the decision tree is recorded in astro.config.mjs.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PEKnDNvPqkNrBonYN6S61r

1,193 of the site's 1,977 <img> tags shipped with no width or height, so a
browser had no aspect ratio to reserve space with until the bytes arrived.
The split was not arbitrary: Astro sizes the images it processes itself,
but it never touches public/, and AGENTS.md prescribes referencing
screenshots as `![Alt](/section/page/image.png)` — which lands in public/.

A remark plugin reads the intrinsic size from the file header and attaches
it via hProperties. Starlight already ships `max-width: 100%; height: auto`
for content images, which is exactly the pairing that makes intrinsic
attributes correct — the ratio comes from the attributes, CSS still
controls the displayed size — so nothing about how images render changes.

Header parsing is synchronous and hand-rolled for PNG, JPEG, GIF, WebP and
SVG rather than calling sharp, so the remark pipeline stays synchronous
like every other plugin here and no dependency is added. Results are cached
per build, since one screenshot is often referenced from several pages.
JPEG retries on the full file, because its frame header can sit past the
first 64KB.

beacon-transforms' transformFigureCaptions builds its <img> by hand, so it
had to be taught to carry the dimensions over — otherwise figures would be
the one place still shipping an unsized image. The plugin runs first for
that reason.

Result: 784 -> 1,958 of 1,977 sized. The 19 left are 6 external images,
which cannot be measured, and 13 hand-written tags inside the dbt page's
bespoke comparison table that already carry an explicit display width —
changing those is a layout decision, not a mechanical fix.

A missing file warns rather than throws: audit-phase2.mjs is what fails on
a genuinely missing image, and a hard error here would block editing a page
whose screenshot has not landed yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEKnDNvPqkNrBonYN6S61r
audit-phase2.mjs checked that a link's path resolves but never that its
#fragment names an anchor that exists. That is the gap a rename falls
through: the path stays valid, the anchor quietly stops existing, and the
link lands at the top of a long reference page instead of its section. It
is also the class of bug review caught in PR #1132, where a link rewrite
changed path prefixes and carried fragments over verbatim.

The check follows redirect_from's meta-refresh stubs before looking for
ids. Without that, every link through a redirect would report as broken —
a false-positive class that would make the check worth ignoring.

It also covers same-page `#fragment` links, which the scan previously
skipped wholesale. That turned out to matter: 7 of the 18 breakages were
same-page, including three on /storage/api/configurations/ pointing at
#modifying-a-configuration when its own heading is #modifying-configuration.

Fixed, each verified against the target page's real headings:
  #dataType                              -> #data-type
  #merging-response                      -> #merging-responses
  #an-object-with-nested-object          -> #object-with-nested-object
  #an-object-with-a-deeply-nested-object -> #object-with-a-deeply-nested-object
  #configuration-sections                -> #json-configuration-sections
  api/#baseurl                           -> api/#base-url
  oauth20/#configuration                 -> oauth20/#configuration-parameters
  publish/#submission                    -> publish/#publishing
  #api-default-parameter                 -> #api-default-parameters
  #modifying-a-configuration             -> #modifying-configuration
  /storage/#backends                     -> #storage-backend-types-and-features
  /management/limits/#project-power      -> #project-power--time-credits
  (/#function-contexts)                  -> (#function-contexts), a stray
                                            leading slash on a same-page link

One is a judgement call and wants a reviewer's eye: /workspace/table-export
said "download the file with the standard file download flow" and pointed
at the Storage API *importer*, where no such anchor exists and the page is
about uploading. Repointed at /storage/files/#file-links, the only section
that describes downloading a file. If that is the wrong home, say so.

audit-phase2 now reports 0 broken links, 0 broken fragments, 0 missing
images.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEKnDNvPqkNrBonYN6S61r
…back on

PR #1132 switched lastUpdated on, the preview showed the "Updated <date>"
line on no page at all, and it came back out. The cause was established
then: Starlight takes each page's date from its last commit, Vercel clones
shallow, getLastUpdated catches the lookup error and returns undefined.

Vercel documents no clone-depth setting, so the build has to deepen the
clone itself. That runs from the `prebuild` npm script rather than a
`buildCommand` in vercel.json, deliberately: `npm run build` is what every
environment already runs, so one mechanism covers Vercel, GitHub Actions
and a local build, and nothing overrides the build command configured in
the Vercel project — which is not visible from this repository.

The script never fails a build. A missing .git, no network, or a credential
the build step cannot use all fall through to a warning, and the site
builds exactly as it does today with the dates absent. That matters because
whether Vercel's build step can fetch is the one thing that cannot be
settled from here — it has to be read off the preview, same as last time.

The workflows are untouched: `fetch-depth: 0` is not reintroduced, since
prebuild now deepens the clone in Actions too.

Verified: all three paths exercised (shallow clone deepened, full clone
no-op, no repository warns and exits 0), then a full build — 366 of 366
content pages carry an Updated line across 33 distinct dates, so the dates
are per-file history rather than one build-time stamp.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEKnDNvPqkNrBonYN6S61r
@vercel

vercel Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
connection-docs Ready Ready Preview Oct 2, 2026 7:10am UTC

Request Review

…turn it back on"

This reverts 8166dce. The preview still shows no "Updated <date>" line, so
the criterion this PR set applies and lastUpdated comes back out rather
than holding up the two changes that do work.

Reverted whole rather than left unwired: a script nothing calls is exactly
the kind of dead weight the previous PR existed to remove. The code is
recoverable from 8166dce when there is something to act on.

What is NOT yet known is *why* it still fails, and there are two candidates
that look identical from outside:

  a) Vercel runs `npm run build`, prebuild fires, and the `git fetch
     --unshallow` is refused because the build step has no usable
     credential. Fix: a different way to get history, or accept no dates.
  b) Vercel's configured build command is not `npm run build`, so prebuild
     never runs at all and the script is inert. Fix: set `buildCommand` in
     vercel.json — which this PR deliberately avoided, because the project's
     current command is not visible from this repository.

One look at a Vercel build log settles it: search for `[unshallow]`.
Present and "done" means the clone was deepened and the cause is elsewhere;
present and "could not deepen" is (a); absent entirely is (b).

Verified locally before reverting: the mechanism itself is sound — a
shallow clone is deepened, a full clone no-ops, a missing repository warns
and exits 0, and a full-history build puts an Updated line on 366 of 366
content pages across 33 distinct dates. So this is about the build
environment, not the script.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEKnDNvPqkNrBonYN6S61r
The comment left behind by the revert proposed exactly the approach that
had just failed. Replaced with what is actually known: the mechanism works
locally, the preview still showed no dates, and the next person should
first search a Vercel build log for `[unshallow]` — present-and-done,
present-and-refused, and absent each point at a different fix, and only
the last one makes vercel.json's buildCommand the right lever.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEKnDNvPqkNrBonYN6S61r
@Iamfle4ka Iamfle4ka changed the title Size every image from public/, repair 18 dangling anchors, make lastUpdated actually work Size every image from public/, repair 18 dangling anchors, add a fragment check Sep 21, 2026
Keeps the PR mergeable — 7 commits behind after the Kai pricing work
(PR #1136). No overlap with this PR's files.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEKnDNvPqkNrBonYN6S61r
@Iamfle4ka Iamfle4ka added the site-tooling Touches build config, CI, scripts or runtime code — the reviewer bot always routes these to a human label Sep 23, 2026
@Iamfle4ka

Copy link
Copy Markdown
Collaborator Author

@keboola-pr-reviewer review

1 similar comment
@Iamfle4ka

Copy link
Copy Markdown
Collaborator Author

@keboola-pr-reviewer review

Main landed PR #1112 (PRDCT-679), which fixed the same dangling anchors
this branch did, independently. Three files conflicted; main's side is
taken in all three, and its version is the better one twice over:

- functions/index.md — main writes the absolute path where this branch
  used a same-page fragment. Equivalent in the browser, more explicit.
- incremental/index.md — main points at /management/project/limits/, the
  canonical page. This branch pointed at /management/limits/, which only
  works because a redirect stub catches it. Main's skips the hop.
- table-export.md — this branch repointed "the standard file download
  flow" at /storage/files/#file-links, arguing the Storage API importer
  page is about uploading. Main instead kept the importer page and dropped
  the dangling fragment. That was flagged as an owner's call in the PR
  description and merging #1112 answered it, so main's stands. The defect
  — an anchor that resolves nowhere — is gone either way.

Eleven of the thirteen anchor fixes turned out identical on both sides,
which is reassuring about both. What remains here is only the work main
does not have: the image-dimensions remark plugin and the fragment check
in audit-phase2.mjs — which now validates main's anchor work too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEKnDNvPqkNrBonYN6S61r
keboola-pr-reviewer-bot

This comment was marked as outdated.

@Iamfle4ka Iamfle4ka changed the title Size every image from public/, repair 18 dangling anchors, add a fragment check Size every image served from public/, and check link fragments in the audit Sep 23, 2026

@keboola-pr-reviewer-bot keboola-pr-reviewer-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: needs_human (risk 4/5) · profile connection-docs

Build tooling and integration code changes require human review, not a content-bucket auto-approve.

Suggested reviewers: @keboola/docs

This branch was successfully deployed

1 active deployment
Preview — 71a23729 Deployed Oct 2, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-needs-human agent-profile:connection-docs site-tooling Touches build config, CI, scripts or runtime code — the reviewer bot always routes these to a human

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants