Matches .python-version and the actual development environment.
uv.lock already declared >=3.12 but the project has been on 3.14 for
a while; the floor was just stale.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add tools/model-checksums.sha256 with sha256 hashes for the five
Xenova/all-MiniLM-L6-v2 files served from static/models/.
download-model.sh was already plumbed to verify against this file
when present; the file itself was missing, so downloads were
unverified. Now every fetch checks against committed hashes and
fails closed on mismatch.
- Pin embed.py's SentenceTransformer load to a specific HF commit
(c9745ed1d9f207416be6d2e6f8de32d1f16199bf of
sentence-transformers/all-MiniLM-L6-v2). A future model bump can no
longer silently change embedding semantics across builds. Bump
deliberately when validating; re-run a full embed pass to refresh
the semantic + similar-links data.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add nginx/security-headers.conf — server_tokens off, HSTS (1y +
preload), X-Content-Type-Options, X-Frame-Options DENY,
Referrer-Policy, Permissions-Policy, and a usage-scoped CSP. CSP
ships in Report-Only mode; promote to enforcing once the report
stream is clean for a week. CSP allowlists are derived from actual
usage (cdn.jsdelivr.net for KaTeX/Vega, *.basemaps.cartocdn.com for
Leaflet tiles); 'unsafe-inline' and 'unsafe-eval' are documented
inline.
- Add nginx/vhost.conf.example — reference vhost showing the canonical
include order. The live vhost on the VPS remains the source of
truth; this file documents the structure so the VPS config can be
reproduced or audited from the repo.
- Shorten unfingerprinted CSS/JS cache from 24h to 1h. Bug fixes ship
to warm clients within an hour; if assets are ever fingerprinted,
this can move to immutable.
- Refresh README repo layout — add nginx/ entry, drop stale paper/
and spec.md references that never existed in the working tree.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comprehensive audit of the repo flagging HIGH/MED/LOW/NIT issues across
the Haskell build, Python and shell tools, content + frontmatter,
templates, static assets, nginx snippets, README, .env, and gitignore.
Records what was fixed, what was deferred, and the recommended fix
order so future me has a paper trail.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Scoped warm accent tokens (--library-accent and friends) defined at
:root inside library.css — page-scoped since the file only loads on
/library.html and /search.html. Section headings become Spectral
small-caps chapter markers at 1.2rem in the accent color, so the
ornament span inherits it via currentColor. Divider and "more on
this shelf" link pick up the muted variant. The leading blockquote
gets an epigraph treatment: narrower measure, italic, warm intro
ink, with the attribution line dropping to the accent-muted tone.
Card-level refinements (oldstyle figures on dates, small-caps on
item-kind) scoped via .library-section so /new.html and tag pages
retain their lining-figure UI treatment.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each shelf gets a dingbat keyed by portal slug: laurel (research),
quill (nonfiction), open book (fiction), lyre (poetry), plus the
existing clef / ai / tech / trefoil glyphs for the remaining four.
Rendered via mask-image with currentColor so a single SVG per
portal inherits whatever color its heading carries. Between rendered
shelves, a centered fleuron flanked by thin rules (library-divider.svg)
sits via CSS adjacent-sibling so hidden sections leave no orphan
dividers. The template swaps its Unicode placeholder for a
data-ornament span, wires a '\$library-intro\$' slot above the shelves,
and renders a "More on this shelf →" link when has-more gates fire.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the library infrastructure without visible change to the rendered
page: a 'featured:' list in each portal's tag-meta sidecar drives shelf
curation (up to 5, default cap 4, recency fills the rest), a content/
library.md snapshot feeds a '\$library-intro\$' slot for a leading
blockquote, and '\$<slug>-has-more\$' gates expose whether the unfiltered
portal overflows the shelf. Items are now loaded once and partitioned
by primary portal rather than scanned per-section.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>