CommunitySchreiben & Editierengithub.com

buildinternet/releases

Registry für Produkt-Release-Notizen, entwickelt für KI-Agenten. Rufe aktuelle Produkt-Changelogs über MCP und CLI ab.

Was ist releases?

Dieses Skill-Paket bietet eine lokale Alternative zum standardmäßigen Remote-Fetch-Pfad für den 'buildinternet/releases'-Workflow. Anstatt den server-seitigen Extraktionsprozess zu durchlaufen, der AI-Token für die Verarbeitung jedes Eintrags verbraucht, ermöglicht es einem lokalen Claude-Code-Agenten, Changelog-Seiten selbstständig abzurufen, zu parsen und strukturierte Release-Notizen zu generieren. Der Agent fungiert dabei selbst als Extraktor und postet die aufbereiteten Daten in einem Batch-Verfahren an einen deterministischen Endpunkt, der ohne AI-Unterstützung auskommt. Dieses Vorgehen ist speziell für das Onboarding neuer Quellen oder das Backfilling vorhandener Einträge optimiert und vermeidet die Kosten und Latenzen einer Remote-Extraktion. Es setzt zwingend eine lokale Claude-Code-Umgebung voraus und ist in Managed-Agent-Sessions nicht einsetzbar. Das Skill-Paket umfasst eine umfassende Anleitung von der Preflight-Prüfung (Opt-out via robots.txt) über die Seitenklassifizierung bis hin zur Batch-Upsert-Validierung, inklusive der Unterstützung für das parallele Verarbeiten vieler Seiten durch Sub-Agenten.

Funktioniert mit✓Claude Code~Codex CLI~Cursor
npx skills add buildinternet/releases

Installed? Explore more Schreiben & Editieren skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Lokale Erfassung (Local Ingest)

Onboarden eines Changelogs ohne den Remote-Fetch-Pfad zu durchlaufen. Der Remote-Pfad (releases admin source fetch → POST /v1/workflows/update → der DeterministicUpdateWorkflow des API-Workers, #1946) führt die Extraktion von Body → Datensätzen serverseitig durch (inkrementelles Haiku oder die Tool-Schleife) und berechnet diese Inferenz — selbst wenn dabei null Releases geschrieben werden. Ein lokaler Claude-Code-Agent ist bereits ein leistungsfähiges Modell mit Web-Fetch- und Datei-Tools. Daher kann er die seitenweise Extraktion inline durchführen (in die laufende Session integriert) und benötigt nur einen deterministischen Schreibpfad.

Kernprinzip: Der Agent ist der Extraktor. Du rufst ab, du parse, du strukturierst die Datensätze, und du POSTest sie an den Batch-Endpunkt, der einen einfachen idempotenten Upsert mit keiner AI beim Einfügen durchführt. Die Remote-Extraktionsschleife wird nie ausgeführt.

Nur für lokales Claude Code. Dieses Skill verwendet das Agent-Tool für Sub-Agent-Fan-Out und setzt ein persistentes lokales Dateisystem sowie die RELEASES_API_*-Umgebungsvariablen der CLI voraus. Es ist zusammen mit seeding-playbooks ein reines Operator-Skill für den lokalen Einsatz — es wird nicht in der Managed-Agent-Flotte bereitgestellt.

Kostenkalkulation (Cost contract)

Teile dies dem Operator vor einem großen Durchlauf explizit mit und halte dich daran:

  • Ausgaben: die eigenen Token deines Agenten für Fetch + Extraktion sowie die Token aller Sub-Agenten, die du ausrollst (deine Modellwahl — sonnet für Qualität, haiku für Massenverarbeitung).
  • Gibt KEINE Ausgaben: keine serverseitige Extraktion (die inkrementelle Haiku- / Tool-Schleife des Update-Workflows), kein Remote-web_fetch-Extraktion. POST /v1/workflows/update wird nie aufgerufen.
  • Der Batch-Endpunkt führt beim Einfügen keine AI aus — keine Zusammenfassung, kein Marketing-Klassifizierer, keine Feed-Anreicherung. Er führt einen insert plus einen Fire-and-Forget-Vektor-Embed (waitUntil) durch. Die AI-Felder (title_generated / title_short / summary) werden absichtlich leer gelassen; siehe Manuelle Anreicherung unten für die bewussten Folgepfade.

Das negative Signal, das bestätigt, dass kein Remote-Durchlauf stattgefunden hat: in Axiom releases-cloudflare-logs gibt es kein POST .../fetch?sessionId=det-… und kein extract-deps-worker-Ereignis für die Quelle.

Wann verwenden

  • Onboarding einer neuen Quelle (oder Backfilling einer vorhandenen) interaktiv in Claude Code, und du möchtest die Remote-Extraktionskosten vermeiden.
  • Ein Remote-Fetch hat eine vollständige Extraktionsschleife verbraucht und 0 Releases geschrieben (der Conductor / Large-Body / max_tokens-Fehlermodus) — das Inline-Extrahieren umgeht die Schleife vollständig.
  • Die Seite ist mit deinen eigenen Web-Tools abrufbar (die folgende Ablehnung ist eine Policy-Einschränkung, kein Fetch-Limit).

Wann NICHT verwenden:

  • Du befindest dich in einer Managed-Agent-Session. Dies ist nur lokal verfügbar (Agent-Tool-Fan-Out + lokales Dateisystem + CLI-Umgebung). MA-Sessions sollten den normalen Fetch-Pfad verwenden.
  • Die Quelle hat einen sauberen, inhaltsreichen Feed. Füge sie einfach hinzu (managing-sources) und lass die Cron-Pipeline sie abrufen. Local Ingest ist für Fälle gedacht, in denen der Agent, der die Extraktion inline durchführt, das richtige Werkzeug ist, kein Ersatz für Feed-/GitHub-Adapter.
  • Der Herausgeber widerspricht (Preflight lehnt ab — siehe Schritt 1).

Workflow

Schritt 1 — Preflight (zwingende Sicherheitsprüfung)

Führe die Opt-out-Prüfung durch, bevor du etwas abrufst oder schreibst:

bun .claude/skills/local-ingest/preflight.ts <url> --json

Es ruft robots.txt ab (mit apex→www-Weiterleitungen), parst die Cloudflare Content Signals-Policy und bewertet anhand des Exit-Codes:

ExitErgebnisAktion
0proceedErlaubend oder abwesend — fortfahren.
1refuseai-input=no deklariert. STOP. Dem Benutzer mitteilen. Nicht abrufen oder schreiben.
2unknownrobots.txt konnte nicht abgerufen/geparst werden. Einmal wiederholen; falls immer noch unbekannt, dem Benutzer mitteilen — nicht von Fortfahren ausgehen.

Die Hürde betrifft nur ai-input. Local Ingest ist die Aufnahme von Such-/Eingabe-Daten — die Befüllung eines Suchindex —, was genau das ist, was ai-input regelt. ai-train=no verweigert das Training von Modellen, eine Nutzungsart, die dieser Pfad nicht durchführt; daher wird ai-train=no allein (z. B. vercel.com) korrekt mit proceed bewertet.

Dies ist die Lektion von Conductor. conductor.build liefert Content-Signal: ai-train=no, search=yes, ai-input=no — seine Organisation/Quelle ist bereits aus genau diesem Grund im Registry angehalten (releases admin playbook conductor). Ein lokales Onboarding darf nicht stillschweigend Tokens ausgeben oder Inhalte gegen einen Publisher-Opt-out erfassen. conductor.build muss abgelehnt werden (über sein ai-input=no) — es ist das Regressionsziel für diese Hürde.

Die Ablehnung ist eine Policy-Entscheidung, keine technische Einschränkung: web_fetch und CF Browser Rendering können diese Seiten trotzdem abrufen (nur Cloudflares /crawl-Endpunkt hat das Signal für Conductor hart durchgesetzt). Halte dich trotzdem an den Opt-out. Überschreibe ihn nur mit expliziter Genehmigung des Herausgebers, die vom Betreiber dokumentiert wurde — es gibt keine stillschweigende Umgehung.

Das Preflight gibt auch alle Sitemap:-URLs aus robots.txt aus — behalte sie für Schritt 3.

Schritt 2 — Ziel auflösen

Stelle sicher, dass Organisation und Quelle existieren, bevor du schreibst — Releases werden an eine source_id angehängt. Greife auf managing-sources für die Erstellungs-/Benennungs-/Primärregeln und auf finding-changelogs zurück, um zu prüfen, ob ein strukturierter Feed existiert (wenn ja, bevorzuge die normale Pipeline). Dann:

  • Lies das Playbook der Organisation für Parse-Hinweise: releases admin playbook <org> (siehe managing-sources → Playbooks).
  • Erfasse den sourceSlug und die src_…-ID aus der erstellten/aufgelösten Zeile — du benötigst einen von beiden für die Batch-URL.

Wenn die Quelle brandneu ist, erstelle sie (z. B. releases admin source create "<name>" --url <url> --org <org> [--primary]), aber löse keinen Fetch aus — das würde den Remote-Update-Workflow auslösen, den du vermeiden möchtest.

Schritt 3 — Seiten zuordnen

Klassifiziere die Seitenform (releases admin discovery evaluate <url> --json meldet pageStructure: single-page | index | unknown):

  • Einseitiger Changelog → ein Fetch in Schritt 4.
  • Index → Detail → zähle die URLs pro Release auf. Bevorzuge /sitemap.xml (wurde vom Preflight gefunden), gefiltert auf den Changelog-Pfad; andernfalls parse das Index-HTML auf Links zu einzelnen Releases.
  • Client-seitig gerenderter Index → der Index ist eine JS-Hülle, deren Karten über einen Client-seitigen Fetch geladen werden. Daher liefern curl / Nicht-JS-Scraping null Einträge. Zähle sie durch Rendern auf: durchlaufe den Index mit Claude in Chrome (oder rolle einen rendernen Sub-Agenten aus), paginiere, wenn die Liste gefenstert ist, und sammle die Links zu den einzelnen Releases. Überprüfe dann, ob die Detail-Seiten statisch gerendert sind — das sind sie oft, auch wenn der Index es nicht ist. Wenn ja, rufe diese massenhaft mit curl direkt ab (HTML→Markdown lokal, kein Rendern, keine AI-Kosten). Harveys help.harvey.ai/release-notes ist genau das: Client-seitig gerenderter Index, statische Detailseiten. Ziehe nicht den Schluss "keine Releases" aus einem leeren Nicht-JS-Scrape des Index.

Begrenze auf ein aktuelles Fenster (Standard ~25–50 aktuellste Releases) und protokolliere, was du übersprungen hast — niemals stillschweigend abschneiden. Wiederholungen sind idempotent, daher fügt ein späteres tieferes Backfilling einfach Zeilen hinzu.

Schritt 4 — Abrufen + Extrahieren

Verwende die Konventionen von parsing-changelogs für Typ/Version/Datum/Medien (siehe Parität unten).

  • Wenige Seiten: rufe ab und extrahiere inline in diesem Agenten.
  • Viele Seiten: rolle parallele Sub-Agenten aus — jeder einen URL-Abschnitt, jeder gibt Datensätze im Batch-Schema zurück. Verwende das Agent-Tool-Dispatch-Muster aus seeding-playbooks (und superpowers:dispatching-parallel-agents für die Fan-Out-Disziplin). Rufe keine Release-URLs im Parent-Agenten ab — delegiere, um den Kontext des Parent sauber zu halten.
  • Parent-saves-Vorbehalt: Sub-Agenten können möglicherweise keinen Batch-Write durchführen (Berechtigungsbeschränkungen). Plane dafür — lasse jeden Sub-Agenten seine Datensätze zurückgeben, und lass den Parent (dich) die Writes in Schritt 5 durchführen. Gleiches Muster, das seeding-playbooks für das Speichern von Notizen verwendet.
  • Extrahiere Medien — sende nicht nur Text. Ziehe die Bilder/Videos aus jedem Release-Body und fülle media, entferne _next/image / _vercel/image Optimizer-Wrapper zur zugrunde liegenden CDN-URL (normalizeMediaUrl aus packages/rendering/src/media-url.ts). Ein reiner Text-Durchgang ist ein häufiger Fehler, der erst bei der Überprüfung durch den Betreiber auffällt (Harvey hat nur Text gesendet und musste dann _next/image → cdn.sanity.io neu extrahieren und neu befüllen); das erneute POST zur Anreicherung erfolgt mit mode: "upsert-content" (Schritt 5).

Jeder Datensatz muss dem Batch-Schema (siehe Schritt 5) und den Paritätsregeln entsprechen.

Optional — deterministische Extraktion (Ansatz A, nur im Monorepo): anstatt von Hand zu extrahieren, kannst du die Produktions-Extraktionsbibliotheken in packages/adapters/src/extract/ aufrufen — extractFromBody, extractWithTools, runDirectFetchExtraction. ExtractDeps.cloudflare ist nullable und repo kann ein Noop sein, sodass sie nur mit einem Anthropic-Key ausgeführt werden (Beweis: scripts/smoke-toolloop.ts). Dies tauscht Agent-Tokens gegen direkte Anthropic-API-Tokens, reproduziert jedoch die Produktions-Tool-Schleife exakt. Diese Pakete sind private: true — nur aus dem Monorepo erreichbar, niemals aus der schlanken CLI. Der Standard bleibt Ansatz B (der Agent ist der Extraktor).

Schritt 5 — Schreiben (Batch-Upsert)

POSTe Datensätze an den Batch-Endpunkt in Blöcken (~50/Request hält Requests klein; der Handler führt das D1-Bind-Limit-Chunking serverseitig durch):

# Verwende den Organisations-spezifischen Pfad. Die bloße Form /v1/sources/:slug akzeptiert NUR eine typisierte
# `src_…`-ID — ein menschlicher Slug liefert dort 400 bare_slug_rejected zurück (Slugs sind
# Organisations-spezifisch, #690). Mit einer typisierten ID funktioniert die bloße Form auch:
#   POST $RELEASES_API_URL/v1/orgs/<orgSlug>/sources/<sourceSlug>/releases/batch
#   POST $RELEASES_API_URL/v1/sources/<src_…>/releases/batch
curl -sS -X POST "$RELEASES_API_URL/v1/orgs/<orgSlug>/sources/<sourceSlug>/releases/batch" \
  -H "Authorization: Bearer $RELEASES_API_KEY" \
  -H "Content-Type: application/json" \
  -d @chunk.json   # { "releases": [ … ] }

Felder pro Release (die tatsächlich akzeptierte Form — workers/api/src/routes/sources.ts, postReleasesBatchHandler):

{
  "version": "1.4.0", // optional; weglassen/null für datumsbasierte oder Blog-Quellen
  "title": "Dark mode", // erforderlich
  "content": "Markdown-Text…", // erforderlich
  "url": "https://…/changelog/dark-mode", // optional, aber IMMER ausfüllen — es ist der Dedup-Key
  "contentHash": "…", // optional
  "publishedAt": "2026-05-30", // optional; ISO-8601 (siehe Parität)
  "media": "https://…/shot.png", // optional
  "type": "feature", // optional; "feature" | "rollup"
  "prerelease": false, // optional
}
  • Idempotent. Upsert auf UNIQUE(source_id, url), füllt, überschreibt nicht: bei einer Kollision werden nur Inhalt/Medien nachgefüllt, wenn die gespeicherte Zeile leer ist. Daher überschreibt eine Wiederholung niemals eine gute Zeile. Sicher wiederholbar und seitenweise durchführbar — gleiche url wird aktualisiert, nicht dupliziert.
  • Anreicherungs-Neu-POST (mode: "upsert-content"). Da der Standard nur füllt, ist ein erneutes POST reicherer Inhalte für eine URL, die bereits Inhalte hat, ein Noop — der Platzhalter bleibt. Für einen bewussten zweiten Durchgang (zuerst Index-Zusammenfassungen, dann vollständige Detailseiten-Bodys + Medien) füge "mode": "upsert-content" auf oberster Ebene des Batch-Bodys hinzu: es überschreibt Inhalt/Medien bei einer URL-Kollision und überspringt den Scrape-Titel-Dedup-Präfilter (#1526). Verwende es nur für gezielte Anreicherung; der Standardpfad bleibt füllend, sodass ein Routine-Neu-Fetch nicht überschreiben kann. Die url muss mit der gesäten Zeile identisch sein — die Anreicherung erfolgt über die URL, eine geänderte URL-Scheme erzeugt stattdessen ein Duplikat. Wenn sich die URL-Scheme geändert hat, säe stattdessen neu: releases release delete --source <src_…> --hard dann ein frisches /batch (Standardmodus).
  • Auth. Erfordert write-Bereich; der statische RELEASES_API_KEY (Root) erfüllt dies. .env lädt RELEASES_API_URL + RELEASES_API_KEY in diesem Repository automatisch.
  • Keine AI beim Einfügen (siehe Kostenkalkulation).

Chunking-Hilfe (liest ein records.json-Array und POSTet in Blöcken von 50):

bun -e '
const orgSlug = process.argv[1], sourceSlug = process.argv[2], file = process.argv[3];
const all = JSON.parse(await Bun.file(file).text());
const base = process.env.RELEASES_API_URL, key = process.env.RELEASES_API_KEY;
for (let i = 0; i < all.length; i += 50) {
  const releases = all.slice(i, i + 50);
  const res = await fetch(`${base}/v1/orgs/${orgSlug}/sources/${sourceSlug}/releases/batch`, {
    method: "POST",
    headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
    body: JSON.stringify({ releases }),
  });
  console.log(`chunk ${i / 50}: ${res.status} ${(await res.text()).slice(0, 200)}`);
}
' <orgSlug> <sourceSlug> records.json

Schritt 6 — Validieren

releases tail <slug> --json   # oder: get_latest_releases (typisiertes Tool)

Bestätige, dass Releases nicht-leere Titel, Daten, Inhalte und Medien haben. Wenn eine Zeile leer ist, korrigiere die Extraktion und wiederhole den Vorgang — der Standard-Upsert füllt leere Zeilen sicher nach. Wenn eine Zeile bereits dünne, aber nicht-leere Inhalte hat (z. B. einen Index-Zusammenfassungs-Platzhalter, den du jetzt durch den vollständigen Body ersetzen möchtest), ist eine einfache Wiederholung ein Noop — verwende den mode: "upsert-content"-Anreicherungs-Neu-POST aus Schritt 5.

Stellst du eine Quelle wieder her, nicht onboards du sie? Dieser Schritt bestätigt nur, was geschrieben wurde — er sagt nichts darüber aus, was nicht geschrieben wurde. Bevor du jemandem mitteilst, dass eine Quelle "wiederhergestellt" ist oder "nichts fehlt", siehe backfilling-sources → Bevor du eine Quelle als "sauber" oder "wiederhergestellt" deklarierst für die erforderliche Live-Seiten-Stichprobenprüfung.

Schritt 7 — Playbook-Notiz

Halte fest, wie diese Quelle erfasst wurde, damit zukünftige Agenten sie nicht neu ableiten. Über manage_playbook (Aktion update_notes) oder releases admin playbook <org> --notes-file -. Notiere, dass die Quelle lokal erfasst wurde (noch nicht auf dem Cron-Pfad), ihre beobachtete Kadenz und alle Extraktions-Besonderheiten. Folge der Playbook-Autorungsrichtlinie von managing-sources (Imperativ, keep-test, ### Fetch instructions / ### Traps / ### Coverage). Schreibe keine …

Verwandte Skills