CLAUDE.md covers patch authoring rules, post-type discovery, the two reply compose paths, ap_timeline insertion timing, fork dependencies, and common debugging entry points. memory/ contains three files: - project_activitypub.md — data flows, syndicator config, all AP patches - project_architecture.md — FreeBSD jails, MongoDB collections, actor URLs - feedback_patches.md — patch pattern, known fragile points, threading gotchas Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.0 KiB
Patch Management — Lessons Learned
Patch Script Pattern
const MARKER = "// [patch] my-patch-name";
const OLD_SNIPPET = `exact text to find`;
const NEW_SNIPPET = `replacement text ${MARKER}`;
// Check MARKER first → skip if already applied
// Check OLD_SNIPPET → warn if not found (upstream may have changed)
// Replace and write only if source changed
Use $setOnInsert for MongoDB upserts in patches that add timeline items — idempotent,
safe to call multiple times (e.g. from both patch and syndicator).
Target File Candidates
Always include both paths:
const candidates = [
"node_modules/@rmdes/indiekit-endpoint-activitypub/...",
"node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/...",
];
Known Fragile Patterns
- Exact whitespace matters. Patch OLD_SNIPPETs must match node_modules source byte-for-byte, including indentation (spaces not tabs) and exact line endings.
- Template literals in patches — escape backticks and
${}in patch script string literals using\`` and${}`. patch-microsub-reader-ap-dispatchis inserveonly (notpostinstall). Reason unknown but may relate to timing or the microsub package being rebuilt differently. Check both scripts when adding new microsub patches.
AP Threading — Two Compose Paths
Replies can come from two different UIs. Each has different syndication logic:
| Path | Syndication checkbox pre-checked by |
|---|---|
AP reader (/activitypub/admin/reader/compose) |
target.defaultChecked set in composeController() |
Microsub reader (/microsub/admin/reader/compose) |
target.checked from Micropub q=config response |
Mastodon client API (POST /api/v1/statuses) |
mp-syndicate-to hardcoded to publicationUrl (always AP) |
The AP reader compose form uses target.defaultChecked (NOT target.checked) in its template.
If composeController() doesn't set defaultChecked correctly, the AP checkbox is unchecked
even though target.checked = true from the Micropub config.
ap_timeline Insertion Timing
Own posts reach ap_timeline via TWO paths:
- Mastodon API (
POST /api/v1/statuses): Now inserts immediately afterpostContent.create()viapatch-ap-mastodon-reply-threading. The syndicator's later upsert is a no-op ($setOnInsert). - Micropub + syndication webhook: AP syndicator inserts after Eleventy build completes (30–120 s).
Always ensure new post creation paths insert to ap_timeline immediately if the post may be
replied to again before the build webhook fires.
Post Type Discovery Dependency
getPostType(postTypes, properties) uses Map.has(key) — only KEY presence matters, not value.
"in-reply-to"present (any value, even empty) →"reply"- If
in-reply-tois absent → falls through to"note"
If in-reply-to is silently lost (null inReplyTo in Mastodon API, unchecked form field, etc.),
the post silently becomes a note with no error. Check this first when debugging "wrong post type" issues.