script/lint exits 0 on render-target collisions, so the signal is printed but not enforced #25

Closed
opened 2026-08-09 12:07:07 +02:00 by clawbot · 5 comments
Collaborator

Problem

#9 fixed script/check so that script/lint actually runs. But the signal it
recovers is printed and then ignored, because Hugo exits 0 on a duplicate
render-target warning.

Demonstrated during the review of PR #24, on a scratch copy of the site with
two pages rendering to the same permalink:

  • script/test (hugo --minify) — prints nothing, exit 0.
  • script/lint (hugo --minify --printPathWarnings) — prints
    WARN Duplicate target paths: /collide/index.html (2), and exits 0.
  • script/check with the collision present — exit 0.

So a real render-target collision does not fail make check, does not fail the
pre-commit hook, and does not fail CI. It scrolls past in the build log.

This is worth stating plainly: #9's rationale was mildly overstated, and I
wrote it.
I described script/lint as "a real check" whose signal was "being
discarded". The accurate version is that the signal moves from discarded to
printed-and-ignored. That is a genuine improvement — you cannot act on output
that never runs — but it is not yet a gate, and #9's issue text implied it was.
Correcting the record here rather than leaving the overstatement standing.

Why this matters for a Hugo site

A duplicate render target means two pieces of content silently overwrite each
other in public/. One of them does not get published, and nothing fails. On a
site whose entire purpose is publishing a small number of pages, losing one
silently is exactly the failure you want a gate to catch. It has not bitten
this repo yet only because the site is currently a single page.

There is also a build-noise interaction: the site already emits an unrelated
taxonomy WARN (#13). Two classes of ignorable warning train everyone to
ignore the third.

Options

Option A — --panicOnWarning on script/lint (recommended, but see the
caveat).
Hugo's documented mechanism for making warnings fatal. One flag,
correct semantics, no bespoke parsing. Caveat: it makes ALL warnings fatal,
not just collisions.
That means #13 (the taxonomy layout warning) and #18
(the languageCode deprecation) must be fixed first, or the gate goes red
immediately. It also means a future Hugo release that adds a new warning
category can break the build — arguably correct behaviour, but it should be a
conscious choice.

Option B — grep the lint output for the specific warning and fail on it.
Narrower: fails only on duplicate target paths, leaves other warnings advisory.
Costs a bespoke shell pipeline in script/lint that has to keep matching
Hugo's message text — brittle across Hugo upgrades in a way Option A is not.

Option C — leave it advisory and rely on humans reading build output. Not
recommended; that is the status quo and the reason this issue exists.

My recommendation: Option A, sequenced after #13 and #18. It is the
supported mechanism and it makes the whole warning class meaningful rather than
carving out one message. The dependency on #13 and #18 is a feature — it forces
the existing warning backlog to zero before warnings become enforcing, which is
the only order in which "warnings are fatal" is a livable policy.

Definition of done

  1. A render-target collision causes make check to exit non-zero.
  2. Demonstrated the way it was found: construct a collision on a scratch copy
    (do not commit colliding content), show make check failing, remove it,
    show make check passing. Paste both.
  3. If Option A: #13 and #18 are closed first, and make check is confirmed
    green on the real site with the flag active before the flag is merged.
  4. script/lint's header comment describes the new behaviour accurately —
    it currently claims it "is a real check", which this issue shows is only
    half true today.
  5. script/cibuild succeeds, with the check layer confirmed genuinely executed
    and not CACHED (see #23).
  6. TODO.md updated in the same commit.

Depends on

  • #13 (taxonomy warning) and #18 (languageCode deprecation) if Option A is
    chosen. Both must be closed first or the gate fails on day one.
  • Interacts with #23: verifying this needs a genuinely-executed check layer.

Out of scope

  • Fixing #13 or #18 themselves.
  • Making script/test strict. script/test is the plain production build and
    should stay the fast path; enforcement belongs in script/lint.
## Problem #9 fixed `script/check` so that `script/lint` actually runs. But the signal it recovers is **printed and then ignored**, because Hugo exits 0 on a duplicate render-target warning. Demonstrated during the review of PR #24, on a scratch copy of the site with two pages rendering to the same permalink: - `script/test` (`hugo --minify`) — prints nothing, exit 0. - `script/lint` (`hugo --minify --printPathWarnings`) — prints `WARN Duplicate target paths: /collide/index.html (2)`, and **exits 0**. - `script/check` with the collision present — **exit 0**. So a real render-target collision does not fail `make check`, does not fail the pre-commit hook, and does not fail CI. It scrolls past in the build log. This is worth stating plainly: **#9's rationale was mildly overstated, and I wrote it.** I described `script/lint` as "a real check" whose signal was "being discarded". The accurate version is that the signal moves from *discarded* to *printed-and-ignored*. That is a genuine improvement — you cannot act on output that never runs — but it is not yet a gate, and #9's issue text implied it was. Correcting the record here rather than leaving the overstatement standing. ## Why this matters for a Hugo site A duplicate render target means two pieces of content silently overwrite each other in `public/`. One of them does not get published, and nothing fails. On a site whose entire purpose is publishing a small number of pages, losing one silently is exactly the failure you want a gate to catch. It has not bitten this repo yet only because the site is currently a single page. There is also a build-noise interaction: the site already emits an unrelated taxonomy `WARN` (#13). Two classes of ignorable warning train everyone to ignore the third. ## Options **Option A — `--panicOnWarning` on `script/lint` (recommended, but see the caveat).** Hugo's documented mechanism for making warnings fatal. One flag, correct semantics, no bespoke parsing. **Caveat: it makes ALL warnings fatal, not just collisions.** That means #13 (the taxonomy layout warning) and #18 (the `languageCode` deprecation) must be fixed first, or the gate goes red immediately. It also means a future Hugo release that adds a new warning category can break the build — arguably correct behaviour, but it should be a conscious choice. **Option B — grep the lint output for the specific warning and fail on it.** Narrower: fails only on duplicate target paths, leaves other warnings advisory. Costs a bespoke shell pipeline in `script/lint` that has to keep matching Hugo's message text — brittle across Hugo upgrades in a way Option A is not. **Option C — leave it advisory and rely on humans reading build output.** Not recommended; that is the status quo and the reason this issue exists. **My recommendation: Option A, sequenced after #13 and #18.** It is the supported mechanism and it makes the whole warning class meaningful rather than carving out one message. The dependency on #13 and #18 is a feature — it forces the existing warning backlog to zero before warnings become enforcing, which is the only order in which "warnings are fatal" is a livable policy. ## Definition of done 1. A render-target collision causes `make check` to exit non-zero. 2. Demonstrated the way it was found: construct a collision on a scratch copy (do **not** commit colliding content), show `make check` failing, remove it, show `make check` passing. Paste both. 3. If Option A: `#13` and `#18` are closed first, and `make check` is confirmed green on the real site with the flag active before the flag is merged. 4. `script/lint`'s header comment describes the new behaviour accurately — it currently claims it "is a real check", which this issue shows is only half true today. 5. `script/cibuild` succeeds, with the check layer confirmed genuinely executed and not `CACHED` (see #23). 6. `TODO.md` updated in the same commit. ## Depends on - #13 (taxonomy warning) and #18 (`languageCode` deprecation) if Option A is chosen. Both must be closed first or the gate fails on day one. - Interacts with #23: verifying this needs a genuinely-executed check layer. ## Out of scope - Fixing #13 or #18 themselves. - Making `script/test` strict. `script/test` is the plain production build and should stay the fast path; enforcement belongs in `script/lint`.
sneak was assigned by clawbot 2026-08-09 17:34:31 +02:00
Author
Collaborator

Both prerequisites are now clear, so this is unblocked and needs your call.

  • #18 (languageCode deprecation) — closed, merged in PR #27.
  • #13 (taxonomy warning) — fixed in PR #29, under review. Build output is
    warning-free.

So the noise floor is genuinely zero and Option A would no longer fail the
build on day one. My recommendation is unchanged: Option A,
--panicOnWarning on script/lint.

Assigning to you because the caveat in the issue body is the whole decision and
it is not mine to make: --panicOnWarning makes every Hugo warning fatal,
not just render-target collisions.
The upside is that a silent
page-overwriting collision can never ship. The cost is that a future Hugo
release which introduces a new warning category will break the build until
someone fixes it — including on main, and including the deploy path. That is
arguably correct behaviour, but it is a standing commitment to keeping the
warning backlog at zero forever, and this repo has just spent real effort
getting it there.

Option B (grep for the specific warning) avoids that commitment at the cost of
a brittle text match. Option C is the status quo.

Not blocking on this — moving to #23 next. Answer whenever; the work is a small
change to script/lint once you pick.

Both prerequisites are now clear, so this is unblocked and needs your call. - #18 (`languageCode` deprecation) — closed, merged in PR #27. - #13 (taxonomy warning) — fixed in PR #29, under review. Build output is warning-free. So the noise floor is genuinely zero and Option A would no longer fail the build on day one. My recommendation is unchanged: **Option A**, `--panicOnWarning` on `script/lint`. Assigning to you because the caveat in the issue body is the whole decision and it is not mine to make: **`--panicOnWarning` makes every Hugo warning fatal, not just render-target collisions.** The upside is that a silent page-overwriting collision can never ship. The cost is that a future Hugo release which introduces a new warning category will break the build until someone fixes it — including on `main`, and including the deploy path. That is arguably correct behaviour, but it is a standing commitment to keeping the warning backlog at zero forever, and this repo has just spent real effort getting it there. Option B (grep for the specific warning) avoids that commitment at the cost of a brittle text match. Option C is the status quo. Not blocking on this — moving to #23 next. Answer whenever; the work is a small change to `script/lint` once you pick.
Owner

option a approved. dumb question, shouldn’t have been asked.

option a approved. dumb question, shouldn’t have been asked.
Author
Collaborator

Recorded: option (a), script/lint fails on a render-target collision. One small reviewed change on next when a slot frees.

Model: opus-5-5

Recorded: option (a), `script/lint` fails on a render-target collision. One small reviewed change on `next` when a slot frees. Model: opus-5-5
sneak was unassigned by clawbot 2026-10-21 12:21:54 +02:00
clawbot self-assigned this 2026-10-21 12:21:54 +02:00
Author
Collaborator

Plan, building option (a) as approved: Hugo's own --panicOnWarning on the lint build. One branch off next, one PR to next.

  1. Dockerfile.lint: the lint step becomes hugo --minify --printPathWarnings --panicOnWarning. --printPathWarnings stays, since it is what makes Hugo report two pages writing the same output file; --panicOnWarning turns that report, and any other Hugo warning, into a failed build. The header comment says so.
  2. script/lint header comment: the lint fails on any Hugo warning, including two pages that write the same output path.
  3. README.md: the script/lint bullet under Entrypoints says the same.
  4. TODO.md: a short Completed Steps entry, and this item removed from Blocked.
  5. script/test and the deploy workflow are unchanged: only the lint is strict.

Before opening the PR, on a scratch copy that is never committed: two pages that render to the same path (for example two files under content/ with the same url in their front matter) make script/lint exit non-zero, and with them removed script/lint and script/check pass on the real site. The PR body gives that recipe in two lines so the reviewer can repeat it. If the pinned Hugo does not stop on that warning under --panicOnWarning, the fallback is to fail the lint step when its output carries Hugo's duplicate target paths warning, stated on the PR.

Model: opus-5-5

Plan, building option (a) as approved: Hugo's own `--panicOnWarning` on the lint build. One branch off `next`, one PR to `next`. 1. `Dockerfile.lint`: the lint step becomes `hugo --minify --printPathWarnings --panicOnWarning`. `--printPathWarnings` stays, since it is what makes Hugo report two pages writing the same output file; `--panicOnWarning` turns that report, and any other Hugo warning, into a failed build. The header comment says so. 2. `script/lint` header comment: the lint fails on any Hugo warning, including two pages that write the same output path. 3. `README.md`: the `script/lint` bullet under Entrypoints says the same. 4. `TODO.md`: a short Completed Steps entry, and this item removed from Blocked. 5. `script/test` and the deploy workflow are unchanged: only the lint is strict. Before opening the PR, on a scratch copy that is never committed: two pages that render to the same path (for example two files under `content/` with the same `url` in their front matter) make `script/lint` exit non-zero, and with them removed `script/lint` and `script/check` pass on the real site. The PR body gives that recipe in two lines so the reviewer can repeat it. If the pinned Hugo does not stop on that warning under `--panicOnWarning`, the fallback is to fail the lint step when its output carries Hugo's duplicate target paths warning, stated on the PR. Model: opus-5-5
Author
Collaborator

Built as planned in #46: the lint step runs with --panicOnWarning, and Hugo stops on its own duplicate target paths warning, so no fallback was needed. The PR has the steps to repeat the check. They include a one-line scratch page layout, because the theme has no layout for ordinary pages.

Model: opus-5-5

Built as planned in https://git.eeqj.de/sneak/lora.vegas/pulls/46: the lint step runs with `--panicOnWarning`, and Hugo stops on its own duplicate target paths warning, so no fallback was needed. The PR has the steps to repeat the check. They include a one-line scratch page layout, because the theme has no layout for ordinary pages. Model: opus-5-5
Sign in to join this conversation.