feat(review): add deterministic review-effort chip and wire real per-PR minutes - #3666
Conversation
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
|
Tip 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 ✅ Gittensory review result - approve/merge recommendedReview updated: 2026-07-06 02:00:28 UTC
✅ Suggested Action - Approve/Merge
Review summary Nits — 6 non-blocking
Review context
Contributor next steps
Signal definitions
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
…PR minutes Adds a review.effort_score manifest toggle (mirroring review.changed_files_summary end-to-end: focus-manifest parse/serialize/resolve, the .gittensory.yml.example reference block, and the exhaustiveness test map) that renders a compact "review effort: N/5 (~M min)" chip in the unified review comment, using the already-shipped deterministic estimateReviewEffort estimator. Default off and byte-identical when absent. Also persists the same per-PR minutes onto the pr_public_surface_published audit event and averages them in the public stats feed, replacing the flat MINUTES_SAVED_PER_PR constant with a real per-PR figure while keeping the constant as a documented fallback for ledger rows that predate this feature. stats.ts (the maintainer dashboard) is left unchanged: it only reads the frozen legacy review_targets/review_audit ledger, which has no live per-PR file source to aggregate from.
05d169a to
09ff1f3
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3666 +/- ##
=======================================
Coverage 93.29% 93.29%
=======================================
Files 314 314
Lines 32057 32076 +19
Branches 11748 11757 +9
=======================================
+ Hits 29906 29925 +19
Misses 1517 1517
Partials 634 634
🚀 New features to boost your workflow:
|
…lock config/examples/gittensory.full.yml must stay byte-identical to .gittensory.yml.example from the WHERE IT LIVES marker onward (test/unit/config-templates.test.ts), which this branch's rebase onto main missed since the new review.effort_score documentation only landed in the root file's auto-merged copy.
Summary
Closes #1955
Adds a deterministic, pure per-PR review-effort/complexity score to the review surfaces. The estimator itself (
estimateReviewEffort— 1-5 band + minutes, weighted by added-line volume and file-type mix, no AI) already shipped in a prior PR (src/review/review-effort.ts, #2924/#2068); this PR wires it into the three places that were not yet done:review.effort_score— a newFocusManifestReviewConfigboolean toggle, wired end-to-end exactly like its siblingreview.changed_files_summary(parse/serialize/resolve insrc/signals/focus-manifest.ts, the.gittensory.yml.examplereference block, and theREVIEW_FIELD_TOKENSexhaustiveness map intest/unit/focus-manifest.test.ts). Default off/null, byte-identical when absent.src/review/unified-comment.ts'sstatusChips()appends a compact`review effort: N/5 (~M min)`chip (mirroring how the readiness/CI chips are conditionally appended), threaded throughbuildUnifiedReviewInput→unified-comment-bridge.ts→src/queue/processors.ts(resolved unconditionally alongsidechangedFilesSummaryEnabledForReview, since it is deterministic and has nothing to do with whether the AI review ran).src/review/public-stats.ts'sMINUTES_SAVED_PER_PRflat constant is no longer the only figure behind "time saved": the processor now persistsestimateReviewEffort's minutes onto the samegithub_app.pr_public_surface_publishedaudit event public-stats already reads, andgetPublicStatsaverages the real per-PR minutes viaAVG(json_extract(metadata_json, '$.reviewEffortMinutes')), falling back to the flat constant when the ledger is empty or predates this feature (an all-NULL average). Regression test proves the real estimate is used when present, and a real-D1 test proves the SQL round-trip end to end.Scope note (read before merge)
src/review/stats.ts(the maintainer dashboard feed) is deliberately not wired to a per-PR effort aggregate. I traced its full read path:computeStatsonly readsreview_targets/review_audit, the legacy reviewbot ledger — nothing writes new rows toreview_targetssince the self-host convergence cutover, and everyreview_auditwrite (pr_outcome,gate_decision) carries only decision/outcome metadata, never a PR's changed files or patches. There is no live source that module could aggregate a real per-PR estimate from today (unlikepublic-stats.ts, which reads the still-activeaudit_eventsledger). Adding a same-shaped field there would only ever read back a permanently-null placeholder, so I left it out rather than fake it, and documented this explicitly in the file header (stats.tslines 18-31). A real maintainer-dashboard effort aggregate needs its own persisted source and is a genuine follow-up, not a one-line addition to this file.Scope
type(scope): short summaryConventional Commit format, for examplefix(api): restore profile access checks.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Validation
git diff --checknpm run actionlint— not run locally (no.github/workflows/**changes in this PR); CI runs it.npm run typechecknpm run test:coveragelocally, scoped to the changed files (not the full unsharded suite, per this repo's own guidance to avoid redundant local runs): ran the 7 affected test files (focus-manifest.test.ts,unified-comment.test.ts,unified-comment-bridge.test.ts,public-stats.test.ts,signals-coverage.test.ts,review-effort.test.ts,queue.test.ts— 1206 tests, all passing) with coverage, then cross-referenced every new-diff line/branch/function fromgit diff origin/main --unified=0againstcoverage/coverage-final.json'sstatementMap/branchMap/s/barrays with a small Node script. Result: 100% statement, branch, and function coverage on every changed line in all 6 touchedsrc/**files, with zero uncovered lines/branches remaining.npm run test:workers— not run locally (no Cloudflare Workers-pool-specific code touched); CI runs it.npm run build:mcp— not run locally (no MCP package changes); CI runs it.npm run test:mcp-pack— not run locally (no MCP package changes); CI runs it.npm run ui:openapi:check— not run locally; no API routes, OpenAPI schema, orStatsPayload/PublicStatsPayloadshape change that's OpenAPI-surfaced (both payload types were extended in a way that doesn't touch any documented route contract — no new field was added to either exported payload shape, only internal aggregation logic changed). CI runs it as a backstop.npm run ui:lint/ui:typecheck/ui:build— not run locally (noapps/gittensory-ui/**changes); CI runs them.npm audit --audit-level=moderateSafety
apps/gittensory-ui/**changes.UI Evidencesection — N/A, no visible UI change (this PR only changes a GitHub PR comment's markdown text and two backend aggregation modules).CHANGELOG.md(never edited in a normal PR); the only doc surface touched is.gittensory.yml.example, which is updated in this PR.UI Evidence
N/A — no visible UI change. The only rendered-text change is the new optional chip line in the GitHub PR review comment (
`review effort: N/5 (~M min)`), which is exercised by unit tests, not a UI screenshot.Notes
public-stats.test.tstests for the real-average/fallback/real-D1 cases).origin/mainimmediately before pushing (main moved by 2 commits mid-session); no conflicts.