feat(miner-discovery-plane): define the public-data-only discovery-index API contract - #4436
Conversation
…dex API contract The optional hosted discovery-index plane (JSONbored#4250, maintainer-only, explicitly blocked on this contract) exists to mitigate the rate-limit incident already fixed once for the review stack (JSONbored#1936): one shared GitHub-metadata crawler across the miner fleet instead of every miner independently hammering the same repos' search/listing endpoints. This defines the client-side request/response contract both sides build against — schema/shape only, no server, no deployed endpoint, no client HTTP (those are JSONbored#4250 and the sibling soft-claim issue). - packages/gittensory-engine/src/discovery-index-contract.ts: * DiscoveryIndexQuery / DiscoveryIndexRequest — scope (repos/orgs/searchTerms) plus clamped limit + opaque cursor, via a tolerant normalizer. * DiscoveryIndexCandidate — metadata-only, field-for-field compatible with opportunity-ranker.js's normalizeCandidate, so rankCandidateIssues consumes hosted results exactly like a local fan-out. * DiscoveryIndexResponse — candidate list + forward nextCursor + version. * The Phase 1 boundary is enforced in code, not just documented: discoveryIndexBoundaryViolations lists any forbidden field, and normalizeDiscoveryIndexCandidate rejects (returns null) — rather than silently trimming — any candidate carrying score/reward/wallet/hotkey/ source-content fields, so a misbehaving server can't smuggle raw economic/identity/source data past the contract. - packages/gittensory-engine/src/index.ts: barrel re-export of the contract. - packages/gittensory-miner/docs/discovery-index-contract.md: field reference, mirroring the miner-goal-spec.md style, for this repo's future JSONbored#4250 server and any third-party consumer. - test/unit/discovery-index-contract.test.ts: full coverage of the normalizers and the boundary enforcement. Closes JSONbored#4300
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4436 +/- ##
==========================================
+ Coverage 93.95% 93.96% +0.01%
==========================================
Files 412 413 +1
Lines 37212 37292 +80
Branches 13591 13624 +33
==========================================
+ Hits 34963 35043 +80
Misses 1594 1594
Partials 655 655
🚀 New features to boost your workflow:
|
|
Tip 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 ✅ Gittensory review result - approve/merge recommendedReview updated: 2026-07-09 18:39:00 UTC
✅ Suggested Action - Approve/Merge
Review summary Nits — 5 non-blocking
Review context
Contributor next steps
Signal definitions
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
Closes #4300
What
Defines the client-side discovery-index API contract — the typed request/response shape a miner uses to query the optional hosted discovery-index service. The server-side counterpart (#4250, maintainer-only) is explicitly blocked on this contract being defined first; this issue is that contract, scoped to schema/shape only — no server, no deployed endpoint, no client HTTP (those are #4250 and the sibling soft-claim-coordination issue).
The plane exists to mitigate the rate-limit incident already fixed once for the review stack (#1936): one shared GitHub-metadata crawler across the fleet instead of every miner instance independently hammering the same repos' search/listing endpoints.
Design
Lives in
@jsonbored/gittensory-engine(shared by both sides) and follows the tolerant-parser convention already established byminer-goal-spec.ts/fleet-run-manifest.ts— every field optional, malformed input degrades to a documented default with a warning rather than throwing.DiscoveryIndexQuery) —repos(owner/repo),orgs(owner),searchTerms, alimitfloored+clamped to[1,200], and an opaque forwardcursor. Invalid entries skipped, deduped, capped at 200;normalizeDiscoveryIndexRequestnever throws.DiscoveryIndexResponse) — acandidateslist plus a forwardnextCursor. EachDiscoveryIndexCandidateis field-for-field compatible withopportunity-ranker.js'snormalizeCandidate(owner,repo,repoFullName,issueNumber,title,labels,commentsCount,createdAt,updatedAt,htmlUrl,aiPolicyAllowed,aiPolicySource), sorankCandidateIssuesconsumes hosted results exactly like a local fan-out — a miner can swap a local fan-out for a hosted query without the ranker changing.Boundary enforced in code
The Phase 1 boundary (
cross-repo-discovery-phase1.md:13-14,54— metadata-only, GET/list-only, no raw scores/rewards/wallet/hotkey/source data) is enforced, not just documented:discoveryIndexBoundaryViolations(raw)returns any forbidden field present on a raw object (DISCOVERY_INDEX_FORBIDDEN_FIELDS:score,reward,wallet,hotkey,coldkey,mnemonic,payout,ranking,rawtrust,trustscore,sourcecontent,diff,patch).normalizeDiscoveryIndexCandidaterejects (returnsnull) — rather than silently trimming — any candidate carrying one, so a misbehaving server can't smuggle economic/identity/source data past the contract.Files
packages/gittensory-engine/src/discovery-index-contract.ts— the contract + normalizers.packages/gittensory-engine/src/index.ts— barrel re-export.packages/gittensory-miner/docs/discovery-index-contract.md— field reference (mirrorsminer-goal-spec.md).test/unit/discovery-index-contract.test.ts— tests.Testing
16/16 tests pass; typecheck clean. New engine file at 100% line + branch coverage (75/75, 108/108, 13/13).