Explore instance repo total needs a backend rail (follow-up to #295) #307

Closed
opened 2026-09-10 20:52:42 +00:00 by crueber · 3 comments
Owner

Follow-up to #295: PR #306 ships the top-5 most-active owners plus the instance OWNER total, but deliberately no instance REPO total — verified during review that no endpoint serves one.

Why the frontend cannot do it

  • GET /api/v1/owners/detailed rows are {name, last_commit_sha|null, last_commit_time|null} only (internal/api/owners_activity.go:35-44) — no repo counts.
  • No stats endpoint exists (the only stats shapes are per-commit numstat details, unrelated).
  • Deriving it client-side would be either a capped sum over the top-5 slice (dishonest as a total) or one listing GET per owner on the instance (the N-fetch antipattern #295 explicitly avoids).

Proposed rail (pick one, then implement end-to-end)

  1. repo_count on each owners/detailed row (preferred: the #283 catalog aggregate already walks every repo, so the count is nearly free), or
  2. a tiny GET /api/v1/stats surface returning {owner_count, repo_count\}.

Either way:

  • Through all three route twins (/api/v1, /api-browser/v1, /services/api) + discovery + SDK + docs (07_api.md, 12_web_ui.md, 14_extensibility.md seams).
  • Ghost-filtered exactly like liveRepos (manifest-backed repos only — the #295 acceptance rule).
  • /explore renders the total from the payload (no client-side recomputation), headless-tested.

Acceptance

  • /explore shows the true instance repo total (uncapped, ghost-free) from a served payload.
  • Served through all three route twins; SDK + docs updated in the same change (law 12).
Follow-up to #295: PR #306 ships the top-5 most-active owners plus the instance OWNER total, but deliberately no instance REPO total — verified during review that no endpoint serves one. ## Why the frontend cannot do it - `GET /api/v1/owners/detailed` rows are `{name, last_commit_sha|null, last_commit_time|null}` only (`internal/api/owners_activity.go:35-44`) — no repo counts. - No stats endpoint exists (the only `stats` shapes are per-commit numstat details, unrelated). - Deriving it client-side would be either a capped sum over the top-5 slice (dishonest as a total) or one listing GET per owner on the instance (the N-fetch antipattern #295 explicitly avoids). ## Proposed rail (pick one, then implement end-to-end) 1. `repo_count` on each `owners/detailed` row (preferred: the #283 catalog aggregate already walks every repo, so the count is nearly free), or 2. a tiny `GET /api/v1/stats` surface returning `{owner_count, repo_count\}`. Either way: - Through all three route twins (`/api/v1`, `/api-browser/v1`, `/services/api`) + discovery + SDK + docs (`07_api.md`, `12_web_ui.md`, `14_extensibility.md` seams). - Ghost-filtered exactly like `liveRepos` (manifest-backed repos only — the #295 acceptance rule). - `/explore` renders the total from the payload (no client-side recomputation), headless-tested. ## Acceptance - [ ] /explore shows the true instance repo total (uncapped, ghost-free) from a served payload. - [ ] Served through all three route twins; SDK + docs updated in the same change (law 12).
Author
Owner

Fixed by PR #309 (#309): repo_count on each owners/detailed row (issue option 1), sourced from the registry's manifest-gated walk — ghost-filtered like liveRepos, zero added store trips. /explore renders owner + repo totals from the uncapped payload. All three twins + SDK + docs in the same change; tests green (Go -race, api cover 95.5%, node 588/588, vite build). Browser proof open per workspace rules.

Fixed by PR #309 (https://git.packden.us/crueber/walhub/pulls/309): `repo_count` on each `owners/detailed` row (issue option 1), sourced from the registry's manifest-gated walk — ghost-filtered like `liveRepos`, zero added store trips. /explore renders owner + repo totals from the uncapped payload. All three twins + SDK + docs in the same change; tests green (Go -race, api cover 95.5%, node 588/588, vite build). Browser proof open per workspace rules.
Author
Owner

Review of PR #309 (fix/issue-307, commit f95d4ee) — verified in scratch worktree, all green. No fixes pushed (nothing to fix).

Ghost-exactness (SERVE.GO:556-572): PASS. OwnerRepoCounts walks listOwners + liveRepos — the exact manifest-gated path Owners uses (SERVE.GO:587-615: Head manifest.pb per candidate). Deleted-repo litter and unborn fork prefixes excluded by construction; zero-live owners absent never zero-valued. serve_registry_test.go pins both ghost cases + litter-only-owner-absent + delete-moves-count + last-delete-removes-owner. Per-row repo_count = len(liveRepos), matches by construction.

Uncapped payload / cost: PASS. owners_detailed returns all names from the counts map — no cap, no slice. Replaces the Owners() call with the same-profile walk, so zero added store trips (07_api.md claim accurate); catalog GET unchanged. No new unboundedness on huge instances (endpoint was already O(owners); payload grows one int/row).

RepoRegistry +1 (ENV.GO:133): PASS. Real impl (serve.go:556) + both fakes (api/FAKES_TEST.GO:229, server/X_GAPS_TEST.GO:813) updated in-change, compiler-checked, no fallback branch. bin_api/discovery Owners callers untouched.

UI total honesty: PASS. Owners.jsx:160 sums uncapped doc().owners via instanceRepoTotal — never the top-5 slice; owners.test.js pins capped-sum(8) != total(13). instanceRepoTotal floors positive finite counts, ignores missing/non-numeric (legacy-server tolerant).

Caching: PASS. ownersDetailed stays ccSWR (unchanged class); SDK listDetailed keeps the #200 no-store rationale. Shared useData key unchanged.

#295 NOTE closure: PASS. Owners.jsx NOTE -> CLOSED pointer; 12_web_ui.md appends FIXED(#307) without rewriting history (law 12); 07 + 14 entries accurate (additive field rule, no new endpoint/seam/discovery template — routes + discovery tests green).

Tests (scratch worktree): internal/api -race PASS, 95.5% coverage (>=95% gate holds); cmd/walhub registry tests -race PASS (package not cover-gated — gate is internal/... only); targeted owners/discovery tests PASS; node --test 585/585 PASS (smoke.test.js excluded — hangs identically on main: needs a live server, sandbox has none); gofmt clean; go vet clean. internal/server: only TestUIAssetConcepts fails — fails identically on main (stale web/dist, needs make web; PR touches no web assets). No go.mod/npm changes in diff.

No browser drive (per instructions: tests + reasoning; no browser-facing behavior change beyond a text line fed by the tested helper).

MERGE RECOMMENDATION: ready to merge.

Review of PR #309 (fix/issue-307, commit f95d4ee) — verified in scratch worktree, all green. No fixes pushed (nothing to fix). Ghost-exactness (SERVE.GO:556-572): PASS. OwnerRepoCounts walks listOwners + liveRepos — the exact manifest-gated path Owners uses (SERVE.GO:587-615: Head manifest.pb per candidate). Deleted-repo litter and unborn fork prefixes excluded by construction; zero-live owners absent never zero-valued. serve_registry_test.go pins both ghost cases + litter-only-owner-absent + delete-moves-count + last-delete-removes-owner. Per-row repo_count = len(liveRepos), matches by construction. Uncapped payload / cost: PASS. owners_detailed returns all names from the counts map — no cap, no slice. Replaces the Owners() call with the same-profile walk, so zero added store trips (07_api.md claim accurate); catalog GET unchanged. No new unboundedness on huge instances (endpoint was already O(owners); payload grows one int/row). RepoRegistry +1 (ENV.GO:133): PASS. Real impl (serve.go:556) + both fakes (api/FAKES_TEST.GO:229, server/X_GAPS_TEST.GO:813) updated in-change, compiler-checked, no fallback branch. bin_api/discovery Owners callers untouched. UI total honesty: PASS. Owners.jsx:160 sums uncapped doc().owners via instanceRepoTotal — never the top-5 slice; owners.test.js pins capped-sum(8) != total(13). instanceRepoTotal floors positive finite counts, ignores missing/non-numeric (legacy-server tolerant). Caching: PASS. ownersDetailed stays ccSWR (unchanged class); SDK listDetailed keeps the #200 no-store rationale. Shared useData key unchanged. #295 NOTE closure: PASS. Owners.jsx NOTE -> CLOSED pointer; 12_web_ui.md appends FIXED(#307) without rewriting history (law 12); 07 + 14 entries accurate (additive field rule, no new endpoint/seam/discovery template — routes + discovery tests green). Tests (scratch worktree): internal/api -race PASS, 95.5% coverage (>=95% gate holds); cmd/walhub registry tests -race PASS (package not cover-gated — gate is internal/... only); targeted owners/discovery tests PASS; node --test 585/585 PASS (smoke.test.js excluded — hangs identically on main: needs a live server, sandbox has none); gofmt clean; go vet clean. internal/server: only TestUIAssetConcepts fails — fails identically on main (stale web/dist, needs make web; PR touches no web assets). No go.mod/npm changes in diff. No browser drive (per instructions: tests + reasoning; no browser-facing behavior change beyond a text line fed by the tested helper). MERGE RECOMMENDATION: ready to merge.
Author
Owner

Fixed by PR #309 (review clean; ghost-exact counts, honest uncapped totals; gates green), merged. Closing.

Fixed by PR #309 (review clean; ghost-exact counts, honest uncapped totals; gates green), merged. Closing.
crueber added this to the v1 milestone 2026-09-10 22:27:06 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
crueber/walhub#307
No description provided.