docs: move the working agreement to AGENTS.md

The guidance in here is not Claude-specific — it is what this project is,
what it refuses to do, and the rules that are invisible from the code. Any
agent working here needs it, and AGENTS.md is the name they all look for.

CLAUDE.md stays as a pointer so Claude Code still finds it, and holds no
guidance of its own; two copies would drift and the wrong one would be read.
Every cross-reference in the source comments, docs, workflow and skills now
names AGENTS.md, and the image ignores both.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Lucas Winther
2026-08-17 22:16:40 +02:00
co-authored by Claude Opus 5
parent c2740760bd
commit 025605aa09
16 changed files with 375 additions and 362 deletions
+1 -1
View File
@@ -118,7 +118,7 @@ function toAdapter(spec: SourceSpec): Adapter {
// keeps parsers what they are — pure readers of one site's markup.
//
// `sanitizeEvents` logs to console.warn by default, so a repaired or
// dropped event is never silent (CLAUDE.md § Silent drops).
// dropped event is never silent (AGENTS.md § Silent drops).
return sanitizeEvents(parser.parse(html, ctx), {
sourceId: ctx.sourceId,
fallbackUrl: ctx.sourceUrl,
+1 -1
View File
@@ -2,7 +2,7 @@
* robots.txt: parsing, matching, and a per-host cache.
*
* Sources are community wikis and this project's standing rule is to behave as
* a guest would (CLAUDE.md § Scraping conduct). That starts with actually
* a guest would (AGENTS.md § Scraping conduct). That starts with actually
* reading robots.txt rather than assuming a path is fair game.
*
* Parsing is a pure function over text, deliberately separated from fetching,
+1 -1
View File
@@ -16,7 +16,7 @@ import { decodeEntities } from "./html.ts";
* sanitiser's job stops at prose and URLs.
* 2. **Clean, do not drop.** A hostile title is truncated and stripped, not
* rejected — an event vanishing without a trace is the failure mode this
* codebase fears most (CLAUDE.md § Silent drops). The one unrecoverable
* codebase fears most (AGENTS.md § Silent drops). The one unrecoverable
* case is a title that sanitises to nothing, and that emits a note the
* caller is expected to surface.
* 3. **Never throw on junk.** Malformed entities, lone surrogates, absurd code
+4 -4
View File
@@ -3,7 +3,7 @@
*
* Every fetched page is stored verbatim on disk so that re-parsing — which is
* the thing we actually iterate on — never costs the source another request
* (CLAUDE.md § Scraping conduct). A snapshot plus its metadata is also what
* (AGENTS.md § Scraping conduct). A snapshot plus its metadata is also what
* makes a conditional request possible on the next cycle: we keep the ETag and
* Last-Modified the server gave us and hand them back.
*
@@ -119,7 +119,7 @@ export interface DecodedBody {
* comes back as a field of U+FFFD. That is not merely ugly: mojibake in a title
* flows through `slugify` into the event ID, which is a localStorage key, so a
* mis-decoded fetch silently orphans every completion mark for that source
* (CLAUDE.md § Event IDs are localStorage keys).
* (AGENTS.md § Event IDs are localStorage keys).
*
* Header first, then a `<meta charset>` sniff, then UTF-8. An encoding label
* the runtime does not know falls back to UTF-8 rather than throwing — the raw
@@ -275,7 +275,7 @@ export class SnapshotStore {
/**
* Has enough time passed to fetch this source again?
*
* The floor is six hours per source (CLAUDE.md). A source we have never
* The floor is six hours per source (AGENTS.md). A source we have never
* checked is always due.
*/
isDue(state: SnapshotState, nowMs: number, minIntervalMs: number): boolean {
@@ -300,7 +300,7 @@ export class SnapshotStore {
* one. The validators are the exception: a server is free to rotate an ETag
* while serving the very same bytes, and keeping the old one would mean
* sending a stale `If-None-Match` forever — every cycle costing the wiki a
* full body where a 304 was the whole point (CLAUDE.md § Scraping conduct).
* full body where a 304 was the whole point (AGENTS.md § Scraping conduct).
* So the metadata is refreshed, and `changed` stays false.
*/
async save(sourceId: string, input: SaveInput): Promise<SaveResult> {