LLog — Path-case drift on the deploy server, and a consistent guard against silent link death#
.claude/effort-level; set in-session via /model to Opus 5 max effort).claude/mode; NOT yet confirmed by LLoL in-session — reported at reply top)ClaOp5Max (PENDING LLoL confirmation; first Opus 5
session in this repo; pattern follows ClaOp48Max / ClaFa5Max)Note
Append-only audit trail. Driving prompt recorded verbatim below. No source content files were modified in this session. This is a check-and-plan session; all remediation is proposed, not applied. The only file written is this llog.
Verbatim prompt 1 (the task)#
Another agent found a bug with the license naming and prepared some work best fixed by you.
I want you to check that other work and make a plan for how to handle all similar errors in
this project consistently, so I don't have to worry about them any more. --- Here is the
work to be done as described by that other agent: Please fix a path-case bug that is
silently 404-ing pages on the live site, then build out the /license/ section. Everything
below was diagnosed by reading the repo and curl-ing the live site on 2026-08-03. Verify each
claim yourself before acting — do not take it on trust. Where it says "ask me", stop and ask;
do not guess.
# THE BUG, IN ONE SENTENCE
Git's index records two directory names in a different CASE than the working tree, so a
checkout on the case-sensitive deploy server produces `source/FF/` and `source/license/JoLi/`
while every reference in the repo points at `ff/` and `joli/` — and those references silently
die.
## Evidence
on disk (python os.listdir, authoritative): source/ff source/license/joli
git ls-files says: source/FF source/license/JoLi
macOS is case-INsensitive, so git never noticed the directories were renamed to lowercase at
some point and kept the old index entries. Everything resolves locally. On the deploy server
it does not:
https://balospe.com/en/license/joli/ 404 https://balospe.com/en/license/JoLi/ 200
https://balospe.com/en/ff/ 404 https://balospe.com/en/FF/ 200
https://balospe.com/en/ff/system/ 404 https://balospe.com/en/FF/system/ 200
https://balospe.com/en/ff/contact/ 404 https://balospe.com/en/FF/contact/ 200
https://balospe.com/en/FF/archive/ 200
https://balospe.com/en/FF/philosophy/ 200
Reference counts in `source/`:
327 files contain "license/joli" | 2 contain "license/JoLi" (both are the 2026-07-30 patch, step 2)
25 files reference "/ff/" | 0 reference "/FF/index"
Sphinx does NOT fail on an unresolvable `:doc:` — it renders the link TEXT with no href. The
build is green, the pages look right locally, and the links are simply dead in production.
That is why this survived. A full-repo scan (`git ls-files` vs `os.listdir`) found EXACTLY
these two and no others.
## Why it matters more than it looks
`/en/ff/` is FeedbackFlow — the site's entire participation mechanism, the thing "Audit the
Math" asks people to use. Six FF pages are unreachable at the URL the whole site points to.
Separately, the FF mailto footer on nearly every page tells contributors their submission is
published "under the Jonah License (balospe.com/en/license/joli/)" — a 404, quoted in outgoing
email as the licence term of their contribution. Outreach is in flight that asks people to
review the work and links into the site, so this is live-fire, not housekeeping.
# A CORRECTION TO MY OWN EARLIER FRAMING
I said the Jonah License page was "buried deep inside the MMv3 folder." That was the symptom
talking, not the layout.
A full Jonah License page already exists at `source/license/joli/index.rst` — ~200 lines: the
MMv1 terms, the Purpose section, and the "On the charge of moral blackmail" admonition. It is
invisible for two compounding reasons:
1. its toctree entry in `source/license/index.rst` never bound (the case mismatch), so it is
absent from site navigation entirely; and
2. `/license/index` itself sits in the LAST toctree of `source/index.rst` — captionless and
`:hidden:`, grouped with `/legal/index` and `/sitemap/index` as a utility page.
What IS buried in MMv3 is a different, smaller thing: the 2-page intro exhibit at
`source/good-news-pack/vv/mmv3/open-letter/files-shared/jonah-license-intro.rst`, an
AI-generated exhibit page wrapping the printable 2-page PDF handout.
So the job is not "copy the JoLi out of MMv3." It is: fix the case so the real page binds,
give `/license/` a proper section with JoLi and CyLi as subpages, and decide what to do with
the exhibit page.
# STEP 0 — BRANCH DISCIPLINE (first, do not skip)
As of 2026-08-03 the repo is on `b16-revision-for-expert-review` with uncommitted changes:
M source/license/JoLi/index.rst <- the 2026-07-30 symptom patch (step 2)
M source/license/index.rst <- the 2026-07-30 symptom patch
?? source/_file/pdf/hell/mm/b/16/b16-riskymad-waiting-time_oov2_...pdf
?? source/_file/pdf/hell/mm/b/16/b16-riskymad-waiting-time_oov3_...pdf
Deploy runs from `main` (`.github/workflows/deploy.yml` →
`balosp/llol-build/.github/workflows/sphinx-deploy.yml@v3`, `on: push: branches: [main]`). A
site-wide URL fix must NOT ride on a paper-revision branch.
Ask me how I want the b16 work handled, then do this on a short branch off `main` (suggested
`fix/path-case-and-license-section`). Leave the two untracked b16 PDFs alone.
# STEP 1 — MAKE GIT AGREE WITH DISK
Lowercase wins: it is already on disk and is what all 352 references expect. Renaming the
directories to match git would mean editing 352 files; fixing the index fixes them all at once.
`git mv` cannot do a case-only rename on a case-insensitive filesystem. Do NOT use the "move to
a temp name and back" dance — nothing needs to move on disk, only the index is wrong:
git rm -r --cached source/license/JoLi
git add source/license/joli
git rm -r --cached source/FF
git add source/ff
Confirm git reports pure renames and nothing else:
git status
git diff --cached --stat -M
Expect `source/license/JoLi/index.rst -> source/license/joli/index.rst` plus five FF files
renamed, with 0 insertions and 0 deletions. If any file shows content changes, STOP — the index
fix has picked up something it should not have.
# STEP 2 — REVERT THE 2026-07-30 SYMPTOM PATCH (it becomes wrong after step 1)
On 2026-07-30 the refs in `source/license/index.rst` were changed from lowercase `joli` to
capital `JoLi` as a one-page workaround. After step 1 that workaround BREAKS the page it was
meant to fix. Revert to lowercase `joli/index` (match on text, not line number):
:doc:`Jonah License <JoLi/index>`
:doc:`full JoLi text <JoLi/index>`
:doc:`The Jonah License page <JoLi/index>`
toctree entry: JoLi/index
NAMING CONVENTION, confirmed: the prose `JoLi` as the licence's NAME stays capitalised
everywhere; only PATH segments go lowercase. Same for the Cyrus License: **`CyLi` in prose,
`cyli` in paths.**
Also collapse the two long `.. TODO AA:` blocks at the top of `source/license/index.rst` and
the CASE BUG item in `source/license/joli/index.rst` — they describe a bug that will no longer
exist. Replace with a one-line note that the path case was fixed 2026-08-03 and `_redirects`
carries the old capitalised URLs.
# STEP 3 — REDIRECTS (StayVS: published URLs are not silently revoked)
`/en/license/JoLi/` and the six `/en/FF/...` URLs are live and indexed today; after step 1 they
404. Root `_redirects` (Cloudflare-Pages style, copied into the build by `Makefile:152`) already
contains case-permutation rules for the audit-the-math campaign — follow that precedent:
###### Path-case fix 2026-08-03 — old capitalised paths kept alive (StayVS)
/en/license/JoLi/* /en/license/joli/:splat 301
/en/FF/* /en/ff/:splat 301
Check the splat syntax against the rules already in the file and against the host before
assuming `:splat` works; if it does not, enumerate the seven URLs explicitly. Test after deploy
that BOTH cases resolve.
# STEP 4 — "Cycrus" → "Cyrus"
`Cycrus` is a typo — the licence is named after Cyrus the Great. Six lines, two files:
source/license/index.rst:2 :description: ... the Cycrus License (CyLi) ...
source/license/index.rst:3 :keywords: ... Cycrus License, CyLi, ...
source/license/index.rst:72 The Cycrus License (CyLi) --- draft
source/license/index.rst:75 The Cycrus License is designed to enable ...
source/license/index.rst:95 **Why "Cycrus"?** The name honors Cyrus the Great, ...
source/sitemap/index.rst:127 - Jonah License (JoLi) and Cycrus License (CyLi) overview.
Line 95 currently reads *Why "Cycrus"? The name honors Cyrus the Great* — the typo sits in the
same sentence as its own etymology. After the fix it reads correctly as *Why "Cyrus"?*.
`CyLi` stays unchanged — the abbreviation is correct. Change only the word `Cycrus` → `Cyrus`.
Also grep the whole repo (not just `source/`) and `locale/` in case translations picked the typo
up, and make sure `build/` is regenerated rather than carrying stale HTML.
# STEP 5 — BUILD A REAL /license/ SECTION (JoLi + CyLi as sibling subpages)
Target structure:
source/license/index.rst Licenses — overview, links to both
source/license/joli/index.rst The Jonah License (exists; surfaced by step 1)
source/license/cyli/index.rst The Cyrus License (NEW — step 5a)
## 5a. Create `source/license/cyli/index.rst`
A real page, honestly marked as not-yet-drafted, carrying a rough outline of what the licence
will be about — not an empty placeholder, and not invented licence text.
- Copy the page skeleton from `source/license/joli/index.rst`: the same `.. meta::` block shape
(description / keywords / author / og:card:title / og:card:description), the `.. TODO AA:`
page-maturity block, and the whole `----- FOOTER FORM OOv1r2p1 -----` footer with the FF
mailto rewritten for `license-cyli`. Mirror how `license-joli` is encoded in the joli page's
`raw:: html` block — the subject and body are URL-encoded, so edit carefully and check the
rendered mailto.
- Page status: use the lowest maturity marker the StayC scheme uses for an unreviewed stub. Copy
the convention from an existing early-stage page rather than inventing a level.
- Content: migrate the existing CyLi section out of `source/license/index.rst` (lines ~72-102 —
the core principle, the three guarantees, the "Why Cyrus?" etymology, the status line) and
expand it into an outline of what the licence will cover. Leave `source/license/index.rst`
with a short summary plus a link, so there is one canonical home.
- WRITE NO LICENCE TERMS. The drafting work is genuinely not done. The page states the principle
(*what has been published cannot be revoked, but newer versions can be published*), its
relationship to `:doc:`/stayvs/index``, the open questions, and that the formal spec is
forthcoming. Anything resembling operative legal language would be invented authority — do not
produce it.
- Link it both ways with `/stayvs/index`, which is the mechanism CyLi is meant to make
institutionally reliable.
Ask me three things in one message before drafting, then offer an outline I can correct — do not
block on a perfect answer: (i) intended scope — does CyLi cover only site content under StayVS
versioning, or also third-party/institutional contributions? (ii) how it relates to JoLi —
successor, complement, or a different layer for a different kind of material? (iii) is it meant
to be reusable by other projects, or balospe-specific?
## 5b. Rework `source/license/index.rst` as a hub
Two parallel sections, each a short summary plus a link to its subpage, and a real toctree:
.. toctree::
:maxdepth: 1
joli/index
cyli/index
The existing toctree is `:hidden:` — part of why the JoLi page was invisible even setting the
case bug aside. Given the whole point is discoverability, exposing it is the obvious default;
confirm with me if you disagree.
# STEP 6 — NAVIGATION VISIBILITY
`/license/index` currently lives in the final toctree of `source/index.rst`:
.. toctree::
:hidden:
/legal/index
/sitemap/index
/license/index
Captionless and hidden — the utility bucket, alongside legal boilerplate and the sitemap. That
is why the licence is hard to find even though the page exists.
The theme is `sphinx_book_theme`; the visible left-nav groups are the captioned toctrees (*The
Problem*, *The Solution*, *Get Involved*, *The Background*). These are genuinely different
editorial choices — present them and let me pick:
- **(a) Move `/license/index` into "The Background"** (alongside `./ff/index`). Cheapest, and
puts it where a curious reader already goes for provenance. Recommended default.
- **(b) Give the last toctree a caption** such as *Reference* or *Legal & Licensing*, surfacing
legal + sitemap + license as a deliberate group.
- **(c) Promote it into "Get Involved"**, on the argument that JoLi is a call to use and share
the material, not a legal footnote. Strongest fit with the JoLi's actual purpose; also the
most opinionated change to the site's information architecture.
Whichever is chosen, verify in a built page that the entry actually renders in the sidebar — a
`:hidden:` toctree elsewhere can still swallow it.
# STEP 7 — THE 2-PAGE INTRO EXHIBIT (do not blind-copy)
I originally asked for a copy of the JoLi intro into the `/license/` folder. Flagging the cost
once, then it is my call:
A literal copy creates the same content at two URLs — duplicate content for search engines, two
files that drift apart, and under StayVS two independently versioned things that are meant to be
one document. The exhibit page also belongs to the Good News Pack exhibit catalogue and is linked
from its MMv3 siblings, so it is not free-floating.
- **Recommended:** do NOT duplicate. `source/license/joli/index.rst` already embeds the same
2-page PDF/WebP under its "Flyer" heading, and cross-links between the two pages were added on
2026-07-30. Make `/license/joli/` the canonical home — give the PDF and its download link real
prominence near the top rather than at the bottom — and leave the MMv3 page as the
exhibit-catalogue entry that points at it. This gets the handout reachable from `/license/` and
into the nav, with one copy.
- **If I still want a page under `/license/`:** create `source/license/joli/intro-2page/index.rst`
that `.. include::`s a shared fragment rather than copying the prose, so there is one source of
truth, and add a `_redirects` note recording the relationship.
Confirm with me before writing.
# STEP 8 — PREVENTION (the real deliverable; without it this recurs invisibly)
## 8a. A guard against index-vs-disk case drift
This is the exact scan that found the bug. It runs in about a second and is the only thing that
makes this class of fault visible on macOS. Add as a script and wire into CI (and/or a pre-commit
hook):
```python
#!/usr/bin/env python3
"""Fail if git's index disagrees with the on-disk case of any tracked path.
Invisible on macOS/case-insensitive filesystems; fatal on the deploy server."""
import subprocess, sys, os
paths = subprocess.run(["git", "ls-files", "-z"], capture_output=True, text=True).stdout.split("\0")
bad, seen = [], set()
for p in filter(None, paths):
cur = ""
for part in p.split("/"):
try:
real = [n for n in os.listdir(cur or ".") if n.lower() == part.lower()]
except FileNotFoundError:
real = []
if real and real[0] != part:
g = f"{cur}/{part}" if cur else part
d = f"{cur}/{real[0]}" if cur else real[0]
if (g, d) not in seen:
seen.add((g, d)); bad.append((g, d))
break
cur = f"{cur}/{part}" if cur else part
for g, d in bad:
print(f"CASE MISMATCH git: {g} disk: {d}", file=sys.stderr)
sys.exit(1 if bad else 0)
```
## 8b. Make dead `:doc:` refs fail the build
Sphinx rendering an unresolvable `:doc:` as href-less plain text is what let this ship green.
Turn on nitpicky mode (`-n`), and consider `-W` in CI. Expect a backlog of pre-existing warnings
on the first run — triage them rather than abandoning the flag. If `-W` is too aggressive today,
at least fail on `toctree contains reference to nonexisting document` and `unknown document`.
## 8c. Do NOT use `git config core.ignorecase false`
On a case-insensitive filesystem it makes git report phantom renames. The 8a guard is the safe
equivalent.
# STEP 9 — VERIFY (do not report done before this)
1. `git diff --cached --stat -M` shows six pure renames, zero content churn.
2. The 8a guard exits clean.
3. Confirm the fix from git's own view, not the filesystem's:
git ls-tree -r --name-only HEAD | grep -iE "^source/(ff|license/)"
Every path lowercase. A local macOS build proves nothing about case; the index state is what
matters.
4. Build locally; confirm `build/html/en/license/joli/index.html`, `.../license/cyli/index.html`
and `.../ff/index.html` all exist.
5. After merge to `main` and deploy, curl and expect 200 on lowercase, 301 on capitalised:
/en/ff/ /en/ff/system/ /en/ff/contact/ /en/ff/archive/ /en/ff/philosophy/
/en/license/ /en/license/joli/ /en/license/cyli/
/en/FF/ /en/license/JoLi/
6. Load `/en/license/` and confirm "The Jonah License" is a real link WITH an href, not plain
text; that JoLi and CyLi both appear in site navigation; and that "Cycrus" appears nowhere
(`grep -ri cycrus build/`).
7. Report what you actually checked and what you did not. If a step was skipped, say so plainly.
# SEPARATE — DO NOT BUNDLE INTO THE SAME COMMIT
`https://balospe.com/en/study/matheo/overview-christian/` is 404 live although committed in
source — the deployed build predates it. Any deploy fixes it, and it is wanted for outreach going
out now, so a deploy has value independent of this bug. Mention it when the fix is ready to
merge; do not fold it into these commits.
Part 1 — Independent check of the prior agent’s claims#
Every claim was re-tested from scratch in this session. Reporting in BREACH-before-HELD order per Language Rule 5 and Rule 7.
BREACH — claims that did not withstand checking#
BREACH 1 — Step 0 and Step 1 contradict each other. Step 1 says: “Expect … 0 insertions
and 0 deletions. If any file shows content changes, STOP.” But Step 0 itself records that
source/license/JoLi/index.rst and source/license/index.rst are modified in the working
tree and uncommitted. git rm -r --cached source/license/JoLi followed by
git add source/license/joli stages the working-tree content, so the 2026m07d30 patch (+55/-3
lines) is swept into the same staging area. Anyone following the brief literally hits their own
STOP condition and cannot tell a benign cause from a real one.
Correction: deal with the uncommitted patch before touching the index — it is being
reverted in Step 2 anyway. git checkout -- source/license/ (or stash) first, then do the index
fix, then re-apply the intended Step 2 edits as a separate commit. This gives a genuinely pure
rename commit, which is what makes the -M check meaningful.
BREACH 2 — Step 8b names the wrong Sphinx lever. -n (nitpicky) does not affect :doc:.
Nitpicky mode escalates missing domain cross-reference targets (py:, c:, cpp:, and
similar). An unresolvable :doc: already emits WARNING: unknown document: ... at default
settings — the build was never silent, it was unread, because SPHINXOPTS ?= is empty
(Makefile:3) and nothing turns warnings into failure. The correct lever is -W
(warnings-as-errors, ideally with --keep-going) or an explicit warning-count gate. Turning on
-n would add a large volume of unrelated noise while still not gating the actual fault.
BREACH 3 — one reference count is off. The brief says “25 files reference ``/ff/``”.
Measured: 32 files. (The license/joli = 327 and license/JoLi = 2 counts both HELD.)
Minor, but the /ff/ figure is the one used to argue the blast radius, so it should be right.
BREACH 4 — the blast radius is understated, not overstated. The brief describes the damage as
“six FF pages unreachable” plus a dead licence link. Resolving all 6027 :doc: roles in
source/ against git’s docname view (i.e. the deploy server’s view) gives 620 unresolved
occurrences, of which 563 are this one bug:
Unresolved |
Occurrences |
|---|---|
|
521 |
|
22 |
|
9 |
|
6 |
|
4 |
|
1 |
subtotal — this bug |
563 |
HELD — claims that withstood checking#
The core diagnosis HELD, exactly.
git ls-filesreportssource/FF/andsource/license/JoLi/;os.listdirreportssource/ffandsource/license/joli.The “exactly two, no others” claim HELD. The full scan was reproduced independently: 2141 tracked paths scanned, 2 case mismatches, both as described.
Every live-site status code HELD. Re-curled 2026m08d03:
/en/ff/404,/en/FF/200,/en/license/joli/404,/en/license/JoLi/200,/en/ff/system/404,/en/ff/contact/404,/en/FF/archive/200,/en/license/200.The separate matheo 404 HELD:
/en/study/matheo/overview-christian/returns 404 live.The “Cycrus” claim HELD precisely — 6 occurrences, 2 files, exactly the lines listed, and 0 in
locale/(so no translation sweep is needed).The mechanism explanation HELD. Sphinx does not fail on an unresolvable
:doc:; it drops the href and renders bare text. ConfirmedSPHINXOPTS ?=is empty andsuppress_warnings = ["bibtex"]is the only warning setting insource/conf.py.The deploy path HELD.
.github/workflows/deploy.ymltriggers on push tomainonly, delegating tobalosp/llol-build/.github/workflows/sphinx-deploy.yml@v3.The branch state HELD.
b16-revision-for-expert-reviewis 3 commits ahead ofmain;maindoes not contain the 2026m07d30 patch (0 occurrences ofJoLi/index).The self-correction about the JoLi page HELD and is credited:
source/license/joli/index.rstreally is a full 202-line page, and the “buried in MMv3” framing really was the symptom talking.Step 8c HELD.
core.ignorecase falseon a case-insensitive filesystem is the wrong remedy.
Corrections to this session’s own work#
Two scans written in this session were wrong before they were right, recorded here because the llog is an audit trail and not a highlight reel:
A first self-URL resolver never tried
.rst/index.rstsuffixes and reported 23 non-resolving URLs. False alarm.A second version handled suffixes but mis-ordered
rstrip('/')andrstrip('.'), so a URL ending a sentence (.../study/matheo/.) kept its trailing slash and reported/study/matheo/as MISSING.source/study/matheo/index.rstexists.
The corrected figures are in Part 2.
Part 2 — Measuring the FULL class, not just the instance#
The prompt asked for consistency across all similar errors, so the class was measured rather than assumed. Three sub-classes of silently-dying reference exist in this repo:
Sub-class |
Surface |
Broken |
Who currently catches it |
|---|---|---|---|
A. git index vs disk case drift |
2141 paths |
2 |
Nothing. Invisible on macOS, fatal on the server. |
B. hardcoded |
596 occurrences / 53 distinct paths |
1 case + 5 stale |
Nothing. Sphinx never looks inside a URL string. |
C. Sphinx |
6027 refs |
620 (563 = sub-class A) |
Sphinx warns; nothing reads the warning. |
Sub-class B detail (corrected figures)#
46 of 53 distinct self-URL paths resolve case-exactly. The exceptions:
1 CASE mismatch:
/license/JoLiinsource/license/index.rst— the 2026m07d30 patch.5 MISSING, of which 4 sit in frozen HELL MockupModel archives (
hell/mm/b/14/mmv1..3pointing at/matheology/e7day/,/matheology/e7he/,/matheology/jub/,/matheology/h-star/,/matheology/call-to-action/) and 1 in a HELL llog. These are append-only historical records; a link that was correct when written is arguably correct as history. Recommendation: exempt HELL from the URL guard rather than “fix” the archive — editing them to point somewhere else would falsify the record.0 broken self-URLs on live funnel pages. This surface is in good shape.
Sub-class C detail#
After the case fix, the :doc: backlog drops from 620 to ~57 occurrences, and of those only
about 14 are on live (non-HELL) pages. That residue includes some noise from the measuring
regex (multi-line prose captured inside backticks), so the true live backlog is smaller still.
Named live candidates worth triage:
gospels-apostles(2x) —source/matheology/compiler/sisyf/synthesis-howto.rstb3-trifecta-registry—source/_POST/DD/b/12/b12-ladder-pedagogy-for-acronyms.rst(note: b3 does not exist; the registry is b13 — looks like a stale pre-renumbering ref)b11-trifecta-italics-no-commas,b12-ladder-pedagogy-for-acronyms—source/_POST/DD/b/13/b13-trifecta-registry.rst(relative refs that need/_POST/DD/b/NN/paths)/good-news-pack/research-theology/abraham/— trailing slash invalid in:doc:/good-news-pack/references/leonhard-2010-visions-of-apocalypse.rst—.rstsuffix invalid in:doc:
This is the load-bearing number for the whole plan. A warnings-as-errors gate is unaffordable today (620 hits) and affordable immediately after the case fix (~14). That is what determines the order of work.
Other uppercase-cased tracked directories (case-fragility inventory)#
11 tracked directories have an uppercase component: AHA, HH, source/FF,
source/_POST, source/_POST/AnyAims, source/_POST/DD, source/good-news-pack/AA,
source/license/JoLi, source/matheology/hell/mm/b/14/OOv1|OOv2|OOv3. Only the two known ones
are drifted; the other nine agree between git and disk. They remain the standing risk surface for
this class, which is why the guard must be permanent rather than a one-off sweep.
Part 3 — EDEN analysis#
Knife Edge #1 — the order of operations is forced#
There is exactly one ordering that survives. Gate first, fix second fails: a -W build with
620 unresolved :doc: occurrences produces an unusable wall of failure, the gate gets reverted
within a day, and the repo ends up believing it is guarded while being less guarded than before
— textbook OSCR (over-Complicate → abandon → over-Simplify back to nothing). Fix first, gate
second works: the backlog collapses by 91% and the gate becomes affordable on the same afternoon.
The canonical case is equally forced: lowercase already exists on disk and 563 references assume it; making git agree costs 2 index operations, while making the references agree costs edits across 327 files. There is no third option that is not strictly worse.
Green Meadow #1 — how to implement the guard (count = many; 3 examples)#
Any of these stays in ZION; the choice is taste, not safety.
A single
scripts/check-paths.pychained into the existingmake linttarget — recommended, becauselintalready chainsrstcheck+check-bib.py, so this adds a member to an existing family rather than a new mechanism.A git
pre-commithook — faster feedback, but hooks are per-clone and silently absent on a fresh clone, so it cannot be the only line of defence.A standalone CI step in the deploy workflow — catches everything, but only after push, and the reusable workflow lives in an external repo (
balosp/llol-build), so changing it is a cross-repo edit.
Grey Edge #1 — HELL archive links#
Whether a dead link inside a frozen MockupModel or an append-only llog is a defect or a faithful
record cannot be settled from the code. Under StayVS and the append-only llog rule, editing them
looks like falsifying history; leaving them looks like tolerating rot. Proposal: exempt
``source/matheology/hell/`` from the link guards by default (with an opt-in --include-hell
flag for auditing), and record the exemption as a deliberate decision rather than an oversight.
Flagged as Grey Edge because a BABL reading is available in both directions.
BABL Danger — the framing “so I don’t have to worry about them any more”#
BABL Danger: over-Reach in the request framing. No finite set of checks can close an open system (Language Rule 4). Any plan that promises “you never have to worry about this class again” is selling closure it cannot deliver, and the confidence it buys is exactly what let the current bug run for months: the build was green, therefore it was assumed fine.
The ZION-shaped version of the goal is narrower and actually achievable: make this class of failure loud and cheap instead of silent and expensive. Concretely — a fault that today requires someone to curl the live site becomes a one-second local check that names the file. That is a real and large gain. It is not immunity. The residual risks that stay uncovered are named explicitly in Part 4 rather than papered over, and the recommended guards therefore report rather than guarantee.
BABL Danger — scope bundling in the incoming brief#
BABL Danger: over-Reach by bundling. The brief packs a live-fire URL fix (Steps 1–4: zero editorial judgement, deployable in under an hour) together with a new-page drafting job, an information-architecture change, and a duplicate-content decision (Steps 5–7: several blocking questions to LLoL, no urgency). Bundled, the urgent fix waits on the editorial decisions. The brief itself sets the right precedent by carving out the matheo 404 as separate — that same reasoning applies one step earlier. Recommendation: split into two branches. Steps 1–4 + guards ship now; Steps 5–7 proceed at their own pace.
Not-BABL notes#
Steps 5a and 7 of the brief are notably ZION-shaped and worth keeping verbatim: refusing to invent licence text for CyLi (invented authority), and refusing to duplicate the JoLi intro across two URLs (drift + StayVS violation). Both should survive into the final plan unchanged.
Part 4 — The plan#
Tier 1 — Stop the bleeding (no editorial decisions; ~1 hour)#
Branch off main, e.g. fix/path-case-and-link-guards.
Park the uncommitted patch first (BREACH 1).
git checkout -- source/license/— the 2026m07d30 workaround is reverted by Step 2 of the brief anyway. Leave the two untracked b16 PDFs untouched.Fix the index as the brief specifies (
git rm -r --cached+git add). Commit alone, sogit diff --cached --stat -Mshows 6 pure renames, 0 insertions, 0 deletions — now achievable.Re-apply the intended Step 2 edits as a separate commit: lowercase
joli/indexrefs insource/license/index.rst, collapse the twoTODO AAblocks there and the CASE BUG item insource/license/joli/index.rst, replaced by a one-line dated note.``_redirects`` for the 7 published capitalised URLs (StayVS). Cloudflare Pages does support
:splat, but this file uses none today, so the low-risk choice is to enumerate the 7 URLs explicitly and skip an untested syntax on a live fix. Splat can follow later if wanted.``Cycrus`` → ``Cyrus``, 6 lines, 2 files,
locale/confirmed clean.Merge to
main→ deploy. This also clears the separate/en/study/matheo/overview-christian/404, as the brief predicted.
Tier 2 — One guard family, wired into make lint (the real deliverable)#
A single scripts/check-paths.py with three checks, chained into the existing lint target
next to rstcheck and check-bib.py:
Check A — index/disk case drift. The prior agent’s 8a script, adopted essentially as written; it is correct and it found the bug.
Check B — hardcoded self-URLs. Every
balospe.com/<lang>/...insource/must resolve case-exactly to a source file. Nothing checks this surface today (596 occurrences).Check C — ``:doc:`` resolved against git’s docname view, not the filesystem’s. This is the check that makes the macOS blind spot visible without a build, and the only one that would have caught this bug locally on day one.
Default scope excludes source/matheology/hell/ (Grey Edge #1) with an --include-hell flag
for audits. Documented by extending AHA/linting.md — not a new AHA file.
Tier 3 — Gate the build on warnings (only after Tier 1)#
Correcting BREACH 2: the lever is -W, not -n. Staged so it cannot be abandoned:
After Tier 1, capture the real warning count from one full build.
Triage the ~14 live
:doc:residue (list in Part 2).Turn on
-W --keep-goingin CI only at first, leaving localmake devungated so day-to-day writing is not slowed.
Tier 4 — Build out /license/ (separate branch, LLoL’s decisions)#
Steps 5–7 of the brief, unbundled. The brief’s own recommendations on 5a (no invented licence text) and 7 (do not duplicate) are endorsed and should stand.
Part 5 — Decisions needed from LLoL#
The b16 branch — how should
b16-revision-for-expert-reviewbe handled while a fix branch is cut frommain? (Leave it parked / merge it first / something else.)Split or bundle — ship Tier 1 + Tier 2 now and defer Tier 4, or do everything on one branch? (Recommendation: split.)
HELL exemption — exempt
source/matheology/hell/from the link guards by default? (Recommendation: yes, per Grey Edge #1.)Navigation — Step 6 options (a) The Background / (b) captioned Reference group / (c) Get Involved.
CyLi scope — the brief’s three questions (scope, relation to JoLi, reusability), needed only when Tier 4 starts.
Recorded as an AnyAims item pending LLoL’s answers; nothing in Tiers 1–4 has been executed.
Summary of this session#
The prior agent’s core diagnosis HELD in full and was reproduced independently: 2 case mismatches out of 2141 tracked paths, all 8 live-site status codes as reported, the
Cycrustypo exactly as listed, the deploy path and branch state as described.4 BREACHes were found in the brief: (1) Step 0 and Step 1 contradict each other via the uncommitted patch; (2) Step 8b names
-nwhere-Wis the correct lever; (3) the/ff/file count is 32, not 25; (4) the blast radius is 563 dead:doc:occurrences, larger than the brief’s own framing.The full class was measured: sub-class A = 2 broken, sub-class B = 1 case + 5 stale (4 of them legitimately historical), sub-class C = 620 → ~14 live after the case fix.
Knife Edge #1: fix-then-gate is the only viable order; gating first guarantees abandonment.
BABL Danger flagged twice: the “never worry again” framing over-Reaches (no finite check set closes an open system), and the brief over-Reaches by bundling an urgent URL fix with an unhurried editorial build-out.
Nothing was executed. No source content file was modified. This llog is the only file written.