maintain-curators

star 821

Audit curator feeder YAML files for stale Twitter, LinkedIn, RSS and website error markers, decide whether each error is real, then clear false positives, repoint renamed handles/links, or tombstone dead entities — leaving a dated audit comment on every entry touched.

tradingstrategy-ai By tradingstrategy-ai schedule Updated 6/10/2026

name: maintain-curators description: Audit curator feeder YAML files for stale Twitter, LinkedIn, RSS and website error markers, decide whether each error is real, then clear false positives, repoint renamed handles/links, or tombstone dead entities — leaving a dated audit comment on every entry touched.

Maintain curators

This skill performs a maintenance pass over the curator feeder YAML files in eth_defi/data/feeds/curators/. It looks at the error / death markers that the feed scanner stamps onto these files, works out whether each marker still reflects reality, and then repairs the entry: clearing false positives, re-pointing renamed sources, or tombstoning entities that are genuinely gone. Every entry that is touched gets a dated # YYYY-MM-DD maintain-curators: … comment recording what was checked and decided.

This is the curator counterpart of check-stablecoins, but the curator files use inline trailing marker fields (not a nested checks: block), so the mechanics are different — follow this skill, not the stablecoin one.

Reference material

Read these before editing — they define the schema and behaviour this skill operates on. Consult only the sections you need.

Documentation (READMEs)

  • eth_defi/feed/README-feed.md — the authoritative feed submodule doc. Especially: Unified feeder schema (field-by-field), Canonical feeder aliases (why alias files carry no sources), Example feeder files (a well-formed multi-source feeder), Collection behaviour → RSS / Twitter/X / LinkedIn sources (how each source is fetched and what "dead" means per transport), and Failure handling.
  • eth_defi/data/feeds/README.md — the data folder layout (curators/, protocols/, stablecoins/, vaults/) and the alias contract.
  • .claude/skills/add-curator/SKILL.md — how new curator entries and their source fields are created; reuse its identity/description conventions when repointing.
  • .claude/skills/check-stablecoins/SKILL.md — the sibling liveness audit for stablecoins. Note the different marker shape (nested checks: block) so you don't copy it here.

Schema and writer code (eth_defi/feed/sources.py)

  • _MAPPING_SCHEMA (strictyaml Map) — the only keys a curator YAML may contain. Adding any other top-level key makes the file fail to load. This is also the exhaustive list of marker fields.
  • _load_mapping_file() — shows exactly how each marker disables its source (sets twitter_username / linkedin_company_id / rss_url to None) and the "all sources disabled → skip feeder" rule.
  • mark_twitter_source_dead(), mark_twitter_handle_unknown(), mark_linkedin_source_disabled(), mark_rss_source_dead(), mark_rss_source_failure() — the append-only writers that set each marker. Mirror their field names exactly when you keep a marker; delete their output line when you clear one.
  • eth_defi/feed/scanner.py_detect_dead_twitter_accounts() and the RSS death sweep — the automation that re-stamps markers, and why clearing twitter-dead-at without proof of activity is futile.

Example curator YAML files (in eth_defi/data/feeds/curators/)

  • unified-labs.yaml — healthy multi-source feeder (website + twitter + rss) with no markers; the clean baseline to compare against. Note that linkedin-rss-hub-disabled-at is present on ~47 files, so "no markers at all" is rare — most live feeders still carry the LinkedIn bridge marker.
  • august-digital.yamltwitter-dead-at where the handle is correct and the profile is live, but no in-window post could be confirmed → marker retained, not cleared. The cautionary example: handle-valid ≠ recently-active.
  • b-cube-ai.yamltwitter-dead-at confirmed genuine: latest indexed post was an Oct-2025 AMA, older than the 180-day cutoff → marker retained.
  • smardex.yamltwitter-handle-resolved-unknown-at plus a hand-written # SmarDex is active … comment; the handle is in fact live.
  • gauntlet.yaml, block-analitica.yamlrss-dead-at on a Medium feed.
  • candle-effect.yaml — website (candleffect.com) that no longer resolves; the website-tombstone case.
  • k3-capital.yaml — an alias file (canonical-feeder-id: sbold) with a leading rationale comment; carries no sources, so never gets markers.

Background: how the markers work

The loader eth_defi/feed/sources.py understands these optional fields. When a marker is present, the matching feed source is disabled (set to None) and silently skipped by the collector. If all of a feeder's sources are disabled, the whole feeder produces no posts.

Marker field Set by Meaning Effect
twitter-dead-at: YYYY-MM-DD mark_twitter_source_dead() / scanner _detect_dead_twitter_accounts() Our collector recorded no post (last_post_published_at) within death_detection_days (default 180, see scanner.py). A recency signal, computed from our DB — not a live scrape Disables the twitter: source
twitter-handle-resolved-unknown-at: YYYY-MM-DD mark_twitter_handle_unknown() X API could not resolve the handle to a user id (suspended, deleted, or renamed) Disables the twitter: source
linkedin-rss-hub-disabled-at: YYYY-MM-DD mark_linkedin_source_disabled() / auto_disable_failed_linkedin_sources() The RSS-Hub LinkedIn bridge stopped returning posts for this company Disables the linkedin: source
rss-dead-at: YYYY-MM-DD mark_rss_source_dead() RSS feed is valid but has published nothing for ~a year or more Disables the rss: source
rss-failure-at / rss-failure-status-code / rss-failure-exception-message mark_rss_source_failure() Most recent RSS fetch failed (HTTP error / exception). Diagnostic only — does not disable the source on its own None (diagnostic)

For how each transport is actually fetched and what "dead" means per source, see Collection behaviour in eth_defi/feed/README-feed.md. The disabling logic is in _load_mapping_file() in eth_defi/feed/sources.py.

There is no website marker field and no typed tombstone field in the schema (_MAPPING_SCHEMA in eth_defi/feed/sources.py is strict — an unknown key makes the file fail to load). Website deadness and whole-entity tombstones are therefore recorded as YAML comments, never as new top-level keys. Do not invent new marker keys unless you also extend _MAPPING_SCHEMA.

The two Twitter markers ask different questions — do not conflate them

This is the single most important distinction in this skill. The two Twitter markers fail for different reasons and need different evidence to clear:

  • twitter-handle-resolved-unknown-at = handle validity. "Does this handle resolve to a real account?" Often an X-API / anti-bot false positive. It is refuted by a live, correctly-named profile — if WebSearch shows the handle exists with the right brand, clear it. Recency is irrelevant here.

  • twitter-dead-at = recency. "Has our collector seen a post in the last death_detection_days (180)?" It is computed from our own database, not a live scrape, so a working account our bridge simply failed to read can be flagged — but so can a genuinely dormant one. "The profile exists and looks active" does NOT refute it. The only thing that refutes twitter-dead-at is a concrete post dated within ~180 days of today (i.e. after today − 180d). If you cannot find such a dated post, you cannot clear the marker — leave it and record that recency was unverifiable. Clearing on a vague "live profile" is the classic mistake: the scanner will just re-stamp it on the next run, and worse, the audit trail will claim a false positive that was never demonstrated.

When you do clear twitter-dead-at, the audit comment must cite the concrete latest-post date you found (e.g. "latest post 2026-05-30, within 180d — cleared"). No date, no clear.

The anti-bot wall (applies to both)

X (Twitter) and LinkedIn return HTTP 402 / 403 to nearly all automated fetchers, including a direct WebFetch of https://x.com/<handle> and, in 2026, most Nitter mirrors. That failure tells you nothing — never treat it as proof of death. Use WebSearch for liveness/handle checks; it surfaces indexed profile pages, dated post snippets, and news. But note its limit: search often confirms a handle exists without giving a dated recent post — which is enough to clear twitter-handle-resolved-unknown-at but not enough to clear twitter-dead-at.

Conversely, website / RSS / DNS failures are usually real and can be confirmed cheaply and conclusively with curl and nslookup.

Required inputs

None. The skill operates on every *.yaml in eth_defi/data/feeds/curators/. Optionally the user may pass a subset (specific feeder slugs or a glob) to limit the pass.

Step 1: Inventory the flagged entries

List every curator file carrying a marker, grouped by marker type:

cd eth_defi/data/feeds/curators
grep -rlE '^(twitter-dead-at|twitter-handle-resolved-unknown-at|linkedin-rss-hub-disabled-at|rss-dead-at|rss-failure-at):' *.yaml

For a per-marker breakdown:

grep -rhoE '^[a-z-]*(dead-at|unknown-at|disabled-at|failure-at):' *.yaml | sort | uniq -c

Read each flagged file in full before acting — existing comments often already record prior findings (e.g. # SmarDex is active - DEX with concentrated liquidity).

Skip alias files (those with canonical-feeder-id:): they carry no feed sources, so markers do not belong there — see Canonical feeder aliases in eth_defi/feed/README-feed.md and k3-capital.yaml for an example. If you find a marker on an alias, flag it in the report rather than editing.

Step 2: Decide whether each marker is real

Handle each marker type with the cheapest reliable check.

Two principles that govern every decision:

  • Confirmed-dead ≠ unverifiable. Only clear a marker on positive evidence of life, and only tombstone on positive evidence of death. When everything is blocked or ambiguous (e.g. rogue-traders: no website, handle won't resolve, search results unrelated), the answer is leave it and record "inconclusive" — never tombstone on mere absence of evidence, and never clear on a hunch. This mirrors check-stablecoins' "never mark dead just because information is missing".
  • The audit comment is the durable artefact, not the cleared marker. A cleared twitter-dead-at can be re-stamped on the next scan if the collector still sees no in-window post, so the lasting value is the comment. Use it to separate the two facts you actually established: handle validity ("@handle is correct, live profile") versus recency ("latest confirmed post 2026-05-30" or "recency unverifiable"). Even when you leave a marker in place, recording "handle verified correct" stops the next maintainer from wrongly deleting the handle. Always leave the comment; cite a concrete date whenever you have one.

twitter-dead-at (recency — needs a concrete in-window post date)

This marker means our collector saw no post within 180 days (death_detection_days). To clear it you must find a concrete post dated after today − 180d — nothing weaker counts. Decide:

  1. WebSearch for "{name}" {handle} site:x.com and for recent dated posts or news referencing a specific recent post.
  2. Found a post dated within 180 days? The marker is a false positive → clear it (Step 3, action 1) and cite that date in the audit comment.
  3. Found only old posts (newest predates today − 180d, e.g. an Oct-2025 AMA checked in Jun 2026)? The marker is accurate — the account is dormant by the scanner's definition. Leave it, and note the latest-post date.
  4. Could confirm the handle exists but found no dated recent post (the common case — X blocks bots, Nitter is dead)? Recency is unverifiableleave the marker in place. Do not clear: "the profile looks active" is not evidence of an in-window post, and the scanner would re-stamp it anyway. Record that the handle is correct but recency could not be confirmed.
  5. If the name now maps to a different/renamed handle, repoint (and the new handle starts fresh, so clear the marker).

Compute the cutoff explicitly from today's date; do not eyeball "about six months". On 2026-06-10 the cutoff is 2025-12-12.

twitter-handle-resolved-unknown-at (handle did not resolve)

Stronger signal that the literal handle is broken, but also the marker most often caused by X anti-bot:

  1. WebSearch for the project's current official X handle.
  2. If the same handle clearly still exists and posts → false positive → clear the marker.
  3. If the project has renamed/moved to a new handle → repoint: update the twitter: value to the new handle and clear the stale marker.
  4. If the account is genuinely suspended/deleted with no replacement → it is real; leave the marker (it already disables the source) and, if the whole entity is gone, add a tombstone comment.

rss-dead-at and rss-failure-*

These are checkable directly (RSS is not anti-bot protected). Fetch the feed and read the newest item date in one go — this recipe handles both RSS (<pubDate>) and Atom (<updated>):

code=$(curl -s -o /tmp/feed.xml -w '%{http_code}' -L --max-time 25 -A 'Mozilla/5.0' "<rss url>")
newest=$(grep -oE '<pubDate>[^<]+</pubDate>|<updated>[^<]+</updated>' /tmp/feed.xml | head -1 | sed 's/<[^>]*>//g')
echo "HTTP $code newest=$newest"
  1. Fresh items (newest within ~12 months) + HTTP 200 → false positive → clear the rss-dead-at marker (and any stale rss-failure-* fields).
  2. HTTP 200 but newest item is over a year old → rss-dead-at is real; leave it. Do this next: a stale-but-200 feed almost always means the org migrated platforms rather than stopped writing — most of these are Medium feeds (medium.com/feed/...) abandoned when the team moved to its own blog, Substack, Mirror, or Paragraph. WebSearch for "{name}" blog OR newsletter and, if you find a live successor feed, repoint rss: to it and clear the marker. If no successor is found, leave the marker and say so.
  3. Persistent 404 / DNS failure → the feed URL itself is gone; look for the current feed and repoint, or leave the marker if none exists.

Caveats that bit during real runs:

  • Medium/Substack feeds return only the ~10 most recent items, so a low item count is normal — judge on the newest date, not the count.
  • A <pubDate> is the post date; ignore the channel-level <lastBuildDate> (it updates even on dead blogs). Taking the first <item>/<entry> date as above avoids this.
  • rss-failure-* (transient fetch error) is not rss-dead-at (stale content). A feed can be failure today (rate-limited 429/503) yet perfectly alive — re-fetch before acting; only clear rss-failure-* once it returns 200.

linkedin-rss-hub-disabled-at

This reflects the RSS-Hub bridge failing for that company, not necessarily the company's LinkedIn page. Bridge availability is outside our control and these are usually left as-is. Only clear if the user has confirmed the bridge works again for that company. Default action: leave untouched, note it.

Website links

The website: field is metadata only — it is not a tracked feed source, so a dead website does not disable collection or get auto-marked. It is the lowest urgency check, but cheap, so do it while you are in the file:

nslookup <domain> 8.8.8.8          # NXDOMAIN from a public resolver == dead
curl -sL --max-time 12 -o /dev/null -w '%{http_code}' <website>
  • 200 (or a 403 Cloudflare challenge page that still has content) → fine.
  • NXDOMAIN from a public resolver (8.8.8.8 and 1.1.1.1, not just your local one — a VPN/corporate resolver can lie both ways), or persistent connection failure → the domain is dead. Search for the project's current site; repoint the website: value if found.
  • A dead website does not by itself mean the entity is dead — it may still run on-chain (e.g. candle-effect: candleffect.com is NXDOMAIN but its Lighter public pools persist). Tombstone only the website with a comment; reserve a whole-entity tombstone for when the sources are dead too.
  • Remember the domain may be referenced more than once (also under other-links: and inside long_description) — note all occurrences, even if you only repoint the canonical website: field.

Step 3: Apply the fix

Use the Edit tool for surgical changes. Never reformat, reorder, or re-indent existing keys. Preserve all comments.

Action 1 — clear a false-positive marker. Delete the entire marker line (and any companion rss-failure-* lines when the RSS feed is confirmed healthy). This re-enables the source on the next load.

Action 2 — repoint a renamed handle / moved link. Edit the source value in place — twitter:, linkedin:, rss:, or website: — to the new value, and delete the now-stale marker for that source.

Action 3 — tombstone a dead entity. When the organisation itself is gone (site dead, socials dead, no replacement found), do not delete its sources. Leave the existing markers in place (they already disable the dead sources) and add a tombstone comment at the top of the file:

# TOMBSTONE 2026-06-10 maintain-curators: entity appears defunct — website
# <url> is NXDOMAIN and X @<handle> is suspended with no successor found.
# Sources left disabled; kept for historical curator-name matching.

For a dead website where the rest of the entity still lives, leave website: as-is (or repoint if a successor exists) and add a comment line next to it rather than a new field.

Step 4: Always leave a dated audit comment

Every entry you inspect and change gets one comment line recording the date and what was done, placed directly above the field it concerns (or at the top of the file for whole-entity decisions). Use this exact prefix so the trail is greppable:

Cleared a twitter-dead-at — note the concrete in-window post date that justified it (without one, you may not clear):

# 2026-06-10 maintain-curators: latest post 2026-05-30 (within 180d cutoff
# 2025-12-12); twitter-dead-at was stale — cleared.
twitter: example_handle

Cleared a twitter-handle-resolved-unknown-at — handle validity is enough here, no recent-date needed:

# 2026-06-10 maintain-curators: @SmarDex resolves to a live, correctly-named
# profile; handle-resolution marker was a false positive — cleared.
twitter: SmarDex

Retained a twitter-dead-at because recency could not be confirmed — record that the handle is correct but no in-window post was found, so the next run does not re-clear it wrongly:

# 2026-06-10 maintain-curators: handle @august_digital correct (live profile),
# but no post within the 180-day cutoff could be confirmed (X blocks bots).
# Recency unverifiable — twitter-dead-at left in place.
twitter: august_digital
linkedin: augustdigital
linkedin-rss-hub-disabled-at: 2026-04-04
twitter-dead-at: 2026-04-06

Repointed a migrated feed:

# 2026-06-10 maintain-curators: blog migrated to Substack; repointed RSS and
# cleared stale rss-dead-at.
rss: https://example.substack.com/feed

Keep comments factual: what you checked, the evidence, the decision. UK/British English. No hype. If a check was inconclusive and you left the marker, say so — an explicit "left untouched, inconclusive" comment stops the next run repeating the same dead-end.

Step 5: Verify the files still load

After edits, confirm every curator file still parses through the strict schema and that you have not accidentally disabled a feeder you meant to revive:

source .local-test.env && PYTHONPATH="$(pwd):$PYTHONPATH" poetry run python - <<'PY'
from pathlib import Path
from eth_defi.feed.sources import load_feeder_metadata, load_post_sources

for path in sorted(Path("eth_defi/data/feeds/curators").glob("*.yaml")):
    load_feeder_metadata(path)  # raises if the file no longer validates

sources, skipped, aliases = load_post_sources()
curator_sources = [s for s in sources if s.role == "curator"]
print(f"ok — {len(curator_sources)} live curator sources, {skipped} skipped")
PY

A file that fails here usually means a stray key was added (schema is strict) or indentation was disturbed — fix before reporting.

Step 6: Report

Summarise as a table plus notes:

Feeder Marker found Verdict Action
smardex twitter-handle-resolved-unknown-at handle resolves (live profile) cleared
b-cube-ai twitter-dead-at latest post Oct 2025, past 180d cutoff retained (genuinely stale)
august-digital twitter-dead-at handle correct, recency unverifiable retained
candle-effect — (website) NXDOMAIN, no successor website-dead comment

Then list, briefly:

  • Markers cleared, with the evidence — for twitter-dead-at the concrete in-window post date, for twitter-handle-resolved-unknown-at the live handle.
  • Sources repointed (old → new handle/URL).
  • Entities tombstoned, with why.
  • Markers left untouched and why (inconclusive, bridge-level, or genuinely dead-but-correct).
  • The result of the Step 5 load check.

Constraints and gotchas

  • Schema is strict. Only the keys in _MAPPING_SCHEMA (eth_defi/feed/sources.py) are allowed. Record anything else (website deadness, tombstones, rationale) as YAML comments.
  • WebFetch of x.com/linkedin.com is unreliable (402/403). Use WebSearch for social liveness; reserve WebFetch/curl for websites and RSS.
  • Do not clear twitter-dead-at on a guess — the scanner re-stamps idle accounts. Only clear with positive evidence of recent activity.
  • Never edit alias files (canonical-feeder-id:) — they hold no sources.
  • Append-only spirit: the Python mark_* helpers append marker lines; when clearing you edit by hand, but keep every other line byte-identical.
  • Convert any relative dates ("2 days ago") to absolute YYYY-MM-DD using today's date.
  • Keep the working set small if the user passed a subset; otherwise process all flagged files, but you do not need to comment on unflagged, healthy entries.
Install via CLI
npx skills add https://github.com/tradingstrategy-ai/web3-ethereum-defi --skill maintain-curators
Repository Details
star Stars 821
call_split Forks 182
navigation Branch main
article Path SKILL.md
More from Creator
tradingstrategy-ai
tradingstrategy-ai Explore all skills →