···1010 */
11111212export type RepoHealthState =
1313- /** Enrolled and at least one ref has synced. Nothing to do. */
1313+ /** Mirrored on tangled (the knot holds the repo). Nothing to do. */
1414 | 'ok'
1515- /** Enrolled but nothing has synced yet: backfill/first push still in flight. */
1616- | 'waiting'
1717- /** Not yet mirrored on tangled (enrolment hasn't completed). */
1515+ /** Enrolment hasn't produced a tangled mirror yet. */
1816 | 'enrolling'
1917 /** User paused sync from the dashboard. */
2018 | 'paused'
···3634 lastError: string | null
3735 disabledAt: string | null
3836 tangledRepoDid: string | null
3939- refCount: number
4037}
41384239/**
···9794 }
9895 }
9996100100- if (!input.tangledRepoDid) {
9797+ // The knot clones the whole repo from `source` at enrolment, so a mapping
9898+ // with a `tangledRepoDid` and `active` status is mirrored regardless of
9999+ // whether we've relayed a push since. `refCount` counts only pushes we've
100100+ // observed after enrolment, so it is NOT a completeness signal: a repo that
101101+ // simply hasn't been pushed to since enrolment still has `refCount === 0`
102102+ // while being fully up to date on the knot.
103103+ if (!input.tangledRepoDid || input.status !== 'active') {
101104 return { state: 'enrolling', message: 'Setting up the mirror on tangled. This can take a minute.', needsAttention: false }
102105 }
103106104104- if (input.refCount === 0) {
105105- return { state: 'waiting', message: 'Enrolled, waiting for the first sync. New installs backfill in the background.', needsAttention: false }
106106- }
107107-108108- return { state: 'ok', message: 'Mirrored and up to date.', needsAttention: false }
107107+ return { state: 'ok', message: 'Mirrored and syncing.', needsAttention: false }
109108}
+14-7
test/unit/repo-health.spec.ts
···77 lastError: null,
88 disabledAt: null,
99 tangledRepoDid: 'did:plc:repo',
1010- refCount: 1,
1110 ...overrides,
1211 }
1312}
14131514describe('repoHealth', () => {
1616- it('reports ok for an active mapping with synced refs', () => {
1515+ it('reports ok for an active mapping with a tangled mirror', () => {
1716 const h = repoHealth(input())
1817 expect(h.state).toBe('ok')
1918 expect(h.needsAttention).toBe(false)
2019 })
21202222- it('reports waiting for an enrolled mapping that has synced nothing yet', () => {
2323- const h = repoHealth(input({ refCount: 0 }))
2424- expect(h.state).toBe('waiting')
2525- expect(h.needsAttention).toBe(false)
2121+ it('reports ok for a mirrored repo even when no push has been relayed since enrolment', () => {
2222+ // The knot clones the whole repo at enrolment, so an active mapping with a
2323+ // repoDid is up to date regardless of relayed-push count. This is the
2424+ // majority case (repos not pushed to since install) and must not read as
2525+ // "waiting for the first sync".
2626+ const h = repoHealth(input({ tangledRepoDid: 'did:plc:repo' }))
2727+ expect(h.state).toBe('ok')
2628 })
27292830 it('reports enrolling before the tangled repo exists', () => {
2929- const h = repoHealth(input({ tangledRepoDid: null, refCount: 0 }))
3131+ const h = repoHealth(input({ tangledRepoDid: null }))
3232+ expect(h.state).toBe('enrolling')
3333+ })
3434+3535+ it('reports enrolling while status is still pending', () => {
3636+ const h = repoHealth(input({ status: 'pending', tangledRepoDid: null }))
3037 expect(h.state).toBe('enrolling')
3138 })
3239