Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 93 additions & 1 deletion assets/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -255,14 +255,96 @@ body {

.prose em { font-style: italic; }

/* Inline `<code>` — only when NOT inside a <pre>. */
.prose code {
font-family: ui-monospace, "SF Mono", "JetBrains Mono", Consolas, monospace;
font-size: 0.9em;
background: rgba(0, 0, 0, 0.04);
background: rgba(0, 0, 0, 0.045);
padding: 0.1em 0.3em;
border-radius: 3px;
word-break: break-word;
}

/* Fenced code blocks: container is <pre>, child <code> resets the inline
tint/padding so we don't get a "background per line" look. */
.prose pre {
font-family: ui-monospace, "SF Mono", "JetBrains Mono", Consolas, monospace;
font-size: 0.85rem;
line-height: 1.55;
background: #f3efe6; /* warmer than the page (#fbfaf6) */
color: var(--color-text);
border: 1px solid var(--color-rule-bold);
border-radius: 6px;
padding: 0.95rem 1.1rem;
margin: 1.3rem 0;
overflow-x: auto; /* horizontal scroll, no line wrap */
-webkit-overflow-scrolling: touch;
tab-size: 2;
font-variant-ligatures: none;
}
.prose pre code {
background: none;
padding: 0;
border-radius: 0;
font-size: inherit;
line-height: inherit;
color: inherit;
white-space: pre;
word-break: normal;
}

/* Tables — editorial: header row in Inter small caps, hairline row dividers,
tabular numerics for metric columns, horizontal scroll on overflow. */
.prose table {
display: block; /* enables overflow-x on the wrapper */
max-width: 100%;
overflow-x: auto;
border-collapse: collapse;
margin: 1.3rem 0;
font-size: 0.92rem;
font-variant-numeric: tabular-nums;
}
.prose table thead,
.prose table tbody,
.prose table tr { width: 100%; }
.prose th,
.prose td {
text-align: left;
padding: 0.55rem 0.85rem 0.55rem 0;
border-bottom: 1px solid var(--color-rule);
vertical-align: top;
}
.prose th:last-child,
.prose td:last-child {
padding-right: 0;
}
.prose th {
font-family: var(--sans);
font-weight: 600;
font-size: 0.74rem;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--color-muted);
border-bottom: 2px solid var(--color-rule-bold);
padding-bottom: 0.5rem;
}
.prose tbody tr:last-child th,
.prose tbody tr:last-child td {
border-bottom: 0;
}

/* Blockquotes — left rule in accent, italic, slightly muted. */
.prose blockquote {
margin: 1.3rem 0;
padding: 0.2rem 0 0.2rem 1.1rem;
border-left: 3px solid var(--color-accent);
color: var(--color-muted);
font-style: italic;
}
.prose blockquote p:first-child { margin-top: 0; }
.prose blockquote p:last-child { margin-bottom: 0; }
.prose blockquote em { font-style: normal; } /* avoid double-italic on nested em */

/* -------- Footer -------- */

.page-footer {
Expand Down Expand Up @@ -484,6 +566,16 @@ a:focus-visible,
.prose ul, .prose ol, .prose p { orphans: 3; widows: 3; }
.prose li { break-inside: avoid; page-break-inside: avoid; }

/* Code, tables, blockquotes — avoid splitting across pages where possible. */
.prose pre,
.prose table,
.prose blockquote {
break-inside: avoid;
page-break-inside: avoid;
}
.prose pre { font-size: 9pt; background: #f3efe6; }
.prose table { font-size: 9.5pt; }

.prose a[href^="http"]::after {
content: " (" attr(href) ")";
color: #5a5a5a;
Expand Down
11 changes: 11 additions & 0 deletions essays/optimizing-claude-code-cache-read-inflation.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,15 @@ Beispiele:

Der schlechteste Weg ist oft: komplette Datei lesen und dann im Chat "verstehen".

Wer das Werkzeug-Set systematisch aufbauen möchte, findet eine kuratierte und dokumentierte Auswahl unter [`netresearch/coding_agent_cli_toolset`](https://github.com/netresearch/coding_agent_cli_toolset) — ein installierbares Bündel der CLI-Tools, die Coding-Agents in der Praxis tatsächlich Tokens sparen.

Zwei Claude-Skills operationalisieren denselben Gedanken auf Prompt-Ebene, damit Claude die passenden Tools selbstständig auswählt statt zum Default-`Read` zu greifen:

- [`netresearch/file-search-skill`](https://github.com/netresearch/file-search-skill) — strukturierte Code- und Inhaltssuche (Serena-Symbol-Operationen, ripgrep-Pattern-Treffer, AST-Suche), damit Claude den gezielten Treffer holt statt der ganzen Datei.
- [`netresearch/data-tools-skill`](https://github.com/netresearch/data-tools-skill) — Routinen für strukturierte Daten (`jq`, `yq`, `xmlstarlet`, Miller), damit JSON/YAML/XML/CSV gezielt abgefragt statt komplett in den Kontext gezogen werden.

Die CLI-Tools liefern die Grundlage; die Skills liefern das Wissen, wann und wie Claude sie einsetzen soll.

### `/insights` und `/status` nutzen

Fragt regelmäßig:
Expand Down Expand Up @@ -472,6 +481,8 @@ Am Session-Ende lohnt sich eine Mini-Retro:
- Wo ist der Kontext unnötig gewachsen?
- Was sollte als Skill-Verbesserung oder Memory-Regel persistiert werden?

Wer diesen Retro-Loop systematisch betreiben statt nur sporadisch durchführen möchte, kann ihn mit [`netresearch/claude-coach-plugin`](https://github.com/netresearch/claude-coach-plugin) automatisieren — das Plugin sammelt Korrekturen, wiederkehrende Fehler und Tool-Failures während der Session und schlägt am Ende konkrete Skill- oder Memory-Regeln vor. Der Loop „Korrektur → persistierte Regel → bessere nächste Session" wird damit zu einem Teil des Workflows statt zu einer freiwilligen Disziplinübung.

Ziel ist nicht Selbstbeschäftigung. Ziel ist, die nächste Session billiger und präziser zu machen.

---
Expand Down
38 changes: 22 additions & 16 deletions scripts/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,17 +123,24 @@ def render_md(body: str) -> str:
return md.convert(body)


def load_inline_css() -> str:
"""Read assets/style.css, rewrite font URLs for inlining into HTML.

Same trick as the CV repo: source CSS references fonts as
`url("fonts/...")` (relative to the CSS file), but when inlined into
HTML via <style> the browser resolves them relative to the *HTML*.
Rewrite to `url("assets/fonts/...")` so the path matches the
<link rel=preload> hints (which are absolute relative to HTML).
def load_inline_css(font_url_prefix: str) -> str:
"""Read assets/style.css and rewrite font URLs for inlining into HTML.

The on-disk CSS at `assets/style.css` references fonts as
`url("fonts/...")` — relative to the CSS file. When that CSS is
*inlined* into an HTML page via <style>, the browser resolves
`url(...)` relative to the HTML page, not the CSS file. So the
rewrite has to match the HTML's depth on the site:

depth 0 (e.g. /index.html) → font_url_prefix = "assets/fonts/"
depth 1 (e.g. /essays/<slug>.html) → font_url_prefix = "../assets/fonts/"

The preload hints emitted by the template use the same prefix via the
`asset()` helper, so the preloaded font URL matches the @font-face
URL exactly and the preload is reused.
"""
css = (ASSETS / "style.css").read_text(encoding="utf-8")
return re.sub(r'url\((\s*"?)fonts/', r'url(\1assets/fonts/', css)
return re.sub(r'url\((\s*"?)fonts/', rf'url(\1{font_url_prefix}', css)


def load_abbreviations() -> str:
Expand Down Expand Up @@ -228,7 +235,7 @@ def essay_breadcrumb(essay: Essay) -> dict:
}


def render_essay(env: Environment, essay: Essay, abbr_block: str, inline_css: str) -> str:
def render_essay(env: Environment, essay: Essay, abbr_block: str) -> str:
front = essay.front
body_md_with_abbr = essay.body_md + "\n\n" + abbr_block
breadcrumb = essay_breadcrumb(essay)
Expand All @@ -252,13 +259,13 @@ def render_essay(env: Environment, essay: Essay, abbr_block: str, inline_css: st
source_url=essay.source_url,
home_url=f"{SITE_BASE}/",
index_url=f"{SITE_BASE}/",
inline_css=inline_css,
inline_css=load_inline_css("../assets/fonts/"),
asset=lambda p: f"../assets/{p}",
)


def render_index(env: Environment, essays: list[Essay], thesis_html: str,
inline_css: str, build_iso: str) -> str:
build_iso: str) -> str:
jsonld = index_jsonld(essays)
tpl = env.get_template("index.html.j2")
return tpl.render(
Expand Down Expand Up @@ -289,7 +296,7 @@ def render_index(env: Environment, essays: list[Essay], thesis_html: str,
for e in essays
],
build_iso=build_iso,
inline_css=inline_css,
inline_css=load_inline_css("assets/fonts/"),
asset=lambda p: f"assets/{p}",
)

Expand Down Expand Up @@ -322,19 +329,18 @@ def main() -> int:
lstrip_blocks=True,
)

inline_css = load_inline_css()
abbr_block = load_abbreviations()
thesis_html = load_thesis_html()
now = datetime.now(timezone.utc).replace(microsecond=0)
build_iso = now.isoformat()

for essay in essays:
html = render_essay(env, essay, abbr_block, inline_css)
html = render_essay(env, essay, abbr_block)
out = PUBLIC / "essays" / f"{essay.slug}.html"
out.write_text(html, encoding="utf-8")
print(f"wrote {out.relative_to(ROOT)}")

index_html = render_index(env, essays, thesis_html, inline_css, build_iso)
index_html = render_index(env, essays, thesis_html, build_iso)
(PUBLIC / "index.html").write_text(index_html, encoding="utf-8")
print(f"wrote public/index.html ({len(essays)} essays)")
return 0
Expand Down
Loading