Skip to content

fix: live rendering defects, dead scaffolding, and a tag taxonomy that starved related posts - #67

Merged
bits-bytes-nn merged 8 commits into
mainfrom
fix/rendering-defects-and-tag-taxonomy
Aug 19, 2026
Merged

fix: live rendering defects, dead scaffolding, and a tag taxonomy that starved related posts#67
bits-bytes-nn merged 8 commits into
mainfrom
fix/rendering-defects-and-tag-taxonomy

Conversation

@bits-bytes-nn

Copy link
Copy Markdown
Owner

A fresh adversarial pass over the areas the last round did not touch — the four
listing pages, search.json/search.js, the CI trigger, and the tag vocabulary.
Findings were verified against the live site with real requests, not inferred.

Two defects that were live in production

/categories/ rendered "Agentic Ai". The page title-cased each word through
Liquid's capitalize, which uppercases the first letter and lowercases the
rest
. index.html and _layouts/post.html render the same value correctly
with a one-line replace. Deleting the 15-line loop removes the bug and the
code that caused it.

Three of the four listing pages printed their own title twice<h1>Insights</h1>
followed by <h2>Insights</h2>. /paper-reviews/ never did, which is what
showed the heading was not load-bearing. Same defect class as the leading #
removed from 35 posts last round.

The highest-leverage finding: the tag layer

Tags described one paper's contribution each, so 220 of 264 were used by
exactly one post
. related_posts.rb requires a shared tag, so 11 posts
shipped with no "Related reading" at all
— including RAPTOR, GraphRAG, LightRAG
and Zep, four graph-retrieval papers with not one tag in common. The Insights
cluster failed the same way by calling one topic three names
(Agentic-Architecture / Agentic-Patterns / Agentic-Infrastructure).

Fixed in the data, not the ranking rule — loosening the rule is what once put
LLaMA under DeepSeek-V3 with nothing in common. Nine topic tags, each on 3–8
posts, added on top of the existing ones. Nothing removed, so no live /tags/
anchor moves.

All 35 posts now get suggestions (31 with a full three), where 11 got none.
Ordering is unaffected: score is shared-tag count, so a shared specific tag still
outranks a shared topic tag.

Also fixed

  • Adjacent-post links pointed at a post's own translation. All three
    translation pairs share a date, so page.previous sent the newest Korean
    post's "← Previous" to that same article in English. Moved into
    related_posts.rb, which already knew both rules (skip the twin, don't cross
    languages); it now steps past an ineligible neighbour instead of dropping the
    link. 9 new tests.
  • The browser's own chrome ignored dark mode. theme-color and
    color-scheme were both pinned to light, so a mobile address bar stayed white
    above a #0d1117 page and UA widgets rendered light. Also removed a comment
    claiming theme-color used prefers-color-scheme media queries — there were
    none, and dark is opt-in only.
  • Dead scaffolding deleted rather than documented. _layouts/archive.html
    (nothing sets layout: archive), the inert simple_jekyll_search: block, two
    exclude: entries for files that don't exist, and two no-op conditionals in
    footer.html. The last pass fixed the docs about these instead of removing
    them.
  • CI gates could not block a bad merge. No pull_request trigger, so the
    four gates ran only after a push to main; nine PRs merged in one day were each
    validated post-merge. Now runs on PRs, with publishing steps skipped.
  • Two empty sections hidden. Every post is a Paper Review (28) or an Insight
    (7), so two of six nav links led to "this space will fill up soon" and both
    pages sat in the sitemap. URLs kept; main_nav dropped, sitemap: false added,
    with the restore instructions in each file's front matter.

Hypotheses checked and dropped

Raw TeX in search snippets (0 of 35); prev/next chronological direction (correct);
dark-mode FOUC (already bootstrapped before paint); head_extra unrendered (it
is rendered); hero image bloat (116 KB); Bourbon/Neat's ~120 vendored partials
(mixin-only, emit no CSS — not dead weight).

Corrections to earlier claims

search.json is 3.2 MB / 991 KB gzip, measured — the docs said 2.7 MB /
786 KB. And the tag counts are 264 distinct / 220 singletons, not the
250/210 first reported: the two Llama posts use a wrapped tags: block that a
single-line grep had skipped, so the problem was slightly worse than stated.

Known limitation, now documented rather than fixed

Search indexes the full body but the result card shows a fixed 40-word head
snippet, so a match at character 50,000 is invisible and unhighlighted. Fixing it
needs a match-centred snippet, outside the library's template.

Verification

All four gates green on the final tree: 82 tests / 136 assertions, clean
build, HTML-Proofer finished successfully, All checks passed (16 checks).
Each change was also confirmed in the rendered output — "Agentic AI", no
duplicated titles, nav down to four links, the two pages absent from
sitemap.xml, color-scheme compiled for both themes, the Korean data post's
"Previous" now the AgentCore piece, and the four graph-RAG papers recommending
each other.

One regression was caught mid-work by validate-site.sh itself: my explanatory
comments contained the literal string <h1>, which the "exactly one h1 per page"
check counts in the served markup. Rewritten as Liquid comments that ship nothing.

/categories/ rendered "Agentic Ai". The page title-cased each word through
Liquid's `capitalize`, which uppercases the first letter and lowercases the
rest — so an acronym in a category name was destroyed. index.html and
_layouts/post.html render the same value correctly with a one-line `replace`,
which is now all this page does too: the 15-line word loop is gone, and with
it the bug it existed to cause.

/insights/ printed "Insights" as an <h1> (from _layouts/page.html) and then
again as an <h2> on the next line. /paper-reviews/ never did this, which is
what showed the heading was unnecessary rather than load-bearing. No inbound
anchor referenced the id.

/paper-reviews/ was also the last page emitting raw category values as heading
ids (`id="Language-Models"`), against the slugify convention documented in
docs/tech-doc.md §3. Nothing links to those anchors, so html-proofer could not
catch the drift.
Every post is a Paper Review (28) or an Insight (7), so two of the six nav
links led to "this space will fill up soon", and both pages sat in sitemap.xml
offering search engines a listing with nothing on it.

They keep their URLs and their content; they lose `main_nav` and gain
`sitemap: false`. Their front matter records which two keys to restore, and
nav_order 2 and 4 are deliberately left vacant so restoring is a one-line
change when the first post of either type lands.

Also drops the <h2> that repeated each page's own title, as on /insights/.
Two values that must change with the theme were pinned to light:

- `<meta name="theme-color" content="#ffffff">` was unconditional, so a mobile
  browser kept a white address bar above a #0d1117 page.
- `<meta name="color-scheme" content="light">` was too, which tells the UA to
  render its own widgets — scrollbar, the search field's clear button, form
  controls — light against that same dark page.

color-scheme moves into _sass/_dark.scss keyed off data-theme, since only the
stylesheet knows which theme is live and the inline bootstrap already sets the
attribute before first paint. theme-color has no CSS equivalent, so the
bootstrap updates it on load and the toggle updates it on click; the bootstrap
had to move below the meta tag it now needs to find.

Also deletes a comment claiming theme-color was "declared at top of <head> with
prefers-color-scheme media queries". There were no media queries, and there is
no OS-preference branch anywhere — dark is opt-in only. css/main.scss carried
the same false claim about why _dark.scss is imported last.
All three translation pairs share a date exactly, so Jekyll's page.previous —
which walks raw date order — sent the newest Korean post's "← Previous" to that
same article in English. Verified on the live site before the change.

related_posts.rb already knew both rules this needs (skip the translation twin,
never cross languages), so the adjacency lives there rather than duplicating the
language default in a Liquid loop: `page.adjacent_older` / `page.adjacent_newer`
replace page.previous / page.next in _layouts/post.html. The shared predicate is
extracted as `eligible?` and both callers use it.

It steps *past* an ineligible neighbour instead of dropping the link, so the
newest Korean post still gets a previous post — the AgentCore piece — and the
English data-architecture post now navigates to an English neighbour instead of
Korean.

9 tests cover it, including the identical-date case that produced the defect.
Each of these was identified in an earlier pass, and each time the
*documentation about it* got fixed instead of the thing itself:

- `_layouts/archive.html` — nothing has ever set `layout: archive`.
- `simple_jekyll_search:` in _config.yml — simple-jekyll-search is a browser
  script and cannot read _config.yml, so the exclude list excluded nothing. Its
  only remaining reference was the doc noting it does nothing. A comment now
  records why the block is absent.
- `exclude: circle.yml, stackbit.yaml` — neither file exists in the repo.
- footer.html's `page.custom_js` loop — no page has ever set it; pages needing
  an extra asset use `head_extra`, as search.md does.
- footer.html's conditional on site.ga_tracking_id — its entire body was a
  comment saying the tracking lives in head.html.

The replacement comment in footer.html is an HTML comment that names no tag
delimiters, because Liquid tokenizes tags inside comments too: naming one in
prose opens it for real and fails the build. That cost two builds to learn.
The workflow triggered on pushes to main alone, so the four gates — plugin
tests, build, html-proofer, validate-site — reported on a bad change only once
it had already landed. Nine PRs merged in one day were each validated
post-merge. The blast radius was bounded (deploy needs: build, so a failure
meant no deploy rather than a broken site) but main was left red.

Adds a pull_request trigger on main and skips the two publishing steps —
artifact upload and the deploy job — for PR events.
Tags described one paper's contribution each — "Dependency-Aware-Tree-Traversal",
"Leiden-Algorithm-For-Text" — which is a precise paper index and a useless
graph: 220 of 264 tags were used by exactly one post. Since
_plugins/related_posts.rb requires a *shared* tag, 11 posts shipped with no
"Related reading" block, among them RAPTOR, GraphRAG, LightRAG and Zep — four
graph-retrieval papers with not one tag in common. The Insights cluster failed
the same way by calling one topic three names: Agentic-Architecture,
Agentic-Patterns, Agentic-Infrastructure.

The fix belongs in the data, not the ranking rule. Loosening the rule to relate
on category alone is what put LLaMA under DeepSeek-V3 with nothing in common,
and that strictness was asked for deliberately.

Nine topic tags, each landing on 3-8 posts: Agentic-AI, Alignment, DeepSeek,
Knowledge-Graph, Llama, Mixture-of-Experts, Multimodal-Models, Reasoning-Models,
Retrieval-Augmented-Generation. Also folds MCP into Model-Context-Protocol,
which were the same tag spelled two ways.

Nothing is removed, so no live /tags/ anchor moves. All 35 posts now get
suggestions (31 with a full three), where 11 got none. Ordering is unaffected:
score is shared-tag count, so posts that share a specific tag still outrank
posts sharing only the topic. The two Llama posts' wrapped `tags:` blocks are
normalised to one line, matching the other 33.
- Both READMEs and docs/tech-doc.md §3 gain the two-layer tag convention, with
  the controlled topic list and the reason it exists.
- The nav table drops the two empty sections and explains the vacant nav_order
  slots, so the gap does not read as an oversight.
- The dark-mode row names the two theme-dependent values that live outside CSS.
- The CI section documents the pull_request trigger.
- Corrected two stale claims: search.json is 3.2 MB / 991 KB gzip (measured), not
  2.7 MB / 786 KB, and the `simple_jekyll_search.exclude` block it described as
  inert is now gone rather than merely inert.
- Added the known search limitation: the index is the full body but the result
  card shows only a fixed 40-word head snippet, so a match deep in a long post
  is invisible and unhighlighted.
@bits-bytes-nn
bits-bytes-nn merged commit 54fe09f into main Aug 19, 2026
2 checks passed
@bits-bytes-nn
bits-bytes-nn deleted the fix/rendering-defects-and-tag-taxonomy branch August 19, 2026 14:52
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