Widen the prettier gate to markdown and CSS everywhere (closes #12) #32

Merged
clawbot merged 2 commits from issue-12-widen-prettier-scope into main 2026-08-09 18:30:36 +02:00
Collaborator

Closes #12.

REPO_POLICIES.md scopes prettier to JS/CSS/Markdown/HTML; script/fmt and script/fmt-check covered only '*.md' — top-level markdown. Both now run over '**/*.md' and '**/*.css', and both header comments, which still described the old narrow scope, were rewritten.

The content/ comparison — output DIFFERED, so content/ is excluded

This was measured, not assumed. public/ was extracted from the built image (pinned hugo v0.164.0) before and after running make fmt with the widened glob including content/.

With content/ in scope, prettier re-wrapped exactly one line of content/_index.md:

-- **#1** — Feb 4, 2026, 7 PM at [Lee's Sandwiches, 3989 Spring Mountain Rd](https://maps.app.goo.gl/4Zc5zRVr8srTVStH9)
+- **#1** — Feb 4, 2026, 7 PM at
+  [Lee's Sandwiches, 3989 Spring Mountain Rd](https://maps.app.goo.gl/4Zc5zRVr8srTVStH9)

and the rendered public/index.html changed with it — the wrap came through as a literal newline between 7 PM at and the following <a> tag inside the <li>. HTML collapses that newline to a space, so the page looks the same, but the published bytes are not the same. Per the issue's rule ("if it differs at all, exclude"), content/ is in .prettierignore with a comment recording this measurement. The raw <div class="settings"> / <br> / <span> blocks were not touched in this instance, but they pass through verbatim under markup.goldmark.renderer.unsafe = true, and a formatter that can silently alter a published page is not worth the consistency.

archetypes/ stays covered: it is a template for new content, not published output, and prettier leaves archetypes/default.md unchanged.

Exclusions, both with recorded reasons

  • themes/loravega/layouts/ — not HTML. Go templates carrying {{ define }}, {{ block }}, {{ .Content }}, {{ readFile ... | safeCSS }}; prettier has no Go-template parser and would fail or mangle the delimiters. Covering them needs an out-of-tree plugin and therefore a package.json, which is out of scope. No plugin was added.
  • content/ — see above.

CSS

themes/loravega/static/css/style.css is now formatted and gated. It is inlined into every page by baseof.html via readFile, so the claim that CSS formatting cannot reach the rendered page was checked rather than asserted: the minified <style> block in the built public/index.html is byte-identical before and after.

Commit ordering — no merge commit required

Two commits, each of which passes make check standing alone:

  1. f3176a1 — the style.css reformat only. The scripts are still at the old '*.md' glob at this point, which does not look at CSS, so make fmt-check passes before and after it.
  2. 3e0694e — the glob widening, the two .prettierignore entries, and TODO.md. It arrives on an already-clean tree, so the widened check passes immediately.

That is the inverse of the trap #31 hit: reformat first under the old scope, then widen. This branch can land by fast-forward or rebase; it does not need a merge commit. Formatting churn is not mixed with the functional change, per REPO_POLICIES.md.

Verification

  • Final rendered-output check against the pre-change baseline: index.html, index.xml and sitemap.xml are byte-identical. The only file that differs across the whole public/ tree is the verbatim-copied public/css/style.css, and with all whitespace stripped the two are identical byte-for-byte (cmp exit 0).
  • make check green on a clean tree at both commits (verified at commit 1 by stashing the rest of the work).
  • script/cibuild succeeded, and the check layer genuinely executed rather than being cache-served: RUN echo "check epoch: ..." && make check shows as [7/7] with its output present, while the script/bootstrap layers above it show CACHED.
  • make fmt file list confirms the ignore file is doing its job: it reports archetypes/default.md, README.md, REPO_POLICIES.md, TODO.md, style.css and does not report content/_index.md.
  • script/bootstrap and .gitea/workflows/deploy.yml are untouched; the deploy path is unaffected.

Not verified: nothing was run against the live Cloudflare Pages deploy — the argument that the deploy is unaffected rests on those two files being unchanged and on the rendered public/ tree being byte-identical apart from whitespace in the static CSS.

Closes https://git.eeqj.de/sneak/lora.vegas/issues/12. `REPO_POLICIES.md` scopes prettier to JS/CSS/Markdown/HTML; `script/fmt` and `script/fmt-check` covered only `'*.md'` — top-level markdown. Both now run over `'**/*.md'` and `'**/*.css'`, and both header comments, which still described the old narrow scope, were rewritten. ## The `content/` comparison — output DIFFERED, so `content/` is excluded This was measured, not assumed. `public/` was extracted from the built image (pinned hugo v0.164.0) before and after running `make fmt` with the widened glob including `content/`. With `content/` in scope, prettier re-wrapped exactly one line of `content/_index.md`: ``` -- **#1** — Feb 4, 2026, 7 PM at [Lee's Sandwiches, 3989 Spring Mountain Rd](https://maps.app.goo.gl/4Zc5zRVr8srTVStH9) +- **#1** — Feb 4, 2026, 7 PM at + [Lee's Sandwiches, 3989 Spring Mountain Rd](https://maps.app.goo.gl/4Zc5zRVr8srTVStH9) ``` and the rendered `public/index.html` changed with it — the wrap came through as a literal newline between `7 PM at` and the following `<a>` tag inside the `<li>`. HTML collapses that newline to a space, so the page **looks** the same, but the published bytes are not the same. Per the issue's rule ("if it differs at all, exclude"), `content/` is in `.prettierignore` with a comment recording this measurement. The raw `<div class="settings">` / `<br>` / `<span>` blocks were not touched in this instance, but they pass through verbatim under `markup.goldmark.renderer.unsafe = true`, and a formatter that can silently alter a published page is not worth the consistency. `archetypes/` stays **covered**: it is a template for new content, not published output, and prettier leaves `archetypes/default.md` unchanged. ## Exclusions, both with recorded reasons - `themes/loravega/layouts/` — not HTML. Go templates carrying `{{ define }}`, `{{ block }}`, `{{ .Content }}`, `{{ readFile ... | safeCSS }}`; prettier has no Go-template parser and would fail or mangle the delimiters. Covering them needs an out-of-tree plugin and therefore a `package.json`, which is out of scope. No plugin was added. - `content/` — see above. ## CSS `themes/loravega/static/css/style.css` is now formatted and gated. It is inlined into every page by `baseof.html` via `readFile`, so the claim that CSS formatting cannot reach the rendered page was checked rather than asserted: the minified `<style>` block in the built `public/index.html` is **byte-identical** before and after. ## Commit ordering — no merge commit required Two commits, each of which passes `make check` standing alone: 1. `f3176a1` — the `style.css` reformat only. The scripts are still at the old `'*.md'` glob at this point, which does not look at CSS, so `make fmt-check` passes before and after it. 2. `3e0694e` — the glob widening, the two `.prettierignore` entries, and `TODO.md`. It arrives on an already-clean tree, so the widened check passes immediately. That is the inverse of the trap https://git.eeqj.de/sneak/lora.vegas/pulls/31 hit: reformat first under the old scope, then widen. This branch can land by fast-forward or rebase; it does not need a merge commit. Formatting churn is not mixed with the functional change, per `REPO_POLICIES.md`. ## Verification - Final rendered-output check against the pre-change baseline: `index.html`, `index.xml` and `sitemap.xml` are byte-identical. The only file that differs across the whole `public/` tree is the verbatim-copied `public/css/style.css`, and with all whitespace stripped the two are identical byte-for-byte (`cmp` exit 0). - `make check` green on a clean tree at both commits (verified at commit 1 by stashing the rest of the work). - `script/cibuild` succeeded, and the check layer genuinely executed rather than being cache-served: `RUN echo "check epoch: ..." && make check` shows as `[7/7]` with its output present, while the `script/bootstrap` layers above it show `CACHED`. - `make fmt` file list confirms the ignore file is doing its job: it reports `archetypes/default.md`, `README.md`, `REPO_POLICIES.md`, `TODO.md`, `style.css` and does **not** report `content/_index.md`. - `script/bootstrap` and `.gitea/workflows/deploy.yml` are untouched; the deploy path is unaffected. Not verified: nothing was run against the live Cloudflare Pages deploy — the argument that the deploy is unaffected rests on those two files being unchanged and on the rendered `public/` tree being byte-identical apart from whitespace in the static CSS.
clawbot added 2 commits 2026-08-09 18:23:52 +02:00
Pure formatting churn, no functional change. Kept as its own commit
ahead of the script change so the widened gate lands on an already-clean
tree, per REPO_POLICIES.md: formatting diffs are large and must not be
mixed with functional changes.

The file is inlined verbatim into every page by baseof.html via
readFile, so this was checked rather than assumed: the minified <style>
block in the built public/index.html is byte-identical before and after.
Prettier CSS formatting is whitespace-only and cannot reach the rendered
page.
Widen the prettier gate to markdown and CSS everywhere (closes #12)
All checks were successful
check / check (push) Successful in 59s
3e0694e6e0
REPO_POLICIES.md scopes prettier to JS/CSS/Markdown/HTML, but script/fmt
and script/fmt-check covered only '*.md' - top-level markdown. The
canonical scripts use '**/*.md'. Both now run over '**/*.md' and
'**/*.css', and both header comments, which still described the old
top-level-only scope, were rewritten.

That brings themes/loravega/static/css/style.css into the gate. It is
inlined into every page by baseof.html via readFile, and its formatting
is whitespace-only: the minified <style> block in the built
public/index.html is byte-identical across the reformat, which is the
preceding commit.

Two paths are excluded, each with the reason recorded in
.prettierignore so the exclusion reads as a decision rather than an
oversight:

themes/loravega/layouts/ - these are not HTML. They are Go templates
carrying {{ define }}, {{ block }}, {{ .Content }} and
{{ readFile ... | safeCSS }}, and prettier has no Go-template parser; it
would fail or reflow the delimiters into markup Hugo cannot parse.
Covering them needs an out-of-tree plugin and therefore a package.json,
which this repo deliberately does not have.

content/ - excluded on measurement, not on the earlier assumption. With
content/ in scope, prettier re-wrapped one list item in
content/_index.md, and the rendered public/index.html changed with it:
the wrap landed as a literal newline between "7 PM at" and the following
<a> tag. HTML collapses that newline to a space, so the page looks the
same, but the published bytes do not match, and this content carries raw
div/span/br blocks that goldmark passes through verbatim because
hugo.toml sets markup.goldmark.renderer.unsafe = true. A formatter that
can silently change a published page is not worth the consistency.
archetypes/ stays covered - it is a template for new content, not
published output, and prettier leaves it unchanged.

Verified by extracting public/ from the built image before and after.
With the final scope, index.html, index.xml and sitemap.xml are
byte-identical; only the verbatim-copied public/css/style.css differs,
in whitespace. Each commit on this branch passes make check on its own -
the reformat lands first, under the old narrow glob that does not look
at CSS, so the widening commit arrives on an already-clean tree and no
merge commit is required to land it.
clawbot self-assigned this 2026-08-09 18:23:57 +02:00
clawbot added the needs-review label 2026-08-09 18:23:57 +02:00
Author
Collaborator

Review: PASS

Independent review of #32 against #12. No blocking findings.

The content/ measurement reproduces exactly. Un-excluded content/, ran make fmt, rebuilt, diffed: prettier re-wraps exactly the one Lee's Sandwiches list item, and public/index.html gains a literal newline between 7 PM at and the &lt;a href=...&gt; inside the &lt;li&gt;. index.xml, sitemap.xml and css/style.css unchanged. The author's characterisation is confirmed on both counts: the diff is ordinary prose reflow, and the raw &lt;div class="settings"&gt; blocks were untouched. DoD 3 satisfied; the exclusion is justified by the issue's own "differs at all" rule.

Rendered output otherwise unchanged, confirmed. Built base 7dea837 and head 3e0694e: index.html, index.xml, sitemap.xml byte-identical; only public/css/style.css differs, identical with whitespace stripped (cmp exit 0). index.html being byte-identical is the inline-&lt;style&gt; proof, verified rather than taken on trust.

Commit ordering claim holds. f3176a1 checked out alone passes make check; 3e0694e passes. Branch fast-forwards onto current main (7dea837), so no merge commit is forced. CI green on the head commit.

script/cibuild genuinely executed. #11 [7/7] RUN echo "check epoch: ..." && make check ran with full output — not CACHED — while the script/bootstrap layers above it cached. #23's guard is working.

Glob scope probed with planted files, not just read: nested .md and .css are caught, top-level README.md/TODO.md/REPO_POLICIES.md remain in scope under '**/*.md' (no coverage lost), and planted files under content/ and themes/loravega/layouts/ are correctly ignored. archetypes/default.md reports unchanged.

Also checked and clean: both script headers rewritten to the new scope (DoD 7); no plugin, no package.json; TODO.md updated (DoD 8); make fmt-check and make check clean on head; landing commit title ends (closes #12); five changed files, no debris, no scope creep — LICENSE/README, script/bootstrap and deploy.yml untouched; no attribution trailers or vendor references anywhere in the diff, commits, or tree.

Non-blocking notes

  • Dot-directory traversal is real; .claude/ is protected only by .gitignore. The widened glob does descend into dot-prefixed directories — a planted .probe-nested/deep/bad.md was flagged. .claude/probe/bad.md was not, because prettier 3 reads .gitignore in addition to .prettierignore by default, and .gitignore lists .claude/. That is correct today and also why public/ is skipped inside the image. It is a latent coupling worth knowing: if .claude/ ever leaves .gitignore, make fmt would start rewriting markdown in sibling agent worktrees. No change requested.
  • .prettierignore line 13 says content/ "is formatted by hand" — it is simply not formatted. Wording only.
  • The Makefile has no cibuild (or precommit) target, so script/cibuild had to be invoked directly. Pre-existing on main, not introduced here.

Disclosures

  • The A/B builds used the host's hugo v0.164.0 (nixpkgs +extended variant) rather than the image's CGO_ENABLED=0 build. Same version, and both halves of each comparison used the same binary, so the deltas are sound; the final state was independently re-confirmed inside the pinned image via script/cibuild.
  • Probe files were planted and deleted; the working tree was left clean and nothing was committed or pushed.
  • The Cloudflare Pages deploy was not exercised, matching the PR body's own disclosure. Nothing else in the body is overstated — every claim checked reproduced.
## Review: PASS Independent review of https://git.eeqj.de/sneak/lora.vegas/pulls/32 against https://git.eeqj.de/sneak/lora.vegas/issues/12. No blocking findings. **The `content/` measurement reproduces exactly.** Un-excluded `content/`, ran `make fmt`, rebuilt, diffed: prettier re-wraps exactly the one Lee's Sandwiches list item, and `public/index.html` gains a literal newline between `7 PM at` and the `&lt;a href=...&gt;` inside the `&lt;li&gt;`. `index.xml`, `sitemap.xml` and `css/style.css` unchanged. The author's characterisation is confirmed on both counts: the diff is ordinary prose reflow, and the raw `&lt;div class="settings"&gt;` blocks were untouched. DoD 3 satisfied; the exclusion is justified by the issue's own "differs at all" rule. **Rendered output otherwise unchanged, confirmed.** Built base `7dea837` and head `3e0694e`: `index.html`, `index.xml`, `sitemap.xml` byte-identical; only `public/css/style.css` differs, identical with whitespace stripped (`cmp` exit 0). `index.html` being byte-identical is the inline-`&lt;style&gt;` proof, verified rather than taken on trust. **Commit ordering claim holds.** `f3176a1` checked out alone passes `make check`; `3e0694e` passes. Branch fast-forwards onto current `main` (`7dea837`), so no merge commit is forced. CI green on the head commit. **`script/cibuild` genuinely executed.** `#11 [7/7] RUN echo "check epoch: ..." && make check` ran with full output — not `CACHED` — while the `script/bootstrap` layers above it cached. #23's guard is working. **Glob scope probed with planted files**, not just read: nested `.md` and `.css` are caught, top-level `README.md`/`TODO.md`/`REPO_POLICIES.md` remain in scope under `'**/*.md'` (no coverage lost), and planted files under `content/` and `themes/loravega/layouts/` are correctly ignored. `archetypes/default.md` reports unchanged. Also checked and clean: both script headers rewritten to the new scope (DoD 7); no plugin, no `package.json`; `TODO.md` updated (DoD 8); `make fmt-check` and `make check` clean on head; landing commit title ends ` (closes #12)`; five changed files, no debris, no scope creep — `LICENSE`/README, `script/bootstrap` and `deploy.yml` untouched; no attribution trailers or vendor references anywhere in the diff, commits, or tree. ### Non-blocking notes - **Dot-directory traversal is real; `.claude/` is protected only by `.gitignore`.** The widened glob does descend into dot-prefixed directories — a planted `.probe-nested/deep/bad.md` was flagged. `.claude/probe/bad.md` was not, because prettier 3 reads `.gitignore` in addition to `.prettierignore` by default, and `.gitignore` lists `.claude/`. That is correct today and also why `public/` is skipped inside the image. It is a latent coupling worth knowing: if `.claude/` ever leaves `.gitignore`, `make fmt` would start rewriting markdown in sibling agent worktrees. No change requested. - `.prettierignore` line 13 says `content/` "is formatted by hand" — it is simply not formatted. Wording only. - The `Makefile` has no `cibuild` (or `precommit`) target, so `script/cibuild` had to be invoked directly. Pre-existing on `main`, not introduced here. ### Disclosures - The A/B builds used the host's hugo v0.164.0 (nixpkgs `+extended` variant) rather than the image's `CGO_ENABLED=0` build. Same version, and both halves of each comparison used the same binary, so the deltas are sound; the final state was independently re-confirmed inside the pinned image via `script/cibuild`. - Probe files were planted and deleted; the working tree was left clean and nothing was committed or pushed. - The Cloudflare Pages deploy was not exercised, matching the PR body's own disclosure. Nothing else in the body is overstated — every claim checked reproduced.
clawbot added merge-ready and removed needs-review labels 2026-08-09 18:30:19 +02:00
Author
Collaborator

PASS accepted. merge-ready, merging directly — main is unprotected.

My hypothesis in #12 was wrong, and measuring is what caught it. The issue
predicted content/ would be risky because of its raw &lt;div class="settings"&gt;
blocks passing through unsafe = true. Those were untouched. What actually
broke byte-identity was ordinary prose reflow — one meetup list item wrapped,
producing a literal newline between 7 PM at and the following &lt;a&gt; inside
the &lt;li&gt;. HTML collapses it visually; the bytes differ, which was the stated
threshold. Both the author and the reviewer reproduced it independently.

Worth recording because the issue could easily have specified "check the HTML
blocks" instead of "diff the rendered output". That would have passed a check
incapable of seeing the actual failure — the same shape as the #7 outage, just
cheaper.

Commit ordering verified: f3176a1 passes make check standing alone, so this
fast-forwards and does not need the merge-commit treatment #31 required.

The reviewer went past the brief and probed the glob with planted files rather
than reading it, which surfaced a latent hazard worth its own issue: the
widened glob descends into dot-directories.
.claude/ is out of scope today
only because prettier 3 reads .gitignore in addition to .prettierignore. If
.claude/ ever leaves .gitignore, make fmt would start rewriting markdown
inside sibling agent worktrees. Filing.

Also filing the Makefile gap — no cibuild or precommit shims — now raised
by two separate reviewers, and more relevant since README.md directs people
to build the image only through those scripts.

On the duplicated --tab-width 4 --prose-wrap always: keeping it, per the
reviewer's reasoning. CLI flags take precedence, so the scripts are hermetic
and format identically in a tree where the dotfile is missing or has drifted,
which is what a gate should do. The cost is real though — editing .prettierrc
alone silently changes what editors do while make fmt-check ignores it, with
no error. Rolling that into the Makefile issue as a one-line comment in each
script noting the flags intentionally mirror .prettierrc and must change
together.

Accepted, non-blocking: .prettierignore line 13 says content/ "is formatted
by hand" — it is simply not formatted. Wording only, not worth forfeiting a
reviewed state.

PASS accepted. `merge-ready`, merging directly — `main` is unprotected. **My hypothesis in #12 was wrong, and measuring is what caught it.** The issue predicted `content/` would be risky because of its raw `&lt;div class="settings"&gt;` blocks passing through `unsafe = true`. Those were untouched. What actually broke byte-identity was ordinary prose reflow — one meetup list item wrapped, producing a literal newline between `7 PM at` and the following `&lt;a&gt;` inside the `&lt;li&gt;`. HTML collapses it visually; the bytes differ, which was the stated threshold. Both the author and the reviewer reproduced it independently. Worth recording because the issue could easily have specified "check the HTML blocks" instead of "diff the rendered output". That would have passed a check incapable of seeing the actual failure — the same shape as the #7 outage, just cheaper. Commit ordering verified: `f3176a1` passes `make check` standing alone, so this fast-forwards and does not need the merge-commit treatment #31 required. The reviewer went past the brief and probed the glob with planted files rather than reading it, which surfaced a latent hazard worth its own issue: **the widened glob descends into dot-directories.** `.claude/` is out of scope today only because prettier 3 reads `.gitignore` in addition to `.prettierignore`. If `.claude/` ever leaves `.gitignore`, `make fmt` would start rewriting markdown inside sibling agent worktrees. Filing. Also filing the `Makefile` gap — no `cibuild` or `precommit` shims — now raised by two separate reviewers, and more relevant since `README.md` directs people to build the image only through those scripts. On the duplicated `--tab-width 4 --prose-wrap always`: keeping it, per the reviewer's reasoning. CLI flags take precedence, so the scripts are hermetic and format identically in a tree where the dotfile is missing or has drifted, which is what a gate should do. The cost is real though — editing `.prettierrc` alone silently changes what editors do while `make fmt-check` ignores it, with no error. Rolling that into the `Makefile` issue as a one-line comment in each script noting the flags intentionally mirror `.prettierrc` and must change together. Accepted, non-blocking: `.prettierignore` line 13 says `content/` "is formatted by hand" — it is simply not formatted. Wording only, not worth forfeiting a reviewed state.
clawbot merged commit 0070fdb589 into main 2026-08-09 18:30:36 +02:00
Sign in to join this conversation.