Tasuki Keifu: Athlete Relationship Labels and Scoring Plan
Athlete Relationship Labels and Scoring Plan
Document date: October 25, 2026. This plan fixes proposed label semantics, the next relationship-cache payload, and a first scoring formula before changing the front end. The distributions below were observations of the then-current production cache; thresholds, scores, and page rules were proposed for the next iteration, not reported as shipped behavior.
1. Label Vocabulary
The proposed informational labels were:
| Label key | User meaning |
|---|---|
same_hometown | Same hometown |
same_junior_high_team | Junior-high teammates for X years |
same_junior_high_origin | Attended the same junior high school, possibly at different times |
same_high_school_team | High-school teammates for X years |
same_high_school_origin | Attended the same high school, possibly at different times |
same_university_team | University teammates for X years |
same_university_origin | Attended the same university, possibly at different times |
same_corporate_team_origin | Both belonged to the same corporate running team at some point |
An “X years together” label applies only to junior high, high school, and university. A shared-school history does not require temporal overlap. Corporate-team history is shown without an overlap duration in this version.
The three proposed competition labels were frequent_matchup (“Repeated matchups”), long_term_matchup (“Long-running matchups”), and cross_stage_matchup (“Matchups across career stages”). User-facing labels should carry stable meaning; numeric signals remain in the payload for ranking and later tuning. A feature state such as “head-to-head details available” is not a relationship label.
2. Boundaries of Informational Signals
The existing Membership data had relatively complete start and end dates for junior-high, high-school, and university organizations. School years are also natural units for explaining an overlap. Since PlayerRelationCache is a derived JSON cache, adding teamOverlapYears to the payload is comparatively small in scope. These facts support “teammates for X years” at those three stages.
Corporate-team dates were less reliable: joining and leaving dates were often missing, and transfers or transition periods were more complicated. Reporting “X years” there would create false precision. The first version therefore allows “Shared corporate-team history,” but does not calculate corporate-team overlap years or include them in teamOverlapYears.
3. Thresholds From Observed Relationship Data
The aim was to avoid labels that applied to nearly everyone or almost no one. The source analysis examined 90,486 relationship edges from PlayerRelationCache.payload.topRelations. matchupYearCount counted distinct years in which both athletes appeared in the same Race.
| Observed condition | Edges |
|---|---|
matchupCount >= 3 | 11,667 |
matchupCount >= 4 | 6,132 |
matchupCount >= 5 | 3,221 |
matchupYearCount >= 2 | 20,893 |
matchupYearCount >= 3 | 6,791 |
matchupYearCount >= 4 | 2,163 |
stageCount >= 2 | 4,435 |
stageCount >= 3 | 83 |
The first proposed cutoffs were:
| Label | Condition | Approximate coverage |
|---|---|---|
| Repeated matchups | matchupCount >= 4 | 6.8% |
| Long-running matchups | matchupYearCount >= 3 | 7.5% |
| Cross-stage matchups | stageCount >= 2 | 4.9% |
Two years would make the long-running label too broad; three career stages would make cross-stage too rare. The selected cutoffs leave useful overlap without making all three labels synonymous.
4. Proposed Cache Schema
No new primary business table was proposed. The next version would extend the derived PlayerRelationCache.payload:
type RelationStageKey =
| "junior_high_school"
| "high_school"
| "university"
| "corporate_team";
type PlayerRelationContextV2 = {
sameHometown: boolean;
sharedOrigins: {
juniorHighSchool: boolean;
highSchool: boolean;
university: boolean;
corporateTeam: boolean;
};
teamOverlapYears: {
juniorHighSchool?: number;
highSchool?: number;
university?: number;
};
};
type PlayerRelationMatchupSignals = {
matchupCount: number;
matchupYearCount: number;
stageCount: number;
ekidenCount: number;
latestMatchAt: string | null;
};
type PlayerRelationLabels = {
info: Array<
| "same_hometown"
| "same_junior_high_team"
| "same_junior_high_origin"
| "same_high_school_team"
| "same_high_school_origin"
| "same_university_team"
| "same_university_origin"
| "same_corporate_team_origin"
>;
matchup: Array<
| "frequent_matchup"
| "long_term_matchup"
| "cross_stage_matchup"
>;
};
type PlayerRelationEntryV2 = {
relatedPersonId: string;
context: PlayerRelationContextV2;
matchupSignals: PlayerRelationMatchupSignals;
labels: PlayerRelationLabels;
hasHeadToHeadDetail: boolean;
latestCompetitionEditionId: string | null;
latestCompetitionName: string | null;
score?: number;
scoreBreakdown?: Record<string, number>;
};
type PlayerRelationCachePayloadV2 = {
personId: string;
generatedAt: string;
topRelations: PlayerRelationEntryV2[];
};
Label generation uses the underlying facts: matching hometowns; a shared school with overlap for teammate labels; a shared school without requiring overlap for origin labels; and shared corporate-team history without requiring overlap. Competition labels use the three numeric cutoffs above.
5. Copy and Page Naming
Examples of informational display copy include “Same hometown,” “Junior-high teammates · 1 year,” “High-school teammates · 3 years,” “University teammates · 4 years,” “Same university history,” and “Shared corporate-team history.” Competition copy is “Repeated matchups,” “Long-running matchups,” and “Matchups across career stages.”
The standalone page name was Relationship Network. The proposed locale labels were ja: 関係ネットワーク, zh: 关系网络, zh-Hant: 關係網絡, en: Relationship Network, and ko: 관계 네트워크.
6. First Scoring Formula
The first score was intended to be explainable, tunable, and conservative. Labels carry semantics; numeric fields carry strength. Shared team or school background and competition intensity both affect rank. The proposal used a human-readable heuristic, without dynamic rarity weights:
finalScore = infoScore + matchupScore + recencyBonus
Informational Score
sameHometown = +3
sameJuniorHighOrigin = +5
sameHighSchoolOrigin = +6
sameUniversityOrigin = +5
sameCorporateTeamOrigin = +4
sameJuniorHighTeam = 8 + 2 * juniorHighOverlapYears
sameHighSchoolTeam = 10 + 2 * highSchoolOverlapYears
sameUniversityTeam = 9 + 1.5 * universityOverlapYears
Overlapping teammates should rank noticeably above merely attending the same institution. Shared high-school and university teams are the strongest informational signals; hometown is a light supplement. Overlap duration affects score, but should not expand without bound. Examples: four university years yield 9 + 1.5 * 4 = 15; three high-school years yield 10 + 2 * 3 = 16; attending the same university without overlap yields 5.
Competition Score
matchupScore =
6 * ln(1 + matchupCount)
+ 5 * ln(1 + matchupYearCount)
+ 4 * max(stageCount - 1, 0)
matchupCount represents frequency, matchupYearCount continuity, and stageCount breadth across career stages. Logarithmic compression prevents ten meetings from overwhelming three meetings simply through a linear count.
Recency Adjustment
latestMatchAt within 2 years => +2
latestMatchAt within 4 years => +1
older or null => +0
Recency is intentionally minor: a recent meeting should not outweigh a durable strong relationship. Missing latestMatchAt gives neither a bonus nor a penalty.
The observed cache was dominated by ordinary co-appearances, with many edges at matchupCount = 1. Shared team and school signals were rarer but meaningful, and many edges lacked a latest-match date. The proposed ranking should put four-year university teammates who also met often near the top; avoid letting a shared university alone outrank a sustained competition relationship; rank ten matchups above three; give cross-stage meetings a distinct effect; and prevent hometown alone from dominating.
7. Relationship Pages and Eligibility
The proposed first page design had two views using one relationship result, one display cutoff, and one sort order: a section on the athlete profile showing the top six, and a standalone Relationship Network page showing all qualifying pairs with pagination.
The standalone page would open for athletes with at least eight race results. The source counted 1,780 athletes at five or more results, 1,060 at eight or more, 838 at ten or more, and 484 at fifteen or more. Eight was selected to limit the full page without excluding too many moderately documented athletes. These were observed counts used to select a proposed gate.
The gate applies only to the standalone page. A less documented athlete can still show a relationship section if qualifying pairs exist. A qualifying athlete with no qualifying pairs should not show an empty profile section.
The proposed pair-level display cutoff was rawScore >= 15. At 10, weaker relationships survived too often for athletes with few races; at 20, too many athletes dropped to zero to two visible relationships. The profile would take six highest pairs from the >= 15 set; the standalone page would show all of them.
The first sort order was fixed:
rawScore DESCmatchupCount DESClatestMatchAt DESCrelatedPersonId ASC
rawScore is internal for eligibility and ranking. displayScore maps to 0–100 with one decimal place for the standalone page. The profile section shows no score. It shows up to three labels per relationship. The standalone page shows all labels, ordered by contribution to that relationship, with displayScore on the right and no extra explanatory sentence.
8. Cache and Pagination Strategy
The profile section should keep reading PlayerRelationCache: it is visited more often, needs a stable top six, and should avoid request-time recomputation. The standalone page should use the same cache result. The proposal was to expand the cache from top 12 to the full set of qualifying relationships, then paginate that set at 24 items per page. No small hard cap was proposed initially.
The document rejected a split where the profile reads a cache but the standalone page recalculates everything online, or where SQL sorts raw edges before the page adds labels. Labels, signals, and scores should form a complete relationship object before rendering, keeping both pages consistent and making later filtering and debugging easier.
9. Proposed Implementation Order
- Extend
RelationStageKeywithjunior_high_school. - Extend
PlayerRelationCache.payload. - Cache the full qualifying set instead of only top 12.
- Generate informational and competition labels.
- Add
rawScore,displayScore, andscoreBreakdown. - Add the Relationship Network page with pagination.
- Adjust the athlete-profile section and entry copy.
The durable decisions in this plan were the meaning of each label, the retained numeric signals, the rationale for competition thresholds, and a shared object for both views. The exact scoring weights could still be tuned later without changing those semantics.