Skip to content

Code analysis tooling + security tightening + dedupe pass#162

Closed
rlorenzo wants to merge 37 commits intomainfrom
code-anaylsis
Closed

Code analysis tooling + security tightening + dedupe pass#162
rlorenzo wants to merge 37 commits intomainfrom
code-anaylsis

Conversation

@rlorenzo
Copy link
Copy Markdown
Contributor

@rlorenzo rlorenzo commented Apr 25, 2026

Summary

Adds three static-analysis tools (fallow for Vue/TS dead code + duplication, jscpd for cross-language clones, and ReSharper inspectcode for C# inspections), enables a tranche of Roslyn maintainability rules, uses the combined findings to drive a multi-phase cleanup of the existing backlog, and tightens the existing security tooling. Net result: ~2,000 fewer lines of code, regression-gated CI for all three new tools, and CodeQL upgraded to its broadest query pack.

Tooling additions

Tool What it does Integration
fallow Dead code, complexity, and duplication for Vue/TS. Whole-project analysis with per-entry-point tracking. npm run lint, lint:staged, and npm run audit:fallow whole-project runner. CI regression job fails PRs that increase any finding count.
jscpd Multi-language clone detection (covers C# in addition to Vue/TS). npm run lint, lint:staged, and npm run audit:dupes. CI regression job fails PRs that raise the C# clone count.
ReSharper inspectcode Roslyn-class C# inspections (NRT contracts, dead conditionals, multi-enumeration, redundant qualifiers / usings / initializers). Whole-solution scan with SARIF output. npm run audit:resharper (full HTML+SARIF), audit:resharper:pr (PR-diff gate), and audit:resharper-staged (staged-files gate for local pre-commit checks). CI resharper-pr-gate fails PRs that introduce findings on PR-touched lines only — pre-existing issues never block.

Both wrappers honor LINT_BLOCK_ON_WARNINGS=true (matching lint-staged-ts.js) so the default pre-commit run stays informational and npm run lint:precommit blocks on findings.

npm run audit runs all three whole-project scanners in sequence (fallow → jscpd → ReSharper); ReSharper adds several minutes, so use the per-tool scripts for fast iteration.

Roslyn analyzer rules enabled

Turned on the maintainability rules that ship off-by-default in NetAnalyzers: CA1501 (excessive inheritance), CA1502 (cyclomatic complexity), CA1505 (low maintainability index), CA1507 (use nameof), CA1508 (dead conditional code), CA1510–1513 (ArgumentNullException.ThrowIfNull family), and CA1514 (redundant Substring length). CA1506 (class coupling) and CA1515 (sealed-by-default) skipped — heavy false-positive rate on ASP.NET Core controllers and DTOs. The one CA1508 false positive (the inner check of ViteProxyHelpers' double-checked-locking pattern) is suppressed inline; the analyzer's dataflow doesn't model thread interleaving.

Security tooling tightening

  • CodeQL upgraded from the default ~70-query pack to security-and-quality (~280 queries) for both csharp and javascript.
  • SonarAnalyzer.CSharp bumped to 10.24.0 in both web/ and test/ projects (previously drifted at 10.22 / 10.21).

(Evaluated Semgrep / Opengrep during this branch; concluded they're redundant on top of CodeQL security-and-quality for this codebase.)

Code cleanup driven by the new tools

  • Phase 1 — dead code: 13 files deleted, 2 devDeps dropped, 8 source files slimmed (colors.ts 256 → 31 lines).
  • Phase 2 — ClinicalScheduler refactor leftovers: 5 orphaned store/cache files (567 lines) and the shadowed permissions/index.ts removed.
  • Phase 3 — dead type exports: 130 type re-exports removed across 18 files (mostly Effort barrel files).
  • Phase 4 — high-ROI duplications:
    • createSpaRouter factory hoisted across 7 SPA routers.
    • EmergencyContactPageShell.vue shared between form and view pages.
    • DialogSubmitActions.vue and PercentAssignmentFormFields.vue extracted from the percent-assignment dialog pair.
  • Phase 4c — CSS migration: 15 .vue files moved from in-template @import url(...) to JS-side imports so Vite extracts shared CSS into one chunk and fallow stops flagging the files.
  • Phase 4d — second-round dedupes (12 sub-commits):
    • Vue: EffortReportPage (-302 lines across 9 report pages), ViperLayout chrome (EmulationBanner / FooterLinks / ViperBrandButton), EffortRecordSharedFields, InstructorPageShell, AsyncOperationDialog (Harvest / PercentRollover / ClinicalImport), TermTable, CourseStudents v-for collapse.
    • C#: BaseReportService.LoadSingleTermContext / LoadYearlyReportContext, IScheduleEntity + ApplyScheduleFilters, StudentGroupService.BuildStudentPhotoListAsync + GetActiveRossIamIdsAsync, Codecs UU/XX collapse onto shared encode/decode helpers, EvaluationReportService.CalculateWeightedAverage.
  • Phase 5 — C# inspection sweep (ReSharper + Roslyn rules):
    • Bulk cleanupcode profiles: OptimizeUsings (sort + drop unused usings), ShortenReferences (drop redundant Namespace.Type qualifiers — largest hit in PhotoExportService.cs from four conflicting OpenXml sub-namespaces), RemoveRedundancies (redundant default arguments, member initializers, else-after-return, anonymous-type property names). All compiler-equivalent.
    • CA1508 — dropped dead ?. / != null guards in 10 files where dataflow proves non-null.
    • InvalidXmlDocComment — ~24 stale empty <param> tags removed; fixed three malformed <T> escapes and a few orphan /// blocks.
    • PossibleMultipleEnumeration — materialized IEnumerable once in 29 call sites enumerating two-or-more times (test assertions, VueTableDefault, EvaluationPolicyService, controllers).
    • Added Viper.sln.DotSettings with the three cleanup profiles. Documented gotcha: ReSharper's umbrella <CSRemoveCodeRedundancies> flag is a no-op without the sparsely-documented sibling <RemoveCodeRedundancies> plus per-rule sub-flags.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 25, 2026

Codecov Report

❌ Patch coverage is 12.67894% with 427 lines in your changes missing coverage. Please review.
✅ Project coverage is 43.64%. Comparing base (bfe8cfa) to head (2a2229d).

Files with missing lines Patch % Lines
web/Areas/Effort/Services/BaseReportService.cs 4.25% 45 Missing ⚠️
web/Areas/CMS/Data/Codecs.cs 0.00% 34 Missing ⚠️
web/Areas/RAPS/Controllers/RAPSController.cs 0.00% 34 Missing ⚠️
...nicalScheduler/Services/ScheduleQueryExtensions.cs 0.00% 31 Missing ⚠️
web/Areas/CTS/Services/CtsNavMenu.cs 0.00% 20 Missing ⚠️
web/Areas/CMS/Data/CMS.cs 0.00% 18 Missing ⚠️
...b/Areas/Effort/Services/EvaluationReportService.cs 0.00% 17 Missing ⚠️
...b/Areas/Directory/Models/IndividualSearchResult.cs 0.00% 15 Missing ⚠️
web/Areas/Effort/Services/DeptSummaryService.cs 0.00% 13 Missing ⚠️
web/Areas/Effort/Services/SchoolSummaryService.cs 0.00% 13 Missing ⚠️
... and 64 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #162      +/-   ##
==========================================
+ Coverage   43.40%   43.64%   +0.24%     
==========================================
  Files         863      866       +3     
  Lines       50476    50208     -268     
  Branches     4724     4695      -29     
==========================================
+ Hits        21909    21915       +6     
+ Misses      28044    27770     -274     
  Partials      523      523              
Flag Coverage Δ
backend 43.75% <12.97%> (+0.26%) ⬆️
frontend 41.57% <0.00%> (-0.12%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@rlorenzo rlorenzo changed the title Code anaylsis Code analysis tooling + security tightening + dedupe pass Apr 25, 2026
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 25, 2026

Bundle Report

Changes will decrease total bundle size by 24.26kB (-1.14%) ⬇️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
viper-frontend-esm 2.11MB -24.26kB (-1.14%) ⬇️

Affected Assets, Files, and Routes:

view changes for bundle: viper-frontend-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/TermManagement-*.js -1.24kB 52.34kB -2.31%
assets/CourseDetail-*.js -148 bytes 40.43kB -0.36%
assets/InstructorEdit-*.js -2.41kB 29.8kB -7.49%
assets/effort-*.js -91 bytes 23.31kB -0.39%
assets/CrossListedCoursesSection-*.js -628 bytes 23.07kB -2.65%
assets/GenericError-*.js 417 bytes 21.53kB 1.98%
assets/MultiYearReport-*.js 1 bytes 18.58kB 0.01%
assets/EmergencyContactForm-*.js -376 bytes 17.2kB -2.14%
assets/permission-*.js -14 bytes 10.65kB -0.13%
assets/cts-*.js -1.97kB 8.45kB -18.89%
assets/MyEffort-*.js 5 bytes 8.21kB 0.06%
assets/EmergencyContactView-*.js -427 bytes 7.6kB -5.32%
assets/schedule-*.css -240 bytes 6.83kB -3.4%
assets/EffortRecordEditDialog-*.js 941 bytes 6.36kB 17.36% ⚠️
assets/ClinicalEffort-*.js 1 bytes 5.41kB 0.02%
assets/InstructorDetail-*.js -383 bytes 5.01kB -7.1%
assets/TermSelection-*.js -1.8kB 4.59kB -28.16%
assets/SchoolSummary-*.js -421 bytes 3.9kB -9.74%
assets/TeachingActivityGrouped-*.js -605 bytes 3.58kB -14.44%
assets/MeritAverage-*.js -597 bytes 3.53kB -14.46%
assets/TeachingActivityIndividual-*.js -498 bytes 3.31kB -13.08%
assets/MeritDetail-*.js -604 bytes 3.24kB -15.73%
assets/students-*.js -123 bytes 3.23kB -3.66%
assets/DeptSummary-*.js -603 bytes 3.09kB -16.32%
assets/EvalDetail-*.js -603 bytes 2.87kB -17.35%
assets/MeritSummary-*.js -596 bytes 2.74kB -17.89%
assets/DialogSubmitActions-*.js (New) 2.13kB 2.13kB 100.0% 🚀
assets/EvalSummary-*.js -603 bytes 2.0kB -23.12%
assets/CourseStudents-*.js -881 bytes 1.95kB -31.08%
assets/report-*.css (New) 1.88kB 1.88kB 100.0% 🚀
assets/EffortReportPage-*.js (New) 1.78kB 1.78kB 100.0% 🚀
assets/cahfs-*.js -123 bytes 1.64kB -6.96%
assets/clinicalscheduler-*.js -123 bytes 1.57kB -7.26%
assets/cms-*.js -123 bytes 1.37kB -8.22%
assets/MultiYearReport-*.css -1.33kB 1.26kB -51.31%
assets/InstructorPageShell-*.js (New) 982 bytes 982 bytes 100.0% 🚀
assets/computing-*.js -123 bytes 976 bytes -11.19%
assets/EmergencyContactPageShell-*.js (New) 875 bytes 875 bytes 100.0% 🚀
assets/report-*.js (New) 664 bytes 664 bytes 100.0% 🚀
assets/colors-*.js -266 bytes 625 bytes -29.85%
assets/SchoolSummary-*.css -1.28kB 467 bytes -73.27%
assets/RotationScheduleView-*.css -1.33kB 349 bytes -79.21%
assets/ClinicalEffort-*.css -1.33kB 307 bytes -81.23%
assets/EvalDetail-*.css -1.33kB 163 bytes -89.08%
assets/EvalSummary-*.css -1.33kB 154 bytes -89.62%
assets/ClinicianScheduleView-*.css -1.33kB 125 bytes -91.41%
assets/TeachingActivityGrouped-*.css -1.33kB 111 bytes -92.29%
assets/MeritAverage-*.css -1.33kB 87 bytes -93.86%
assets/TeachingActivityIndividual-*.css -1.33kB 83 bytes -94.12%
assets/record-*.js (Deleted) -1.52kB 0 bytes -100.0% 🗑️
assets/DeptSummary-*.css (Deleted) -1.34kB 0 bytes -100.0% 🗑️
assets/ReportLayout-*.js (Deleted) -664 bytes 0 bytes -100.0% 🗑️
assets/ReportLayout-*.css (Deleted) -549 bytes 0 bytes -100.0% 🗑️

Files in assets/permission-*.js:

  • ./src/ClinicalScheduler/constants/permission-messages.ts → Total Size: 1.42kB

Files in assets/cts-*.js:

  • ./src/CTS/router/index.ts → Total Size: 444 bytes

Files in assets/CourseStudents-*.js:

  • ./src/CTS/pages/CourseStudents.vue → Total Size: 145 bytes

Files in assets/cahfs-*.js:

  • ./src/CAHFS/router/index.ts → Total Size: 420 bytes

Files in assets/clinicalscheduler-*.js:

  • ./src/ClinicalScheduler/router/index.ts → Total Size: 228 bytes

Files in assets/cms-*.js:

  • ./src/CMS/router/index.ts → Total Size: 414 bytes

Comment thread web/Program.cs Fixed
Comment thread web/Controllers/HomeController.cs Fixed
Comment thread web/Areas/Students/Services/PhotoExportService.cs Fixed
Comment thread web/Areas/Students/Services/PhotoExportService.cs Fixed
Comment thread web/Areas/Students/Services/PhotoExportService.cs Fixed
Comment thread web/Areas/Students/Services/PhotoExportService.cs Fixed
Comment thread web/Program.cs Fixed
Comment thread web/Areas/RAPS/Models/RoleTemplateSimplified.cs Fixed
Comment thread web/Program.cs Outdated
rlorenzo added 23 commits May 4, 2026 15:00
- PR CI fails if fallow or jscpd issue counts exceed main's
  baseline; existing duplication is capped but not required to
  reach zero.
- npm run lint + lint:staged run both tools as report-only
  warnings on in-scope files (VueApp/src for fallow, VueApp/src +
  web/Areas for jscpd); findings do not block commits yet.
- VueApp/.fallowrc.json declares the 8 SPA entry points and
  VueApp/tsconfig.json mirrors paths from tsconfig.app.json so
  fallow resolves @/* imports — without these, fallow false-flags
  ~200 files as unused.
- Delete 13 unused files: Vite-starter icon components, an
  orphan footer layout, two abandoned CTS pages, a refactor-
  leftover percent-edit dialog, two unused photo-gallery
  variants, and CSS assets with zero imports.
- Drop @quasar/extras and @pinia/testing from package.json
  after verifying zero usages. Future SVG-icon migration is
  tracked separately in PLAN-svg.md.
- Remove unused exports (and the now-dead local declarations
  that supported them) from colors.ts, DateFunctions.ts,
  QuasarConfig.ts, photo-gallery-service.ts, use-percentage-
  form.ts, error-transformer.ts, permission-messages.ts, and
  schedule-config.ts. colors.ts in particular collapses from
  256 lines to 31 after the dead palette plumbing is removed.
- Fallow's `percentRule` and SCHEDULE_LABELS/SCHEDULE_MESSAGES
  findings are false positives (direct imports and dynamic
  imports fallow missed) — kept as-is.
- Delete schedule.ts + schedule-{actions,cache,state}.ts: a 549-line
  state-management refactor landed in Sep 2025 ("Phase 4 - Frontend
  state management refactoring") was never wired in. `useScheduleStore`
  has zero importers anywhere in the codebase; RotationScheduleView and
  ClinicianScheduleView talk to services directly.
- Delete stores/permissions/index.ts: shadowed by sibling
  stores/permissions.ts (TypeScript resolves the .ts file before the
  directory index), making it structurally unreachable.
- Drop 5 redundant named exports from use-schedule-normalization.ts
  (normalizeSemesterWeeks, filterExcludedRotations, getAssignedRotationNames,
  normalizeClinicianSchedule, normalizeRotationSchedule). All five are
  only accessed via the useScheduleNormalization() composable; the
  module-level duplicates had no importers. normalizeWeek and
  normalizeScheduleSemesters stay exported — tests import them directly.
- Drop 42 dead re-exports from Effort/types/index.ts barrel: types
  passed through but no consumer imported them via the barrel.
- Drop 15 types each from report-types.ts and report-types-extended.ts
  and 9 from harvest-types.ts: row/group intermediates used only as
  property types within the same module; now file-local.
- Drop 8 service response types from api-responses.ts and parallel
  cleanups in instructor-schedule-service.ts, clinician-service.ts,
  ClinicalScheduler/types/index.ts, rotation-types.ts.
- Drop singletons: PageInitialData, PermissionErrorType,
  PermissionsGetters, UpdateRotationParams, EffortColumnOptions,
  HarvestProgressEvent, DataHygieneSummaryDto, EvalCourseInfoDto,
  EmailFailure, ValidationErrors, 3 CTS types.
- Fix BreadCrumb duplicate-export across ViperLayout.vue and
  ViperLayoutSimple.vue by making the local-only type file-private
  in both.
- WeekAssignment, WeekItem, ViewMode dropped to file-local in their
  component files.
- UserInfo stays exported — TypeScript requires it externally
  nameable because ProfilePic.vue's script-setup exposes store
  values typed with it.
Seven SPA routers (CAHFS, CMS, CTS, ClinicalScheduler, Computing, Effort,
Students) duplicated the same createRouter + VITE_VIPER_HOME + scroll-top
+ useRouteFocus setup. Hoist into src/shared/createSpaRouter.ts so each
router only owns its bespoke beforeEach guard. No behavior change.
Hoist breadcrumb, loading spinner, and not-found banner from
EmergencyContactForm.vue and EmergencyContactView.vue into a new
EmergencyContactPageShell.vue. Each page now owns only its h1 and
body content. Pure template refactor; no runtime change.
AddCourseEffortDialog and EffortRecordEditDialog shared a Cancel +
primary-action q-card-actions block with an identical #loading slot.
Hoist into DialogSubmitActions.vue parameterized by submitLabel and
isSaving. Other Effort dialogs still inline the pattern; migrate as
they are touched.
PercentAssignmentAddDialog and PercentAssignmentEditDialog shared a
165-line q-select/q-input/q-checkbox form body. Hoist into
PercentAssignmentFormFields.vue, v-modeled on the form state the
usePercentageForm composable already centralized. Each dialog now
owns only its title, save logic, banner variants, and footer. Also
migrate both footers to DialogSubmitActions. PercentageFormState
and TypeOption are re-exported from the composable so the new
component can type its props.
Replace style-block @import url() with script-setup side-effect
imports across 15 .vue files. Vite extracts shared CSS chunks
instead of inlining the file into each consumer's stylesheet, and
fallow/jscpd can now follow the dependency graph (the two CSS
files no longer surface as "unused"). Behavior-preserving for
report-tables.css and compact-form.css since their selectors are
already class-prefixed.

For schedule-shared.css, the bare h2/h3 element selectors are
re-scoped under .clinical-scheduler-container so the rules keep
their previous reach now that the file applies globally.

Drop the redundant colors.css @import in WeekCell.vue —
bootstrap-spa.ts already loads colors via styles/index.css for
every SPA.
Consolidates the h1 + ReportFilterForm + loading spinner + ReportLayout
header/ExportToolbar + empty-state boilerplate that was duplicated
across all 9 standard Effort report pages into a single wrapper, so
individual pages only define their table content.
Extract EmulationBanner, FooterLinks, and ViperBrandButton from the
ViperLayout/ViperLayoutSimple pair so both layouts render identical
header badges, emulation indicator, and footer links from a single
source.
Share the Role / Effort Value / Notes / warning + error banner block
between EffortRecordAddDialog and EffortRecordEditDialog, and route the
Add dialog's footer through the existing DialogSubmitActions component.
Share the Instructors breadcrumb, loading spinner, and error banner
between InstructorDetail and InstructorEdit via a slot-based shell.
Share the dialog scaffold (close affordance, title/subtitle, loading
spinner, progress bar, and error state) between HarvestDialog,
PercentRolloverDialog, and ClinicalImportDialog. Per-dialog preview
bodies and action buttons stay in the consumers.
Share the desktop table + mobile list rendering across unopened / open /
closed term sections instead of inlining three near-identical q-table
blocks.
The three repeated prototype rows now iterate over a single mock array,
which is how the real implementation will pull the API response.
Move ITermService injection into BaseReportService and add shared
helpers (LoadSingleTermContextAsync, LoadYearlyReportContextAsync,
ExtractDistinctEffortTypes) so DeptSummary, SchoolSummary, and
TeachingActivity services no longer hand-roll the same row +
clinical-faculty + term-name plumbing.
Add an IScheduleEntity interface implemented by InstructorSchedule and
StudentSchedule, then route both services through a generic
ApplyScheduleFilters extension instead of duplicating the rotation /
service / week / date filter clauses.
Introduce the StudentBaseRecord projection shape, BuildStudentPhotoListAsync,
and GetActiveRossIamIdsAsync so the by-class-level / by-group / by-course
methods no longer hand-roll the same photo lookup, group-assignment
formatting, and Ross-IamIds pre-query.
The four public methods now delegate to private EncodeWithMap /
DecodeWithMap routines parameterised by the encoding map, eliminating
the byte-for-byte duplication between the UU and XX variants.
…tService

Replace four near-identical N5..N1 weighted-average + divide-by-zero
guards in the summary and detail builders with a single helper.
…ages

Both wrappers now exit 1 when findings are present and the block-on-
warnings flag is set, matching the lint-staged-ts.js convention. The
default pre-commit run keeps them informational.
- Bump SonarAnalyzer.CSharp to 10.24.0 in both web and test (lockstep
  alignment; previously 10.22 / 10.21).
- Enable CodeQL's security-and-quality query pack (the broadest tier),
  upgrading from the default ~70-query pack to the full ~280-query set.
rlorenzo added 14 commits May 4, 2026 15:01
The InstructorPageShell extraction (refactor 4601108) replaced the
StatusBanner import in InstructorEdit.vue, but the template still
references StatusBanner three times for save/error feedback. Vue logged
"Failed to resolve component: StatusBanner" warnings and rendered those
banners blank.
- Enable Roslyn maintainability rules CA1501/1502/1505/1507/1508/
  1510-1513/1514. CA1506 and CA1515 skipped — heavy false
  positives on ASP.NET Core controllers and DTOs.
- ReSharper inspectcode added as resharper-pr-gate CI job. Only
  new findings at PR-touched lines vs origin/main fail, so
  pre-existing issues don't block.
- Bulk-applied via `jb cleanupcode --profile=OptimizeUsings` then
  `dotnet format`. Removes unused usings and sorts/groups remaining
  ones — compiler-equivalent, no behavior change.
- Adds Viper.sln.DotSettings with OptimizeUsings, ShortenReferences,
  and RemoveRedundancies profiles for this and follow-up PRs.
- Reproduction note: cleanupcode deadlocks on MSBuild's shared
  compiler server while the dev server runs; set
  DOTNET_USE_COMPILER_SERVER=0 to bypass.
- Bulk-applied via `jb cleanupcode --profile=ShortenReferences` then
  `dotnet format`. Replaces inline `Namespace.Type` with imported
  `Type` (or `using Alias = Namespace.Type;` for ambiguous names).
  Compiler-equivalent — no behavior change.
- PhotoExportService.cs gets the largest single diff because it
  juggles four DocumentFormat.OpenXml sub-namespaces with conflicting
  type names (`Picture`, `Paragraph`, `Run`, etc.); aliases at the
  top replace the inline full qualifiers throughout.
- Drop dead `?.` and `!= null` guards in 10 files where the analyzer
  proves the value is non-null (DI-injected helpers, catch
  parameters, values guaranteed by preceding null-checks).
- ViteProxyHelpers.cs: suppress CA1508 with `#pragma warning disable`
  on the inner check of a double-checked locking pattern. The outer
  guard may have raced; CA1508's dataflow analysis doesn't model
  thread interleaving, so this is a known false positive.
- Strip empty `<param name="X"></param>` tags whose corresponding
  parameter doesn't exist (or where the tag adds no doc value).
  Removes ~24 InvalidXmlDocComment findings.
- Fix malformed XML: escape literal `<T>` as `&lt;T&gt;` in three
  doc comments (HttpHelper, IamApi, UinformService); promote two
  `// <summary>` typos in IamApi to `///`; demote orphan `///`
  block on Program.cs SetAwsCredentials (top-level local function
  doesn't accept doc comments) to plain comment.
- Rename three orphan `<param>` tags to match real parameters
  (UserHelper.HasPermission, RAPSSecurityService.IsAllowedTo,
  VMACSExport.ExportToInstances).

Partial-coverage cases (methods with descriptive `<param>` tags
for some but not all parameters) intentionally left alone — their
existing documentation has real value, and selectively adding
empty tags or deleting good ones would be a net loss.
Fixes the 29 PossibleMultipleEnumeration findings flagged by ReSharper.
Each call site enumerated an IEnumerable two-or-more times (e.g.,
`source.Any()` then `source.First()`, or `source.Sum(a)` then
`source.Sum(b)`). Materialising once with `.ToList()` is semantically
equivalent and avoids re-evaluating deferred queries.

- Test files: cast assertion targets to lists so xUnit's `Assert.NotEmpty`
  + `Assert.Equal(N, x.Count())` doesn't iterate twice.
- VueTableDefault.cs: `data`, `skipColumns`, `altColumnNames` are
  enumerated 3+ times per helper and 3 times across helpers from
  `InvokeAsync`. Materialise at both layers.
- EvaluationPolicyService.IsRequiredFor*: `rotationWeeks` is enumerated
  by `.Any()`, `.FirstOrDefault(currentWeek)`, and `.FirstOrDefault(nextWeek)`.
  Materialise at top of method.
- CMSContentController.GetContentBlockByFn: switch `Any()/First()`
  to `Count == 0` / indexer.
- CliniciansController.FilterCliniciansByPermissions: rename param to
  `cliniciansSource`, materialise once into `clinicians`.
- Smaller fixes in RotationsController, EvaluationReportService.
- Bulk-applied via `jb cleanupcode --profile=RemoveRedundancies`
  then `dotnet format`. Removes redundant argument default values,
  redundant member initializers, redundant `else` after `return`,
  redundant anonymous-type property names, and other rewrites in
  the ReSharper "Remove code redundancies" category — all
  compiler-equivalent, no behavior change.
- Updates `Viper.sln.DotSettings`: the umbrella
  `<CSRemoveCodeRedundancies>` flag alone is a no-op. ReSharper
  also requires the (sparsely-documented) sibling
  `<RemoveCodeRedundancies>` flag plus per-rule sub-flags
  (`<CSRemoveRedundantArgumentDefaultValues>`,
  `<CSRemoveRedundantInitializers>`) to actually trigger the
  rewrites.
- Fixes a minor follow-up: `CliniciansController.cs:569` was
  changed to `clinicians.Count` (List property) from
  `clinicians.Count()` (LINQ). The IEnumerable to List
  materialisation in `9823a4c9` made `clinicians` a List, which
  Sonar S2971 then flagged as preferring the property. No new
  lint rule violations introduced by this commit.
- `npm run audit` now includes the ReSharper inspectcode pass and
  takes several minutes; use the per-tool scripts (`audit:fallow`,
  `audit:dupes`, `audit:resharper`) for fast iteration.
- Real cleanups across CMS / ClinicalScheduler / Effort / RAPS / Students:
  drop dead null-checks Roslyn flow analysis can prove unreachable,
  collapse a duplicated return path, simplify the cache lookup that
  was capturing a never-assigned outer variable, fix the stale XML doc
  param tag left behind by the IEnumerable materialisation rename,
  remove a virtual call from a DTO constructor, and split a SqlCommand
  object initializer outside the using statement.
- Replace anonymous-type byte[]? gymnastics in PhotoExportService with
  a named record so CodeQL stops flagging the casts as redundant.
- Gate now supports `--exclude-rule` and ships defaults for rules that
  fire false positives on ASP.NET Core / EF surfaces (DTO accessors
  bound at runtime by JSON / MVC, record positional properties used
  via record pattern Equals, and the EF nav-property NRT contract that
  Roslyn intentionally distrusts because Include() can be missing).
The UserHelper.GetByLoginId cache populator doesn't need the
ICacheEntry parameter; using `_` silences the new ReSharper
UnusedParameter.Local finding the previous commit introduced
when it collapsed the closure.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

Important

Review skipped

Too many files!

This PR contains 298 files, which is 148 over the limit of 150.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2a84c74d-cdb6-4900-aa11-e7b8c5d3627b

📥 Commits

Reviewing files that changed from the base of the PR and between bfe8cfa and 2a2229d.

⛔ Files ignored due to path filters (2)
  • VueApp/package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
  • package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
📒 Files selected for processing (298)
  • .config/dotnet-tools.json
  • .editorconfig
  • .github/workflows/code-quality.yml
  • .github/workflows/codeql.yml
  • .gitignore
  • .jscpd.json
  • README.md
  • Viper.sln.DotSettings
  • VueApp/.fallowrc.json
  • VueApp/package.json
  • VueApp/src/CAHFS/assets/cahfs.css
  • VueApp/src/CAHFS/router/index.ts
  • VueApp/src/CMS/assets/cms.css
  • VueApp/src/CMS/router/index.ts
  • VueApp/src/CTS/pages/CourseList.vue
  • VueApp/src/CTS/pages/CourseStudents.vue
  • VueApp/src/CTS/pages/MyAssessmentCharts.vue
  • VueApp/src/CTS/router/index.ts
  • VueApp/src/CTS/types/index.ts
  • VueApp/src/ClinicalScheduler/assets/schedule-shared.css
  • VueApp/src/ClinicalScheduler/components/ScheduleView.vue
  • VueApp/src/ClinicalScheduler/components/WeekCell.vue
  • VueApp/src/ClinicalScheduler/composables/use-schedule-normalization.ts
  • VueApp/src/ClinicalScheduler/constants/permission-messages.ts
  • VueApp/src/ClinicalScheduler/constants/schedule-config.ts
  • VueApp/src/ClinicalScheduler/pages/ClinicianScheduleView.vue
  • VueApp/src/ClinicalScheduler/pages/RotationScheduleView.vue
  • VueApp/src/ClinicalScheduler/router/index.ts
  • VueApp/src/ClinicalScheduler/services/clinician-service.ts
  • VueApp/src/ClinicalScheduler/services/error-transformer.ts
  • VueApp/src/ClinicalScheduler/services/instructor-schedule-service.ts
  • VueApp/src/ClinicalScheduler/services/page-data-service.ts
  • VueApp/src/ClinicalScheduler/services/permission-service.ts
  • VueApp/src/ClinicalScheduler/stores/permissions-utils.ts
  • VueApp/src/ClinicalScheduler/stores/permissions/index.ts
  • VueApp/src/ClinicalScheduler/stores/schedule-actions.ts
  • VueApp/src/ClinicalScheduler/stores/schedule-cache.ts
  • VueApp/src/ClinicalScheduler/stores/schedule-state.ts
  • VueApp/src/ClinicalScheduler/stores/schedule.ts
  • VueApp/src/ClinicalScheduler/types/api-responses.ts
  • VueApp/src/ClinicalScheduler/types/index.ts
  • VueApp/src/ClinicalScheduler/types/rotation-types.ts
  • VueApp/src/ClinicalScheduler/utils/schedule-update-helpers.ts
  • VueApp/src/Computing/router/index.ts
  • VueApp/src/Effort/components/AddCourseEffortDialog.vue
  • VueApp/src/Effort/components/AsyncOperationDialog.vue
  • VueApp/src/Effort/components/ClinicalImportDialog.vue
  • VueApp/src/Effort/components/DialogSubmitActions.vue
  • VueApp/src/Effort/components/EffortRecordAddDialog.vue
  • VueApp/src/Effort/components/EffortRecordEditDialog.vue
  • VueApp/src/Effort/components/EffortRecordSharedFields.vue
  • VueApp/src/Effort/components/EffortReportPage.vue
  • VueApp/src/Effort/components/HarvestDialog.vue
  • VueApp/src/Effort/components/InstructorPageShell.vue
  • VueApp/src/Effort/components/InstructorPercentEditDialog.vue
  • VueApp/src/Effort/components/PercentAssignmentAddDialog.vue
  • VueApp/src/Effort/components/PercentAssignmentEditDialog.vue
  • VueApp/src/Effort/components/PercentAssignmentFormFields.vue
  • VueApp/src/Effort/components/PercentRolloverDialog.vue
  • VueApp/src/Effort/components/TermTable.vue
  • VueApp/src/Effort/composables/use-effort-type-columns.ts
  • VueApp/src/Effort/composables/use-percentage-form.ts
  • VueApp/src/Effort/pages/ClinicalEffort.vue
  • VueApp/src/Effort/pages/DeptSummary.vue
  • VueApp/src/Effort/pages/EvalDetail.vue
  • VueApp/src/Effort/pages/EvalSummary.vue
  • VueApp/src/Effort/pages/InstructorDetail.vue
  • VueApp/src/Effort/pages/InstructorEdit.vue
  • VueApp/src/Effort/pages/MeritAverage.vue
  • VueApp/src/Effort/pages/MeritDetail.vue
  • VueApp/src/Effort/pages/MeritSummary.vue
  • VueApp/src/Effort/pages/MultiYearReport.vue
  • VueApp/src/Effort/pages/SchoolSummary.vue
  • VueApp/src/Effort/pages/TeachingActivityGrouped.vue
  • VueApp/src/Effort/pages/TeachingActivityIndividual.vue
  • VueApp/src/Effort/pages/TermSelection.vue
  • VueApp/src/Effort/router/index.ts
  • VueApp/src/Effort/services/harvest-service.ts
  • VueApp/src/Effort/types/dashboard-types.ts
  • VueApp/src/Effort/types/evaluation-types.ts
  • VueApp/src/Effort/types/harvest-types.ts
  • VueApp/src/Effort/types/index.ts
  • VueApp/src/Effort/types/report-types-extended.ts
  • VueApp/src/Effort/types/report-types.ts
  • VueApp/src/Effort/types/verification-types.ts
  • VueApp/src/Students/EmergencyContact/components/EmergencyContactPageShell.vue
  • VueApp/src/Students/EmergencyContact/pages/EmergencyContactForm.vue
  • VueApp/src/Students/EmergencyContact/pages/EmergencyContactView.vue
  • VueApp/src/Students/components/PhotoGallery/PhotoGrid.vue
  • VueApp/src/Students/components/PhotoGallery/PhotoList.vue
  • VueApp/src/Students/router/index.ts
  • VueApp/src/Students/services/photo-gallery-service.ts
  • VueApp/src/components/icons/IconCommunity.vue
  • VueApp/src/components/icons/IconDocumentation.vue
  • VueApp/src/components/icons/IconEcosystem.vue
  • VueApp/src/components/icons/IconSupport.vue
  • VueApp/src/components/icons/IconTooling.vue
  • VueApp/src/composables/DateFunctions.ts
  • VueApp/src/composables/QuasarConfig.ts
  • VueApp/src/composables/validation-error.ts
  • VueApp/src/config/colors.ts
  • VueApp/src/layouts/EmulationBanner.vue
  • VueApp/src/layouts/FooterLinks.vue
  • VueApp/src/layouts/ViperBrandButton.vue
  • VueApp/src/layouts/ViperFooter.vue
  • VueApp/src/layouts/ViperLayout.vue
  • VueApp/src/layouts/ViperLayoutSimple.vue
  • VueApp/src/shared/createSpaRouter.ts
  • VueApp/tsconfig.json
  • package.json
  • scripts/audit-fallow.js
  • scripts/audit-jscpd-regression.js
  • scripts/audit-jscpd.js
  • scripts/audit-resharper-regression.js
  • scripts/audit-resharper.js
  • scripts/audit.js
  • scripts/lint-any.js
  • scripts/lint-staged-fallow.js
  • scripts/lint-staged-jscpd.js
  • test/AsyncQueryable.cs
  • test/CTS/AssessmentControllerTest.cs
  • test/CTS/CompetencyBundleAssociationControllerTest.cs
  • test/CTS/SetupAssessments.cs
  • test/CTS/SetupPeople.cs
  • test/Classes/ApiResponseAttributeTests.cs
  • test/Classes/CustomAntiforgeryFilterTests.cs
  • test/ClinicalScheduler/ClinicalSchedulerContextTest.cs
  • test/ClinicalScheduler/ClinicalSchedulerTestBase.cs
  • test/ClinicalScheduler/EmailNotificationTest.cs
  • test/ClinicalScheduler/GradYearServiceTest.cs
  • test/ClinicalScheduler/InstructorScheduleControllerTest.cs
  • test/ClinicalScheduler/Integration/ControllerServiceIntegrationTest.cs
  • test/ClinicalScheduler/Integration/ServiceLayerIntegrationTest.cs
  • test/ClinicalScheduler/IntegrationTestBase.cs
  • test/ClinicalScheduler/PermissionsControllerTest.cs
  • test/ClinicalScheduler/PersonServiceTest.cs
  • test/ClinicalScheduler/RotationServiceTest.cs
  • test/ClinicalScheduler/RotationsControllerTest.cs
  • test/ClinicalScheduler/ScheduleAuditServiceTest.cs
  • test/ClinicalScheduler/ScheduleEditServiceTest.cs
  • test/ClinicalScheduler/SchedulePermissionServiceTest.cs
  • test/ClinicalScheduler/TestDataBuilder.cs
  • test/ClinicalScheduler/TestableScheduleEditService.cs
  • test/ClinicalScheduler/WeekServiceTest.cs
  • test/Effort/BaseReportServiceTests.cs
  • test/Effort/ClinicalImportServiceTests.cs
  • test/Effort/CourseRelationshipsControllerTests.cs
  • test/Effort/EffortIntegrationTestBase.cs
  • test/Effort/EffortRecordServiceTests.cs
  • test/Effort/ExcelGenerationTests.cs
  • test/Effort/HarvestServiceTests.cs
  • test/Effort/InstructorServiceTests.cs
  • test/Effort/InstructorsControllerTests.cs
  • test/Effort/PercentRolloverServiceTests.cs
  • test/Effort/PercentageServiceTests.cs
  • test/Effort/ReportsControllerTests.cs
  • test/Effort/TermServiceTests.cs
  • test/Effort/VerificationServiceTests.cs
  • test/RAPS/RolesTests.cs
  • test/RAPS/SetupRoles.cs
  • test/Services/EmailServiceTests.cs
  • test/Students/EmergencyContactControllerTests.cs
  • test/Students/EmergencyContactServiceTests.cs
  • test/Students/PhotoGalleryControllerTest.cs
  • test/Students/PhotoServiceTest.cs
  • web/Areas/CMS/Controllers/CMSContentController.cs
  • web/Areas/CMS/Controllers/CMSController.cs
  • web/Areas/CMS/Data/CMS.cs
  • web/Areas/CMS/Data/Codecs.cs
  • web/Areas/CMS/Models/CMSFile.cs
  • web/Areas/CMS/Models/Link.cs
  • web/Areas/CMS/Models/LinkCollection.cs
  • web/Areas/CMS/Models/LinkCollectionTagCategory.cs
  • web/Areas/CMS/Models/LinkTag.cs
  • web/Areas/CMS/Services/CmsNavMenu.cs
  • web/Areas/CMS/Validation/SafeUrlAttribute.cs
  • web/Areas/CTS/Controllers/AssessmentController.cs
  • web/Areas/CTS/Controllers/BundleCompetencyController.cs
  • web/Areas/CTS/Controllers/BundleCompetencyGroupController.cs
  • web/Areas/CTS/Controllers/BundleController.cs
  • web/Areas/CTS/Controllers/CTSController.cs
  • web/Areas/CTS/Controllers/ClinicalScheduleController.cs
  • web/Areas/CTS/Controllers/CompetencyBundleAssociationController.cs
  • web/Areas/CTS/Controllers/CompetencyController.cs
  • web/Areas/CTS/Controllers/CourseController.cs
  • web/Areas/CTS/Controllers/DomainController.cs
  • web/Areas/CTS/Controllers/EpaController.cs
  • web/Areas/CTS/Controllers/LegacyCompetenciesController.cs
  • web/Areas/CTS/Controllers/LevelsController.cs
  • web/Areas/CTS/Controllers/MilestonesController.cs
  • web/Areas/CTS/Models/Epa.cs
  • web/Areas/CTS/Models/StudentEpaAssessment.cs
  • web/Areas/CTS/Services/AuditService.cs
  • web/Areas/CTS/Services/CrestCourseService.cs
  • web/Areas/CTS/Services/CtsNavMenu.cs
  • web/Areas/CTS/Services/CtsSecurityService.cs
  • web/Areas/CTS/Services/EncounterCreationService.cs
  • web/Areas/ClinicalScheduler/Controllers/BaseClinicalSchedulerController.cs
  • web/Areas/ClinicalScheduler/Controllers/CliniciansController.cs
  • web/Areas/ClinicalScheduler/Controllers/InstructorScheduleController.cs
  • web/Areas/ClinicalScheduler/Controllers/PermissionsController.cs
  • web/Areas/ClinicalScheduler/Controllers/RotationsController.cs
  • web/Areas/ClinicalScheduler/Extensions/InstructorScheduleMappingExtensions.cs
  • web/Areas/ClinicalScheduler/Services/BaseClinicalSchedulerService.cs
  • web/Areas/ClinicalScheduler/Services/ClinicalSchedulerNavMenu.cs
  • web/Areas/ClinicalScheduler/Services/EvaluationPolicyService.cs
  • web/Areas/ClinicalScheduler/Services/InstructorScheduleService.cs
  • web/Areas/ClinicalScheduler/Services/PermissionValidator.cs
  • web/Areas/ClinicalScheduler/Services/ScheduleAuditService.cs
  • web/Areas/ClinicalScheduler/Services/ScheduleEditService.cs
  • web/Areas/ClinicalScheduler/Services/SchedulePermissionService.cs
  • web/Areas/ClinicalScheduler/Services/ScheduleQueryExtensions.cs
  • web/Areas/ClinicalScheduler/Services/StudentScheduleService.cs
  • web/Areas/Computing/Controllers/PersonController.cs
  • web/Areas/Computing/Services/BiorenderStudentLookup.cs
  • web/Areas/Directory/Controllers/DirectoryController.cs
  • web/Areas/Directory/Models/IndividualSearchResult.cs
  • web/Areas/Directory/Models/IndividualSearchResultCreator.cs
  • web/Areas/Directory/Models/IndividualSearchResultWithIDs.cs
  • web/Areas/Directory/Models/LdapUserContact.cs
  • web/Areas/Directory/Services/VMACSService.cs
  • web/Areas/Effort/Controllers/ClinicalImportController.cs
  • web/Areas/Effort/Controllers/CoursesController.cs
  • web/Areas/Effort/Controllers/CoursesSelfServiceController.cs
  • web/Areas/Effort/Controllers/HarvestController.cs
  • web/Areas/Effort/Controllers/InstructorsController.cs
  • web/Areas/Effort/Controllers/PercentRolloverController.cs
  • web/Areas/Effort/Models/DTOs/Responses/ClinicalImportDto.cs
  • web/Areas/Effort/Services/BaseReportService.cs
  • web/Areas/Effort/Services/ClinicalEffortService.cs
  • web/Areas/Effort/Services/ClinicalImportService.cs
  • web/Areas/Effort/Services/ClinicalScheduleService.cs
  • web/Areas/Effort/Services/CourseRelationshipService.cs
  • web/Areas/Effort/Services/CourseService.cs
  • web/Areas/Effort/Services/DashboardService.cs
  • web/Areas/Effort/Services/DeptSummaryService.cs
  • web/Areas/Effort/Services/EffortRecordService.cs
  • web/Areas/Effort/Services/EvalHarvestService.cs
  • web/Areas/Effort/Services/EvaluationReportService.cs
  • web/Areas/Effort/Services/Harvest/CrestHarvestPhase.cs
  • web/Areas/Effort/Services/Harvest/NonCrestHarvestPhase.cs
  • web/Areas/Effort/Services/HarvestService.cs
  • web/Areas/Effort/Services/IHarvestService.cs
  • web/Areas/Effort/Services/InstructorService.cs
  • web/Areas/Effort/Services/MeritMultiYearService.cs
  • web/Areas/Effort/Services/MeritReportService.cs
  • web/Areas/Effort/Services/MeritSummaryService.cs
  • web/Areas/Effort/Services/PercentageService.cs
  • web/Areas/Effort/Services/RCourseService.cs
  • web/Areas/Effort/Services/SchoolSummaryService.cs
  • web/Areas/Effort/Services/TeachingActivityService.cs
  • web/Areas/Effort/Services/TermService.cs
  • web/Areas/Effort/Services/VerificationService.cs
  • web/Areas/RAPS/Controllers/AdGroupRolesController.cs
  • web/Areas/RAPS/Controllers/AdGroupsController.cs
  • web/Areas/RAPS/Controllers/AuditController.cs
  • web/Areas/RAPS/Controllers/MemberPermissionsController.cs
  • web/Areas/RAPS/Controllers/MembersController.cs
  • web/Areas/RAPS/Controllers/PermissionsController.cs
  • web/Areas/RAPS/Controllers/RAPSController.cs
  • web/Areas/RAPS/Controllers/RoleMembersController.cs
  • web/Areas/RAPS/Controllers/RolePermissionsController.cs
  • web/Areas/RAPS/Controllers/RoleTemplatesController.cs
  • web/Areas/RAPS/Controllers/RolesController.cs
  • web/Areas/RAPS/Models/LdapGroup.cs
  • web/Areas/RAPS/Models/LdapUser.cs
  • web/Areas/RAPS/Models/MemberPermissionCreateUpdate.cs
  • web/Areas/RAPS/Models/PermissionClone.cs
  • web/Areas/RAPS/Models/RoleMemberCreateUpdate.cs
  • web/Areas/RAPS/Models/RolePermissionComparison.cs
  • web/Areas/RAPS/Models/RoleTemplateSimplified.cs
  • web/Areas/RAPS/Services/CloneService.cs
  • web/Areas/RAPS/Services/OuGroupService.cs
  • web/Areas/RAPS/Services/RAPSAuditService.cs
  • web/Areas/RAPS/Services/RAPSSecurityService.cs
  • web/Areas/RAPS/Services/RoleViews.cs
  • web/Areas/RAPS/Services/UinformService.cs
  • web/Areas/RAPS/Services/VMACSExport.cs
  • web/Areas/Students/Controllers/DvmController.cs
  • web/Areas/Students/Controllers/PhotoGalleryController.cs
  • web/Areas/Students/Services/CourseService.cs
  • web/Areas/Students/Services/EmergencyContactExportService.cs
  • web/Areas/Students/Services/EmergencyContactService.cs
  • web/Areas/Students/Services/GradYearClassLevel.cs
  • web/Areas/Students/Services/PhotoExportService.cs
  • web/Areas/Students/Services/PhotoService.cs
  • web/Areas/Students/Services/StudentGroupService.cs
  • web/Areas/Students/Services/StudentList.cs
  • web/Classes/ApiController.cs
  • web/Classes/ApiExceptionFilterAttribute.cs
  • web/Classes/ApiPaginatedResponse.cs
  • web/Classes/ApiPaginationAttribute.cs
  • web/Classes/ApiResponseAttribute.cs
  • web/Classes/ApiSessionUpdateFilter.cs
  • web/Classes/AreaController.cs
  • web/Classes/ClaimsTransformer.cs
  • web/Classes/ClientIpRestrictionsAttribute.cs
  • web/Classes/CorrelationIdMiddleware.cs

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch code-anaylsis

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rlorenzo rlorenzo closed this May 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants