Releases: Resgrid/Core
Release list
4.539.0
Here is the analysis of the code changes and the generated Summary:
Summary
This pull request implements RG-T125 (IC bug fixes and weather alert fixes). The work is focused on improving the Incident Command (IC) module (specifically the Command Board and Command Definition Templates) and enhancing weather alert notifications.
Key Changes:
-
Incident Command (IC) Improvements:
- Command Definition Board (Templates): Fixed several issues related to the creation and management of command board templates (templates for the default incident command board layout). This includes ensuring that templates, roles, and lanes (assignments on the board) are properly persisted, retrieved, and linked.
- Lane Requirements: Implemented advisory and forced lane requirements (via
ForceRequirements). When enabled, the system can now block assigning a resource (personnel/unit) to a lane on the command board if they do not meet the template-defined lane requirements (e.g., required personnel roles or unit types). This ensures only qualified resources are placed in specific lanes on the default board templates. - Lane Assignment: Implemented the logic to correctly assign, move, and remove resources (personnel and units) to lanes on the command board. This includes ensuring proper relationships and preventing assignment when forced lane requirements are not met.
- Lane Definition Persistence: The system now correctly saves and retrieves the lane requirements (roles, unit types) when a user creates or edits a command board template.
- Command Board View: Fixed the view of the command board to correctly display the lanes, assignments, and requirements.
-
Weather Alert Fixes:
- Fixed the creation and sending of weather alert notifications. Weather alerts are now properly categorized with a new
WeatherAlertmessage type and include a customPushSubTitle(based on the alert headline) for better user visibility. - Added new exception handling (
CommandRequirementsNotMetException) to ensure the system gracefully handles and reports errors when a user attempts to assign a resource that does not meet the forced lane requirements.
- Fixed the creation and sending of weather alert notifications. Weather alerts are now properly categorized with a new
-
Database and Infrastructure:
- Added necessary database migrations to support the new lane requirements and exception handling.
- Implemented unit tests and integration tests to cover the new command board templates and lane assignment functionality.
4.531.0
Description
This PR fixes bugs in the voice dispatch TTS (text-to-speech) path where long dispatch text exceeding the TTS chunk limit would cause ArgumentException failures, breaking voice dispatch playback (Sentry: RESGRID-API-78).
Changes
-
Pre-warm path (
PreWarmPromptAsync): Previously threwArgumentExceptionfor any input spanning more than one TTS chunk. It now iterates over all chunks and pre-warms each individually, so long dispatch text no longer faults the pre-warm/redirect flow. -
Playback path (
TryAppendDispatchPlaybackAsyncinTwilioController): Switched fromGetPromptUrlAsync(single-chunk only) to the multi-chunk-awareAppendPromptAsync, which emits one<Play>element per chunk. This prevents failures when dispatch text (e.g., long notes or addresses) exceeds the chunk limit. -
Tests: Added two regression tests verifying that
PreWarmPromptAsyncno longer throws for multi-chunk text and thatAppendPromptAsyncproduces a<Play>per chunk for multi-chunk input.
4.529.0
Pull Request Description
This PR fixes a SQL Server compatibility issue with the Resgrid Workers that occurs on .NET 8+ (the project now targets .NET 9.0).
Problem
The external Quidjibo.SqlServer NuGet package (v0.6.0) uses the legacy System.Data.SqlClient, which throws a SqlGuidCaster TypeLoadException on .NET 8 and above, breaking the worker's ability to use SQL Server as its background job store.
Changes
-
Vendored the Quidjibo.SqlServer provider locally: Replaced the NuGet package reference with a new in-repository
Quidjibo.SqlServerproject that usesMicrosoft.Data.SqlClientinstead. This project includes:- Configuration, factory, and provider implementations for Work, Schedule, and Progress job storage
- All required SQL scripts (schema setup, work receive/complete/fault/send, schedule create/receive/complete, progress tracking)
- Utility classes for SQL execution and embedded script loading
-
Fixed semaphore lock patterns in the Postgres provider factories: Moved
SyncLock.WaitAsync()calls outside of theirtryblocks in the Progress, Schedule, and Work provider factories. This prevents a scenario where a cancellation or exception duringWaitAsyncwould cause thefinallyblock to incorrectly callRelease(), leading to aSemaphoreFullException.
4.526.0
Summary
This PR implements the backend infrastructure for the Resgrid IC (Incident Command) app's offline-first shift-start workflow, comprising three major additions: (1) two new aggregate sync endpoints that let a field client pull everything it needs in minimal round-trips, (2) a per-endpoint incident-capability authorization layer, and (3) associated tests and documentation.
1. Shift-start aggregate sync endpoints
GET /api/v4/Sync/Bundle — Returns a render-ready IncidentCommandBoard (lanes, resources, objectives, timers, roles, annotations, and computed accountability/PAR) for every active incident in the caller's department, plus active ad-hoc units and personnel, in a single call. The client uses the returned ServerTimestampMs as the cursor for subsequent incremental /Sync/Changes pulls.
IncidentCommandService.GetBundleForDepartmentAsyncscans each board table once for the whole department and groups byCallIdin memory — O(tables) instead of the previous O(active incidents × department size) per-incident N+1 pattern.- The bundle is read-only: unlike the per-call
GetCommandBoardAsync, it does not run the write-side PAR sweep (no marker writes or SignalR pushes). ?includeAccountability=falselets very busy departments skip the per-incident PAR computation.
GET /api/v4/Sync/Reference — Returns the slowly-changing department configuration and a safe personnel roster needed to start and run an incident offline (call types, priorities, command templates, units, groups, POIs, protocols, custom statuses, feature flags).
- A new
SyncServiceaggregates data from 12 existing services into a singleSyncReferenceDatapayload. - Personnel and groups are projected to safe DTOs (
ReferencePersonnel,ReferenceGroup) that structurally excludeIdentityUsernavs, password/security fields, andUserProfilecontact-verification secrets. - Department-scoped cache-aside (5-minute TTL) via a protobuf-safe JSON envelope (
ReferenceCacheEnvelope);?bypassCache=trueforces a fresh read.
Batched ad-hoc resources: IncidentResourcesService.GetActiveAdHocResourcesForDepartmentAsync returns all active (non-released) ad-hoc units and personnel scoped to the department's active incidents in one scan per table, replacing the per-incident N+1 lookups.
2. Incident-scoped capability authorization
A new RequiresIncidentCapabilityAttribute action filter enforces that the calling user holds the required IncidentCapabilities (e.g., ManageStructure, AssignResources, ManageAccountability) for the specific incident targeted by the request — layered on top of the existing broad [Authorize(Policy = Command_*)] claims.
- Resolves the target Call from the request via three strategies: explicit
callIdroute value,IncidentCommandId(resolved to its Call with department-ownership verification), orCallIdon the bound body. - Fails open when the Call cannot be determined or belongs to another department — the broad Command_* claim and service-layer ownership guards still apply, so the filter only ever adds protection.
- Applied to 14 endpoints across
IncidentCommandController,IncidentResourcesController,IncidentRolesController, andIncidentVoiceController. - Intentionally not applied to
EstablishCommand(it creates the command, so no capabilities exist yet) or to entity-id "second action" verbs where the target Call isn't on the request.
3. Tests & documentation
- Added unit tests for the bundle assembly, tombstone filtering, read-only behavior, ad-hoc batching, the safe personnel projection, cache behavior, and the capability filter's allow/deny/fail-open paths.
- Updated
docs/architecture/offline-first-architecture.mdwith the authoritative shift-start manifest (§6.1) and marked the delta and aggregate endpoints as done (§9).
4.521.0
Pull Request Description
This PR delivers the backend foundation for offline-first Incident Command (IC) plus several SMS deliverability, phone-number validation, and data-truncation fixes.
1. Offline-First Sync Foundation (RIC-T39)
Establishes the server-side primitives the mobile apps need to work fully offline and reconcile on reconnect (documented in the new docs/architecture/offline-first-architecture.md):
- Change tracking: adds a
ModifiedOncursor (and aDeletedOnsoft-delete tombstone for lanes) to all mutable incident-command entities via the newIChangeTrackedinterface. Every insert/update stamps it through the newTouch()/UpsertOwnedAsync()helpers. - Delta sync endpoint: new
GET /api/v4/Sync/Changes?since=returns every changed row (including soft-deleted/closed/released) for the caller's department, with aServerTimestampMscursor for the next pull. - Idempotent creates: create paths now honor a client-supplied GUID PK and upsert by existence (not by id-presence), so an offline-created row replays without duplicating; foreign-department rows are rejected.
- Idempotency keys for check-ins:
CheckInRecord.IdempotencyKey(with a filtered unique index) dedups replayed offline check-ins; a post-violation recovery adopts the winning row instead of erroring. - Soft-delete for lanes:
DeleteNodeAsyncnow sets a tombstone instead of hard-deleting, so removals propagate on delta sync. - DB migrations M0081–M0083 (SQL Server + PostgreSQL twins) add the columns/indexes.
2. SMS Deliverability & Cost
New SmsContentHelper applied at the single chokepoint (and the workflow SMS executor) before sending:
- Strips non-allow-listed URLs (A2P 10DLC carrier filtering) while preserving Resgrid/map domains and bit.ly.
- Normalizes smart punctuation to keep messages in cheaper GSM-7 encoding.
- Truncates to a configurable max length (avoids Twilio error 21617).
- Invalid/unreachable "To" numbers from Twilio are now logged quietly instead of surfacing as fatal errors.
- Adds configurable
SmsMaxLengthandSmsAllowedUrlDomainssettings.
3. Phone-Number Validation & E.164 Normalization
- New
PhoneRegionHelpermaps country names to ISO region codes so national-format numbers can be recognized. ContactVerificationServicenow validates/normalizes to E.164 before sending SMS or voice calls (prevents Twilio "Invalid 'To'" failures).- Profile editing validates and normalizes mobile/home numbers server-side, with a new AJAX
ValidatePhoneNumberendpoint and a shared client-sideresgrid.phonevalidate.jsthat offers a one-click canonical fix on the profile, contact, and personnel forms.
4. Data-Truncation Fixes
- Addresses: widens
Addressescolumns (street to max, others generously) and tightens model-levelStringLengthvalidation across all address-entry view models so client + server reject oversized values before save. M0085 (both DBs). - Autofills.Data: widened from
nvarchar(255)to max to prevent truncation of long call-note templates. M0084 (both DBs).
5. Other Fixes
- HealthRepository: now uses the configured connection provider and
CURRENT_TIMESTAMPso the health check works on PostgreSQL-backed datacenters (was hardcoded to SQL Server +GETDATE()). - Contacts Index view: corrected a model-indexer bug (
Model.Contacts[i]→Model.ContactCategories[i]). - Switched append-only timeline/transfer inserts from
SaveOrUpdateAsyncto explicitInsertAsync(pre-set GUIDs would otherwise cause silent 0-row updates).
Tests
Adds comprehensive coverage for idempotent check-ins (incl. concurrent-replay race), offline-aware IC service behavior (change tracking, idempotent creates, soft-delete, delta pull), ad-hoc resource idempotency/delta, SmsContentHelper, and PhoneRegionHelper.
4.516.0
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
4.513.0
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
4.509.0
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
4.507.0
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
4.504.0
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->