Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/api/DashboardApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,14 @@ export const useStats = () => useDashboardQuery<Stats>('useStats', '/stats');
export const getReposQueryKey = () =>
['useReposAndWeights', '/dash/repos', undefined] as const;

export const useReposAndWeights = () =>
useDashboardQuery<Repository[]>('useReposAndWeights', '/repos');
export const useReposAndWeights = (enabled?: boolean) =>
useDashboardQuery<Repository[]>(
'useReposAndWeights',
'/repos',
undefined,
undefined,
enabled,
);

export const useLanguagesAndWeights = () =>
useDashboardQuery<LanguageWeight[]>('useLanguagesAndWeights', '/languages');
Expand Down
17 changes: 17 additions & 0 deletions src/api/ReposApi.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Repository API hooks - uses /repos endpoints
import { useApiQuery } from './ApiUtils';
import { useReposAndWeights } from './DashboardApi';
import { type RepositoryMaintainer, type RepositoryIssue } from './models';
import { type Repository, type RepositoryMiner } from './models/Dashboard';

Expand Down Expand Up @@ -29,6 +30,22 @@ export const useRepositoryConfig = (repo: string) =>
`/${encodeURIComponent(repo)}`,
);

/**
* Repository config resolved case-insensitively. `/repos/{name}` matches the
* canonical casing only, while PR records may carry a lowercased repository
* name — on a direct miss this falls back to matching the full repositories
* list by lowercased name.
*/
export const useResolvedRepositoryConfig = (
repo: string,
): Repository | null => {
const direct = useRepositoryConfig(repo);
const fallback = useReposAndWeights(direct.isError);
if (direct.data) return direct.data;
const lower = repo.toLowerCase();
return fallback.data?.find((r) => r.fullName.toLowerCase() === lower) ?? null;
};

/**
* Get maintainers (assignees) for a specific repository
* @param repo - Full repository name (e.g., "opentensor/btcli")
Expand Down
12 changes: 6 additions & 6 deletions src/components/prs/PRTimeDecayChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useMemo } from 'react';
import { Box, Typography, alpha, useTheme } from '@mui/material';
import { type Theme } from '@mui/material/styles';
import ReactECharts from 'echarts-for-react';
import { useGeneralConfig, useRepositoryConfig } from '../../api';
import { useGeneralConfig, useResolvedRepositoryConfig } from '../../api';
import { STATUS_COLORS, TEXT_OPACITY } from '../../theme';
import {
echartsAxisTooltipChrome,
Expand Down Expand Up @@ -65,10 +65,10 @@ function buildGradient(opacities?: number[]) {
function resolveNowMarker(projection: DecayProjection): NowMarker | null {
if (!projection.isMerged || !projection.inWindow) return null;
if (projection.daysSinceMerge == null) return null;
if (projection.chartNowMultiplier == null) return null;
if (projection.nowMultiplier == null) return null;
return {
day: +projection.daysSinceMerge.toFixed(2),
multiplier: projection.chartNowMultiplier,
multiplier: projection.nowMultiplier,
};
}

Expand Down Expand Up @@ -272,7 +272,7 @@ function PRTimeDecayChart({
}: PRTimeDecayChartProps) {
const theme = useTheme();
const { data: generalConfig } = useGeneralConfig();
const { data: repoData } = useRepositoryConfig(repository);
const repoData = useResolvedRepositoryConfig(repository);
const params = useMemo(
() => resolveDecayParams(generalConfig, repoData?.config),
[generalConfig, repoData],
Expand Down Expand Up @@ -347,7 +347,7 @@ function PRTimeDecayChart({
</Typography>
</Box>
)}
{showNow && projection.chartNowMultiplier != null && (
{showNow && projection.nowMultiplier != null && (
<Box sx={{ textAlign: 'right' }}>
<Typography
sx={{
Expand All @@ -360,7 +360,7 @@ function PRTimeDecayChart({
lineHeight: 1.1,
}}
>
{projection.chartNowMultiplier.toFixed(2)}×
{projection.nowMultiplier.toFixed(2)}×
</Typography>
<Typography sx={{ color: muted, fontSize: '0.65rem' }}>
multiplier
Expand Down
25 changes: 23 additions & 2 deletions src/components/prs/prTimeDecayModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,21 @@ export interface DecayProjection {
chartNowMultiplier: number | null;
/** Subnet-reported multiplier (used to back-derive pre-decay score). */
currentMultiplier: number | null;
/** Multiplier to display — the subnet value wins when the curve disagrees. */
nowMultiplier: number | null;
/** True when the subnet-reported multiplier contradicts the local curve. */
curveMismatch: boolean;
preDecayScore: number | null;
chartNowScore: number | null;
}

/**
* Subnet multipliers arrive rounded to 2dp, so small drift from the local
* curve is expected; beyond this the curve params must be wrong (e.g. the
* repo config failed to load) and the subnet value is authoritative.
*/
const NOW_MULTIPLIER_TOLERANCE = 0.05;

const DEFAULT_PARAMS: DecayParams = {
graceHours: 12,
midpoint: 10,
Expand Down Expand Up @@ -176,15 +187,22 @@ export function buildDecayProjection(
earned,
currentMultiplier ?? chartNowMultiplier,
);
const curveMismatch =
currentMultiplier != null &&
chartNowMultiplier != null &&
Math.abs(currentMultiplier - chartNowMultiplier) > NOW_MULTIPLIER_TOLERANCE;
const nowMultiplier = curveMismatch ? currentMultiplier : chartNowMultiplier;
return {
isMerged,
inWindow: isWithinLookbackWindow(daysSinceMerge, params.lookbackDays),
lookbackDays: params.lookbackDays,
daysSinceMerge,
chartNowMultiplier,
currentMultiplier,
nowMultiplier,
curveMismatch,
preDecayScore,
chartNowScore: applyMultiplier(preDecayScore, chartNowMultiplier),
chartNowScore: applyMultiplier(preDecayScore, nowMultiplier),
};
}

Expand All @@ -194,5 +212,8 @@ export function buildDecaySubline(projection: DecayProjection): string {
if (projection.daysSinceMerge > projection.lookbackDays) {
return `Outside ${projection.lookbackDays}-day scoring window.`;
}
return `${projection.daysSinceMerge.toFixed(1)} day(s) since merge`;
const base = `${projection.daysSinceMerge.toFixed(1)} day(s) since merge`;
return projection.curveMismatch
? `${base} · showing subnet-reported multiplier (curve is approximate)`
: base;
}
Loading