commits
remove buildDocUrl function and docUrl callbacks from PLATFORM_CONFIG.
frontend now reads doc.url directly from the API response (-72 lines).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
backend buildDocUrl mirrors frontend logic (leaflet basePath+rkey,
basePath+path, leaflet.pub fallback, whtwnd.com fallback). MCP now
reads url from API response instead of computing it via a property.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Join publications table in all search queries to return
the human-readable publication name alongside basePath.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The author filter was a post-filter applied after LIMIT 40, so for
common words like "up", the target author's docs could rank outside
the top 40 and get cut before filtering. Now the condition is in the
SQL itself using (? = '' OR d.did = ?) which is a no-op when empty.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clicking the handle in results goes to their bsky profile (original
behavior). Author filtering is done via @handle in the search box.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
"@zzstoatzz.io" searches for the text literally.
@zzstoatzz.io (unquoted) triggers author filter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Type @zzstoatzz.io compels to search "compels" filtered to that author.
The @handle is stripped from the query, set as currentAuthor, and sent
as the author param to the API (which resolves handles server-side).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- backend: parse ?author= param, resolve handles via zat HandleResolver
(HTTP .well-known + DNS-over-HTTPS fallback), post-filter all search
modes by DID, add DocsByAuthor/DocsByAuthorAndPlatform for browse-by-author
- frontend: currentAuthor state, setAuthor/clearAuthor, author chip in
active filters, click author name to filter, ?author= URL param support
- MCP: author param on search/search_semantic/search_hybrid tools
- OG tags: pass author through to title/description/image URL
- deps: upgrade zat v0.1.9 → v0.2.13, align websocket to zat's fork
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
when searching with quoted terms like "foo", the OG title/description
and og-image were wrapping already-quoted input in another pair of quotes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Zig convention uses foo.zig next to foo/ directory, not foo/mod.zig
(which is a Rust pattern). Renamed all four module entry points and
updated imports throughout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This reverts commit e37c920804fac1b6f47122b33d71877397c9a6d0.
When turso is unreachable, schema.init() hangs indefinitely (no
timeouts in zig 0.15 http.Client). Since it was the first thing in
initServices, it blocked local DB init, sync, and all other services.
Spawn schema init in its own thread so local DB can initialize and
serve search queries even when turso is down.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
zig 0.15 stdlib http.Client has no timeout support. When turso is
slow/unreachable, schema.init() blocks the main thread indefinitely,
preventing the accept loop from ever starting. Move schema migrations
to the background initServices thread so the server can accept
connections immediately.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
did:web docs (and any DID with unresolvable PDS) stayed at
verified_at=NULL forever, permanently clogging the front of the
reconciler queue. Now they get pushed to the back with the normal
7-day reverify window.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
OR was being silently dropped from queries — if someone searched for
the word "OR" it would vanish. now OR is quoted ("OR") so FTS5 treats
it as a literal word rather than an operator, preserving user intent.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
"cat dog" and "a OR b" do the same thing — terms are already OR'd
implicitly. drop the third row, clarify descriptions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
tags were wrapping into 4-5 rows on mobile, pushing results below
the fold. switch to single-row horizontal scroll on small screens
so results are immediately visible.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
button was before header-stats instead of after it. mobile CSS
applied min-height: 44px to all buttons, stretching the 14px
circle into a tall oval. exclude .syntax-help-btn from that rule.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
move [?] button from search box to header row as a subtle circled ?
next to [src] and stats. removes the "pyth → prefix on last word"
example that was unclear — tooltip now shows 3 rows: OR between terms,
phrase match, explicit OR.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
buildFtsQuery was treating quotes and OR as plain characters, causing:
- "bertha OR burton" → "bertha OR OR OR burton*" (0 results)
- python "machine learning" → "python OR machine OR learning*" (no phrase match)
rewrite tokenizer to recognize quoted phrases, bare words, and literal OR.
add search syntax tooltip to frontend and docs/search-syntax.md reference.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
expand timing ring buffer from 24 to 720 hours (30 days), add aggregate
traffic series endpoint, render as SVG sparkline on dashboard between
metrics and documents-by-platform sections.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move server.zig, search.zig, dashboard.zig into server/ directory.
Move reconcile.zig into ingest/ as reconciler.zig alongside other
background workers. Replace top-level ingest.zig and metrics.zig
shims with mod.zig inside their directories. Root tests at main.zig
with shared imports to fix zig 0.15 module path boundary.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
was doing full sync (~80 min for 12k docs) on every deploy, blocking
new content from appearing in search. the full sync existed to clean
up stale docs deleted from Turso.
now: incremental sync on startup (seconds), with tombstone queries to
handle deletions. fullSync only runs on first-ever boot (no last_sync).
tombstones table was already populated by deleteDocument/deletePublication
but never queried during sync.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The blanket bridgy-fed filter was added because we couldn't build links
(empty base_path). Now that the indexer resolves base_path from HTTP
site URLs in publication_uri, bridgy-fed documents can get working links
like any other standard.site content.
Removes isBridgyFed, resolvePdsIsBridgy, and PdsCache (no longer needed).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
base_path values like "tedium.co/" combined with paths like "/some-post"
produce double-slash URLs ("https://tedium.co//some-post") that 404.
The trailing slash comes from publication URLs like "https://tedium.co/"
where stripUrlScheme preserved it. Fix in three places:
- tap.zig: stripUrlScheme strips trailing slash
- indexer.zig: HTTP fallback strips trailing slash
- indexer.zig: normalize base_path after all resolution (catches values
already stored in publications table with trailing slashes)
Backfill: RTRIM(base_path, '/') on both documents and publications.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
f2c6d29 intended to keep local FTS serving during re-sync by only
calling setReady(false) on first-ever sync. But is_ready initializes
to false, so the has_data branch needed an explicit setReady(true).
Without it, every deploy caused ~60min of turso fallback (3-5s per
search) until fullSync completed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
standard.site documents store the origin URL (e.g., "https://attoshi.com")
in publication_uri, but the indexer only resolves base_path from AT-URIs
via the publications table. When publication_uri is an HTTP URL, the lookup
fails silently and base_path stays empty, breaking frontend links.
Add a fallback: if base_path is still empty and publication_uri starts with
http(s)://, strip the scheme and use the remainder as base_path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
background worker verifies documents still exist at their source PDS
via com.atproto.repo.getRecord. catches deletions missed while the tap
was down (firehose delete events are ephemeral and never replayed).
also fixes the forward path: firehose deletes now clean turbopuffer
vectors in addition to turso records.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
accessible explainer of how the search engine works — covers keyword
(FTS5), semantic (voyage + turbopuffer), hybrid (RRF), content
extraction challenges, and what's custom vs off-the-shelf.
also fixes ~25k → actual count and v2 format (offset → hasMore) in
root README.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- fix FTS5 description: local replica, not Turso directly
- fix doc count (~11k not ~25k), query count (14 not 10)
- fix /similar: uses turbopuffer, not Turso
- remove phantom /platforms endpoint from api.md
- fix v2 format (hasMore not offset), timing keys (search_keyword etc)
- add coverImage/handle fields, clarify hybrid-only score/source
- fix tap examples: wget not curl (container has no curl)
- add missing tap env vars (TAP_RELAY_URL, cursor/timeout settings)
- fix voyage model in perf saga (voyage-3-lite 512 dims)
- add embedder thread to architecture diagram
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fullSync now tracks synced URIs in a temp table and deletes local docs
that no longer exist in Turso. Startup always does a full sync instead
of incremental, so deleted docs get cleaned up on every deploy.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- delete 31 stale docs from turso (16 dead offprint links, 18 .test domains, 3 overlap)
- add .test domain filter in tap.zig (processPublication) and indexer.zig (insertDocument)
- revert offprint URL slug construction — old docs purged, only /a/ format remains
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous deploy caused a full re-sync which set local DB to
not-ready, forcing all searches through Turso (10-60s response times).
- Only set not-ready on first-ever sync (empty DB)
- Skip DELETE when re-syncing — INSERT OR REPLACE updates in place
- Add ALTER TABLE migration for cover_image on existing local DBs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Semantic and similar searches used tpuf.QueryResult which lacks
cover_image. Now fetchLocalExtras() fetches both snippets and
cover images from local SQLite in a single query per URI. Hybrid
search also falls back to local DB for semantic-only cover images.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add cover_image column to local SQLite schema and sync queries
- Replace 3-button theme toggle with single cycling icon (dark/light/system)
- Make platform badges clickable as search filters
- Fix cover image hover jump with position compensation
- Move cover thumbnail to right-side absolute positioning
- Add backfill-cover-images script for populating existing docs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- stats/dashboard links use /dashboard.html (same origin) instead of
going through backend redirect to dead leaflet-search.pages.dev URL
- theme toggle moved from cramped header to bottom of page where it
doesn't clutter the title line
- dashboard "back" and title links use relative paths (same origin)
- backend DASHBOARD_URL default updated to pub-search.waow.tech
- localStorage theme now shared between search and stats pages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 3-way theme toggle (dark/light/system) with localStorage persistence
and flash prevention on both search and dashboard pages
- extract cover image blob CID from document records (coverImage field
for pckt/offprint/greengale, first image block fallback for leaflet)
- add cover_image column to documents table, pass through indexer/search
- render 32x32 thumbnails in search results via bsky CDN, graceful
fallback when image unavailable
- convert all hardcoded colors to CSS custom properties
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
the homepage was missing og:image tags, so link previews showed no image.
also fixed CLAUDE.md deploy command — must run from inside site/ dir
to include the Functions bundle.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- add limit param to get_tags (default 10, was returning all ~100 tags)
- strip timing data from get_stats (36 latency numbers most callers
don't need — use the dashboard for that)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- expand Stats type with embeddings, searches, errors, started_at,
cache_hits, cache_misses, and per-endpoint timing metrics
- update backfill-embeddings to use voyage-4-lite with output_dimension
- update rebuild-documents-table to use F32_BLOB(1024)
- add regression tests for full Stats model
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
generate per-query 1200x630 PNG images via workers-og with dark terminal
aesthetic, filter chips with type-matched colors, top 3 result titles,
and result count. rewrite meta tag injection to use HTMLRewriter with
support for all 5 URL params (q, tag, platform, since, mode).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
20 concurrent PLC lookups, 10 concurrent turso deletes.
What took ~15min sequential now finishes in ~40s.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PLC directory lookup in tap worker to detect brid.gy-hosted DIDs and
skip indexing their documents/publications. Results cached per worker
lifetime, fails open on errors.
Purge script queries turso for platform='other' DIDs, resolves PDS,
and batch-deletes bridgy fed content (documents, tags, FTS entries).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
created_at is the document's publication date (set by the author),
which can be in the future. indexed_at is when we actually indexed
it, which is what the timeline should show. also caps at today.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
reflects voyage-4-lite + turbopuffer, hybrid search mode, whitewind
platform, content dedup, format=v2, and corrected code paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
add content_hash (wyhash of title+content) to documents table. on
ingest, skip documents where the same author already has identical
content under a different rkey (cross-platform publishing dedup).
frontend: add date filter (any/week/month/year) with since param,
URL state sync, and active filter bar.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- backfill-pds now handles com.whtwnd.blog.entry collection
- extracts markdown content from whitewind's content field
- sets platform to "whitewind", skips visibility:"author" entries
- prefers publishedAt over createdAt for date extraction
- update tangled.sh URLs to tangled.org in build.zig.zon
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The author filter was a post-filter applied after LIMIT 40, so for
common words like "up", the target author's docs could rank outside
the top 40 and get cut before filtering. Now the condition is in the
SQL itself using (? = '' OR d.did = ?) which is a no-op when empty.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- backend: parse ?author= param, resolve handles via zat HandleResolver
(HTTP .well-known + DNS-over-HTTPS fallback), post-filter all search
modes by DID, add DocsByAuthor/DocsByAuthorAndPlatform for browse-by-author
- frontend: currentAuthor state, setAuthor/clearAuthor, author chip in
active filters, click author name to filter, ?author= URL param support
- MCP: author param on search/search_semantic/search_hybrid tools
- OG tags: pass author through to title/description/image URL
- deps: upgrade zat v0.1.9 → v0.2.13, align websocket to zat's fork
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When turso is unreachable, schema.init() hangs indefinitely (no
timeouts in zig 0.15 http.Client). Since it was the first thing in
initServices, it blocked local DB init, sync, and all other services.
Spawn schema init in its own thread so local DB can initialize and
serve search queries even when turso is down.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
zig 0.15 stdlib http.Client has no timeout support. When turso is
slow/unreachable, schema.init() blocks the main thread indefinitely,
preventing the accept loop from ever starting. Move schema migrations
to the background initServices thread so the server can accept
connections immediately.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
buildFtsQuery was treating quotes and OR as plain characters, causing:
- "bertha OR burton" → "bertha OR OR OR burton*" (0 results)
- python "machine learning" → "python OR machine OR learning*" (no phrase match)
rewrite tokenizer to recognize quoted phrases, bare words, and literal OR.
add search syntax tooltip to frontend and docs/search-syntax.md reference.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move server.zig, search.zig, dashboard.zig into server/ directory.
Move reconcile.zig into ingest/ as reconciler.zig alongside other
background workers. Replace top-level ingest.zig and metrics.zig
shims with mod.zig inside their directories. Root tests at main.zig
with shared imports to fix zig 0.15 module path boundary.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
was doing full sync (~80 min for 12k docs) on every deploy, blocking
new content from appearing in search. the full sync existed to clean
up stale docs deleted from Turso.
now: incremental sync on startup (seconds), with tombstone queries to
handle deletions. fullSync only runs on first-ever boot (no last_sync).
tombstones table was already populated by deleteDocument/deletePublication
but never queried during sync.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The blanket bridgy-fed filter was added because we couldn't build links
(empty base_path). Now that the indexer resolves base_path from HTTP
site URLs in publication_uri, bridgy-fed documents can get working links
like any other standard.site content.
Removes isBridgyFed, resolvePdsIsBridgy, and PdsCache (no longer needed).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
base_path values like "tedium.co/" combined with paths like "/some-post"
produce double-slash URLs ("https://tedium.co//some-post") that 404.
The trailing slash comes from publication URLs like "https://tedium.co/"
where stripUrlScheme preserved it. Fix in three places:
- tap.zig: stripUrlScheme strips trailing slash
- indexer.zig: HTTP fallback strips trailing slash
- indexer.zig: normalize base_path after all resolution (catches values
already stored in publications table with trailing slashes)
Backfill: RTRIM(base_path, '/') on both documents and publications.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
f2c6d29 intended to keep local FTS serving during re-sync by only
calling setReady(false) on first-ever sync. But is_ready initializes
to false, so the has_data branch needed an explicit setReady(true).
Without it, every deploy caused ~60min of turso fallback (3-5s per
search) until fullSync completed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
standard.site documents store the origin URL (e.g., "https://attoshi.com")
in publication_uri, but the indexer only resolves base_path from AT-URIs
via the publications table. When publication_uri is an HTTP URL, the lookup
fails silently and base_path stays empty, breaking frontend links.
Add a fallback: if base_path is still empty and publication_uri starts with
http(s)://, strip the scheme and use the remainder as base_path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
background worker verifies documents still exist at their source PDS
via com.atproto.repo.getRecord. catches deletions missed while the tap
was down (firehose delete events are ephemeral and never replayed).
also fixes the forward path: firehose deletes now clean turbopuffer
vectors in addition to turso records.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
accessible explainer of how the search engine works — covers keyword
(FTS5), semantic (voyage + turbopuffer), hybrid (RRF), content
extraction challenges, and what's custom vs off-the-shelf.
also fixes ~25k → actual count and v2 format (offset → hasMore) in
root README.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- fix FTS5 description: local replica, not Turso directly
- fix doc count (~11k not ~25k), query count (14 not 10)
- fix /similar: uses turbopuffer, not Turso
- remove phantom /platforms endpoint from api.md
- fix v2 format (hasMore not offset), timing keys (search_keyword etc)
- add coverImage/handle fields, clarify hybrid-only score/source
- fix tap examples: wget not curl (container has no curl)
- add missing tap env vars (TAP_RELAY_URL, cursor/timeout settings)
- fix voyage model in perf saga (voyage-3-lite 512 dims)
- add embedder thread to architecture diagram
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- delete 31 stale docs from turso (16 dead offprint links, 18 .test domains, 3 overlap)
- add .test domain filter in tap.zig (processPublication) and indexer.zig (insertDocument)
- revert offprint URL slug construction — old docs purged, only /a/ format remains
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous deploy caused a full re-sync which set local DB to
not-ready, forcing all searches through Turso (10-60s response times).
- Only set not-ready on first-ever sync (empty DB)
- Skip DELETE when re-syncing — INSERT OR REPLACE updates in place
- Add ALTER TABLE migration for cover_image on existing local DBs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Semantic and similar searches used tpuf.QueryResult which lacks
cover_image. Now fetchLocalExtras() fetches both snippets and
cover images from local SQLite in a single query per URI. Hybrid
search also falls back to local DB for semantic-only cover images.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add cover_image column to local SQLite schema and sync queries
- Replace 3-button theme toggle with single cycling icon (dark/light/system)
- Make platform badges clickable as search filters
- Fix cover image hover jump with position compensation
- Move cover thumbnail to right-side absolute positioning
- Add backfill-cover-images script for populating existing docs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- stats/dashboard links use /dashboard.html (same origin) instead of
going through backend redirect to dead leaflet-search.pages.dev URL
- theme toggle moved from cramped header to bottom of page where it
doesn't clutter the title line
- dashboard "back" and title links use relative paths (same origin)
- backend DASHBOARD_URL default updated to pub-search.waow.tech
- localStorage theme now shared between search and stats pages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 3-way theme toggle (dark/light/system) with localStorage persistence
and flash prevention on both search and dashboard pages
- extract cover image blob CID from document records (coverImage field
for pckt/offprint/greengale, first image block fallback for leaflet)
- add cover_image column to documents table, pass through indexer/search
- render 32x32 thumbnails in search results via bsky CDN, graceful
fallback when image unavailable
- convert all hardcoded colors to CSS custom properties
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- expand Stats type with embeddings, searches, errors, started_at,
cache_hits, cache_misses, and per-endpoint timing metrics
- update backfill-embeddings to use voyage-4-lite with output_dimension
- update rebuild-documents-table to use F32_BLOB(1024)
- add regression tests for full Stats model
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
generate per-query 1200x630 PNG images via workers-og with dark terminal
aesthetic, filter chips with type-matched colors, top 3 result titles,
and result count. rewrite meta tag injection to use HTMLRewriter with
support for all 5 URL params (q, tag, platform, since, mode).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PLC directory lookup in tap worker to detect brid.gy-hosted DIDs and
skip indexing their documents/publications. Results cached per worker
lifetime, fails open on errors.
Purge script queries turso for platform='other' DIDs, resolves PDS,
and batch-deletes bridgy fed content (documents, tags, FTS entries).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
add content_hash (wyhash of title+content) to documents table. on
ingest, skip documents where the same author already has identical
content under a different rkey (cross-platform publishing dedup).
frontend: add date filter (any/week/month/year) with since param,
URL state sync, and active filter bar.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- backfill-pds now handles com.whtwnd.blog.entry collection
- extracts markdown content from whitewind's content field
- sets platform to "whitewind", skips visibility:"author" entries
- prefers publishedAt over createdAt for date extraction
- update tangled.sh URLs to tangled.org in build.zig.zon
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>