Add AI topics, a sources discovery command, and intelligence-radar's pointer sources.

Topics ai.benchmarks, ai.pricing and ai.models; 'sources --repo' lists
visible sources matching a repo's topics. Registers the five ir-* URL
sources contributed by intelligence-radar (IR-WP-0010-T04).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 7780@bnt-lap001
Assistant-Session: 622cd705-cb31-4322-ba08-dd42e44fa583
This commit is contained in:
tegwick 2026-10-06 00:31:51 +02:00
parent ac8227eead
commit c810fe6dfe
11 changed files with 117 additions and 2 deletions

View file

@ -106,6 +106,7 @@ Python 3.11+ standard library only; no install step.
| `python3 -m inbound_library run-due` | one timer wake; safe to repeat |
| `python3 -m inbound_library new --repo <r> [--topic t] [--since iso]` | pull notices |
| `python3 -m inbound_library show <item>` / `get <item>` | derived text / original bytes |
| `python3 -m inbound_library sources --repo <r> [--topic t]` | registered sources a repo may see that match its topics (URL sources are pointers, never fetched) |
| `python3 -m inbound_library health` | last scan, silence, quarantine, pending notices |
Retained mail lives in the gitignored `data/` (see `docs/data-contracts.md`).

View file

@ -76,6 +76,10 @@ subscriptions may only use registered topics.
| `cadence` | `daily`, `weekly` or `irregular`; daily and weekly email sources are watched for silence (36 h or 9 days) |
| `visibility` | `shared` (any matching repo) or `restricted` (owner only) |
A `url` source is a pointer. The library does not fetch it, so it produces no
items or notices. Repos find these sources with `sources --repo <r>`. Extra
descriptive fields such as `publisher` and `notes` are allowed.
## Subscription
| Field | Meaning |

View file

@ -81,6 +81,9 @@ def main(argv=None):
get = sub.add_parser('get', help='write the original bytes of an item to stdout')
get.add_argument('item')
sub.add_parser('health', help='last scan, per-source counts, silence, quarantine')
sources = sub.add_parser('sources', help='list registered sources a repo may see, by topic')
sources.add_argument('--repo', required=True)
sources.add_argument('--topic', help='limit to one topic (default: the repo subscription topics)')
args = parser.parse_args(argv)
root = Path(args.root)
config = _config(root, args.config)
@ -121,6 +124,9 @@ def main(argv=None):
if args.command == 'get':
sys.stdout.buffer.write(store.get_item(root, args.item))
return 0
if args.command == 'sources':
_print(reg.sources_for(reg.load(root), args.repo, args.topic))
return 0
if args.command == 'health':
report = notify.health(root)
_print(report)

View file

@ -120,3 +120,20 @@ def subscribers_for(registry, source_id):
set(source.get('topics', [])) & set(subscription.get('topics', [])):
matched.append(repo)
return matched
def sources_for(registry, repo, topic=None):
"""Sources visible to a repo whose topics match its subscription (or one topic).
URL sources are pointers: the library lists them and does not fetch them.
"""
if topic:
wanted = {topic}
else:
subscription = registry['subscriptions'].get(repo, {})
wanted = set(subscription.get('topics', []))
listed = []
for ident, source in sorted(visible_sources(registry, repo).items()):
if wanted & set(source.get('topics', [])):
listed.append({key: value for key, value in source.items() if key != '_file'})
return listed

View file

@ -0,0 +1,13 @@
{
"id": "ir-anthropic-pricing",
"owner_repo": "intelligence-radar",
"channel": "url",
"url": "https://claude.com/pricing",
"topics": [
"ai.pricing"
],
"cadence": "irregular",
"visibility": "shared",
"publisher": "Anthropic",
"notes": "Primary source captured by intelligence-radar (catalog/sources.json id anthropic-pricing). Pointer only; inbound-library does not fetch it."
}

View file

@ -0,0 +1,13 @@
{
"id": "ir-anthropic-sonnet-launch",
"owner_repo": "intelligence-radar",
"channel": "url",
"url": "https://www.anthropic.com/news/claude-sonnet-5",
"topics": [
"ai.models"
],
"cadence": "irregular",
"visibility": "shared",
"publisher": "Anthropic",
"notes": "Primary source captured by intelligence-radar (catalog/sources.json id anthropic-sonnet-launch). Pointer only; inbound-library does not fetch it."
}

View file

@ -0,0 +1,13 @@
{
"id": "ir-anthropic-sonnet",
"owner_repo": "intelligence-radar",
"channel": "url",
"url": "https://www.anthropic.com/claude/sonnet",
"topics": [
"ai.models"
],
"cadence": "irregular",
"visibility": "shared",
"publisher": "Anthropic",
"notes": "Primary source captured by intelligence-radar (catalog/sources.json id anthropic-sonnet). Pointer only; inbound-library does not fetch it."
}

View file

@ -0,0 +1,14 @@
{
"id": "ir-openrouter-models",
"owner_repo": "intelligence-radar",
"channel": "url",
"url": "https://openrouter.ai/api/v1/models",
"topics": [
"ai.models",
"ai.pricing"
],
"cadence": "irregular",
"visibility": "shared",
"publisher": "OpenRouter",
"notes": "Primary source captured by intelligence-radar (catalog/sources.json id openrouter-models). Pointer only; inbound-library does not fetch it."
}

View file

@ -0,0 +1,13 @@
{
"id": "ir-swe-bench",
"owner_repo": "intelligence-radar",
"channel": "url",
"url": "https://www.swebench.com/",
"topics": [
"ai.benchmarks"
],
"cadence": "irregular",
"visibility": "shared",
"publisher": "SWE-bench maintainers",
"notes": "Primary source captured by intelligence-radar (catalog/sources.json id swe-bench). Pointer only; inbound-library does not fetch it."
}

View file

@ -1,7 +1,10 @@
{
"version": 2,
"version": 3,
"topics": {
"intelligence.daily-brief": "Daily editorial brief on intelligence and AI (automation-generated, unreviewed)",
"intelligence.weekly-brief": "Weekly editorial brief on intelligence and AI (automation-generated, unreviewed)"
"intelligence.weekly-brief": "Weekly editorial brief on intelligence and AI (automation-generated, unreviewed)",
"ai.benchmarks": "Capability benchmarks and leaderboards for AI systems (primary maintainer pages)",
"ai.pricing": "Published prices and availability of AI models and APIs",
"ai.models": "Model releases, model cards and provider model catalogs"
}
}

View file

@ -98,6 +98,24 @@ class Library(unittest.TestCase):
self.assertEqual(reg.validate(registry), [])
self.assertEqual(reg.subscribers_for(registry, 'arxiv-agents'), ['markitect'])
def test_sources_lists_visible_matches_only(self):
write(self.root / 'registry' / 'sources' / 'arxiv-agents.json', {
'id': 'arxiv-agents', 'owner_repo': 'intelligence-radar', 'channel': 'url',
'url': 'https://arxiv.org/list/cs.MA/recent', 'topics': ['research.papers'],
'cadence': 'irregular', 'visibility': 'shared'})
write(self.root / 'registry' / 'sources' / 'private.json', {
'id': 'private', 'owner_repo': 'intelligence-radar', 'channel': 'url',
'url': 'https://example.org', 'topics': ['research.papers'],
'cadence': 'irregular', 'visibility': 'restricted'})
write(self.root / 'registry' / 'subscriptions' / 'markitect.json', {
'repo': 'markitect', 'agent': 'markitect', 'topics': ['research.papers'],
'channels': ['pull']})
registry = reg.load(self.root)
self.assertEqual([s['id'] for s in reg.sources_for(registry, 'markitect')], ['arxiv-agents'])
self.assertEqual([s['id'] for s in reg.sources_for(registry, 'intelligence-radar',
'research.papers')],
['arxiv-agents', 'private'])
# Intake
def test_scan_retains_message_attachment_and_provenance(self):
self.drop('1.eml', mail('Grok <noreply@x.ai>', 'Daily brief', 'Body text', b'attached'))