Skip to content

/new-source skill — contract

Project-local Claude Code skill at .claude/skills/new-source/SKILL.md. Invoked with /new-source <name> [landscape-id] to onboard a new data source.

Why this exists

Adding a data source is OGUR's most-repeated workstream — roughly one in three recent merged PRs (wires #130, cninfo #124/#132, HKEX #120/#131, CDE #138, SEC variants #84/#118/#128, patent backends #119/#154). The mechanical recipe already lives in CONTRIBUTING — Adding a new data source. What a checklist can't capture is the part that actually burns time:

  1. Feasibility is unknown until you probe. The same four sirna_plan_*.md docs were hand-written, each re-deriving "does this source have an API, what's the auth, does it even recover anything new." CDE (#138) is the canonical example — its direct API is Ruishu-WAF-blocked, and only a Phase-0 probe surfaced that before integration code existed. A naive build would have shipped a source that 403s.
  2. The same gotchas recur every time. compute_hash as the dedup key, event_date population (#90/#98/#103), the source_filters opt-in with the non-dict guard (#138), hermetic tests (#121), and the "feed the corpus, never fork the matcher" boundary (the CDE/CJK split). Each new source has re-learned at least one of these the hard way.

/new-source converts the recipe-you-must-remember into a procedure with a hard feasibility gate and the gotchas baked in.

The contract — two phases, one gate

Phase Output Gate
0 — Probe Throwaway probe_<name>.py + a docs/development/<name>_plan.md in the workstream template. Answers: programmatic access (REST/GraphQL/OAuth/RSS/WAF), auth, rate limit + paging, relevance (does it recover corpus-missing assets?), signal shape. Ends in an explicit GO or NO-GO/REFRAME verdict, surfaced to the user before any ogur/sources/ code.
1 — Scaffold ogur/sources/<name>.py + hermetic tests/unit/sources/test_<name>.py + seed registration + source_filters opt-in + docs/data-sources.md entry. Pass criterion is recovery of the named assets (via inspect_sirna_seed.py), not a green suite. make lint && make test-fast must also pass.

Baked-in invariants

Invariant Why Enforced where
Source.compute_hash(name, source_id, signal_type) is the only hashing scheme DB-level dedup unique constraint depends on it ogur/sources/base.py:28
event_date populated from the real event timestamp Temporal layer (ordering, recency rendering) reads it detector + frontend (#90/#98/#103)
source_filters.<name> opt-in, default off, with a non-dict guard Existing indication landscapes must stay unchanged; a "yes" value must not crash ADR-0005; #138 Codex fix
Never fork the matcher — emit raw provenance (incl. raw CJK), normalize via config not code Keeps sources additive; matcher changes are a separate reviewed concern CDE/CJK boundary, PR #138/#147
Hermetic tests (httpx_mock + make_landscape, no live ogur.db) Source suite must be deterministic in CI #121

Scope

  • In scope: ogur/sources/<name>.py, its test, seed registration, the source_filters block, the data-sources.md entry, and (only if a genuinely new domain) a DomainAgent.
  • Out of scope: editing matchers/aliases for recall, fixing an existing source, building the landscape to point at, anything the source can't actually deliver (if Phase 0 is no-go, the no-go is the deliverable).

How it differs from the CONTRIBUTING recipe

CONTRIBUTING recipe /new-source skill
Feasibility Assumed — you've already decided to build A Phase-0 probe that can return no-go before integration
Relevance Not addressed A coverage test against the landscape benchmark is the gate
Gotchas Listed as steps to remember Baked into the scaffold with the failing-PR references
Verification "Register in the seed script" "The named assets now carry a <name> signal" — recovery, not just green

The recipe is the reference; the skill is the executable procedure that won't skip the gate.

When to invoke

  • At the start of any "we should pull in source X" idea — invoke for Phase 0 to get a go/no-go cheaply, before committing a branch.
  • When a closed-world benchmark (e.g. the siRNA deck) has named gaps and you're hunting for a source to close them.

Future versions

  • v2 — fold in a DomainAgent decision helper (most sources reuse company; the skill currently flags this but doesn't decide it).
  • v2 — emit the coverage before/after diff as a committed artifact under archived_data/, the way the eval harnesses do, so each onboarding leaves an audit trail.
  • Pairs naturally with a future coverage-eval skill (the second-ranked candidate from the skills audit): /new-source proves recovery once; coverage-eval re-measures it on a cadence.

Implementing rule of thumb

When unsure whether to start writing the Source subclass: write the probe first. If the probe can't pull a single relevant record the corpus is missing, the source isn't worth building yet — and you've spent 30 minutes, not a PR.