refactor(rees): promote reconstructOldContent into a shared analyzer helper - #4752
Conversation
…helper reconstructOldContent (unified-diff reverse-patch reconstruction) was private to doc-comment-drift.ts and imported cross-file from there by exhaustiveness-drift.ts -- a one-off trick, not shared infrastructure. Move it into its own co-located module (matching the existing diff-lines.ts / binary-extensions.ts / github-headers.ts precedent for shared analyzer helpers) so any analyzer can recover a changed file's pre-PR text without re-deriving this. Pure code motion: every executable line is byte-identical (verified via diff + matching MD5 after stripping whole-line comments); only the function's own doc comment and one inline comment were reworded to drop doc-comment-drift-specific framing now that the helper is shared. Both existing callers are migrated with a one-line import-path change each and no other modifications. Adds a dedicated test file for the promoted helper: the four existing reconstructOldContent unit tests move over verbatim, plus five new tests closing every previously-untested branch (a non-hunk preamble line, out-of-order/overlapping hunks, a "no newline at end of file" marker, the trailing-flush no-op case, and the wholly-new-file case) -- 100% line/branch/function coverage confirmed via node's built-in coverage instrumentation.
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
|
Warning 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏸️ Gittensory review result - manual review recommendedReview updated: 2026-07-10 22:44:48 UTC
⏸️ Suggested Action - Manual Review
Review summary Nits — 4 non-blocking
Concerns raised — review before merging
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.
|
Summary
reconstructOldContent(newContent, patch)— theunified-diff reverse-patch reconstruction primitive that recovers a changed file's pre-PR text — was
private to
doc-comment-drift.ts(feat(enrichment): Doc-comment-vs-signature drift #1519) and imported cross-file from there byexhaustiveness-drift.ts(feat(enrichment): enum / literal-union exhaustiveness-drift analyzer #2028): a one-off trick living in the wrong place, not sharedinfrastructure. This PR promotes it into its own co-located module,
review-enrichment/src/analyzers/reconstruct-old-content.ts, and migrates both existing callers to it.not by eyeballing, matching the verification discipline Break up processors.ts mega-functions #4607's sibling extraction PRs
(refactor(queue): extract plan-input builder from runAgentMaintenancePlanAndExecute #4688/refactor(queue): split processGitHubWebhook into per-event handlers #4695/refactor(queue): extract manifest-policy gate from maybePublishPrPublicSurface #4728) established for this kind of change.
Architecture decision: co-located module, not an
AnalysisContextmethodThe issue offered either shape. I looked at both before choosing:
AnalysisContext(review-enrichment/src/analysis-context.ts) does not expose the GitHub token —it's pure authenticated transport (
fetchJson/fetchText/fetchStatus); every current caller buildsits own
Authorizationheader fromreq.githubToken. Acontext-based version would still need thetoken threaded in from
req, so it buys no real encapsulation over a plain function.reconstructOldContentis already pure and I/O-free — the fetch happens separately in each caller. Theonly thing to "promote" is the reverse-patch algorithm itself, which needs no context at all.
(req, fetchFn, options)analyzer-function shape, not theAnalysisContext-consuming shape some other analyzers use (codeowners.ts,caller-impact.ts,coverage-delta.ts,duplication-scan.ts, etc.). Making this anAnalysisContextmethod would forceboth callers' exported signatures (and both existing test files' mocking convention — bare
fetchFninjection, no
AnalysisContextconstruction today) to change just to reach one small helper, and wouldtouch
registry.ts's wiring for both analyzers — a materially larger blast radius than the issue's own"pure extraction, not a behavior change" framing calls for.
reqanalyzers this issuetouches today, and the
AnalysisContext-based analyzers Real complexity-delta analyzer (true before/after comparison) #4740/Real duplication-delta analyzer (before/after comparison) #4741 will likely extend(
coverage-delta.ts/duplication-scan.tsalready receiveAnalysisContext) — without forcing eitherstyle on the other. This matches the existing precedent in the same directory for shared analyzer
helpers:
diff-lines.ts,binary-extensions.ts,github-headers.tsare all plain sibling modules, notcontext methods.
Byte-faithful verification (mechanical, not eyeballed)
files and diffed them directly: the only difference anywhere in the 37-line function is one inline
comment (2 lines), reworded from doc-comment-drift-specific language ("bail so we never trust a bad old
signature") to caller-agnostic language ("bail so we never trust a reconstructed result that isn't
provably faithful to the real pre-PR file") — a comment has zero runtime effect.
(
7b195bf14c422e114a54d91da3d3e4a6) — the executable code is provably byte-identical, not justeyeballed-similar.
exhaustiveness-drift.ts's diff is a single line: the import path(
./doc-comment-drift.js→./reconstruct-old-content.js). Nothing else in the file changed.doc-comment-drift.ts's diff is exactly: one import line added, and the function's 40 lines (doccomment + body) removed. The call site (
reconstructOldContent(content, file.patch!)insidescanDocCommentDrift) is untouched — same name, now resolved via import instead of a local definition.scanDocCommentDrifttests, 4scanExhaustivenessDrifttests) pass unmodified, proving both callers' end-to-end observablebehavior is unchanged.
Edge cases (patch doesn't cleanly reverse-apply / binary file / newly-added file)
null(a hunk starting before thecursor or past the content's end, or a context/added line that doesn't match
newContentat theexpected position).
it cannot detect this itself — both callers already filter to known source extensions and require
file.patchto be present before calling it (GitHub omits.patchentirely for binary/oversizedfiles), and that filtering is untouched by this PR (see the byte-faithful diffs above).
-0,0oldrange) reconstructs to an empty string, not
null. I deliberately did not normalize this tonullin this PR: doing so is 100% safe for both current callers (they already branch on
if (!oldContent),which treats
""andnullidentically) but it's a real, separate behavior decision on the sharedfunction's own contract, and this issue is explicitly scoped as pure extraction. I instead pinned the
current contract with an explicit regression test and documented it plainly in the module's doc comment
so every future caller (including Real complexity-delta analyzer (true before/after comparison) #4740/Real duplication-delta analyzer (before/after comparison) #4741) knows to check falsiness, not
=== null. Happy to open asmall separate follow-up if a strict-null contract is preferred later.
Coverage
Added
review-enrichment/test/reconstruct-old-content.test.ts: the four existingreconstructOldContentunit tests move over verbatim (removed fromdoc-comment-drift.test.ts, which nolonger imports the function directly), plus five new tests closing every branch the original four didn't
already exercise — a non-hunk preamble line before the first
@@header, out-of-order/overlapping hunks,a
\ No newline at end of filemarker line, the trailing-flush no-op case (last hunk already reachesEOF), and the wholly-new-file case. Verified with node's own coverage instrumentation
(
node --test --experimental-test-coverage --test-coverage-include='dist/analyzers/reconstruct-old-content.js'):100.00% line / 100.00% branch / 100.00% function coverage on the new module.
Scope
type(scope): short summaryConventional Commit format, for examplefix(api): restore profile access checks.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Closes #123) — a linked open issue is required for every contributor PR.Note on the issue link: this implements #4739, a sub-issue of epic #4737 — not
Fixes/Closeswording, per the epic's multi-part sub-issue convention.
Validation
git diff --check— clean.npm run actionlint— not run; no.github/workflows/**files touched.npm run typecheck— clean (root-level;review-enrichmenthas its own separatetsconfig.jsonand is not part of this project reference, but its own build/typecheck is covered by
rees:testbelow).
npm run test:coverage— not run;review-enrichment/is a standalone project (its ownpackage.json/package-lock.json, not an npm workspace member) tested via node's built-in testrunner, not vitest — it is not part of the root Codecov
codecov/patchgate (that gate'scoverage comes only from the root
vitest run --coverage→coverage/lcov.info, which nevertouches
review-enrichment/src/**).review-enrichment/changes are gated entirely bynpm run rees:test(CI's own "REES build, source-map validation, and tests" step, path-filtered onreview-enrichment/**), run below.npm run test:workers— not run; notest/workers/**-relevant code touched.npm run build:mcp/npm run test:mcp-pack— not run; no MCP package changes.npm run ui:openapi:check/npm run ui:lint/npm run ui:typecheck/npm run ui:build— notrun; no
apps/gittensory-ui/**or API/schema changes.npm audit --audit-level=moderate— not run; no dependency changes (nopackage.json/lockfiletouched in either the root or
review-enrichment).If any required check was skipped, explain why:
review-enrichment/-only, behavior-preserving extraction with no rootsrc/**, UI, MCP,workers, schema, OpenAPI, or dependency surface touched, so those gates are left to CI rather than
duplicated locally (they path-filter out for this diff anyway). The checks that matter for this change
were run directly, in full, in the foreground:
npm ci(root) andnpm run rees:install(fresh worktree, no priornode_modules): both clean.npm run rees:test(npm --prefix review-enrichment test— build, source-map validation, analyzermetadata drift check, then the full node-test suite): 1229/1229 tests passed, including all 14
scanDocCommentDrifttests, all 4scanExhaustivenessDrifttests, the 4 relocatedreconstructOldContenttests, and the 5 new tests — zero failures.npm run typecheck(root): clean.git diff --check: clean.--experimental-test-coverage, scoped to the new module via--test-coverage-include, reported 100.00% line / 100.00% branch / 100.00% function — see Coverageabove.
the Summary section above for the full method and result.
Safety
UI Evidencesection below... — N/A, no visible/UI changes (backend-only refactor, internal toreview-enrichment/).CHANGELOG.mdintentionally not touched.UI Evidence
Not applicable — this PR has no visible/UI/frontend surface; it is an internal
review-enrichment/(REES) refactor with no API, schema, or behavior change.
Notes
epic's own text, likely extending
coverage-delta.ts/duplication-scan.ts, which already receiveAnalysisContext— either of those can import this shared helper directly regardless of which analyzercalling convention they end up using).
AnalysisContext.beforeContentFor-styleconvenience method with no real caller today; see the architecture-decision writeup above for why, and
happy to add one in a follow-up once a concrete
AnalysisContext-based consumer exists.