Skip to content

Implement Priority Engine for Civic Intelligence#513

Merged
RohanExploit merged 1 commit into
mainfrom
feat/priority-engine-193245470447998047
Jun 13, 2026
Merged

Implement Priority Engine for Civic Intelligence#513
RohanExploit merged 1 commit into
mainfrom
feat/priority-engine-193245470447998047

Conversation

@RohanExploit

@RohanExploit RohanExploit commented Mar 6, 2026

Copy link
Copy Markdown
Owner

Implemented the missing PriorityEngine module as part of the Daily Civic Intelligence Refinement Engine architecture. It correctly computes priority based on description keywords and dynamic category weights.


PR created automatically by Jules for task 193245470447998047 started by @RohanExploit


Summary by cubic

Added PriorityEngine to score issue severity and urgency using keywords, adaptive category weights, and upvotes. This makes prioritization consistent and explainable in the Civic Intelligence pipeline.

  • New Features
    • Base severity from keywords with labels: Critical, High, Medium, Low.
    • Category multipliers from AdaptiveWeights (1 + weight/10), capped at 100, with clear reasoning.
    • Urgency = severity + upvote boost (+2 per upvote, max +20), capped at 100.
    • Unit tests cover low severity, boosted high severity, and critical keyword paths.

Written for commit 36c0aa3. Summary will update on new commits.

Summary by CodeRabbit

Release Notes

  • New Features

    • Automatically prioritizes items based on severity assessment (Low/Medium/High/Critical)
    • Priority system incorporates user engagement through upvotes for dynamic urgency scoring
    • Provides detailed reasoning and explanation for each priority determination
    • Supports custom category-based priority weight adjustments
  • Tests

    • Added comprehensive test coverage for priority calculation and severity assessment scenarios

…c weights\n\n- Created `services/priorityEngine.ts` to calculate issue severity and urgency using historical category weights from `modelWeights.json`.\n- Added `tests/priorityEngine.test.ts` to verify score calculations and boundary conditions.\n- Verified all 11 unit tests pass.
Copilot AI review requested due to automatic review settings March 6, 2026 18:39
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@netlify

netlify Bot commented Mar 6, 2026

Copy link
Copy Markdown

Deploy Preview for fixmybharat canceled.

Name Link
🔨 Latest commit 36c0aa3
🔍 Latest deploy log https://app.netlify.com/projects/fixmybharat/deploys/69ab1f4d84633d00086e6a25

@github-actions

github-actions Bot commented Mar 6, 2026

Copy link
Copy Markdown

🙏 Thank you for your contribution, @RohanExploit!

PR Details:

Quality Checklist:
Please ensure your PR meets the following criteria:

  • Code follows the project's style guidelines
  • Self-review of code completed
  • Code is commented where necessary
  • Documentation updated (if applicable)
  • No new warnings generated
  • Tests added/updated (if applicable)
  • All tests passing locally
  • No breaking changes to existing functionality

Review Process:

  1. Automated checks will run on your code
  2. A maintainer will review your changes
  3. Address any requested changes promptly
  4. Once approved, your PR will be merged! 🎉

Note: The maintainers will monitor code quality and ensure the overall project flow isn't broken.

@coderabbitai

coderabbitai Bot commented Mar 6, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

A new PriorityEngine class has been added that calculates issue priority by evaluating severity keywords, applying adaptive category weight multipliers, and boosting scores based on upvotes. The implementation includes comprehensive test coverage demonstrating low, high, and critical severity classifications.

Changes

Cohort / File(s) Summary
PriorityEngine Implementation
services/priorityEngine.ts
New PriorityEngine class with constructor accepting optional AdaptiveWeights and calculatePriority method that scores issues (0-100) based on keywords, applies category weight adjustments, derives severity labels, and boosts urgency from upvotes with detailed reasoning documentation.
PriorityEngine Test Suite
tests/priorityEngine.test.ts
Jest test suite with three comprehensive scenarios: low-severity classification without multiplier effects, high-severity classification with category weight multiplication reaching Critical level, and critical keyword handling with unknown category weights.

Sequence Diagram

sequenceDiagram
    participant User
    participant PriorityEngine
    participant AdaptiveWeights
    
    User->>PriorityEngine: calculatePriority(issue)
    
    PriorityEngine->>PriorityEngine: Extract keywords & identify category
    
    PriorityEngine->>PriorityEngine: Assign base severityScore<br/>(10, 40, 70, or 90)
    
    PriorityEngine->>AdaptiveWeights: Load categoryWeights
    
    AdaptiveWeights-->>PriorityEngine: Return weights for category
    
    PriorityEngine->>PriorityEngine: Apply weight multiplier<br/>if weight > 1
    
    PriorityEngine->>PriorityEngine: Derive severity label<br/>from severityScore thresholds
    
    PriorityEngine->>PriorityEngine: Calculate urgencyScore<br/>from severityScore + upvotes<br/>(capped at 100)
    
    PriorityEngine->>PriorityEngine: Compile reasoning array
    
    PriorityEngine-->>User: Return {severity, severityScore,<br/>urgencyScore, reasoning}
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A clever engine now takes flight,
Weighing issues, scoring right,
Keywords dance and weights align,
Priority predictions so divine,
Tests confirm each calculated sign! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description lacks required template sections: no explicit Type of Change selection, no Related Issue link format, incomplete Testing Done checklist, and missing Checklist items. While it provides good context, it doesn't follow the repository's template structure. Update PR description to follow the template: select a Type of Change (✨ New feature), link the issue (Closes #193245470447998047), mark Testing Done checkboxes, and complete the Checklist section.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: implementing a Priority Engine feature for the Civic Intelligence system.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/priority-engine-193245470447998047

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.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new PriorityEngine service to compute issue severity/urgency from keyword-based heuristics and AdaptiveWeights category multipliers, along with a Jest test suite validating several core scenarios.

Changes:

  • Introduce services/priorityEngine.ts to calculate severity/urgency scores + reasoning from issue descriptions, category weights, and upvotes.
  • Add tests/priorityEngine.test.ts covering low/high/critical keyword paths and category-weight boosting behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
services/priorityEngine.ts New PriorityEngine implementation applying keyword-based scoring and AdaptiveWeights category multipliers.
tests/priorityEngine.test.ts New Jest tests validating scoring, labels, upvote urgency boost, and reasoning output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

@@ -0,0 +1,77 @@
import { Issue, ModelWeights } from './types';

Copilot AI Mar 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ModelWeights is imported but never used in this module. Removing the unused import will reduce noise and avoid unused-import lint failures if enabled later.

Suggested change
import { Issue, ModelWeights } from './types';
import { Issue } from './types';

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +34
let baseLabel = "Low";

if (criticalKeywords.some(word => text.includes(word))) {
severityScore = 90;
baseLabel = "Critical";
reasoning.push("Flagged as Critical due to critical keywords in description.");
} else if (highKeywords.some(word => text.includes(word))) {
severityScore = 70;
baseLabel = "High";
reasoning.push("Flagged as High Severity due to high keywords in description.");
} else if (mediumKeywords.some(word => text.includes(word))) {
severityScore = 40;
baseLabel = "Medium";

Copilot AI Mar 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

baseLabel is assigned in each keyword branch but is never read afterwards. This looks like leftover logic and can be removed, or (if you intended to surface the pre-weight label) returned explicitly to avoid dead code.

Suggested change
let baseLabel = "Low";
if (criticalKeywords.some(word => text.includes(word))) {
severityScore = 90;
baseLabel = "Critical";
reasoning.push("Flagged as Critical due to critical keywords in description.");
} else if (highKeywords.some(word => text.includes(word))) {
severityScore = 70;
baseLabel = "High";
reasoning.push("Flagged as High Severity due to high keywords in description.");
} else if (mediumKeywords.some(word => text.includes(word))) {
severityScore = 40;
baseLabel = "Medium";
if (criticalKeywords.some(word => text.includes(word))) {
severityScore = 90;
reasoning.push("Flagged as Critical due to critical keywords in description.");
} else if (highKeywords.some(word => text.includes(word))) {
severityScore = 70;
reasoning.push("Flagged as High Severity due to high keywords in description.");
} else if (mediumKeywords.some(word => text.includes(word))) {
severityScore = 40;

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +43
// 2. Apply Adaptive Category Weights
const currentWeights = this.adaptiveWeights.loadWeights();
const categoryWeight = currentWeights.categoryWeights[issue.category];

Copilot AI Mar 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculatePriority() calls adaptiveWeights.loadWeights() on every invocation. In the current implementation loadWeights() does synchronous filesystem I/O + JSON.parse, so calling this per issue can be a significant bottleneck and will also throw if the weights file is corrupted. Consider caching the loaded weights in PriorityEngine (with an explicit refresh) and/or wrapping the load in a try/catch with a safe fallback.

Copilot uses AI. Check for mistakes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
tests/priorityEngine.test.ts (2)

5-93: Consider adding tests for medium severity and edge cases.

The existing tests provide good coverage of core paths. For completeness, consider adding:

  • Medium severity test (with "maintenance", "crack", or "dirty" keywords)
  • Empty/missing description handling
  • Edge case where severityScore exactly equals threshold boundaries (40, 70, 90)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/priorityEngine.test.ts` around lines 5 - 93, Add unit tests in
tests/priorityEngine.test.ts that cover medium severity, empty/missing
description, and boundary cases for severity thresholds: create a test invoking
PriorityEngine.calculatePriority with a description containing medium keywords
(e.g., "maintenance", "crack", "dirty") and assert severityScore in the 40–69
range and severity === 'Medium'; add a test where Issue.description is empty or
undefined to ensure calculatePriority handles it gracefully (no crash, returns a
default/base severity and appropriate reasoning); and add tests where generated
severityScore equals exactly 40, 70, and 90 (mock or craft inputs and category
weights so calculatePriority produces those exact scores) asserting the
resulting severity classifications match the expected boundaries. Ensure tests
reference PriorityEngine and calculatePriority so they fail if behavior changes.

9-28: Consider using typed mocks for better type safety.

The as any assertion on line 26 bypasses TypeScript's type checking. While this works, using jest.Mocked<AdaptiveWeights> or jest.mock() with proper typing would catch interface mismatches at compile time.

🧹 Alternative approach with typed mock
beforeEach(() => {
  const mockLoadWeights = jest.fn().mockReturnValue({
    categoryWeights: {
      'Pothole': 5,
      'Garbage': 3,
      'Water Supply': 4,
      'Streetlight': 1,
      'Flooding': 8
    },
    duplicateThreshold: 0.85,
    lastUpdated: new Date().toISOString(),
    history: []
  } as ModelWeights);

  adaptiveWeights = {
    loadWeights: mockLoadWeights,
    optimizeWeights: jest.fn(),
    saveWeights: jest.fn(),
    configPath: 'mock-path'  // include all required properties
  } as jest.Mocked<AdaptiveWeights>;

  priorityEngine = new PriorityEngine(adaptiveWeights);
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/priorityEngine.test.ts` around lines 9 - 28, The test uses an unsafe
"as any" cast for adaptiveWeights; replace it with a properly typed Jest mock by
creating jest.fn() mocks for loadWeights, optimizeWeights, and saveWeights
(e.g., const mockLoadWeights =
jest.fn().mockReturnValue(<ModelWeights>({...}))), include any required
properties on the AdaptiveWeights interface (e.g., configPath) and then assign
adaptiveWeights = { loadWeights: mockLoadWeights, optimizeWeights: jest.fn(),
saveWeights: jest.fn(), /* other props */ } as jest.Mocked<AdaptiveWeights>;
finally pass that typed mock into new PriorityEngine(adaptiveWeights) so
TypeScript catches interface mismatches and you can use jest assertions on the
mock methods (referencing AdaptiveWeights, loadWeights, optimizeWeights,
saveWeights, and PriorityEngine).
services/priorityEngine.ts (2)

63-66: Minor: Redundant truthiness check on upvotes.

The issue.upvotes && check is redundant since issue.upvotes > 0 already evaluates to false for 0, negative numbers, and falsy values.

🧹 Simplify condition
-    if (issue.upvotes && issue.upvotes > 0) {
+    if (issue.upvotes > 0) {
       urgencyScore += Math.min(20, issue.upvotes * 2); // Cap upvote boost at 20
       reasoning.push(`Urgency increased by upvotes (${issue.upvotes}).`);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/priorityEngine.ts` around lines 63 - 66, The condition in
services/priorityEngine.ts redundantly checks issue.upvotes twice; replace the
`if (issue.upvotes && issue.upvotes > 0)` with a single check `if (issue.upvotes
> 0)` in the block that updates urgencyScore and reasoning (the code that
increases urgencyScore by Math.min(20, issue.upvotes * 2) and pushes to
reasoning) so the logic remains identical but simpler and clearer.

42-43: Consider caching weights to avoid repeated file I/O.

loadWeights() performs synchronous file reads (per adaptiveWeights.ts). Calling it on every calculatePriority invocation creates unnecessary I/O overhead, especially when processing multiple issues in batch.

Consider loading weights once (e.g., in constructor or via a lazy-initialized cache) and reloading only when explicitly requested or when the file changes.

♻️ Suggested approach
 export class PriorityEngine {
   private adaptiveWeights: AdaptiveWeights;
+  private cachedWeights: ModelWeights | null = null;

   constructor(adaptiveWeights?: AdaptiveWeights) {
     this.adaptiveWeights = adaptiveWeights || new AdaptiveWeights();
   }

+  private getWeights(): ModelWeights {
+    if (!this.cachedWeights) {
+      this.cachedWeights = this.adaptiveWeights.loadWeights();
+    }
+    return this.cachedWeights;
+  }
+
+  public refreshWeights(): void {
+    this.cachedWeights = null;
+  }

   public calculatePriority(issue: Issue): { severity: string; severityScore: number; urgencyScore: number; reasoning: string[] } {
     // ...
-    const currentWeights = this.adaptiveWeights.loadWeights();
+    const currentWeights = this.getWeights();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/priorityEngine.ts` around lines 42 - 43, The current
calculatePriority flow calls adaptiveWeights.loadWeights() on every invocation
which causes repeated synchronous file I/O; modify the PriorityEngine class to
cache weights (e.g., a private this.cachedWeights initialized in the constructor
or via lazy getter) and have calculatePriority read from this.cachedWeights
instead of calling loadWeights() each time, and add an explicit reloadWeights()
method (or watch/refresh hook) that updates this.cachedWeights by calling
adaptiveWeights.loadWeights() when needed; update references to currentWeights
and categoryWeight to use the cached copy.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@services/priorityEngine.ts`:
- Around line 63-66: The condition in services/priorityEngine.ts redundantly
checks issue.upvotes twice; replace the `if (issue.upvotes && issue.upvotes >
0)` with a single check `if (issue.upvotes > 0)` in the block that updates
urgencyScore and reasoning (the code that increases urgencyScore by Math.min(20,
issue.upvotes * 2) and pushes to reasoning) so the logic remains identical but
simpler and clearer.
- Around line 42-43: The current calculatePriority flow calls
adaptiveWeights.loadWeights() on every invocation which causes repeated
synchronous file I/O; modify the PriorityEngine class to cache weights (e.g., a
private this.cachedWeights initialized in the constructor or via lazy getter)
and have calculatePriority read from this.cachedWeights instead of calling
loadWeights() each time, and add an explicit reloadWeights() method (or
watch/refresh hook) that updates this.cachedWeights by calling
adaptiveWeights.loadWeights() when needed; update references to currentWeights
and categoryWeight to use the cached copy.

In `@tests/priorityEngine.test.ts`:
- Around line 5-93: Add unit tests in tests/priorityEngine.test.ts that cover
medium severity, empty/missing description, and boundary cases for severity
thresholds: create a test invoking PriorityEngine.calculatePriority with a
description containing medium keywords (e.g., "maintenance", "crack", "dirty")
and assert severityScore in the 40–69 range and severity === 'Medium'; add a
test where Issue.description is empty or undefined to ensure calculatePriority
handles it gracefully (no crash, returns a default/base severity and appropriate
reasoning); and add tests where generated severityScore equals exactly 40, 70,
and 90 (mock or craft inputs and category weights so calculatePriority produces
those exact scores) asserting the resulting severity classifications match the
expected boundaries. Ensure tests reference PriorityEngine and calculatePriority
so they fail if behavior changes.
- Around line 9-28: The test uses an unsafe "as any" cast for adaptiveWeights;
replace it with a properly typed Jest mock by creating jest.fn() mocks for
loadWeights, optimizeWeights, and saveWeights (e.g., const mockLoadWeights =
jest.fn().mockReturnValue(<ModelWeights>({...}))), include any required
properties on the AdaptiveWeights interface (e.g., configPath) and then assign
adaptiveWeights = { loadWeights: mockLoadWeights, optimizeWeights: jest.fn(),
saveWeights: jest.fn(), /* other props */ } as jest.Mocked<AdaptiveWeights>;
finally pass that typed mock into new PriorityEngine(adaptiveWeights) so
TypeScript catches interface mismatches and you can use jest assertions on the
mock methods (referencing AdaptiveWeights, loadWeights, optimizeWeights,
saveWeights, and PriorityEngine).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1c1a2d2a-fbb1-4336-88b4-f48b4782a41f

📥 Commits

Reviewing files that changed from the base of the PR and between 40cb681 and 36c0aa3.

📒 Files selected for processing (2)
  • services/priorityEngine.ts
  • tests/priorityEngine.test.ts

@RohanExploit
RohanExploit merged commit 6f0b5dc into main Jun 13, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants