feat(trust-score): F2 cold-start score from public data for new agents#73
Merged
Conversation
Phase 2 trust scoring withholds a score for agents with fewer than three
endorsements, leaving brand-new agents indistinguishable from inactive
ones. This change derives a `cold_start_score` from public signals so
the onboarding response carries something honest instead of an
ambiguous null.
Sources (max contribution each):
- On-chain wallet history via Basescan (Base L2) ........ 20 pts
tx_count / 10 (cap 8), age_days / 30 (cap 7), usdc_volume / 1000 (cap 5)
- GitHub account activity (when github_url available) ... 15 pts
- ERC-8004 registry presence (local outreach scan) ...... 10 pts
Hard ceiling: 60.0 — a cold-start agent can never appear stronger than
an endorsed one. Score is `null` with basis `no_public_data` when no
source returns data (deliberate — we do not fabricate a default).
Integration: `GET /skill/trust-score/{did}` returns the new
`cold_start_*` fields ONLY when `endorser_count == 0`. As soon as
real endorsements arrive the Phase 2 score takes over.
Cache: 24h TTL, stored on `agents.cold_start_*` columns to avoid
hammering Basescan/GitHub on every lookup.
Changes:
- app/migrations/007_cold_start_score.sql — 4 columns on `agents`
- app/cold_start.py — fetchers + pure scoring + 24h-cached orchestrator
- app/main.py — non-breaking addition to `/skill/trust-score/{did}`
- tests/test_cold_start.py — 10 pure-unit tests for the scoring formula
Out of scope (documented as future work):
- `github_url` is not yet a column on `agents`; the GitHub fetcher is
wired but currently fed `None`. Activates once registration captures
the field.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/skill/trust-score/{did}.endorser_count == 0— Phase 2 takes over the moment endorsements arrive.nullwith basis"no_public_data"when no source has data. No fabricated defaults.Scoring (max per source)
erc8004_outreachConfidence:
none(no sources) →medium(1 source) →high(≥2 sources).Response shape (new fields, when
endorser_count == 0){ "did": "did:moltrust:abc...", "trust_score": null, // (Phase 2 still withholds) "endorser_count": 0, "cold_start_score": 34.2, "cold_start_basis": "onchain+erc8004", "cold_start_confidence": "high", "cold_start_computed_at": "2026-05-25T...", "cold_start_note": "Score based on public data. Will be replaced by behavioral history." }Touched files
app/migrations/007_cold_start_score.sqlagents(score, basis, confidence, computed_at)app/cold_start.pyapp/main.py/skill/trust-score/{did}— only fires whenendorser_count == 0tests/test_cold_start.pyOut of scope (intentional)
github_urlis not yet a column onagents; the GitHub fetcher is wired but fedNonefor now. Will activate once registration captures the field.None, which is the documented behaviour.Migration plan
app/migrations/007_cold_start_score.sqlon the production DB (idempotent — usesADD COLUMN IF NOT EXISTS).moltrust-api(config reload picks up the new module)./skill/trust-score/{a-new-did}— expectcold_start_*fields populated within ~8s (Basescan timeout).Test plan
pytest tests/test_cold_start.py— 10/10 pass (server scratch w/ venv)from app.main import app→ OKpython -m compileallonapp/cold_start.py+app/main.py→ cleancold_start_scorewhen wallet has on-chain historycold_start_score: null, basis: "no_public_data"🤖 Generated with Claude Code