aboutsummaryrefslogtreecommitdiffstats
path: root/AGENTS.md
AgeCommit message (Collapse)AuthorFilesLines
37 hoursdocs: point at the author's idea note, and log its first itemDanilo M.1-0/+10
The author keeps ideas in an Obsidian note outside this repository. Nothing in the repo pointed at it, so a fresh session would never find it and the backlog would look complete while ideas sat unread. AGENTS.md now names the path and the rule: the note is the inbox, the backlog is the tracked list, and they are reconciled in both directions. Item 3 is that note's one open idea, an API letting qtmaildir tag incoming mail by resemblance to a kept case. It is recorded unsized and explicitly marked as having no verified cause, because unlike items 1 and 2 there is nothing built to verify. The entry records the real tension rather than just the request: this tool has been deliberately mechanical, reporting what a message declared and refusing rather than guessing, and a resemblance score would be its first opinion. It also notes that source.eml is unredacted, so matching must not become a route by which a stored recipient identifier reaches a comparison. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wrfqr2xqQfhtXCscU7zrdz
37 hoursdocs: record the fourth property and the contacts commandDanilo M.1-9/+85
An RDAP query discloses what the user is looking at, and property 1 covers only what is published, so the query rule needed stating in its own right beside the other three. The section records the three leaks it came from, because each one was the same shape: validation applied per branch, forgotten on the next. Also moves rdap.py's imports into one block at the top. Pure move, the suite is 192 either side of it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wrfqr2xqQfhtXCscU7zrdz
38 hoursdocs: plan the contacts implementationDanilo M.1-4/+4
Twelve tasks, TDD throughout, building bottom up: transport, bootstrap cache, server selection, jCard extraction, querying with the label walk, the worklist, resolution, the subcommand, an offline proof, two sweeps and the documentation. The code in the plan was assembled and run before committing it, against the plan's own tests, 44 of which pass. That found two defects in the plan itself: a parameter that shadowed the cache_dir function and forced a globals() lookup, now renamed to cache_root, and a hand-rolled fake request in the redirect test that lacked the attributes HTTPRedirectHandler reads, now a real urllib Request. Also drops the em dashes from the Documents list in AGENTS.md, which the no-em-dash writing rule covers and which the contacts entry had matched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wrfqr2xqQfhtXCscU7zrdz
38 hoursdocs: spec the contacts subcommandDanilo M.1-0/+4
Settles what the umbrella design left open for this part: RDAP bootstrap, caching policy, and what happens for a netblock that publishes no abuse contact. Scope is IPs and domains, with a URL contributing only its host. The bootstrap files are fetched from IANA and cached with a long TTL, while responses are cached in memory for one run only: a stale bootstrap merely misses, but a stale abuse address sends a report into a dead mailbox silently. The registrable domain is found by walking up the labels and asking the registry rather than by bundling a Public Suffix List, which would be a transcribed table that goes stale weekly. Address extraction is a strict abuse-role match with no fallback. In particular it never constructs abuse@<domain>, because for a phishing domain that mailbox belongs to the attacker and mailing it would confirm both the catch and that the address is live. Adds a fourth non-negotiable property: a query carries a bare host or IP and never a URL. Property 1 governs what is published, and a query is a disclosure that appears in no report, so a path segment that parse deliberately flags rather than redacts would otherwise leak. Transport is urllib with an injected fetch, so requirements.txt stays empty and the suite keeps passing with sockets raising, for the whole suite rather than for everything except contacts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wrfqr2xqQfhtXCscU7zrdz
2 daysdocs: record the real-mail sweep and open a backlogDanilo M.1-0/+38
A synthetic fixture only ever holds the shapes someone thought of, and the shapes nobody thought of are the ones that leak. Sweeping the user's own tag:spam is the only source of those, and it earned its place: the first message found a header the parser did not read, and the corpus exercised a spoofed Reply-To display name no fixture had. AGENTS.md records it under Testing, beside the fixture rule it is the deliberate exception to: ask first, work from the scratchpad, compare every address in the raw source against the whole ioc output, and let only counts and stripped domains out. A finding becomes a synthetic fixture; the real message never enters the repository. BACKLOG.md opens with the one thing the sweep found that was not worth fixing blind: boilerplate namespace urls (w3.org doctypes) reported as indicators. Noise rather than a defect, and the argument for leaving it alone is real, so it is written down with both sides rather than built. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019NHaqA1Rz5ybed7wFUeQbK
2 daysdocs: AGENTS.md, with CLAUDE.md pointing at itDanilo M.1-0/+251
Same arrangement as qtmaildir: one source of truth, and a thin pointer beside it so every agent tool reads the same file. It leads with the three properties that are not negotiable, because each has a concrete victim and each is a thing a later change could quietly break. Recipient identifiers must never reach a report, and the guarantee is structural rather than a step someone remembers. Nothing is fetched or resolved, which the suite verifies by running with sockets disabled. The trust boundary is configured rather than guessed, and its test is mutation-checked because walking one hop too far reports an innocent party the attacker named. It also records the traps that were found the hard way rather than reasoned about: ipaddress.ip_network(42) returning 0.0.0.42/32 instead of raising, a string trusted_relays iterating characters, goog.json looking like the right SPF source while listing all of Google, and the provider table having been wrong in every entry when it was first written from memory. The hand-testing rule is stated with its evidence: the prompts are the user's to test, and one pass over them found five defects, four being the same mistake of validating an answer somewhere other than where it was given. Every factual claim in it was checked against the code before committing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KphFXTc2QajxXsHWyvGJ4R