Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,22 @@ GITTENSORY_REVIEW_ENRICHMENT=false
# provenance,codeowners,secretLog,assetWeight,typosquat,commitSignature,iacMisconfig,nativeBuild
# history,docCommentDrift,duplication,churnHotspot,blameLink,approvalIntegrity,ciCheckSignals
# undocumentedExport,staleBranch,commitHygiene,pendingReviewRequests,testRatio,migrationSafety
# looseRange,terminology,todoMarker
# looseRange,terminology,todoMarker,magicNumber
#
# Profile defaults:
# fast: dependency,lockfileDrift,secret,license,installScript,heavyDependency,actionPin,eol
# redos,provenance,secretLog,typosquat,iacMisconfig,nativeBuild,testRatio,migrationSafety
# looseRange,terminology,todoMarker
# looseRange,terminology,todoMarker,magicNumber
# balanced (default): dependency,lockfileDrift,secret,license,installScript,heavyDependency
# actionPin,eol,redos,provenance,codeowners,secretLog,assetWeight,typosquat,commitSignature
# iacMisconfig,nativeBuild,history,docCommentDrift,duplication,churnHotspot,blameLink
# approvalIntegrity,ciCheckSignals,undocumentedExport,staleBranch,commitHygiene
# pendingReviewRequests,testRatio,migrationSafety,looseRange,terminology,todoMarker
# pendingReviewRequests,testRatio,migrationSafety,looseRange,terminology,todoMarker,magicNumber
# deep: dependency,lockfileDrift,secret,license,installScript,heavyDependency,actionPin,eol
# redos,provenance,codeowners,secretLog,assetWeight,typosquat,commitSignature,iacMisconfig
# nativeBuild,history,docCommentDrift,duplication,churnHotspot,blameLink,approvalIntegrity
# ciCheckSignals,undocumentedExport,staleBranch,commitHygiene,pendingReviewRequests,testRatio
# migrationSafety,looseRange,terminology,todoMarker
# migrationSafety,looseRange,terminology,todoMarker,magicNumber
# END GENERATED REES ANALYZERS

# Submitter-reputation spend control (internal-only): downgrades new/burst/low-rep
Expand Down
23 changes: 23 additions & 0 deletions apps/gittensory-ui/src/lib/rees-analyzers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,29 @@ export const REES_ANALYZERS = [
"Precision-first: only UPPERCASE, comment-anchored markers are reported (a lowercase `todo` identifier or a marker inside a string literal is never flagged); a bare marker inside a multi-line block comment is intentionally not matched.",
},
},
{
name: "magicNumber",
title: "Magic numbers",
category: "quality",
cost: "local",
defaultEnabled: true,
profiles: ["fast", "balanced", "deep"],
requires: ["files"],
limits: {
maxFindings: 25,
maxLineChars: 2000,
},
docs: {
summary:
"Flags newly-added non-trivial numeric literals in non-test source where a named constant would clarify intent.",
looksAt:
"Added lines in source files, excluding tests, strings, comments, trivial sentinels/scales, named constants, array indexes, and enum-like initializers.",
reports: "File, line, and numeric literal text only.",
network: "Pure local analyzer. No external network call.",
notes:
"Precision-first: common values such as 0, 1, -1, 2, 100, 1000, and powers of ten are silent.",
},
},
] as const satisfies readonly ReesAnalyzerDoc[];

export const REES_ANALYZER_NAMES = REES_ANALYZERS.map((analyzer) => analyzer.name);
64 changes: 64 additions & 0 deletions review-enrichment/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ inside the operator's trust boundary. The engine prefers a short-lived installat
| `iacMisconfig` | Risky IaC/config changes like public buckets, open ingress, or insecure CORS. | Pure local. |
| `nativeBuild` | Newly-added dependencies that compile native code or ship sdist-only builds. | Calls npm/PyPI registries. |
| `history` | Author track record, same-file PR history, and linked-issue alignment. | Calls GitHub API with bounded fanout; needs author/token for private repos. |
| `magicNumber` | Non-trivial numeric literals newly added in non-test source. | Pure local. |

The engine can send `analyzers: ["secret", "actionPin"]` to run a subset. If the field is omitted, REES runs the
full registry. An explicit empty array runs no analyzers; the engine uses that fail-closed shape when an
Expand Down Expand Up @@ -76,6 +77,69 @@ classes, per-analyzer limits, and self-host configuration. When adding or migrat
- Make external-call analyzers fail open and respect the orchestrator abort signal when the scanner supports it.
- Prefer a focused analyzer test file instead of expanding the shared `enrichment.test.ts` mega-test.

### Magic-number analyzer

`magicNumber` is a precision-first local analyzer for unexplained numeric literals added by a PR. It is intended to
surface values that look like policy, timing, sizing, retry, threshold, or scoring decisions hidden directly inside an
expression, where a named constant would make intent and future review safer.

The analyzer scans only added diff lines in non-test source files. It never fetches repository content, never
evaluates code, and never returns source snippets. Findings carry only `{ file, line, value }`, so the review brief can
say that `src/retry.ts:42` added `37` without copying the surrounding line.

What it reports:

- Numeric literals in expressions, such as `attempt * 37`, `timeout + 250`, `ratio > 0.73`, or `0xff` masks.
- Signed and fractional forms when they are part of the literal, such as `-42`, `.75`, `6e-3`, or `99n`.
- Multiple reportable values on one added line, capped by the analyzer-level finding limit.
- Added content whose source text begins with plus signs, matching the unified-diff edge cases covered by sibling
analyzers.

What it suppresses:

- Test files and snapshot paths, because assertion literals are usually expected examples rather than production
policy.
- Documentation, JSON/YAML, lockfiles, fixtures, and other non-source files.
- String literals and inline comments before numeric scanning, so prose like `"wait 37 seconds"` or `// retry in 42`
does not generate a finding.
- Common sentinel and scale values: `0`, `1`, `-1`, `2`, `100`, `1000`, and powers of ten.
- Named constant declarations, including common language forms such as `const MAX_BATCH = 250`, `static readonly
RETRY_WINDOW = 30`, `public static final int LIMIT = 50`, `final DEFAULT_LIMIT = 50`, and `val MAX_PAGE_SIZE = 250`.
- Array indexes like `rows[3]`, numeric object keys like `{ 404: handler }`, and enum-like member initializers like
`PENDING = 3`.

The goal is not to ban numeric literals. It is to highlight newly-added non-obvious values that can silently encode
review-critical behavior. The analyzer favors false negatives over noisy findings: if a literal looks named,
structural, test-only, or conventional, it stays silent.

Example outcomes:

| Added source line | Analyzer result | Rationale |
| ----------------- | --------------- | --------- |
| `const timeoutMs = attempts * 37;` | Reports `37`. | The value is embedded in behavior and is not self-describing. |
| `const RETRY_WINDOW_MS = 37;` | Suppressed. | The uppercase declaration gives the literal a reviewable name. |
| `if (ratio > 0.73) return true;` | Reports `0.73`. | Fractional thresholds are usually policy choices. |
| `return rows[3];` | Suppressed. | Small positional array indexes are structural. |
| `return items[:37];` | Reports `37`. | Slice bounds can encode a batch or display limit. |
| `{ 404: handleMissing }` | Suppressed. | Numeric object keys are commonly protocol or lookup labels. |
| `enum State { Pending = 3 }` | Suppressed. | Enum-like member initializers are named states. |
| `const mask = flags & 0xff;` | Reports `0xff`. | Radix literals can hide bitmask decisions. |
| `const sample = 1_337;` | Reports `1_337`. | Numeric separators keep the original literal readable in findings. |
| `const scale = 1000;` | Suppressed. | Powers and common scales are intentionally quiet. |

Operational notes:

- Keep findings public-safe: report the file, line, and literal only, never the surrounding source text.
- Use the diff hunk line number, not a best-effort grep against the repository checkout.
- Scan added lines only. Removed or context lines should never create findings.
- Apply the source-path filter before scanning content so generated metadata and docs stay quiet.
- Respect the abort signal both before and during patch scanning.
- Keep line-level work bounded; very long added lines are skipped to avoid pathological input.
- Preserve deterministic ordering by scanning files, hunks, and literals in diff order.
- Cap per-line and per-request findings so one generated file cannot dominate the brief.
- Add tests for both the reported and suppressed side of every new heuristic.
- Regenerate analyzer metadata whenever the registry descriptor changes.

## Shared analysis context

Each `/v1/enrich` request now gets a request-scoped `AnalysisContext` before analyzers run. New and migrated
Expand Down
26 changes: 26 additions & 0 deletions review-enrichment/analyzer-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,32 @@
"network": "Pure local analyzer. No external network call.",
"notes": "Precision-first: only UPPERCASE, comment-anchored markers are reported (a lowercase `todo` identifier or a marker inside a string literal is never flagged); a bare marker inside a multi-line block comment is intentionally not matched."
}
},
{
"name": "magicNumber",
"title": "Magic numbers",
"category": "quality",
"cost": "local",
"defaultEnabled": true,
"profiles": [
"fast",
"balanced",
"deep"
],
"requires": [
"files"
],
"limits": {
"maxFindings": 25,
"maxLineChars": 2000
},
"docs": {
"summary": "Flags newly-added non-trivial numeric literals in non-test source where a named constant would clarify intent.",
"looksAt": "Added lines in source files, excluding tests, strings, comments, trivial sentinels/scales, named constants, array indexes, and enum-like initializers.",
"reports": "File, line, and numeric literal text only.",
"network": "Pure local analyzer. No external network call.",
"notes": "Precision-first: common values such as 0, 1, -1, 2, 100, 1000, and powers of ten are silent."
}
}
]
}
Loading
Loading