Two problems this session surfaced on the Forgejo box, both now addressed
in tracked source rather than in shell history.
forgejo-sync.sh pushes the local repositories to git.levineuwirth.org over
HTTPS. It exists because the only Forgejo remote anywhere used
ssh://…:2222, which is filtered on most public networks, so eight of nine
repos had drifted three to four months behind while the CV cited them as
canonical. Dry-run by default, never force-pushes, never touches `origin`,
creates new repos private, and refuses to push when the remote holds
commits the local machine does not — the check that caught `levshell`,
where Forgejo's copy turned out to be the abandoned prototype rather than
an older version of the current project.
The divergence check authenticates via GIT_ASKPASS. An earlier version ran
ls-remote unauthenticated, which fails silently on private repositories and
returned empty — indistinguishable from "remote has nothing we lack", i.e.
a false all-clear on exactly the repos where the check matters. A failed
listing now blocks instead of passing.
forgejo-backup.sh plus its timer close the other gap: there were no backups
at all, of an instance holding nine repositories, four of them cited by URL
in a CV PDF already in circulation. Nightly, no downtime, SQLite `.backup`
for a consistent snapshot rather than cp, integrity-checked before it is
trusted, archive verified readable before retention prunes anything, and 14
days kept. Aborts non-zero on any of those checks so a bad run surfaces as
a failed unit instead of a corrupt archive displacing a good one.
Both are still local to that box. Copying an archive off-site remains the
open half of the backup story.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SUGesXiMmACsLBTGG1xuEU
Six follow-ups from the link-archive audit, all the items with a
code-shaped fix. Behaviour verified per feature (stubbed + live where a
network or browser was needed); ARCHIVE.md status notes updated alongside.
- suggest: `archive.py suggest` + `make archive-suggest` print works
cited in data/*.bib (url wins, DOI-only resolves to doi.org/...) but
absent from the manifest, as manifest-ready lines. Read-only, offline.
- aliases: optional `aliases:` manifest field for equivalent URLs no
normalisation can derive (DOI vs. landing URL). Enforced like canonical
URLs on both sides (archive.py pre-scan + Archive.hs validator);
ArchiveIndex drops alias keys matching a takedown. FIPS 203 now carries
its DOI form, so the simd paper's DOI citation resolves.
- check scheduling: systemd user timer (systemd/archive-check.{service,
timer}, symlink-installed) runs the rot scan daily. cmd_check gains an
offline canary guard so an unattended scan on an offline machine leaves
state untouched instead of mass-flipping entries to rotted.
- Wayback fallback: an original already dead at first fetch falls back to
its most recent existing Wayback capture (raw id_ bytes through the
normal pipeline), honouring a preserved X-Archive-Orig noarchive and
recording `fetched-from`. Dead-only, never during refresh.
- bibliography annotation: Filters.Archive exports annotateBlock;
Citations.hs applies it to each CSL-rendered entry, so bibliography
links get the same affordance / rotted-flip as body links.
- search-UI filter: archive "exclude/only" + "link status" filters on the
search page, backed by a new data/archive-meta.json. Scoped apart from
the epistemic `status` filter (own state, classes, labels) rather than
renamed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>