Tooling, manifest, and content polish
- import-photo.sh deletes the copied JPEG when EXIF stripping fails, so the auto-commit can never publish GPS/serial metadata (AUDIT §4.11) - pre-commit-marks hook: tab-aware path parsing, probes the staged blob rather than the working tree (§4.11) - preset-signing-passphrase uses printf; stamp-build-time writes via temp + os.replace; archive.py passes -- to pdftotext and verifies the vendored monolith binary against its recorded sha256 (mismatch is fatal, consistent with the tool's integrity contract); extract-exif ./-prefixes relative paths (§4.11) - blog-post.html: id="similar-links"/"backlinks" each appear once; rendered output unchanged (§6.4) - site.webmanifest: start_url/scope/description added, maskable icon purpose restored alongside any (§9.3) - Frontmatter cleanup: scaffold comments out of scaling_outage, dangling null confidence-history keys removed (populated ones kept), dead modified: key dropped from colophon (§6.4) - canto31.jpg: 4.0 MB -> 1.9 MB (2400px, q80, grayscale — the source is a monochrome Doré engraving, so single-channel is colorimetrically lossless); webp sidecar regenerated (§6.4, prior-audit §6.1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
56afdb867a
commit
9f61ce5949
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
title: Colophon
|
||||
date: 2026-03-21
|
||||
modified: 2026-04-27
|
||||
status: "Durable"
|
||||
confidence: 93
|
||||
tags: [meta]
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ importance: 1
|
|||
scope: personal
|
||||
novelty: conventional
|
||||
practicality: moderate
|
||||
confidence-history:
|
||||
---
|
||||
|
||||
A fuller write-up follows. In the meantime, see the [projects index](/cv/projects/).
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ evidence: 4
|
|||
scope: broad
|
||||
novelty: innovative
|
||||
practicality: high
|
||||
confidence-history:
|
||||
---
|
||||
|
||||
A fuller write-up follows with the clinical-implications manuscript. In the meantime, see the [projects index](/cv/projects/).
|
||||
|
|
|
|||
|
|
@ -1,23 +1,20 @@
|
|||
---
|
||||
title: "Speculative Reluctance"
|
||||
date: 2026-04-15 # required; used for ordering, feed, and display
|
||||
abstract: > # optional; shown in the metadata block and link previews
|
||||
date: 2026-04-15
|
||||
abstract: >
|
||||
AI labs are likely deliberately reluctant to scale because they are aware that any imminient shift to locally run models as the norm would render their compute redundant. We take Anthropic as a principal case study to validate this hypothesis.
|
||||
tags: # optional; see Tags section
|
||||
tags:
|
||||
- ai
|
||||
- tech
|
||||
- speculative
|
||||
- open
|
||||
|
||||
# Epistemic profile — all optional; the entire section is hidden unless `status` is set
|
||||
status: "Draft" # Draft | Working model | Durable | Refined | Superseded | Deprecated
|
||||
confidence: 55 # 0–100 integer (%)
|
||||
importance: 3 # 1–5 integer (rendered as filled/empty dots ●●●○○)
|
||||
evidence: 1 # 1–5 integer (same)
|
||||
scope: broad # personal | local | average | broad | civilizational
|
||||
novelty: moderate # conventional | moderate | idiosyncratic | innovative
|
||||
practicality: high # abstract | low | moderate | high | exceptional
|
||||
confidence-history: # list of integers; trend arrow derived from last two entries
|
||||
status: "Draft"
|
||||
confidence: 55
|
||||
importance: 3
|
||||
evidence: 1
|
||||
scope: broad
|
||||
novelty: moderate
|
||||
practicality: high
|
||||
---
|
||||
|
||||
Running a lab that develops frontier LLMs is somewhat like playing a game that, by all measurable metrics external, you are bound to lose. The amount of compute required to train a frontier LLM is unbelievably expensive. The expense of inference is even more astronomical. OpenAI claims at the time of this writing to have somewhere between 900 Million and 1 Billion active users, all of whom require some amount of inference cost, and some small subset of whom consume an enormous amount of compute - to use their words, this is ["commercial scale."](https://openai.com/index/accelerating-the-next-phase-ai/). This isn't to mention the immense amount of competition - there are many major players in the United States alone contributing models that push the boundaries. OpenAI may have been the first, but Anthropic, Google, Meta, xAI, and, yes, even Amazon and Bytedance are following right along.
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ evidence: 5
|
|||
scope: civilizational
|
||||
novelty: innovative
|
||||
practicality: moderate
|
||||
confidence-history:
|
||||
---
|
||||
|
||||
There are at least two distinct ways to reduce the search space over which AGI^[The definition of "Artificial General Intelligence", or whether such a definition exists, is contentious. My use of the term is not intended to endorse any proposed timeline for AGI, nor to suggest that it is inevitable. It is rather to provide calibration through a hypothetical goal that clearly justifies pursuit.] will have to operate. The first involves a harmonious interaction of agent and human, not transactional in origin, not fully autonomous nor fully human-driven, but rather collaborative in nature - the agent augments the capacity of the human, just as any other good tool for thought does, by working within the scope of something well specified and ideated upon. This is not to say that the agent cannot have a place in such planning, but rather that the human is ultimately the driver of the actions and tasks, defining the scope of what is to be done in as much detail as possible without being the one to actually do it.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ importance: 1
|
|||
scope: local
|
||||
novelty: moderate
|
||||
practicality: low
|
||||
confidence-history:
|
||||
---
|
||||
|
||||
A fuller write-up follows. In the meantime, see the [projects index](/cv/projects/).
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 4.0 MiB After Width: | Height: | Size: 1.8 MiB |
|
|
@ -1,6 +1,9 @@
|
|||
{
|
||||
"name": "Levi Neuwirth",
|
||||
"short_name": "ln",
|
||||
"description": "Personal site of Levi Neuwirth — essays, research, music, and photography.",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/web-app-manifest-192x192.png",
|
||||
|
|
@ -8,11 +11,23 @@
|
|||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/web-app-manifest-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/web-app-manifest-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/web-app-manifest-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"theme_color": "#16140f",
|
||||
|
|
|
|||
|
|
@ -17,24 +17,27 @@
|
|||
$body$
|
||||
$if(backlinks)$
|
||||
<footer class="page-meta-footer">
|
||||
$else$
|
||||
$if(similar-links)$
|
||||
<footer class="page-meta-footer">
|
||||
$endif$
|
||||
$endif$
|
||||
$if(backlinks)$
|
||||
<div class="meta-footer-full meta-footer-backlinks" id="backlinks">
|
||||
<h3>Backlinks</h3>
|
||||
$backlinks$
|
||||
</div>
|
||||
$endif$
|
||||
$if(similar-links)$
|
||||
<div class="meta-footer-full meta-footer-similar" id="similar-links">
|
||||
<h3>Related</h3>
|
||||
$similar-links$
|
||||
</div>
|
||||
$endif$
|
||||
$if(backlinks)$
|
||||
</footer>
|
||||
$else$
|
||||
$if(similar-links)$
|
||||
<footer class="page-meta-footer">
|
||||
<div class="meta-footer-full meta-footer-similar" id="similar-links">
|
||||
<h3>Related</h3>
|
||||
$similar-links$
|
||||
</div>
|
||||
</footer>
|
||||
$endif$
|
||||
$endif$
|
||||
|
|
|
|||
|
|
@ -270,7 +270,10 @@ def extract_text_pdf(pdf: Path, txt: Path) -> None:
|
|||
"""Extract plain text from `pdf` into `txt` via pdftotext. On any
|
||||
failure an empty file is written so downstream steps still find it."""
|
||||
try:
|
||||
subprocess.run(["pdftotext", "-q", str(pdf), str(txt)], check=True)
|
||||
# `--` ends option parsing so a slug starting with `-` cannot be
|
||||
# mistaken for a pdftotext option.
|
||||
subprocess.run(["pdftotext", "-q", "--", str(pdf), str(txt)],
|
||||
check=True)
|
||||
except (subprocess.CalledProcessError, FileNotFoundError) as exc:
|
||||
err(f"{pdf.name}: pdftotext failed ({exc}); writing empty text sidecar")
|
||||
txt.write_text("", encoding="utf-8")
|
||||
|
|
@ -292,6 +295,51 @@ def find_monolith() -> str | None:
|
|||
return shutil.which("monolith")
|
||||
|
||||
|
||||
MONOLITH_VERSION_FILE = REPO_ROOT / "tools" / "monolith-version.txt"
|
||||
|
||||
# Binaries already verified this run — the pin check hashes the binary
|
||||
# once, not once per snapshot.
|
||||
_monolith_verified: set[str] = set()
|
||||
|
||||
|
||||
def _pinned_monolith_sha256() -> str | None:
|
||||
"""Parse the `sha256 = <hex>` line from tools/monolith-version.txt.
|
||||
Returns None when the file is missing or unparseable (the caller
|
||||
warns and continues — only a *mismatch* is fatal)."""
|
||||
try:
|
||||
text = MONOLITH_VERSION_FILE.read_text(encoding="utf-8")
|
||||
except OSError:
|
||||
return None
|
||||
m = re.search(r"^\s*sha256\s*=\s*([0-9a-fA-F]{64})\s*$",
|
||||
text, re.MULTILINE)
|
||||
return m.group(1).lower() if m else None
|
||||
|
||||
|
||||
def verify_monolith(mono: str) -> None:
|
||||
"""Integrity gate for the snapshot tool itself: the binary that
|
||||
produces committed artifacts must match the SHA-256 pinned in
|
||||
tools/monolith-version.txt. A mismatch is an integrity error (print
|
||||
loudly, exit non-zero, halt `make build`); a missing or unparseable
|
||||
version file is a warning only."""
|
||||
if mono in _monolith_verified:
|
||||
return
|
||||
pinned = _pinned_monolith_sha256()
|
||||
if pinned is None:
|
||||
print(f"[archive] WARNING: {MONOLITH_VERSION_FILE.name} is missing "
|
||||
f"or has no parseable `sha256 = …` line — monolith binary "
|
||||
f"integrity NOT verified ({mono})", file=sys.stderr)
|
||||
_monolith_verified.add(mono)
|
||||
return
|
||||
live = sha256_of(Path(mono))
|
||||
if live != pinned:
|
||||
err(f"monolith binary {mono} fails SHA-256 verification "
|
||||
f"(pinned {pinned}, found {live}). The snapshot tool's bytes "
|
||||
f"do not match tools/monolith-version.txt — re-vendor the "
|
||||
f"binary or update the pin (see that file's instructions).")
|
||||
sys.exit(1)
|
||||
_monolith_verified.add(mono)
|
||||
|
||||
|
||||
def body_noarchive(path: Path) -> bool:
|
||||
"""True if the snapshot declares <meta name=robots ... noarchive> —
|
||||
the in-document equivalent of the X-Robots-Tag header."""
|
||||
|
|
@ -356,6 +404,7 @@ def fetch_html(url: str, dest: Path) -> bool:
|
|||
f"tools/bin/monolith (see tools/monolith-version.txt) or set "
|
||||
f"$MONOLITH_BIN; HTML snapshot skipped")
|
||||
return False
|
||||
verify_monolith(mono)
|
||||
|
||||
source = dest.with_suffix(dest.suffix + ".source.part")
|
||||
tmp = dest.with_suffix(dest.suffix + ".part")
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ images are logged and the rest of the walk continues.
|
|||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
|
@ -133,6 +134,12 @@ def _read_exif_via_exiftool(image: Path) -> dict[str, Any]:
|
|||
entry. Numeric values come through as numbers; text values as
|
||||
strings. We accept missing keys silently.
|
||||
"""
|
||||
# exiftool does not reliably support `--` as an end-of-options
|
||||
# marker, so make the path argument non-option-shaped instead: a
|
||||
# relative path is prefixed with ./ so it can never start with `-`.
|
||||
image_arg = str(image)
|
||||
if not os.path.isabs(image_arg):
|
||||
image_arg = os.path.join(os.curdir, image_arg)
|
||||
result = subprocess.run(
|
||||
[
|
||||
"exiftool",
|
||||
|
|
@ -156,7 +163,7 @@ def _read_exif_via_exiftool(image: Path) -> dict[str, Any]:
|
|||
"-ImageWidth",
|
||||
"-ImageHeight",
|
||||
"-n", # numeric output for shutter/aperture/GPS/dimensions
|
||||
str(image),
|
||||
image_arg,
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
|
|
|
|||
|
|
@ -20,9 +20,11 @@
|
|||
set -u
|
||||
|
||||
# Newly-added .md files under content/essays/ in this commit.
|
||||
# `--name-status` output is TAB-separated (status<TAB>path); split on the
|
||||
# tab so paths containing spaces survive intact.
|
||||
mapfile -t added < <(
|
||||
git diff --cached --name-status --diff-filter=A -- 'content/essays/*.md' \
|
||||
| awk '{ print $2 }'
|
||||
| cut -f2-
|
||||
)
|
||||
|
||||
if [[ ${#added[@]} -eq 0 ]]; then
|
||||
|
|
@ -47,8 +49,10 @@ for path in "${added[@]}"; do
|
|||
# Best-effort frontmatter probe: does any line in the YAML head
|
||||
# block start with `status:`? Avoids a YAML dependency in the
|
||||
# hook, which has to run before the build environment is sourced.
|
||||
if awk '/^---$/{f++; next} f==1 && /^status:[[:space:]]*[^[:space:]]/{print; exit}' \
|
||||
-- "$path" \
|
||||
# Probe the STAGED blob (`git show :path`), not the working tree —
|
||||
# the commit contains the index content, which may differ.
|
||||
if git show ":$path" 2>/dev/null \
|
||||
| awk '/^---$/{f++; next} f==1 && /^status:[[:space:]]*[^[:space:]]/{print; exit}' \
|
||||
| grep -q .; then
|
||||
has_status=1
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -148,7 +148,14 @@ fi
|
|||
|
||||
echo "import-photo: stripping EXIF from delivered file..."
|
||||
magick mogrify -strip "$TARGET" \
|
||||
|| { echo "import-photo: magick mogrify -strip failed for $TARGET (EXIF NOT stripped)" >&2; exit 1; }
|
||||
|| {
|
||||
# The copy under content/ still carries full EXIF (GPS, serial
|
||||
# numbers); the Makefile's `git add content/` could auto-commit
|
||||
# and publish it. Remove it before bailing out.
|
||||
rm -f -- "$TARGET"
|
||||
echo "import-photo: magick mogrify -strip failed for $TARGET (EXIF NOT stripped); deleted the copied target so the EXIF-laden JPEG cannot be auto-committed" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Step 4: extract palette (does its own walk; idempotent on already-done photos)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@ echo -n "Signing subkey passphrase: "
|
|||
read -rs PASSPHRASE
|
||||
echo
|
||||
|
||||
echo -n "$PASSPHRASE" | GNUPGHOME="$GNUPGHOME" "$GPG_PRESET" --homedir "$GNUPGHOME" --preset "$KEYGRIP"
|
||||
# printf, not `echo -n`: a passphrase starting with -e/-n/-E would be
|
||||
# eaten as an echo option.
|
||||
printf '%s' "$PASSPHRASE" | GNUPGHOME="$GNUPGHOME" "$GPG_PRESET" --homedir "$GNUPGHOME" --preset "$KEYGRIP"
|
||||
|
||||
echo "Passphrase cached for keygrip $KEYGRIP (24 h TTL)."
|
||||
echo "Test: GNUPGHOME=$GNUPGHOME gpg --homedir $GNUPGHOME --batch --detach-sign --armor --output /dev/null /dev/null"
|
||||
|
|
|
|||
|
|
@ -49,8 +49,19 @@ def stamp_file(path: str, replacement_bytes: bytes) -> bool:
|
|||
data,
|
||||
)
|
||||
if count and new_data != data:
|
||||
with open(path, "wb") as f:
|
||||
# Write to a sibling temp file and os.replace so an interrupt
|
||||
# mid-write never leaves a truncated deployed HTML file.
|
||||
tmp = path + ".stamp-tmp"
|
||||
try:
|
||||
with open(tmp, "wb") as f:
|
||||
f.write(new_data)
|
||||
os.replace(tmp, path)
|
||||
except BaseException:
|
||||
try:
|
||||
os.unlink(tmp)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
raise
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue