diff --git a/app/pages/dashboard.vue b/app/pages/dashboard.vue index ae2a703..e327f1c 100644 --- a/app/pages/dashboard.vue +++ b/app/pages/dashboard.vue @@ -184,10 +184,9 @@ async function logout() { const HEALTH_ORDER: Record = { error: 0, enrolling: 1, - waiting: 2, - info: 3, - paused: 4, - ok: 5, + info: 2, + paused: 3, + ok: 4, } const sortedRepos = computed(() => { const list = data.value?.repos ?? [] @@ -359,12 +358,12 @@ function fmtDate(iso: string | null): string { {{ data.summary.needsAttention }} {{ data.summary.needsAttention === 1 ? 'repository needs' : 'repositories need' }} attention. - + -
+
- {{ data.summary.waiting }} {{ data.summary.waiting === 1 ? 'repository is' : 'repositories are' }} still catching up. This is normal right after installing. + {{ data.summary.enrolling }} {{ data.summary.enrolling === 1 ? 'repository is' : 'repositories are' }} still setting up on tangled. This is normal right after installing.
@@ -850,9 +849,7 @@ code { .badge-ok { border-color: var(--color-ok); color: var(--color-ok); } .badge-ok::before { content: "●"; } -.badge-waiting, .badge-enrolling { border-color: var(--color-warn); color: var(--color-warn); } -.badge-waiting::before, .badge-enrolling::before { content: "◐"; } .badge-info { border-color: var(--color-accent-dim); color: var(--color-accent-dim); } .badge-info::before { content: "ℹ"; } diff --git a/server/api/me/dashboard.get.ts b/server/api/me/dashboard.get.ts index 6d64bc8..0ec5615 100644 --- a/server/api/me/dashboard.get.ts +++ b/server/api/me/dashboard.get.ts @@ -23,7 +23,7 @@ export interface DashboardRepo { export interface DashboardSummary { total: number ok: number - waiting: number + enrolling: number needsAttention: number } @@ -108,7 +108,6 @@ export default defineEventHandler(async (event): Promise => { lastError: row.lastError, disabledAt, tangledRepoDid: row.tangledRepoDid, - refCount: refKeys.length, }), } }) @@ -116,7 +115,7 @@ export default defineEventHandler(async (event): Promise => { const summary: DashboardSummary = { total: repos.length, ok: repos.filter(r => r.health.state === 'ok').length, - waiting: repos.filter(r => r.health.state === 'waiting' || r.health.state === 'enrolling').length, + enrolling: repos.filter(r => r.health.state === 'enrolling').length, needsAttention: repos.filter(r => r.health.needsAttention).length, } diff --git a/server/utils/repo-health.ts b/server/utils/repo-health.ts index a5954f6..232b7bc 100644 --- a/server/utils/repo-health.ts +++ b/server/utils/repo-health.ts @@ -10,11 +10,9 @@ */ export type RepoHealthState = - /** Enrolled and at least one ref has synced. Nothing to do. */ + /** Mirrored on tangled (the knot holds the repo). Nothing to do. */ | 'ok' - /** Enrolled but nothing has synced yet: backfill/first push still in flight. */ - | 'waiting' - /** Not yet mirrored on tangled (enrolment hasn't completed). */ + /** Enrolment hasn't produced a tangled mirror yet. */ | 'enrolling' /** User paused sync from the dashboard. */ | 'paused' @@ -36,7 +34,6 @@ export interface RepoHealthInput { lastError: string | null disabledAt: string | null tangledRepoDid: string | null - refCount: number } /** @@ -97,13 +94,15 @@ export function repoHealth(input: RepoHealthInput): RepoHealth { } } - if (!input.tangledRepoDid) { + // The knot clones the whole repo from `source` at enrolment, so a mapping + // with a `tangledRepoDid` and `active` status is mirrored regardless of + // whether we've relayed a push since. `refCount` counts only pushes we've + // observed after enrolment, so it is NOT a completeness signal: a repo that + // simply hasn't been pushed to since enrolment still has `refCount === 0` + // while being fully up to date on the knot. + if (!input.tangledRepoDid || input.status !== 'active') { return { state: 'enrolling', message: 'Setting up the mirror on tangled. This can take a minute.', needsAttention: false } } - if (input.refCount === 0) { - return { state: 'waiting', message: 'Enrolled, waiting for the first sync. New installs backfill in the background.', needsAttention: false } - } - - return { state: 'ok', message: 'Mirrored and up to date.', needsAttention: false } + return { state: 'ok', message: 'Mirrored and syncing.', needsAttention: false } } diff --git a/test/unit/repo-health.spec.ts b/test/unit/repo-health.spec.ts index bfee317..8e16230 100644 --- a/test/unit/repo-health.spec.ts +++ b/test/unit/repo-health.spec.ts @@ -7,26 +7,33 @@ function input(overrides: Partial = {}): RepoHealthInput { lastError: null, disabledAt: null, tangledRepoDid: 'did:plc:repo', - refCount: 1, ...overrides, } } describe('repoHealth', () => { - it('reports ok for an active mapping with synced refs', () => { + it('reports ok for an active mapping with a tangled mirror', () => { const h = repoHealth(input()) expect(h.state).toBe('ok') expect(h.needsAttention).toBe(false) }) - it('reports waiting for an enrolled mapping that has synced nothing yet', () => { - const h = repoHealth(input({ refCount: 0 })) - expect(h.state).toBe('waiting') - expect(h.needsAttention).toBe(false) + it('reports ok for a mirrored repo even when no push has been relayed since enrolment', () => { + // The knot clones the whole repo at enrolment, so an active mapping with a + // repoDid is up to date regardless of relayed-push count. This is the + // majority case (repos not pushed to since install) and must not read as + // "waiting for the first sync". + const h = repoHealth(input({ tangledRepoDid: 'did:plc:repo' })) + expect(h.state).toBe('ok') }) it('reports enrolling before the tangled repo exists', () => { - const h = repoHealth(input({ tangledRepoDid: null, refCount: 0 })) + const h = repoHealth(input({ tangledRepoDid: null })) + expect(h.state).toBe('enrolling') + }) + + it('reports enrolling while status is still pending', () => { + const h = repoHealth(input({ status: 'pending', tangledRepoDid: null })) expect(h.state).toBe('enrolling') })