Walks every post's <img>/<a> (markdown + raw HTML, .md/.mdx, plus the frontmatter cover field) across both content collections, flags missing local assets and internal links that don't match any real route, and can optionally probe external refs for dead hosts (HEAD-then-GET, cached, concurrency-capped, off by default). Wired into `npm run build`'s postbuild step in warn mode — it can never fail the build, only crash-guards + logs a warning. External checks are a separate on-demand npm run audit:links:full since they touch the network and would make every build network-dependent otherwise. Report artefacts land in .ai/link-audit/ (gitignored). First real run: 0 missing local assets, 4 broken internal links (dead pre-Astro /Projects/... style paths from the old site structure), 18 dead external refs with --external (6 confirmed, 12 low-confidence itch.io-style 403s that are more likely bot-blocking than actually dead — flagged as such in the report rather than reported at face value). Ticks the "Link & image audit" wishlist item in GOAL.md; "Fix what the audit finds" is next and deliberately untouched here.
190 lines
10 KiB
Markdown
190 lines
10 KiB
Markdown
# GOAL — where `blog` is going
|
|
|
|
## North star
|
|
|
|
**A blog worth subscribing to: honest, measured write-ups of what the lab
|
|
actually did — every number taken from a real run, every AI-written word labelled
|
|
as such — on a site I host, that loads in a second, and that will still resolve
|
|
in ten years.**
|
|
|
|
The archive spans 2015 to today: student game-design essays, internships,
|
|
prototypes, and now the homelab. The bet is that the *lab* is the material — a
|
|
Strix Halo box doing image→3D, agents running from a phone, a Backrooms map
|
|
generator — and that writing it down carefully is worth more than posting often.
|
|
|
|
Modest by design: the goal is not an audience number. It is **an archive that is
|
|
presentable end to end, a publishing loop that doesn't stall, and a transparency
|
|
record nobody else in this genre bothers to keep.**
|
|
|
|
## Target
|
|
|
|
- **Me** — writing is how the lab work gets thought through and remembered. The
|
|
post is the artefact that outlives the project.
|
|
- **The person who hit this from a search** — someone debugging ROCm on gfx1151,
|
|
or wondering whether agents-from-a-phone is real. They want measurements and
|
|
code, not a listicle. They should be able to read it with JS off.
|
|
- **People who follow the lab** — via RSS, on their own reader, with no
|
|
newsletter signup and no tracking beyond self-hosted Matomo.
|
|
|
|
## What exists today (v1.0)
|
|
|
|
Astro 2 (astro-ink lineage) with MDX posts, client-side Lunr search, tags, RSS
|
|
and a sitemap; static build → zipgo on raspy2 at **blog.dev.gabvdl.xyz**. One
|
|
design, the **blueprint** — a cyanotype engineering drawing, graph-paper ground,
|
|
`FIG.` numbering, a drawing title block for a footer — with a load-bearing split
|
|
between `base.css` (structure, tokens only) and `blueprint.css` (identity).
|
|
Animation islands from `@gabvdl/ui` hydrate without flashing and leave complete
|
|
HTML for crawlers; `ArticleExtras` gives every post a full-screen image viewer, a
|
|
"play the figures as a story" control and copy buttons for free. 24 posts, three
|
|
of them recent AI-written lab articles carrying the mandatory AI-disclosure
|
|
callout. Analytics is self-hosted Matomo. No CI: **nothing publishes on push**,
|
|
only `npm run deploy`.
|
|
|
|
## Being worked on
|
|
|
|
<!-- Claims by goal-keeper agents. One bullet per in-flight item; the agent
|
|
removes its own line in the same commit that ticks the checkbox. Leave
|
|
the section empty (this comment only) when nothing is in flight. -->
|
|
|
|
## Horizons
|
|
|
|
### Short term — v1.1: the archive is presentable (now)
|
|
Eleven years of posts moved through three themes and two hosts. Before writing
|
|
anything new, make what exists hold up: no broken images or dead links, tags
|
|
that mean something, every post legible in the blueprint look in both colour
|
|
schemes, and correct metadata (dates, descriptions, canonical URLs, per-post
|
|
social cards).
|
|
|
|
### Middle term — v1.2: a publishing loop and a transparency record
|
|
Make the site say out loud what it is: a public page listing which posts were
|
|
written by a model and which by me, wired to the lab's AI-transparency
|
|
convention. Alongside it, the mechanics that keep publishing from stalling — a
|
|
draft queue that renders locally, series/collection pages, prev-next navigation,
|
|
reading time, and a full-content RSS feed that reads well in a reader.
|
|
|
|
### Long term — v2 / someday
|
|
A site people subscribe to: a steady rhythm of lab write-ups, a few of them the
|
|
canonical English-language reference for something niche (ROCm on Strix Halo,
|
|
agent harness economics), an archive page that makes eleven years navigable, and
|
|
a look that is unmistakably this site and nobody else's. Still static, still
|
|
self-hosted, still free of trackers.
|
|
|
|
## Wishlist
|
|
|
|
Ordered roughly by value. Items marked **[human]** need a taste call or writing
|
|
that is mine to do — the goal-keeper must skip those.
|
|
|
|
### The archive holds up
|
|
- [x] **Link & image audit**: a script that walks every post's `<img>` and `<a>`,
|
|
checks local assets exist and reports external 404s, run as part of
|
|
`npm run build` (warn) with a report artefact. Several 2015-2021 posts have
|
|
already had broken images fixed by hand. — `scripts/audit/link-image-audit.mjs`,
|
|
wired into `postbuild` (internal checks only — fast, no network); full run
|
|
with external HTTP checks via `npm run audit:links:full`. Report at
|
|
`.ai/link-audit/report.{json,md}`. First real run found 4 broken internal
|
|
links (dead pre-Astro `/Projects/...`-style paths) and, with `--external`,
|
|
18 dead external refs (6 confirmed 404/unreachable, 12 low-confidence
|
|
403s — mostly itch.io, which blocks scripted requests from this host
|
|
regardless of whether the page is live). 0 missing local assets. Fixing
|
|
what it found is the next wishlist item, deliberately left undone here.
|
|
- [ ] **Fix what the audit finds** — one pass over the old posts, replacing dead
|
|
external images with local copies under `public/assets/posts/<Post>/`.
|
|
- [ ] **Tag taxonomy pass**: consolidate the tag vocabulary (they accreted over
|
|
eleven years), give each tag a description, and make `/tags` a real index
|
|
rather than a word cloud.
|
|
- [ ] **Dark-mode audit of the old posts** — tables, inline code, callouts and
|
|
raw-HTML blocks in pre-2023 posts, which predate the token system.
|
|
- [ ] **Frontmatter completeness**: every post gets a `description` (used by
|
|
search, RSS and og:description) and a sane `cover`; validate it in the zod
|
|
schema so a missing one fails the build.
|
|
- [ ] **Per-post og:image**: generate a blueprint-styled social card per post
|
|
(title + date + tags on the graph-paper ground) at build time. The site's
|
|
curated `public/og-image.png` stays as the home-page default — this is
|
|
per-post, not a screenshot.
|
|
- [ ] **Archive page**: all posts by year, with counts — eleven years is too much
|
|
for one scroll.
|
|
|
|
### Transparency (the disclosure record)
|
|
- [ ] **`/transparency` page**: every post listed with who wrote it — me, or a
|
|
named model working from my repos and runs — derived from frontmatter, not
|
|
hand-maintained. Explain the rule in one paragraph: any generated post
|
|
carries the callout, and no number in one is estimated.
|
|
- [ ] **`author` / `generated_by` frontmatter field** validated by the schema,
|
|
feeding both that page and a small badge on the post header.
|
|
- [ ] **Build-time check**: a post whose `generated_by` is set but whose body
|
|
lacks the AI-disclosure callout fails the build. The rule currently lives
|
|
in `CLAUDE.md` and depends on the agent remembering it — make it
|
|
mechanical.
|
|
- [ ] Adopt the lab's **ai-transparency widget** convention if it fits without
|
|
bloating the page (`~/projects/ai-transparency-widget`).
|
|
|
|
### The publishing loop
|
|
- [ ] **Prev/next post navigation** and related-by-tag links at the end of a
|
|
post.
|
|
- [ ] **Reading time + word count** in the post header.
|
|
- [ ] **Full-content RSS** (currently likely summary-only) with correct
|
|
enclosures for cover images, validated against a feed validator.
|
|
- [ ] **Draft preview route** — `src/drafts/` renders in `astro dev` behind a
|
|
flag and is excluded from the build, so a half-written post can be looked
|
|
at without publishing it.
|
|
- [ ] **Series / collection support**: a `series` frontmatter field with an
|
|
index page (the three 2026 lab posts are already a series in everything but
|
|
name).
|
|
- [ ] **Search quality**: the Lunr index should cover descriptions and tags, and
|
|
the results UI should show a snippet — the index is already a `postbuild`
|
|
step whose `dist/` mirror is load-bearing, don't break that.
|
|
- [ ] Blueprint-styled **404 page** and a proper `/about`.
|
|
|
|
### Craft & performance
|
|
- [ ] **Lighthouse pass** on a post page — image dimensions, `loading="lazy"`,
|
|
font loading, no layout shift from the animation islands.
|
|
- [ ] **Image pipeline**: convert the large post screenshots to WebP/AVIF with
|
|
width variants at build time; the TRELLIS post alone is image-heavy.
|
|
- [ ] Print stylesheet for a long technical post.
|
|
- [ ] **[human]** Decide whether the blueprint identity extends to the RSS/og
|
|
surfaces or stays on-site only.
|
|
|
|
### Writing (mine)
|
|
- [ ] **[human]** A post on the homelab itself — Traefik, Authelia, the generated
|
|
root compose, and why it is worth running.
|
|
- [ ] **[human]** A post on what a year of agent-run projects actually cost,
|
|
using the real numbers the lab already records.
|
|
|
|
## Non-goals (for now)
|
|
|
|
- **A newsletter, signups, or an email list.** RSS is the subscription.
|
|
- **Any third-party analytics, ads, comments platform, or embedded tracker.**
|
|
Self-hosted Matomo, cookie-less, is the ceiling.
|
|
- **CI that publishes on push.** Deploying is a deliberate act; the deploy
|
|
script's guard (refuse without a fresh search index) is part of that.
|
|
- **A CMS or a headless backend.** A post is one markdown file in the repo.
|
|
- **Chasing SEO or posting frequency for its own sake.** Fewer, measured posts.
|
|
- **A second design.** The switcher was deliberately removed; blueprint is the
|
|
look.
|
|
|
|
## Guard rails (for the goal-keeper)
|
|
|
|
- One wishlist item per run, finished end-to-end, then commit and push. `origin`
|
|
is the self-hosted **Gitea** (canonical); `github` is a dormant mirror —
|
|
pushing there is optional and nothing depends on it.
|
|
- **Never publish a generated post without the AI-disclosure callout**, verbatim
|
|
as specified in `CLAUDE.md`, as the first thing in the body. This is not
|
|
negotiable and not something to ask about.
|
|
- **Never invent a number.** Benchmarks, timings, costs and code in a post come
|
|
from a real run or a real file — measure or read it, never estimate.
|
|
- **Don't write a new post on a scheduled run.** Editorial voice is Gabriel's;
|
|
the goal-keeper improves the *site*, fixes the archive, and prepares tooling.
|
|
A post is written only when asked for by name.
|
|
- **Respect the CSS split**: `base.css` is structure and reads tokens only;
|
|
`blueprint.css` fills them. Tokens live on `html:root` (a bare `html {}` loses
|
|
to `:root` regardless of order), and design rules keep the `html ` prefix.
|
|
- **Import islands by one specifier** (`src/components/islands/X.tsx`, with the
|
|
extension) — mixing relative and aliased imports breaks the Astro client build
|
|
at generate time.
|
|
- **Don't narrow the Tailwind content glob** — it scans `.md`/`.mdx` on purpose,
|
|
or classes used only inside posts vanish.
|
|
- The **search index is a `postbuild` step** that writes `public/` *and* mirrors
|
|
into `dist/`; that mirror is load-bearing. `npm run deploy` refuses without it.
|
|
- Verify before deploying: `npm run build`, then actually look at the rendered
|
|
page (screenshot it) in **both** light and dark mode.
|