Skip to content
Open
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
2 changes: 2 additions & 0 deletions frontend/netlify.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
publish = "dist"
5 changes: 5 additions & 0 deletions frontend/public/_headers
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/*
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
1 change: 1 addition & 0 deletions frontend/public/_redirects
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* /index.html 200
15 changes: 0 additions & 15 deletions netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,3 @@

# Environment variables (set these in Netlify dashboard)
# VITE_API_URL = https://your-backend.onrender.com

# Redirects for SPA
[[redirects]]
from = "/*"
to = "/index.html"
status = 200

# Headers for security
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
X-XSS-Protection = "1; mode=block"
Referrer-Policy = "strict-origin-when-cross-origin"
22 changes: 11 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@
"sqlite3": "^5.1.7"
},
"devDependencies": {
"@types/jest": "^29.5.12",
"@types/jest": "^29.5.14",
"@types/node": "^20.11.24",
"@types/node-cron": "^3.0.11",
"@types/sqlite3": "^3.1.11",
"jest": "^29.7.0",
"jest-util": "^30.2.0",
"ts-jest": "^29.1.2",
"jest-util": "^30.3.0",
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

jest-util is pinned as a direct devDependency at major v30 while jest is v29, and it doesn’t appear to be imported/used anywhere in the repo. This introduces an additional major version of Jest internals (and pulls in @jest/types v30) which can cause subtle incompatibilities. Consider removing the direct jest-util dependency (let Jest manage its internal deps) or pin it to a compatible v29.x version if it’s truly needed.

Suggested change
"jest-util": "^30.3.0",

Copilot uses AI. Check for mistakes.
"ts-jest": "^29.4.6",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
}
Expand Down
13 changes: 13 additions & 0 deletions scheduler/dailyRefinementJob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@ import { Issue } from "../services/types";
const dbPath =
process.env.DB_PATH || path.join(__dirname, "../../backend/app.db");

/**
* DailyRefinementJob: Orchestrates the daily process of trend detection,
* adaptive weight optimization, and index generation. Runs locally with SQLite.
*
* Algorithm and Evolution Logic:
* 1. Executes every 24 hours via node-cron.
* 2. Fetches all civic issues reported in the past 24 hours.
* 3. Uses TrendAnalyzer to find the top 5 emerging keywords and category spikes (>50% increase).
* 4. Uses AdaptiveWeights to increase severity multipliers for critical issue patterns
* and dynamically tighten/relax the global duplicate threshold based on volume.
* 5. Calculates a Daily Civic Intelligence Index using IntelligenceIndex, heavily weighting resolutions.
* 6. Stores these insights locally for auditability without external APIs.
*/
export class DailyRefinementJob {
private db: sqlite3.Database;
private trendAnalyzer: TrendAnalyzer;
Expand Down
13 changes: 13 additions & 0 deletions services/adaptiveWeights.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@ import { Issue, ModelWeights } from "./types";
import * as fs from "fs";
import * as path from "path";

/**
* AdaptiveWeights: Dynamically tunes category priorities based on historical
* critical assignments, storing previous states for full auditability.
*
* Algorithm and Evolution Logic:
* If a particular category (like "Pothole" or "Flooding") is frequently marked Critical manually
* or resolved (> 5 times), this engine increases the internal category weight by +0.5,
Comment on lines +10 to +11
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 21, 2026

Choose a reason for hiding this comment

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

P2: The new algorithm documentation is inaccurate: weight increases are triggered by resolved/assigned issues, not by manually marking issues as Critical.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At services/adaptiveWeights.ts, line 10:

<comment>The new algorithm documentation is inaccurate: weight increases are triggered by resolved/assigned issues, not by manually marking issues as Critical.</comment>

<file context>
@@ -4,18 +4,16 @@ import * as path from "path";
- * issues that are resolved or explicitly assigned (> 5 times), this engine
- * increases the internal category weight by +0.5, capping at 10 to
- * auto-prioritize it going forward.
+ * If a particular category (like "Pothole" or "Flooding") is frequently marked Critical manually
+ * or resolved (> 5 times), this engine increases the internal category weight by +0.5,
+ * capping at 10 to auto-prioritize it going forward.
</file context>
Suggested change
* If a particular category (like "Pothole" or "Flooding") is frequently marked Critical manually
* or resolved (> 5 times), this engine increases the internal category weight by +0.5,
* If a particular category (like "Pothole" or "Flooding") accumulates many
* issues that are resolved or explicitly assigned (> 5 times), this engine increases the internal category weight by +0.5,
Fix with Cubic

* capping at 10 to auto-prioritize it going forward.
* It also intelligently manipulates the Duplicate Pattern detection threshold:
* Tightening (+0.01 to 0.95 max) when platform usage > 100, and relaxing (-0.01 to 0.70 min)
* on slower days to adaptively manage noise filtering.
* Saves an explicit snapshot history within modelWeights.json for complete AI model audibility.
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 21, 2026

Choose a reason for hiding this comment

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

P3: This documentation overstates retention/audit behavior; history is capped to 30 entries, so it is not a complete record.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At services/adaptiveWeights.ts, line 16:

<comment>This documentation overstates retention/audit behavior; history is capped to 30 entries, so it is not a complete record.</comment>

<file context>
@@ -4,18 +4,16 @@ import * as path from "path";
  * Tightening (+0.01 to 0.95 max) when platform usage > 100, and relaxing (-0.01 to 0.70 min)
  * on slower days to adaptively manage noise filtering.
- * Saves recent snapshot history (up to the last 30 days) within modelWeights.json for auditability.
+ * Saves an explicit snapshot history within modelWeights.json for complete AI model audibility.
  */
 export class AdaptiveWeights {
</file context>
Fix with Cubic

*/
export class AdaptiveWeights {
private configPath: string;

Expand Down
11 changes: 11 additions & 0 deletions services/intelligenceIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@ import { Issue, DailySnapshot } from "./types";
import * as fs from "fs";
import * as path from "path";

/**
* IntelligenceIndex: Produces a daily Civic Intelligence snapshot, aggregating
* system performance metrics to capture overall municipal health and activity.
*
* Algorithm and Evolution Logic:
* Starts at a 50.0 Index Baseline.
* Increases (+): Base score raises by ratio of resolved civic issues (high resolutions = better performance).
* Minor Bonus (+): If top keywords contain rich descriptions, minor bump (+0.5 per keyword).
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 21, 2026

Choose a reason for hiding this comment

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

P3: The new JSDoc describes a conditional keyword bonus that the code does not implement, which can mislead future changes and tests.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At services/intelligenceIndex.ts, line 12:

<comment>The new JSDoc describes a conditional keyword bonus that the code does not implement, which can mislead future changes and tests.</comment>

<file context>
@@ -9,8 +9,8 @@ import * as path from "path";
  * Increases (+): Base score raises by ratio of resolved civic issues (high resolutions = better performance).
- * Minor Bonus (+): Adds a small bump (+0.5 per top keyword).
- * Penalizes (-): Immediate penalty per category spike (-1.5 per concern) to represent unmet issues.
+ * Minor Bonus (+): If top keywords contain rich descriptions, minor bump (+0.5 per keyword).
+ * Penalizes (-): Immediate penalty per significant Category Spikes (-1.5 per concern) to represent unmet issues.
  * Stores a JSON snapshot daily (e.g. data/dailySnapshots/YYYY-MM-DD.json) comparing delta changes.
</file context>
Suggested change
* Minor Bonus (+): If top keywords contain rich descriptions, minor bump (+0.5 per keyword).
* Minor Bonus (+): Adds a small bump (+0.5 per top keyword).
Fix with Cubic

* Penalizes (-): Immediate penalty per significant Category Spikes (-1.5 per concern) to represent unmet issues.
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 21, 2026

Choose a reason for hiding this comment

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

P3: The JSDoc now claims spike penalties apply only to significant spikes, but the current logic penalizes all spikes equally.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At services/intelligenceIndex.ts, line 13:

<comment>The JSDoc now claims spike penalties apply only to significant spikes, but the current logic penalizes all spikes equally.</comment>

<file context>
@@ -9,8 +9,8 @@ import * as path from "path";
- * Minor Bonus (+): Adds a small bump (+0.5 per top keyword).
- * Penalizes (-): Immediate penalty per category spike (-1.5 per concern) to represent unmet issues.
+ * Minor Bonus (+): If top keywords contain rich descriptions, minor bump (+0.5 per keyword).
+ * Penalizes (-): Immediate penalty per significant Category Spikes (-1.5 per concern) to represent unmet issues.
  * Stores a JSON snapshot daily (e.g. data/dailySnapshots/YYYY-MM-DD.json) comparing delta changes.
  */
</file context>
Suggested change
* Penalizes (-): Immediate penalty per significant Category Spikes (-1.5 per concern) to represent unmet issues.
* Penalizes (-): Immediate penalty per category spike (-1.5 per concern) to represent unmet issues.
Fix with Cubic

* Stores a JSON snapshot daily (e.g. data/dailySnapshots/YYYY-MM-DD.json) comparing delta changes.
*/
export class IntelligenceIndex {
private snapshotsDir: string;

Expand Down
11 changes: 11 additions & 0 deletions services/priorityEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,17 @@ const CATEGORIES: Record<string, string[]> = {
Environment: ["tree", "cutting", "deforestation", "forest", "nature"],
};

/**
* PriorityEngine: Analyzes issues to assign a severity and urgency score,
* leveraging adaptive weights for continuously optimized dynamic routing.
*
* Algorithm and Evolution Logic:
* Uses pre-defined keyword dictionaries across Critical, High, Medium, and Low severity tiers.
* It scores issues using substring matches. Critically, it multiplies the base score by dynamic
* parameters managed by AdaptiveWeights.
* If `modelWeights.json` determines that "Pothole" incidents are currently severe,
* the multiplier > 1 upgrades the severity automatically, representing self-improving infrastructure.
*/
export class PriorityEngine {
private adaptiveWeights: AdaptiveWeights;
private lastWeightsCheck: number = 0;
Expand Down
11 changes: 11 additions & 0 deletions services/trendAnalyzer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import { Issue } from "./types";

/**
* TrendAnalyzer: Performs local pattern detection, finding emerging keywords,
* significant category volume spikes, and geographical clustering of issues.
*
* Algorithm and Evolution Logic:
* 1. Trend detection extracts the top 5 most common keywords (>= 3 chars) after filtering stop-words.
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 21, 2026

Choose a reason for hiding this comment

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

P3: JSDoc states a fixed top-5 keyword limit, but the method supports a configurable limit parameter.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At services/trendAnalyzer.ts, line 8:

<comment>JSDoc states a fixed top-5 keyword limit, but the method supports a configurable `limit` parameter.</comment>

<file context>
@@ -5,14 +5,11 @@ import { Issue } from "./types";
- *    as "Emerging Concerns" when their volume increases by at least 50% over the previous window.
- *    When the previous count is zero, only categories whose new count exceeds 5 are considered;
- *    when the previous count is nonzero, any >= 50% increase is considered, even for small counts.
+ * 1. Trend detection extracts the top 5 most common keywords (>= 3 chars) after filtering stop-words.
+ * 2. It compares the last 24h category distribution with the previous 24h.
+ *    If an issue spikes > 50% and exceeds a baseline of 5, it's flagged as an "Emerging Concern".
</file context>
Suggested change
* 1. Trend detection extracts the top 5 most common keywords (>= 3 chars) after filtering stop-words.
* 1. Trend detection extracts the most common keywords (>= 3 chars) after filtering stop-words, up to a configurable limit (default: 5).
Fix with Cubic

* 2. It compares the last 24h category distribution with the previous 24h.
* If an issue spikes > 50% and exceeds a baseline of 5, it's flagged as an "Emerging Concern".
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 21, 2026

Choose a reason for hiding this comment

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

P2: Spike-rule documentation is inaccurate: baseline >5 applies only when previous count is zero, and the threshold check is >= 50% for existing categories.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At services/trendAnalyzer.ts, line 10:

<comment>Spike-rule documentation is inaccurate: baseline >5 applies only when previous count is zero, and the threshold check is `>= 50%` for existing categories.</comment>

<file context>
@@ -5,14 +5,11 @@ import { Issue } from "./types";
- *    when the previous count is nonzero, any >= 50% increase is considered, even for small counts.
+ * 1. Trend detection extracts the top 5 most common keywords (>= 3 chars) after filtering stop-words.
+ * 2. It compares the last 24h category distribution with the previous 24h.
+ *    If an issue spikes > 50% and exceeds a baseline of 5, it's flagged as an "Emerging Concern".
  * 3. Uses rounded coordinate boundaries (approx. 1.1km) to locate geographical clusters
- *    and determine the region with the highest concentration of issues.
</file context>
Suggested change
* If an issue spikes > 50% and exceeds a baseline of 5, it's flagged as an "Emerging Concern".
* Categories are flagged as "Emerging Concerns" when volume increases by at least 50% over the previous window; when the previous count is zero, only categories with current count > 5 are considered.
Fix with Cubic

* 3. Uses rounded coordinate boundaries (approx. 1.1km) to locate geographical clusters
* and determine the region with the highest concentration of issues.
*/
export class TrendAnalyzer {
public getTopKeywords(issues: Issue[], limit: number = 5): string[] {
const wordCounts: Record<string, number> = {};
Expand Down
8 changes: 8 additions & 0 deletions tests/dailyRefinement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ describe("Daily Civic Intelligence Refinement Engine Tests", () => {
}
});

test("should handle empty issue lists gracefully", () => {
const snapshot = intelligenceIndex.generateIndex([], [], []);
expect(snapshot.indexScore).toBe(50.0);
expect(snapshot.delta).toBe(0.0);
expect(snapshot.emergingConcerns).toHaveLength(0);
expect(snapshot.topKeywords).toHaveLength(0);
});

test("should generate and save daily snapshot score", () => {
const snapshot = intelligenceIndex.generateIndex(
dummyIssues,
Expand Down
10 changes: 10 additions & 0 deletions tests/priorityEngine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,14 @@ describe('PriorityEngine Tests', () => {
const result = priorityEngine.calculateSeverityAndUrgency('Huge pothole on the road', 'Pothole');
expect(result.suggestedCategories).toContain('Pothole');
});

test('should handle empty descriptions or descriptions with no keywords gracefully', () => {
const result = priorityEngine.calculateSeverityAndUrgency('Normal everyday thing', 'Other');

// No matching severity keyword implies default score of 10
expect(result.severityScore).toBe(10);
expect(result.severity).toBe('Low');
expect(result.urgencyScore).toBe(10);
expect(result.reasoning).toContain('Classified as Low Severity (maintenance/cosmetic issue)');
});
});
Loading