Context
Part of the review-stack architecture audit (parent epic — duplication dimension). Flagged by the audit
as "the highest-value, lowest-risk fix in this whole audit" — entirely self-inflicted within one
package (all 63 review-enrichment/src/analyzers/*.ts files share one tsconfig, one build, one Railway
deploy — zero deploy-boundary excuse).
17 of 63 analyzer files hand-copy a private githubHeaders() helper, already drifted into 4 shapes:
- Shape A (11 files —
approval-integrity.ts:49, blame-link.ts:65, churn-hotspot.ts:60,
commit-hygiene.ts:46, commit-lint.ts:56, commit-signature.ts:44, coverage-delta.ts:249,
flaky-test.ts:56, pending-review-requests.ts:45, revert-recurrence.ts:150,
stale-branch.ts:37): Authorization + Accept: application/vnd.github+json + literal
"2022-11-28", no User-Agent.
- Shape B (3 files —
asset-weight.ts:51, duplication-scan.ts:51, history.ts:182): same as A
plus "User-Agent": "gittensory-review-enrichment" and a GITHUB_API_VERSION constant instead of the
literal.
- Shape C (1 file —
exhaustiveness-drift.ts:48): Accept hardcoded to
application/vnd.github.raw only — this analyzer can never request JSON.
- Shape D (2 files —
caller-impact.ts:72, unused-export.ts:49): adds a raw boolean param
toggling Accept, plus GITHUB_API_VERSION + User-Agent.
Impact: 11 of 17 analyzers send no User-Agent on outbound GitHub calls; analysts who needed
raw-blob fetching had to either give up JSON entirely (Shape C) or reinvent the header function a 4th
way (Shape D) instead of reusing one.
Fix
Add one exported githubHeaders(token: string, opts?: { raw?: boolean }): Record<string,string> to
review-enrichment/src/external-fetch.ts (or a new sibling file) using the Shape-D superset, and replace
all 17 call sites with an import. No cross-service-boundary risk, no version-bump ceremony needed — it's
one package.
Acceptance criteria
Context
Part of the review-stack architecture audit (parent epic — duplication dimension). Flagged by the audit
as "the highest-value, lowest-risk fix in this whole audit" — entirely self-inflicted within one
package (all 63
review-enrichment/src/analyzers/*.tsfiles share onetsconfig, one build, one Railwaydeploy — zero deploy-boundary excuse).
17 of 63 analyzer files hand-copy a private
githubHeaders()helper, already drifted into 4 shapes:approval-integrity.ts:49,blame-link.ts:65,churn-hotspot.ts:60,commit-hygiene.ts:46,commit-lint.ts:56,commit-signature.ts:44,coverage-delta.ts:249,flaky-test.ts:56,pending-review-requests.ts:45,revert-recurrence.ts:150,stale-branch.ts:37):Authorization+Accept: application/vnd.github+json+ literal"2022-11-28", noUser-Agent.asset-weight.ts:51,duplication-scan.ts:51,history.ts:182): same as Aplus
"User-Agent": "gittensory-review-enrichment"and aGITHUB_API_VERSIONconstant instead of theliteral.
exhaustiveness-drift.ts:48):Accepthardcoded toapplication/vnd.github.rawonly — this analyzer can never request JSON.caller-impact.ts:72,unused-export.ts:49): adds arawboolean paramtoggling
Accept, plusGITHUB_API_VERSION+User-Agent.Impact: 11 of 17 analyzers send no
User-Agenton outbound GitHub calls; analysts who neededraw-blob fetching had to either give up JSON entirely (Shape C) or reinvent the header function a 4th
way (Shape D) instead of reusing one.
Fix
Add one exported
githubHeaders(token: string, opts?: { raw?: boolean }): Record<string,string>toreview-enrichment/src/external-fetch.ts(or a new sibling file) using the Shape-D superset, and replaceall 17 call sites with an import. No cross-service-boundary risk, no version-bump ceremony needed — it's
one package.
Acceptance criteria
githubHeaders()export used by all 17 analyzer files.review-enrichmentnow send a consistentUser-AgentandGITHUB_API_VERSION.exhaustiveness-drift.tscan request JSON where needed (Shape C gap closed).