nnb9: black-box internal/stats tests (bookshelf-nnb9.29) #1442

Merged
zombor merged 1 commit from bd-bookshelf-nnb9.29 into main 2026-08-13 01:30:10 +00:00
Owner

Summary

Removes the internal/stats/export_test.go re-export shim and converts handler_internal_test.go from white-box (package stats) to black-box (package stats_test) — every internal/stats/*_test.go now drives only public entry points (GetHandler, GetDashboard, GetReadingDebt, GetSurvivalCurve, ListSeriesProgress).

  • render_test.go replaces handler_internal_test.go, covering every unexported template-data-building helper in handler.go (bar-pct math, hourLabel/bucketLabel, heatmap grid, donut-slice adapters) by rendering GetHandler's real HTML path against a delimited plain-text template and asserting on the rendered body.
  • completion_race_test.go, era_vs_rating_test.go, length_sweet_spot_test.go drive their row-mapping helpers through GetDashboard's stubbed query deps.
  • reading_debt_test.go / survival_curve_test.go cover computeReadingDebt/computeSurvivalCurve and the SQL/arg builders by capturing what GetReadingDebt/GetSurvivalCurve actually send to a spy query func.
  • series_progress_test.go covers buildLibraryFilter/buildSeriesProgressQuery via ListSeriesProgress, and fetchSeriesProgressWithQuery's pagination via GetDashboard's listSeriesProgress + seriesCursor/seriesQuery params.
  • fetchSeriesProgressWithQuery's limit param was dead code (GetDashboard's only call site always passed the fixed defaultSeriesProgressLimit) — removed the unreachable default/clamp branches along with the param, since an unreachable branch is dead code per policy, not a reason to add a white-box test.

Deletes both grandfathered internal/stats entries from scripts/test_policy_check/allowlist.txt, shrinking the burn-down list.

Test plan

  • go build ./...
  • go test ./internal/stats/... — 372 specs green
  • go test ./internal/stats/... -coverprofile=... -covermode=set — 99.0% (only wire.go, excluded by the coverage gate, is uncovered)
  • make test-policy-check — OK, no net-new white-box tests
  • golangci-lint run ./internal/stats/... — 0 issues
  • make test — full unit suite green

Closes bead bookshelf-nnb9.29 on merge.

## Summary Removes the `internal/stats/export_test.go` re-export shim and converts `handler_internal_test.go` from white-box (`package stats`) to black-box (`package stats_test`) — every `internal/stats/*_test.go` now drives only public entry points (`GetHandler`, `GetDashboard`, `GetReadingDebt`, `GetSurvivalCurve`, `ListSeriesProgress`). - `render_test.go` replaces `handler_internal_test.go`, covering every unexported template-data-building helper in `handler.go` (bar-pct math, `hourLabel`/`bucketLabel`, heatmap grid, donut-slice adapters) by rendering `GetHandler`'s real HTML path against a delimited plain-text template and asserting on the rendered body. - `completion_race_test.go`, `era_vs_rating_test.go`, `length_sweet_spot_test.go` drive their row-mapping helpers through `GetDashboard`'s stubbed query deps. - `reading_debt_test.go` / `survival_curve_test.go` cover `computeReadingDebt`/`computeSurvivalCurve` and the SQL/arg builders by capturing what `GetReadingDebt`/`GetSurvivalCurve` actually send to a spy query func. - `series_progress_test.go` covers `buildLibraryFilter`/`buildSeriesProgressQuery` via `ListSeriesProgress`, and `fetchSeriesProgressWithQuery`'s pagination via `GetDashboard`'s `listSeriesProgress` + `seriesCursor`/`seriesQuery` params. - `fetchSeriesProgressWithQuery`'s `limit` param was dead code (`GetDashboard`'s only call site always passed the fixed `defaultSeriesProgressLimit`) — removed the unreachable default/clamp branches along with the param, since an unreachable branch is dead code per policy, not a reason to add a white-box test. Deletes both grandfathered `internal/stats` entries from `scripts/test_policy_check/allowlist.txt`, shrinking the burn-down list. ## Test plan - [x] `go build ./...` - [x] `go test ./internal/stats/...` — 372 specs green - [x] `go test ./internal/stats/... -coverprofile=... -covermode=set` — 99.0% (only `wire.go`, excluded by the coverage gate, is uncovered) - [x] `make test-policy-check` — OK, no net-new white-box tests - [x] `golangci-lint run ./internal/stats/...` — 0 issues - [x] `make test` — full unit suite green Closes bead bookshelf-nnb9.29 on merge.
nnb9: black-box internal/stats tests (bookshelf-nnb9.29)
All checks were successful
/ Test Race (pull_request) Successful in 1m57s
/ E2E API (pull_request) Successful in 1m32s
/ JS Unit Tests (pull_request) Successful in 1m6s
/ Coverage (pull_request) Successful in 2m34s
/ Integration (pull_request) Successful in 2m34s
/ Lint (pull_request) Successful in 4m39s
/ E2E Browser (pull_request) Successful in 6m45s
654b4068d9
Remove the internal/stats/export_test.go re-export shim and convert
handler_internal_test.go to black-box: every internal/stats/*_test.go now
declares package stats_test and drives only public entry points
(GetHandler, GetDashboard, GetReadingDebt, GetSurvivalCurve,
ListSeriesProgress).

- render_test.go replaces handler_internal_test.go, covering every
  unexported template-data-building helper in handler.go (bar-pct math,
  hourLabel/bucketLabel, heatmap grid, donut-slice adapters) by rendering
  GetHandler's real HTML path against a delimited plain-text template and
  asserting on the rendered body.
- completion_race_test.go, era_vs_rating_test.go, length_sweet_spot_test.go
  drive their row-mapping helpers through GetDashboard's stubbed query deps.
- reading_debt_test.go / survival_curve_test.go cover computeReadingDebt/
  computeSurvivalCurve and the SQL/arg builders by capturing what
  GetReadingDebt/GetSurvivalCurve actually send to a spy query func.
- series_progress_test.go covers buildLibraryFilter/buildSeriesProgressQuery
  via ListSeriesProgress, and fetchSeriesProgressWithQuery's pagination via
  GetDashboard's listSeriesProgress + seriesCursor/seriesQuery params.
- fetchSeriesProgressWithQuery's limit param was dead code (GetDashboard's
  only call site always passed the fixed defaultSeriesProgressLimit); removed
  the unreachable default/clamp branches along with the param.

Deletes both grandfathered entries from
scripts/test_policy_check/allowlist.txt, shrinking the burn-down list.

Closes bead bookshelf-nnb9.29 on merge.
Author
Owner

Security review (adversarial, diff-only) — PR #1442 / bookshelf-nnb9.29

Reviewed the full diff origin/main...origin/bd-bookshelf-nnb9.29 for internal/stats, focused on the two production-code changes (series_progress.go, service.go) plus the white-box→black-box test conversion.

DoS/Scale surface (limit clamp removal): Traced fetchSeriesProgressWithQuery's only caller (GetDashboard in service.go:324) and confirmed there is no HTTP-exposed series_limit/tunable query param anywhere in internal/handler or internal/stats — grepped for series_limit, SeriesProgressLimit, maxSeriesProgressLimit, fetchSeriesProgressWithQuery repo-wide. Limit is now hardcoded to the defaultSeriesProgressLimit constant (50) at the single call site (series_progress.go:241), so the removed if limit <= 0 { … } / if limit > max { … } clamp was genuinely dead code — no caller could ever have supplied a limit ≤0 or >200 to begin with. Confirmed bounded: the query is still capped at 50 rows per page with cursor pagination (series_progress.go:253). No regression.

Per-user/library scoping: No scoping code was touched. service.go's one-line diff only drops the trailing limit argument from the fetchSeriesProgressWithQuery call — userID and core.libraryIDs are passed through unchanged. series_progress.go's fail-closed guard (len(libIDs) == 0 → empty page, no unscoped DB query) is untouched. The former white-box buildLibraryFilter/ExportBuildLibraryFilter unit tests (asserting 1=0 fail-closed clause when ids is nil/empty, and library_id IN (?, ?) when populated) were preserved and re-expressed as black-box coverage via the public stats.ListSeriesProgress in series_progress_test.go — scoping behavior is still under test, just exercised through the public API per the black-box test policy.

Exported symbols: Grepped the diff for +func/+type/+var/+const — every addition is confined to _test.go files, all declaring package stats_test (verified across completion_race_test.go, era_vs_rating_test.go, reading_debt_test.go, series_progress_test.go, survival_curve_test.go, render_test.go, length_sweet_spot_test.go, audio_v2_test.go). No new exported production symbol was added to series_progress.go/service.go to make this conversion pass. export_test.go and handler_internal_test.go were correctly deleted along with their corresponding scripts/test_policy_check/allowlist.txt entries (no orphaned allowlist grandfather).

Secrets/PII: grepped the full diff for password/secret/token/apikey/PEM-block patterns — none found. No secrets in new test fixtures.

No BLOCKER, MAJOR, or MINOR findings. This is a clean, mechanical black-box conversion; the production-code delta is a legitimate dead-code removal, not a behavioral or security regression.

REVIEW VERDICT: 0 blocker, 0 major, 0 minor

**Security review (adversarial, diff-only) — PR #1442 / bookshelf-nnb9.29** Reviewed the full diff `origin/main...origin/bd-bookshelf-nnb9.29` for `internal/stats`, focused on the two production-code changes (`series_progress.go`, `service.go`) plus the white-box→black-box test conversion. **DoS/Scale surface (limit clamp removal):** Traced `fetchSeriesProgressWithQuery`'s only caller (`GetDashboard` in `service.go:324`) and confirmed there is no HTTP-exposed `series_limit`/tunable query param anywhere in `internal/handler` or `internal/stats` — grepped for `series_limit`, `SeriesProgressLimit`, `maxSeriesProgressLimit`, `fetchSeriesProgressWithQuery` repo-wide. `Limit` is now hardcoded to the `defaultSeriesProgressLimit` constant (50) at the single call site (`series_progress.go:241`), so the removed `if limit <= 0 { … }` / `if limit > max { … }` clamp was genuinely dead code — no caller could ever have supplied a limit ≤0 or >200 to begin with. Confirmed bounded: the query is still capped at 50 rows per page with cursor pagination (`series_progress.go:253`). No regression. **Per-user/library scoping:** No scoping code was touched. `service.go`'s one-line diff only drops the trailing `limit` argument from the `fetchSeriesProgressWithQuery` call — `userID` and `core.libraryIDs` are passed through unchanged. `series_progress.go`'s fail-closed guard (`len(libIDs) == 0` → empty page, no unscoped DB query) is untouched. The former white-box `buildLibraryFilter`/`ExportBuildLibraryFilter` unit tests (asserting `1=0` fail-closed clause when `ids` is nil/empty, and `library_id IN (?, ?)` when populated) were preserved and re-expressed as black-box coverage via the public `stats.ListSeriesProgress` in `series_progress_test.go` — scoping behavior is still under test, just exercised through the public API per the black-box test policy. **Exported symbols:** Grepped the diff for `+func`/`+type`/`+var`/`+const` — every addition is confined to `_test.go` files, all declaring `package stats_test` (verified across `completion_race_test.go`, `era_vs_rating_test.go`, `reading_debt_test.go`, `series_progress_test.go`, `survival_curve_test.go`, `render_test.go`, `length_sweet_spot_test.go`, `audio_v2_test.go`). No new exported production symbol was added to `series_progress.go`/`service.go` to make this conversion pass. `export_test.go` and `handler_internal_test.go` were correctly deleted along with their corresponding `scripts/test_policy_check/allowlist.txt` entries (no orphaned allowlist grandfather). **Secrets/PII:** grepped the full diff for password/secret/token/apikey/PEM-block patterns — none found. No secrets in new test fixtures. No BLOCKER, MAJOR, or MINOR findings. This is a clean, mechanical black-box conversion; the production-code delta is a legitimate dead-code removal, not a behavioral or security regression. REVIEW VERDICT: 0 blocker, 0 major, 0 minor
Author
Owner

Adversarial review of PR #1442 (bookshelf-nnb9.29) — black-box conversion of internal/stats tests.

Phase 1 (spec compliance): matches the bead — export_test.go + handler_internal_test.go removed, render_test.go added, allowlist.txt trimmed to exactly the two removed files, all internal/stats test files declare package stats_test.

Phase 2 findings:

No BLOCKER/MAJOR/MINOR findings.

Verification notes (adversarial checks performed):

  1. Scale/limit-clamp removal (the flagged risk)fetchSeriesProgressWithQuery dropped its limit int param and the <=0/>max clamp logic (internal/stats/series_progress.go:219-260). Traced the only caller: GetDashboard (internal/stats/service.go:324) previously always passed the constant defaultSeriesProgressLimit (50) — never a user/request-derived value (handler.go has no ?series_limit query param). So the clamp was genuinely dead code; the query is still bounded (fixed Limit: defaultSeriesProgressLimit at series_progress.go:241, LIMIT ? still present in buildSeriesProgressQuery). maxSeriesProgressLimit (200) was removed since nothing reads it. Not a Scale violation — confirmed via series_progress_test.go "when entries returned equal the fixed page size (50), sets next cursor" which exercises the fixed-50 path end-to-end through the public GetDashboard.

  2. Anti-gaming (no unexported symbol newly exported) — diffed exported identifiers; production diff is limited to series_progress.go/service.go (comment + signature simplification only, no new exports). export_test.go (11 Export* shims) is fully deleted, not relocated/renamed.

  3. Real vs. vacuous coverage — spot-checked render_test.go (all 24 buildXxxBars/donut/mini-hist helpers from the old handler_internal_test.go have a corresponding Describe block, e.g. CATBAR:Sci-Fi:5:50| asserts a real relative-percentage computation, not an echoed stub value), series_progress_test.go, reading_debt_test.go, survival_curve_test.go — all drive production code via the public API (stats.ListSeriesProgress, stats.GetDashboard, stats.GetReadingDebt, stats.GetSurvivalCurve, stats.GetHandler) with spy/stub query funcs and assert on production-computed values (percentages, SQL text/arg positions, mapped rows). No guarded/tautological/self-validating patterns observed.

  4. allowlist.txt — diff is exactly the two removed-file lines (internal/stats/export_test.go, internal/stats/handler_internal_test.go); no other lines touched.

REVIEW VERDICT: 0 blocker, 0 major, 0 minor

Adversarial review of PR #1442 (bookshelf-nnb9.29) — black-box conversion of internal/stats tests. Phase 1 (spec compliance): matches the bead — export_test.go + handler_internal_test.go removed, render_test.go added, allowlist.txt trimmed to exactly the two removed files, all internal/stats test files declare `package stats_test`. Phase 2 findings: No BLOCKER/MAJOR/MINOR findings. Verification notes (adversarial checks performed): 1. **Scale/limit-clamp removal (the flagged risk)** — `fetchSeriesProgressWithQuery` dropped its `limit int` param and the `<=0`/`>max` clamp logic (internal/stats/series_progress.go:219-260). Traced the only caller: `GetDashboard` (internal/stats/service.go:324) previously always passed the constant `defaultSeriesProgressLimit` (50) — never a user/request-derived value (handler.go has no `?series_limit` query param). So the clamp was genuinely dead code; the query is still bounded (fixed `Limit: defaultSeriesProgressLimit` at series_progress.go:241, `LIMIT ?` still present in buildSeriesProgressQuery). `maxSeriesProgressLimit` (200) was removed since nothing reads it. Not a Scale violation — confirmed via `series_progress_test.go` "when entries returned equal the fixed page size (50), sets next cursor" which exercises the fixed-50 path end-to-end through the public `GetDashboard`. 2. **Anti-gaming (no unexported symbol newly exported)** — diffed exported identifiers; production diff is limited to series_progress.go/service.go (comment + signature simplification only, no new exports). export_test.go (11 `Export*` shims) is fully deleted, not relocated/renamed. 3. **Real vs. vacuous coverage** — spot-checked render_test.go (all 24 `buildXxxBars`/donut/mini-hist helpers from the old handler_internal_test.go have a corresponding `Describe` block, e.g. `CATBAR:Sci-Fi:5:50|` asserts a real relative-percentage computation, not an echoed stub value), series_progress_test.go, reading_debt_test.go, survival_curve_test.go — all drive production code via the public API (`stats.ListSeriesProgress`, `stats.GetDashboard`, `stats.GetReadingDebt`, `stats.GetSurvivalCurve`, `stats.GetHandler`) with spy/stub query funcs and assert on production-computed values (percentages, SQL text/arg positions, mapped rows). No guarded/tautological/self-validating patterns observed. 4. **allowlist.txt** — diff is exactly the two removed-file lines (`internal/stats/export_test.go`, `internal/stats/handler_internal_test.go`); no other lines touched. REVIEW VERDICT: 0 blocker, 0 major, 0 minor
Author
Owner

Adversarial security review of PR #1435 (bookshelf-v3bi0).

Scope: static/js/controllers/bookdrop_file_editor_controller.js + its Vitest spec only — the bookdrop:metadatacopied CustomEvent now dispatches on save success instead of optimistically.

Findings:

[MINOR] static/js/controllers/bookdrop_file_editor_controller.js:433 — _pendingDispatchCopy is a per-controller instance flag, not per-field
When a copy-triggered save collides with an in-flight save and a second _doSave call queues behind it (_savePendingAfterInflight), _pendingDispatchCopy is OR'd across whichever calls queued in that window (ctrl._pendingDispatchCopy || !!dispatchCopyEvent). This is a UI-correctness nit (the review row could auto-check slightly earlier/later than the exact field that succeeded), not a security issue — no cross-user or cross-proposal leak, since proposalIdValue on the dispatched event is scoped to the mounted controller's own row. No fix required before merge; flag only if the review-row auto-check behavior is later found flaky under rapid-copy usage.

Security assessment:

  • No XSS / unsafe DOM injection: no innerHTML touched; the event detail.proposalId is echoed from an already-rendered proposalIdValue back into a same-page CustomEvent, never re-inserted into markup.
  • No per-user/cross-user leak or ownership bypass: this changes only timing of a UI notification for an already-authorized, already-persisted per-request save; it does not change what data is fetched/written or which proposal it targets.
  • No secret/token handling in this diff.
  • Change is additive/gating only (fires strictly less often — after success, not optimistically) so it cannot newly expose state that wasn't already server-confirmed.

REVIEW VERDICT: 0 blocker, 0 major, 1 minor

Adversarial security review of PR #1435 (bookshelf-v3bi0). Scope: `static/js/controllers/bookdrop_file_editor_controller.js` + its Vitest spec only — the `bookdrop:metadatacopied` CustomEvent now dispatches on save success instead of optimistically. Findings: [MINOR] static/js/controllers/bookdrop_file_editor_controller.js:433 — `_pendingDispatchCopy` is a per-controller instance flag, not per-field When a copy-triggered save collides with an in-flight save and a second `_doSave` call queues behind it (`_savePendingAfterInflight`), `_pendingDispatchCopy` is OR'd across whichever calls queued in that window (`ctrl._pendingDispatchCopy || !!dispatchCopyEvent`). This is a UI-correctness nit (the review row could auto-check slightly earlier/later than the exact field that succeeded), not a security issue — no cross-user or cross-proposal leak, since `proposalIdValue` on the dispatched event is scoped to the mounted controller's own row. No fix required before merge; flag only if the review-row auto-check behavior is later found flaky under rapid-copy usage. Security assessment: - No XSS / unsafe DOM injection: no `innerHTML` touched; the event `detail.proposalId` is echoed from an already-rendered `proposalIdValue` back into a same-page CustomEvent, never re-inserted into markup. - No per-user/cross-user leak or ownership bypass: this changes only *timing* of a UI notification for an already-authorized, already-persisted per-request save; it does not change what data is fetched/written or which proposal it targets. - No secret/token handling in this diff. - Change is additive/gating only (fires strictly less often — after success, not optimistically) so it cannot newly expose state that wasn't already server-confirmed. REVIEW VERDICT: 0 blocker, 0 major, 1 minor
Author
Owner

Adversarial review of PR #1442 (bookshelf-nnb9.29) — black-box conversion of internal/stats tests.

Phase 1 (spec compliance): matches the bead — export_test.go + handler_internal_test.go removed, render_test.go added, allowlist.txt trimmed to exactly the two removed files, all internal/stats test files declare package stats_test.

Phase 2 findings:

No BLOCKER/MAJOR/MINOR findings.

Verification notes (adversarial checks performed):

  1. Scale/limit-clamp removal (the flagged risk) - fetchSeriesProgressWithQuery dropped its limit int param and the <=0 / >max clamp logic (internal/stats/series_progress.go:219-260). Traced the only caller: GetDashboard (internal/stats/service.go:324) previously always passed the constant defaultSeriesProgressLimit (50) - never a user/request-derived value (handler.go has no ?series_limit query param). So the clamp was genuinely dead code; the query is still bounded (fixed Limit: defaultSeriesProgressLimit at series_progress.go:241, LIMIT ? still present in buildSeriesProgressQuery). maxSeriesProgressLimit (200) was removed since nothing reads it. Not a Scale violation - confirmed via series_progress_test.go "when entries returned equal the fixed page size (50), sets next cursor" which exercises the fixed-50 path end-to-end through the public GetDashboard.

  2. Anti-gaming (no unexported symbol newly exported) - diffed exported identifiers; production diff is limited to series_progress.go/service.go (comment + signature simplification only, no new exports). export_test.go (11 Export* shims) is fully deleted, not relocated/renamed.

  3. Real vs. vacuous coverage - spot-checked render_test.go (all 24 buildXxxBars/donut/mini-hist helpers from the old handler_internal_test.go have a corresponding Describe block, e.g. CATBAR:Sci-Fi:5:50| asserts a real relative-percentage computation, not an echoed stub value), series_progress_test.go, reading_debt_test.go, survival_curve_test.go - all drive production code via the public API (stats.ListSeriesProgress, stats.GetDashboard, stats.GetReadingDebt, stats.GetSurvivalCurve, stats.GetHandler) with spy/stub query funcs and assert on production-computed values (percentages, SQL text/arg positions, mapped rows). No guarded/tautological/self-validating patterns observed.

  4. allowlist.txt - diff is exactly the two removed-file lines (internal/stats/export_test.go, internal/stats/handler_internal_test.go); no other lines touched.

REVIEW VERDICT: 0 blocker, 0 major, 0 minor

Adversarial review of PR #1442 (bookshelf-nnb9.29) — black-box conversion of internal/stats tests. Phase 1 (spec compliance): matches the bead — export_test.go + handler_internal_test.go removed, render_test.go added, allowlist.txt trimmed to exactly the two removed files, all internal/stats test files declare `package stats_test`. Phase 2 findings: No BLOCKER/MAJOR/MINOR findings. Verification notes (adversarial checks performed): 1. Scale/limit-clamp removal (the flagged risk) - fetchSeriesProgressWithQuery dropped its limit int param and the <=0 / >max clamp logic (internal/stats/series_progress.go:219-260). Traced the only caller: GetDashboard (internal/stats/service.go:324) previously always passed the constant defaultSeriesProgressLimit (50) - never a user/request-derived value (handler.go has no ?series_limit query param). So the clamp was genuinely dead code; the query is still bounded (fixed Limit: defaultSeriesProgressLimit at series_progress.go:241, LIMIT ? still present in buildSeriesProgressQuery). maxSeriesProgressLimit (200) was removed since nothing reads it. Not a Scale violation - confirmed via series_progress_test.go "when entries returned equal the fixed page size (50), sets next cursor" which exercises the fixed-50 path end-to-end through the public GetDashboard. 2. Anti-gaming (no unexported symbol newly exported) - diffed exported identifiers; production diff is limited to series_progress.go/service.go (comment + signature simplification only, no new exports). export_test.go (11 Export* shims) is fully deleted, not relocated/renamed. 3. Real vs. vacuous coverage - spot-checked render_test.go (all 24 buildXxxBars/donut/mini-hist helpers from the old handler_internal_test.go have a corresponding Describe block, e.g. CATBAR:Sci-Fi:5:50| asserts a real relative-percentage computation, not an echoed stub value), series_progress_test.go, reading_debt_test.go, survival_curve_test.go - all drive production code via the public API (stats.ListSeriesProgress, stats.GetDashboard, stats.GetReadingDebt, stats.GetSurvivalCurve, stats.GetHandler) with spy/stub query funcs and assert on production-computed values (percentages, SQL text/arg positions, mapped rows). No guarded/tautological/self-validating patterns observed. 4. allowlist.txt - diff is exactly the two removed-file lines (internal/stats/export_test.go, internal/stats/handler_internal_test.go); no other lines touched. REVIEW VERDICT: 0 blocker, 0 major, 0 minor
Author
Owner

Adversarial review of PR #1442 (bookshelf-nnb9.29) — black-box conversion of internal/stats tests.

Phase 1 (spec compliance): matches the bead — export_test.go + handler_internal_test.go removed, render_test.go added, allowlist.txt trimmed to exactly the two removed files, all internal/stats test files declare package stats_test.

Phase 2 findings:

No BLOCKER/MAJOR/MINOR findings.

Verification notes (adversarial checks performed):

  1. Scale/limit-clamp removal (the flagged risk) - fetchSeriesProgressWithQuery dropped its limit int param and the <=0 / >max clamp logic (internal/stats/series_progress.go:219-260). Traced the only caller: GetDashboard (internal/stats/service.go:324) previously always passed the constant defaultSeriesProgressLimit (50) - never a user/request-derived value (handler.go has no ?series_limit query param). So the clamp was genuinely dead code; the query is still bounded (fixed Limit: defaultSeriesProgressLimit at series_progress.go:241, LIMIT ? still present in buildSeriesProgressQuery). maxSeriesProgressLimit (200) was removed since nothing reads it. Not a Scale violation - confirmed via series_progress_test.go "when entries returned equal the fixed page size (50), sets next cursor" which exercises the fixed-50 path end-to-end through the public GetDashboard.

  2. Anti-gaming (no unexported symbol newly exported) - diffed exported identifiers; production diff is limited to series_progress.go/service.go (comment + signature simplification only, no new exports). export_test.go (11 Export* shims) is fully deleted, not relocated/renamed.

  3. Real vs. vacuous coverage - spot-checked render_test.go (all 24 buildXxxBars/donut/mini-hist helpers from the old handler_internal_test.go have a corresponding Describe block, e.g. CATBAR:Sci-Fi:5:50| asserts a real relative-percentage computation, not an echoed stub value), series_progress_test.go, reading_debt_test.go, survival_curve_test.go - all drive production code via the public API (stats.ListSeriesProgress, stats.GetDashboard, stats.GetReadingDebt, stats.GetSurvivalCurve, stats.GetHandler) with spy/stub query funcs and assert on production-computed values (percentages, SQL text/arg positions, mapped rows). No guarded/tautological/self-validating patterns observed.

  4. allowlist.txt - diff is exactly the two removed-file lines (internal/stats/export_test.go, internal/stats/handler_internal_test.go); no other lines touched.

REVIEW VERDICT: 0 blocker, 0 major, 0 minor

Adversarial review of PR #1442 (bookshelf-nnb9.29) — black-box conversion of internal/stats tests. Phase 1 (spec compliance): matches the bead — export_test.go + handler_internal_test.go removed, render_test.go added, allowlist.txt trimmed to exactly the two removed files, all internal/stats test files declare `package stats_test`. Phase 2 findings: No BLOCKER/MAJOR/MINOR findings. Verification notes (adversarial checks performed): 1. Scale/limit-clamp removal (the flagged risk) - fetchSeriesProgressWithQuery dropped its limit int param and the <=0 / >max clamp logic (internal/stats/series_progress.go:219-260). Traced the only caller: GetDashboard (internal/stats/service.go:324) previously always passed the constant defaultSeriesProgressLimit (50) - never a user/request-derived value (handler.go has no ?series_limit query param). So the clamp was genuinely dead code; the query is still bounded (fixed Limit: defaultSeriesProgressLimit at series_progress.go:241, LIMIT ? still present in buildSeriesProgressQuery). maxSeriesProgressLimit (200) was removed since nothing reads it. Not a Scale violation - confirmed via series_progress_test.go "when entries returned equal the fixed page size (50), sets next cursor" which exercises the fixed-50 path end-to-end through the public GetDashboard. 2. Anti-gaming (no unexported symbol newly exported) - diffed exported identifiers; production diff is limited to series_progress.go/service.go (comment + signature simplification only, no new exports). export_test.go (11 Export* shims) is fully deleted, not relocated/renamed. 3. Real vs. vacuous coverage - spot-checked render_test.go (all 24 buildXxxBars/donut/mini-hist helpers from the old handler_internal_test.go have a corresponding Describe block, e.g. CATBAR:Sci-Fi:5:50| asserts a real relative-percentage computation, not an echoed stub value), series_progress_test.go, reading_debt_test.go, survival_curve_test.go - all drive production code via the public API (stats.ListSeriesProgress, stats.GetDashboard, stats.GetReadingDebt, stats.GetSurvivalCurve, stats.GetHandler) with spy/stub query funcs and assert on production-computed values (percentages, SQL text/arg positions, mapped rows). No guarded/tautological/self-validating patterns observed. 4. allowlist.txt - diff is exactly the two removed-file lines (internal/stats/export_test.go, internal/stats/handler_internal_test.go); no other lines touched. REVIEW VERDICT: 0 blocker, 0 major, 0 minor
zombor force-pushed bd-bookshelf-nnb9.29 from 654b4068d9
All checks were successful
/ Test Race (pull_request) Successful in 1m57s
/ E2E API (pull_request) Successful in 1m32s
/ JS Unit Tests (pull_request) Successful in 1m6s
/ Coverage (pull_request) Successful in 2m34s
/ Integration (pull_request) Successful in 2m34s
/ Lint (pull_request) Successful in 4m39s
/ E2E Browser (pull_request) Successful in 6m45s
to 4f21e0d919
All checks were successful
/ Test Race (pull_request) Successful in 1m49s
/ E2E API (pull_request) Successful in 1m26s
/ JS Unit Tests (pull_request) Successful in 53s
/ Coverage (pull_request) Successful in 2m10s
/ Lint (pull_request) Successful in 2m36s
/ Integration (pull_request) Successful in 2m18s
/ E2E Browser (pull_request) Successful in 5m2s
2026-08-13 01:24:07 +00:00
Compare
zombor merged commit 7378182c33 into main 2026-08-13 01:30:10 +00:00
Sign in to join this conversation.
No reviewers
No labels
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
zombor/pergamum!1442
No description provided.