Skip to content

feat: Status message customisation#418

Merged
rjmunro merged 23 commits into
Sofie-Automation:mainfrom
bbc:rjmunro/error-message-customisation
May 14, 2026
Merged

feat: Status message customisation#418
rjmunro merged 23 commits into
Sofie-Automation:mainfrom
bbc:rjmunro/error-message-customisation

Conversation

@rjmunro
Copy link
Copy Markdown
Contributor

@rjmunro rjmunro commented Jan 14, 2026

About the Contributor

This pull request is posted on behalf of the BBC.

Type of Contribution

This is a Feature.

Current Behavior

Errors are reported as plain strings. This makes them hard to understand in an machine readable way by e.g. sofie blueprints or translate.

New Behavior

Errors will include error codes and context as well as the message for backwards compatibility.

Testing Instructions

Other Information

Status

  • PR is ready to be reviewed.
  • The functionality has been tested by the author.
  • Relevant unit tests has been added / updated.
  • Relevant documentation (code comments, system documentation) has been added / updated.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 14, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Introduces a structured device-status model: adds DeviceStatusDetail and DeviceStatusInput; changes template interpolation; converts many device integrations to emit typed statusDetails[] via new create...StatusDetail helpers; DeviceInstance normalises DeviceStatusInputDeviceStatus(active); types/exports and tests updated.

Changes

Structured status core

Layer / File(s) Summary
Data Shape & new detail type
packages/timeline-state-resolver-types/src/deviceStatusDetail.ts, packages/timeline-state-resolver-types/src/device.ts
Add DeviceStatusDetail<TCode,TContext>; add optional statusDetails?: DeviceStatusDetail[] to DeviceStatus; add exported DeviceStatusInput union (messages-based
Template API change
packages/timeline-state-resolver-types/src/templateString.ts, packages/timeline-state-resolver-types/src/__tests__/templateString.spec.ts
Change interpolateTemplateString(template, context) signature and behavior: only {{word}} placeholders; preserve {{key}} when context value is undefined/null; default context in helper.
Public re-exports
packages/timeline-state-resolver-types/src/index.ts
Re-export new typings modules (integrations/httpWatcher.js, deviceStatusDetail.js).

Per-integration status typing & messages

Layer / File(s) Summary
Status code/type additions
packages/timeline-state-resolver-types/src/integrations/*.ts
Add per-integration *StatusCode consts, derived union types, *StatusContextMap, *StatusDetail<T> = DeviceStatusDetail<..., ...>, and *StatusMessages for many integrations (atem, casparCG, httpWatcher, hyperdeck, lawo, obs, osc, panasonicPTZ, pharos, quantel, shotoku, sisyfos, sofieChef, telemetrics, tricaster, vizMSE, vmix, websocketClient, etc.).

Message factory helpers

Layer / File(s) Summary
create...StatusDetail helpers
packages/timeline-state-resolver/src/integrations/*/messages.ts
Add typed create<Integration>StatusDetail<T>(code, context) helpers that enforce context shape and produce { code, context, message } by interpolating *StatusMessages[code] (fallback to code).

Integrations: getStatus migrations

Layer / File(s) Summary
Integration getStatus
packages/timeline-state-resolver/src/integrations/*/index.ts
Change many device getStatus() signatures to return DeviceStatusInput; replace messages: string[] with statusDetails: DeviceStatusDetail[]; persist init options where needed; add defensive guards for optional connections.
HTTP watcher specifics
packages/timeline-state-resolver/src/integrations/httpWatcher/index.ts
Refactor to emit specific HTTPWatcherStatusDetail codes and use _setStatus to compare serialized details and emit connectionChanged on change.
WebSocket connection tracking
packages/timeline-state-resolver/src/integrations/websocketClient/connection.ts
Track lastError and disconnectReason; connectionStatus() returns DeviceStatusInput with CONNECTION_FAILED / NOT_CONNECTED statusDetails carrying context.

Device core, normalization, and API

Layer / File(s) Summary
DeviceInstance normalization
packages/timeline-state-resolver/src/service/DeviceInstance.ts
Add normaliseDeviceStatus(DeviceStatusInput, active) to convert inputs into full DeviceStatus with active; getStatus() returns normalized DeviceStatus; connectionChanged handlers accept DeviceStatusInput and emit normalized DeviceStatus; constructor order adjusted so deviceName set before device instantiation.
Device core typings
packages/timeline-state-resolver/src/devices/device.ts, packages/timeline-state-resolver/src/integrations/abstract/index.ts
Switch legacy device/event types to use DeviceStatusInput for getStatus() / connectionChanged payloads.
Public API updates
packages/timeline-state-resolver-api/src/device.ts, packages/timeline-state-resolver-api/src/tsr-api.ts
Expose DeviceStatusInput in re-exports; add deviceName: string to DeviceContextAPI; update getStatus() and connectionChanged callback typings to use DeviceStatusInput.

Tests & helpers

Layer / File(s) Summary
Test updates
packages/timeline-state-resolver/src/integrations/*/__tests__/*.spec.ts, packages/timeline-state-resolver/src/service/__tests__/deviceInstance.spec.ts, packages/timeline-state-resolver-types/src/__tests__/templateString.spec.ts
Update assertions to expect statusDetails arrays and new template interpolation behavior; add deviceName to mocked DeviceContextAPI in test helpers.
Integration test adjustments
examples: .../atem/__tests__/atem.spec.ts, .../httpWatcher/__tests__/httpwatcher.spec.ts, .../hyperdeck/__tests__/hyperdeck.spec.ts, .../telemetrics/__tests__/telemetrics.spec.ts
Replace string-message assertions with structured DeviceStatusDetail objects or message-match expectations on statusDetails entries.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Integration as Device Integration
  participant DeviceInstance as DeviceInstance / Context
  participant Normaliser as normaliseDeviceStatus
  participant Consumers as Event Consumers

  Integration->>DeviceInstance: connectionChanged(status: DeviceStatusInput) / getStatus() -> DeviceStatusInput
  DeviceInstance->>Normaliser: normaliseDeviceStatus(statusInput, active?)
  Normaliser-->>DeviceInstance: DeviceStatus { statusCode, statusDetails, active }
  DeviceInstance->>Consumers: emit normalized DeviceStatus
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

Contribution from BBC

Suggested reviewers

  • Julusian
  • jstarpl
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

@rjmunro
Copy link
Copy Markdown
Contributor Author

rjmunro commented Jan 14, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@packages/timeline-state-resolver/README.md`:
- Around line 129-134: The example uses a placeholder comment for the context
parameter type in createMyDeviceError; replace it with a mapped type so the
context is strongly typed (e.g., use MyDeviceErrorContextMap[T] as the context
type) and ensure a MyDeviceErrorContextMap type exists mapping each
MyDeviceErrorCode to its context shape; update the function signature for
createMyDeviceError<T extends MyDeviceErrorCode>(code: T, context:
MyDeviceErrorContextMap[T]): DeviceStatusError<T> and import/declare
MyDeviceErrorContextMap where needed so the example compiles and gives concrete
types for each error code.

In `@packages/timeline-state-resolver/src/deviceErrorMessages.ts`:
- Around line 34-38: The JSDoc example output is inconsistent with the template
interpolation: update the example so the output matches the actual template
behavior used by errorsToMessages; either adjust the allMessages template for
AtemErrorCode.DISCONNECTED to not include {{host}} or change the expected
messages array to include the host (e.g., "ATEM disconnected from
192.168.1.10"); reference the AtemErrorCode.DISCONNECTED entry and the
errorsToMessages/allMessages usage in the example and ensure the example's input
context (host) and expected output align.

In `@packages/timeline-state-resolver/src/index.ts`:
- Around line 12-18: The export lists a non-existent symbol AtemErrorContext
causing the build to fail; update the export block to remove AtemErrorContext
and instead export the actual context types exported by the errors module
(AtemErrorContextMap, AtemDisconnectedContext, AtemPsuFaultContext) or re-export
whichever specific context aliases you need (e.g., export type AtemErrorContext
= AtemErrorContextMap if you prefer a single alias), ensuring the exported
symbols match those declared in the './integrations/atem/errors' module.
🧹 Nitpick comments (1)
packages/timeline-state-resolver/src/__tests__/deviceErrorMessages.spec.ts (1)

11-13: Minor: Test context differs from actual definition.

The test defines host: string as required, but in errors.ts, AtemDisconnectedContext has host?: string (optional). This inconsistency is minor since it doesn't affect test correctness, but consider aligning them for consistency.

♻️ Suggested fix
 interface AtemDisconnectedContext extends DeviceErrorContextBase {
-	host: string
+	host?: string
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dddc23e and 4031274.

📒 Files selected for processing (10)
  • packages/timeline-state-resolver-types/src/device.ts
  • packages/timeline-state-resolver-types/src/deviceError.ts
  • packages/timeline-state-resolver-types/src/index.ts
  • packages/timeline-state-resolver/README.md
  • packages/timeline-state-resolver/src/__tests__/deviceErrorMessages.spec.ts
  • packages/timeline-state-resolver/src/deviceErrorMessages.ts
  • packages/timeline-state-resolver/src/index.ts
  • packages/timeline-state-resolver/src/integrations/atem/__tests__/atem.spec.ts
  • packages/timeline-state-resolver/src/integrations/atem/errors.ts
  • packages/timeline-state-resolver/src/integrations/atem/index.ts
🧰 Additional context used
🧬 Code graph analysis (4)
packages/timeline-state-resolver-types/src/device.ts (1)
packages/timeline-state-resolver-types/src/deviceError.ts (1)
  • DeviceStatusError (27-35)
packages/timeline-state-resolver/src/deviceErrorMessages.ts (1)
packages/timeline-state-resolver-types/src/deviceError.ts (1)
  • DeviceStatusError (27-35)
packages/timeline-state-resolver/src/integrations/atem/errors.ts (1)
packages/timeline-state-resolver-types/src/deviceError.ts (2)
  • DeviceErrorContextBase (5-8)
  • DeviceStatusError (27-35)
packages/timeline-state-resolver/src/__tests__/deviceErrorMessages.spec.ts (3)
packages/timeline-state-resolver/src/index.ts (2)
  • AtemErrorCode (14-14)
  • AtemErrorMessages (15-15)
packages/timeline-state-resolver-types/src/deviceError.ts (2)
  • DeviceErrorContextBase (5-8)
  • DeviceStatusError (27-35)
packages/timeline-state-resolver/src/deviceErrorMessages.ts (1)
  • errorsToMessages (40-48)
🪛 GitHub Actions: Node CI
packages/timeline-state-resolver-types/src/deviceError.ts

[error] 1-1: Build failed for timeline-state-resolver:build. Process completed with exit code 130.

packages/timeline-state-resolver/README.md

[error] 1-1: Build failed for timeline-state-resolver:build. Process completed with exit code 130.

packages/timeline-state-resolver/src/index.ts

[error] 17-17: "./integrations/atem/errors" has no exported member named 'AtemErrorContext'. Did you mean 'AtemErrorContextMap'?


[error] 1-1: Build failed for timeline-state-resolver:build. Process completed with exit code 130.

packages/timeline-state-resolver/src/integrations/atem/index.ts

[error] 1-1: Build failed for timeline-state-resolver:build. Process completed with exit code 130.

packages/timeline-state-resolver-types/src/index.ts

[error] 17-17: "./integrations/atem/errors" has no exported member named 'AtemErrorContext'. Did you mean 'AtemErrorContextMap'?


[error] 1-1: Build failed for timeline-state-resolver:build. Process completed with exit code 130.

packages/timeline-state-resolver-types/src/device.ts

[error] 1-1: Build failed for timeline-state-resolver:build. Process completed with exit code 130.

packages/timeline-state-resolver/src/deviceErrorMessages.ts

[error] 1-1: Build failed for timeline-state-resolver:build. Process completed with exit code 130.

packages/timeline-state-resolver/src/integrations/atem/errors.ts

[error] 1-1: Build failed for timeline-state-resolver:build. Process completed with exit code 130.

packages/timeline-state-resolver/src/__tests__/deviceErrorMessages.spec.ts

[error] 1-1: Build failed for timeline-state-resolver:build. Process completed with exit code 130.

packages/timeline-state-resolver/src/integrations/atem/__tests__/atem.spec.ts

[error] 1-1: Build failed for timeline-state-resolver:build. Process completed with exit code 130.

🔇 Additional comments (12)
packages/timeline-state-resolver-types/src/index.ts (1)

55-55: LGTM!

The new export follows the established pattern and correctly exposes the deviceError module types for consumers.

packages/timeline-state-resolver/README.md (1)

49-88: Good documentation for the new feature.

The documentation clearly explains the structured error system, provides usage examples for blueprints, and shows how to convert errors to messages. This will help consumers understand and adopt the new error handling approach.

packages/timeline-state-resolver-types/src/deviceError.ts (1)

1-35: Well-designed type definitions.

The generic interface design with sensible defaults provides flexibility for device-specific implementations while maintaining type safety. The JSDoc documentation clearly explains the naming convention and usage patterns.

packages/timeline-state-resolver-types/src/device.ts (1)

3-3: Clean extension of DeviceStatus.

The optional errors field maintains backward compatibility while enabling the new structured error system. The documentation clearly explains the fallback behavior when errors are not present or understood.

Also applies to: 15-24

packages/timeline-state-resolver/src/deviceErrorMessages.ts (1)

9-17: Solid implementation.

The interpolate function correctly preserves placeholders when values are missing, providing graceful degradation. The errorsToMessages function has a sensible fallback to the error code when no template is found.

Also applies to: 40-47

packages/timeline-state-resolver/src/integrations/atem/errors.ts (1)

1-68: Well-structured type-safe error system.

The implementation follows TypeScript best practices:

  • Const object + derived type pattern for error codes ensures type safety
  • Context interfaces properly extend DeviceErrorContextBase
  • Factory function with generics enforces correct context types per error code
  • Message templates use consistent {{variable}} interpolation syntax
packages/timeline-state-resolver/src/index.ts (1)

20-37: TODO comment is reasonable for tracking future work.

The TODO provides a clear pattern reference and lists all remaining integrations. Consider creating an issue to track this work instead of leaving it in code long-term.

packages/timeline-state-resolver/src/__tests__/deviceErrorMessages.spec.ts (1)

58-148: Comprehensive test coverage for errorsToMessages.

Tests cover all essential scenarios:

  • Single and multiple error conversion
  • Variable interpolation from context
  • Custom template overrides
  • Fallback to raw error code when no template exists
  • Missing placeholder handling
  • Empty array edge case
  • Optional deviceName/deviceId fields
  • Cross-device template combination
packages/timeline-state-resolver/src/integrations/atem/__tests__/atem.spec.ts (2)

2-2: Import will work once the build issue in index.ts is resolved.

The import from '..' correctly references the re-exported AtemErrorCode and createAtemError from the ATEM integration barrel.


57-121: Thorough test coverage for the new error structure.

The 'Check Status' test comprehensively validates:

  • Initial disconnected state produces DISCONNECTED error
  • Connected state clears errors to undefined
  • PSU faults generate PSU_FAULT errors with correct context (psuNumber, totalPsus)
  • Reconnection/disconnect transitions

The assertions for both messages and errors arrays ensure backward compatibility while validating the new structured error system.

packages/timeline-state-resolver/src/integrations/atem/index.ts (2)

39-43: Clean separation of error imports and re-exports.

The error-related imports and exports are well-organized, making the error types available for blueprint customization while keeping the implementation details in errors.ts.


158-188: Well-structured error accumulation and status reporting.

The refactored getStatus() method:

  • Properly accumulates errors into a typed array
  • Uses early return for disconnected state (appropriate optimization)
  • Iterates PSUs with 1-indexed display for human-readable messages
  • Derives messages via errorsToMessages for consistency
  • Returns errors: undefined when empty (backward compatible)

The status code logic correctly prioritizes: BAD (disconnected) > WARNING_MAJOR (PSU fault) > GOOD.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread packages/timeline-state-resolver/README.md Outdated
Comment thread packages/timeline-state-resolver/src/deviceErrorMessages.ts Outdated
Comment thread packages/timeline-state-resolver/src/index.ts Outdated
@rjmunro rjmunro force-pushed the rjmunro/error-message-customisation branch 5 times, most recently from f3d719f to 2d26d58 Compare January 16, 2026 18:07
@rjmunro rjmunro force-pushed the rjmunro/error-message-customisation branch 5 times, most recently from 687fb30 to 099da5e Compare January 22, 2026 12:48
@rjmunro
Copy link
Copy Markdown
Contributor Author

rjmunro commented Jan 22, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 22, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@packages/timeline-state-resolver-types/src/index.ts`:
- Around line 29-31: Duplicate export of './integrations/atem' is present;
remove the redundant export line so './integrations/atem' is only exported once
(keep a single export statement for the ATEM integration in index.ts to avoid
duplicate re-exports).

In `@packages/timeline-state-resolver/src/integrations/casparCG/errors.ts`:
- Around line 8-12: The JSDoc example for createCasparCGError uses
CasparCGErrorCode.QUEUE_OVERFLOW but omits required context fields; update the
example to supply the full CasparCGErrorContextMap for QUEUE_OVERFLOW (include
deviceName, host, and port) so it matches the required shape, i.e., modify the
example call to createCasparCGError(CasparCGErrorCode.QUEUE_OVERFLOW, {
deviceName: 'CasparCG 1', host: '192.168.1.10', port: 5250 }) to mirror the
DISCONNECTED example and ensure consistency with CasparCGErrorContextMap.

In `@packages/timeline-state-resolver/src/integrations/casparCG/index.ts`:
- Around line 788-792: The returned status object currently always includes an
errors array which can be empty and is truthy; in the function that returns the
object (the block using statusCode, messages: errorsToMessages(errors,
CasparCGErrorMessages), errors, active: this.isActive) change the construction
so that the errors property is only added when errors.length > 0 (and likewise
omit messages if errors is empty), e.g. build the result object conditionally
and only assign errors when non-empty to match the ATEM integration and avoid
consumers misinterpreting an empty array as an error.
- Around line 760-785: The getStatus() method dereferences this._ccg.host and
this._ccg.port which can be undefined before init() or during teardown; update
getStatus() to safely read host/port (e.g., const host = this._ccg?.host ??
this.initOptions?.host ?? '' and const port = this._ccg?.port ??
this.initOptions?.port ?? '') and use those fallback values when calling
createCasparCGError(CasparCGErrorCode.DISCONNECTED, ...) and
createCasparCGError(CasparCGErrorCode.QUEUE_OVERFLOW, ...) so no property access
on an uninitialized this._ccg occurs.

In `@packages/timeline-state-resolver/src/service/DeviceInstance.ts`:
- Around line 269-272: The constructor currently calls new
deviceSpecs.deviceClass(...) which triggers _getDeviceContextAPI() and reads
this._deviceName before _deviceName is assigned; move the assignment that sets
this._deviceName to occur before instantiating deviceSpecs.deviceClass (i.e.,
set this._deviceName early in the constructor), so _getDeviceContextAPI()
returns the correct name; update any constructor flow that builds the
DeviceContextAPI or calls _getDeviceContextAPI() to happen after this
assignment.
🧹 Nitpick comments (1)
packages/timeline-state-resolver-types/src/templateString.ts (1)

22-29: Consider removing or conditionalizing the console.log for missing values.

Line 25 logs a message for every missing placeholder value. This could be noisy in production, especially if:

  • Custom templates intentionally use fewer variables than available in context
  • Templates are processed frequently

Since this is expected behavior when leaveUnknownPlaceholders is true (placeholders are preserved for later substitution), the log may not always indicate a problem.

♻️ Consider removing or making logging conditional
 export function interpolateTemplateString(
 	template: string,
 	context: Record<string, any>,
-	leaveUnknownPlaceholders = false
+	leaveUnknownPlaceholders = false,
+	debug = false
 ): string {
 	return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
 		const value = context[key]
 		if (value === undefined || value === null) {
-			console.log(`interpolateTemplateString: missing value for key ${key} in template:`, template)
+			if (debug) {
+				console.log(`interpolateTemplateString: missing value for key ${key} in template:`, template)
+			}
 			return leaveUnknownPlaceholders ? match : key // Keep key or placeholder if no value
 		}
 		return String(value)
 	})
 }

Alternatively, remove the log entirely since the behavior (preserving placeholder or using key name) is well-documented.

Comment thread packages/timeline-state-resolver-types/src/index.ts Outdated
Comment thread packages/timeline-state-resolver/src/integrations/casparCG/index.ts Outdated
Comment thread packages/timeline-state-resolver/src/integrations/casparCG/index.ts Outdated
Comment thread packages/timeline-state-resolver/src/service/DeviceInstance.ts Outdated
@rjmunro rjmunro force-pushed the rjmunro/error-message-customisation branch 3 times, most recently from b76e7c4 to 46cf25d Compare January 27, 2026 14:21
* These codes can be customized in blueprints via deviceErrorMessages.
*/
export const AtemErrorCode = {
DISCONNECTED: 'DEVICE_ATEM_DISCONNECTED',
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Here they are DEVICE_ATEM_DISCONNECTED, in some others they are HTTPWATCHER_KEYWORD_NOT_FOUND. It would be good for that to be consistent

Copy link
Copy Markdown
Contributor Author

@rjmunro rjmunro Jan 28, 2026

Choose a reason for hiding this comment

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

Changed to always start DEVICE_

Comment thread packages/timeline-state-resolver-types/src/device.ts Outdated
Comment thread packages/timeline-state-resolver/src/index.ts Outdated
Comment thread packages/timeline-state-resolver/src/deviceErrorMessages.ts Outdated
Comment thread packages/timeline-state-resolver-types/src/device.ts Outdated
Comment thread packages/timeline-state-resolver/README.md Outdated
Comment thread packages/timeline-state-resolver/README.md Outdated
Comment thread packages/timeline-state-resolver/README.md Outdated
Comment thread packages/timeline-state-resolver/README.md Outdated
Comment thread packages/timeline-state-resolver/README.md Outdated
@rjmunro rjmunro force-pushed the rjmunro/error-message-customisation branch 5 times, most recently from bea7594 to ec334bd Compare February 3, 2026 10:14
@rjmunro rjmunro marked this pull request as ready for review February 3, 2026 10:15
@rjmunro rjmunro requested a review from a team as a code owner February 3, 2026 10:15
@rjmunro
Copy link
Copy Markdown
Contributor Author

rjmunro commented Feb 3, 2026

@coderabbitai review

@rjmunro rjmunro force-pushed the rjmunro/error-message-customisation branch from de8eb8b to 945a3c6 Compare May 11, 2026 13:46
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/timeline-state-resolver/src/integrations/websocketClient/__tests__/websocketClient.spec.ts (1)

112-122: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add assertions for both mocked connectionStatus() branches.

These two mocks are currently unused, so the test doesn’t actually verify the legacy messages-based return path or status transitions. Add expect(device.getStatus()).toEqual(...) after each mock setup.

Suggested patch
 			//@ts-expect-error - is set to private
 			MockWebSocketConnection.prototype.isWsConnected = true
 			jest.spyOn(WebSocketConnection.prototype, 'connectionStatus').mockReturnValue({
 				statusCode: StatusCode.GOOD,
 				messages: ['WS Connected'],
 			})
+			expect(device.getStatus()).toEqual({
+				statusCode: StatusCode.GOOD,
+				messages: ['WS Connected'],
+			})

 			//@ts-expect-error - is set to private
 			MockWebSocketConnection.prototype.isWsConnected = false
 			jest.spyOn(WebSocketConnection.prototype, 'connectionStatus').mockReturnValue({
 				statusCode: StatusCode.BAD,
 				messages: ['WS DisConnected'],
 			})
+			expect(device.getStatus()).toEqual({
+				statusCode: StatusCode.BAD,
+				messages: ['WS DisConnected'],
+			})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/timeline-state-resolver/src/integrations/websocketClient/__tests__/websocketClient.spec.ts`
around lines 112 - 122, The test sets two different mocks for
WebSocketConnection.prototype.connectionStatus and flips
MockWebSocketConnection.prototype.isWsConnected but never asserts the resulting
device status; after the first mock (StatusCode.GOOD / ['WS Connected']) add an
assertion like expect(device.getStatus()).toEqual(...) to verify the
GOOD/messages branch, then after toggling isWsConnected and setting the second
mock (StatusCode.BAD / ['WS DisConnected']) add another
expect(device.getStatus()).toEqual(...) to verify the BAD/messages branch and
status transition; locate these changes around the mocked
WebSocketConnection.prototype.connectionStatus and
MockWebSocketConnection.prototype.isWsConnected calls in the test file and
assert using device.getStatus().
♻️ Duplicate comments (2)
packages/timeline-state-resolver/src/service/DeviceInstance.ts (1)

45-51: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve legacy feature detection: do not synthesize statusDetails from messages.

The legacy branch currently fabricates statusDetails, which prevents consumers from distinguishing legacy devices from structured-status devices.

🔧 Proposed fix
 	// Old style device, with only messages
 	return {
 		statusCode: input.statusCode,
 		messages: input.messages,
-		statusDetails: input.messages.map((message) => ({ message })),
+		statusDetails: undefined,
 		active,
 	}

Based on learnings: In the DeviceStatus interface within timeline-state-resolver, treat errors as a feature-detection mechanism: errors: [] means structured errors are supported, while errors: undefined means structured errors are not supported and consumers should fall back to the plain text messages array.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/timeline-state-resolver/src/service/DeviceInstance.ts` around lines
45 - 51, The current legacy branch in DeviceInstance.ts is synthesizing
statusDetails from messages which hides feature-detection; change the return so
it does not fabricate statusDetails — only include statusDetails when the
incoming input.statusDetails (or structured errors) is present; for the
old-style path return { statusCode: input.statusCode, messages: input.messages,
active } (leave statusDetails undefined/omitted) so consumers can detect legacy
devices by errors/statusDetails being undefined.
packages/timeline-state-resolver/src/integrations/hyperdeck/index.ts (1)

261-269: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use runtime deviceName for status context.

Line 261 hardcodes "Hyperdeck", so renamed instances emit misleading context in status details.

🔧 Proposed fix
-		const deviceName = 'Hyperdeck'
+		const deviceName = this.context.deviceName || 'Hyperdeck'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/timeline-state-resolver/src/integrations/hyperdeck/index.ts` around
lines 261 - 269, The status detail uses a hardcoded const deviceName =
'Hyperdeck' which produces misleading context; replace the hardcoded value with
the runtime device name when building the NOT_CONNECTED status (use the instance
property or init option such as this.deviceName or this._initOptions?.deviceName
if available) so createHyperdeckStatusDetail(HyperdeckStatusCode.NOT_CONNECTED,
{ deviceName, host: ..., port: ... }) receives the actual device name; update
the declaration and any other usages of deviceName in this block to reference
the runtime property instead of the literal.
🧹 Nitpick comments (1)
packages/timeline-state-resolver/src/integrations/tricaster/messages.ts (1)

9-15: 💤 Low value

Consider enhancing the documentation with usage examples.

The JSDoc comment accurately describes the function but is minimal. Adding a usage example would help developers understand how to use this helper, especially showing how the generic type parameter ensures type safety between the code and its context.

📝 Enhanced documentation example
 /**
  * Type-safe helper for creating TriCaster device status details
+ * 
+ * `@example`
+ * ```ts
+ * const detail = createTriCasterStatusDetail(
+ *   TriCasterStatusCode.NOT_CONNECTED,
+ *   { /* context for NOT_CONNECTED */ }
+ * );
+ * // Returns: { code, context, message: "Rendered message..." }
+ * ```
  */
 export function createTriCasterStatusDetail<T extends TriCasterStatusCode>(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/timeline-state-resolver/src/integrations/tricaster/messages.ts`
around lines 9 - 15, Add a short usage example to the JSDoc for
createTriCasterStatusDetail showing how to call it with a TriCasterStatusCode
and the matching context so readers see the type-safety in action; reference the
generic T and the related types (TriCasterStatusCode, TriCasterStatusContextMap,
TriCasterStatusDetail) in the example and include a small inline sample call and
expected return shape to demonstrate that the code and context must match.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/timeline-state-resolver/src/integrations/tricaster/messages.ts`:
- Around line 1-7: The import in messages.ts includes TriCasterStatusDetail
which doesn't exist in the timeline-state-resolver-types package; update the
import to match the actual exported type names or add the missing type to the
types package. Inspect the types file that defines TriCasterStatusCode,
TriCasterStatusContextMap, TriCasterStatusMessages, and
interpolateTemplateString in the 10.0.0-release53 branch, then either replace
TriCasterStatusDetail with the correct existing type name used there or add a
new export named TriCasterStatusDetail to
packages/timeline-state-resolver-types/src/integrations/tricaster.ts and
re-export it so messages.ts can import it alongside TriCasterStatusCode,
TriCasterStatusContextMap, TriCasterStatusMessages, and
interpolateTemplateString.

In `@packages/timeline-state-resolver/src/service/DeviceInstance.ts`:
- Line 30: Remove the trailing whitespace in the doc comment for the
DeviceStatus normalization comment (the comment starting "/** Normalise a
DeviceStatusInput (from device.getStatus()) to a full DeviceStatus.") in
DeviceInstance.ts so it complies with Prettier; update the comment text to have
no trailing spaces and close the JSDoc block correctly.

---

Outside diff comments:
In
`@packages/timeline-state-resolver/src/integrations/websocketClient/__tests__/websocketClient.spec.ts`:
- Around line 112-122: The test sets two different mocks for
WebSocketConnection.prototype.connectionStatus and flips
MockWebSocketConnection.prototype.isWsConnected but never asserts the resulting
device status; after the first mock (StatusCode.GOOD / ['WS Connected']) add an
assertion like expect(device.getStatus()).toEqual(...) to verify the
GOOD/messages branch, then after toggling isWsConnected and setting the second
mock (StatusCode.BAD / ['WS DisConnected']) add another
expect(device.getStatus()).toEqual(...) to verify the BAD/messages branch and
status transition; locate these changes around the mocked
WebSocketConnection.prototype.connectionStatus and
MockWebSocketConnection.prototype.isWsConnected calls in the test file and
assert using device.getStatus().

---

Duplicate comments:
In `@packages/timeline-state-resolver/src/integrations/hyperdeck/index.ts`:
- Around line 261-269: The status detail uses a hardcoded const deviceName =
'Hyperdeck' which produces misleading context; replace the hardcoded value with
the runtime device name when building the NOT_CONNECTED status (use the instance
property or init option such as this.deviceName or this._initOptions?.deviceName
if available) so createHyperdeckStatusDetail(HyperdeckStatusCode.NOT_CONNECTED,
{ deviceName, host: ..., port: ... }) receives the actual device name; update
the declaration and any other usages of deviceName in this block to reference
the runtime property instead of the literal.

In `@packages/timeline-state-resolver/src/service/DeviceInstance.ts`:
- Around line 45-51: The current legacy branch in DeviceInstance.ts is
synthesizing statusDetails from messages which hides feature-detection; change
the return so it does not fabricate statusDetails — only include statusDetails
when the incoming input.statusDetails (or structured errors) is present; for the
old-style path return { statusCode: input.statusCode, messages: input.messages,
active } (leave statusDetails undefined/omitted) so consumers can detect legacy
devices by errors/statusDetails being undefined.

---

Nitpick comments:
In `@packages/timeline-state-resolver/src/integrations/tricaster/messages.ts`:
- Around line 9-15: Add a short usage example to the JSDoc for
createTriCasterStatusDetail showing how to call it with a TriCasterStatusCode
and the matching context so readers see the type-safety in action; reference the
generic T and the related types (TriCasterStatusCode, TriCasterStatusContextMap,
TriCasterStatusDetail) in the example and include a small inline sample call and
expected return shape to demonstrate that the code and context must match.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ec1ba018-8b57-40b8-9200-4d37584c7d86

📥 Commits

Reviewing files that changed from the base of the PR and between de8eb8b and 945a3c6.

⛔ Files ignored due to path filters (2)
  • packages/timeline-state-resolver-types/src/__tests__/__snapshots__/index.spec.ts.snap is excluded by !**/*.snap
  • packages/timeline-state-resolver/src/__tests__/__snapshots__/index.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (75)
  • packages/timeline-state-resolver-types/src/index.ts
  • packages/timeline-state-resolver-types/src/integrations/atem.ts
  • packages/timeline-state-resolver-types/src/integrations/casparcg.ts
  • packages/timeline-state-resolver-types/src/integrations/httpWatcher.ts
  • packages/timeline-state-resolver-types/src/integrations/hyperdeck.ts
  • packages/timeline-state-resolver-types/src/integrations/lawo.ts
  • packages/timeline-state-resolver-types/src/integrations/obs.ts
  • packages/timeline-state-resolver-types/src/integrations/osc.ts
  • packages/timeline-state-resolver-types/src/integrations/panasonicPTZ.ts
  • packages/timeline-state-resolver-types/src/integrations/pharos.ts
  • packages/timeline-state-resolver-types/src/integrations/quantel.ts
  • packages/timeline-state-resolver-types/src/integrations/shotoku.ts
  • packages/timeline-state-resolver-types/src/integrations/sisyfos.ts
  • packages/timeline-state-resolver-types/src/integrations/sofieChef.ts
  • packages/timeline-state-resolver-types/src/integrations/telemetrics.ts
  • packages/timeline-state-resolver-types/src/integrations/tricaster.ts
  • packages/timeline-state-resolver-types/src/integrations/vizMSE.ts
  • packages/timeline-state-resolver-types/src/integrations/vmix.ts
  • packages/timeline-state-resolver-types/src/integrations/websocketClient.ts
  • packages/timeline-state-resolver/src/devices/device.ts
  • packages/timeline-state-resolver/src/integrations/__tests__/testlib.ts
  • packages/timeline-state-resolver/src/integrations/abstract/index.ts
  • packages/timeline-state-resolver/src/integrations/atem/__tests__/atem.spec.ts
  • packages/timeline-state-resolver/src/integrations/atem/index.ts
  • packages/timeline-state-resolver/src/integrations/atem/messages.ts
  • packages/timeline-state-resolver/src/integrations/casparCG/index.ts
  • packages/timeline-state-resolver/src/integrations/casparCG/messages.ts
  • packages/timeline-state-resolver/src/integrations/httpSend/index.ts
  • packages/timeline-state-resolver/src/integrations/httpWatcher/__tests__/httpwatcher.spec.ts
  • packages/timeline-state-resolver/src/integrations/httpWatcher/index.ts
  • packages/timeline-state-resolver/src/integrations/httpWatcher/messages.ts
  • packages/timeline-state-resolver/src/integrations/hyperdeck/__tests__/hyperdeck.spec.ts
  • packages/timeline-state-resolver/src/integrations/hyperdeck/index.ts
  • packages/timeline-state-resolver/src/integrations/hyperdeck/messages.ts
  • packages/timeline-state-resolver/src/integrations/kairos/index.ts
  • packages/timeline-state-resolver/src/integrations/lawo/index.ts
  • packages/timeline-state-resolver/src/integrations/lawo/messages.ts
  • packages/timeline-state-resolver/src/integrations/multiOsc/index.ts
  • packages/timeline-state-resolver/src/integrations/obs/index.ts
  • packages/timeline-state-resolver/src/integrations/obs/messages.ts
  • packages/timeline-state-resolver/src/integrations/ograf/index.ts
  • packages/timeline-state-resolver/src/integrations/osc/index.ts
  • packages/timeline-state-resolver/src/integrations/osc/messages.ts
  • packages/timeline-state-resolver/src/integrations/panasonicPTZ/index.ts
  • packages/timeline-state-resolver/src/integrations/panasonicPTZ/messages.ts
  • packages/timeline-state-resolver/src/integrations/pharos/index.ts
  • packages/timeline-state-resolver/src/integrations/pharos/messages.ts
  • packages/timeline-state-resolver/src/integrations/quantel/index.ts
  • packages/timeline-state-resolver/src/integrations/quantel/messages.ts
  • packages/timeline-state-resolver/src/integrations/shotoku/index.ts
  • packages/timeline-state-resolver/src/integrations/shotoku/messages.ts
  • packages/timeline-state-resolver/src/integrations/singularLive/index.ts
  • packages/timeline-state-resolver/src/integrations/sisyfos/index.ts
  • packages/timeline-state-resolver/src/integrations/sisyfos/messages.ts
  • packages/timeline-state-resolver/src/integrations/sofieChef/__tests__/sofieChef.spec.ts
  • packages/timeline-state-resolver/src/integrations/sofieChef/index.ts
  • packages/timeline-state-resolver/src/integrations/sofieChef/messages.ts
  • packages/timeline-state-resolver/src/integrations/tcpSend/index.ts
  • packages/timeline-state-resolver/src/integrations/telemetrics/__tests__/telemetrics.spec.ts
  • packages/timeline-state-resolver/src/integrations/telemetrics/index.ts
  • packages/timeline-state-resolver/src/integrations/telemetrics/messages.ts
  • packages/timeline-state-resolver/src/integrations/tricaster/index.ts
  • packages/timeline-state-resolver/src/integrations/tricaster/messages.ts
  • packages/timeline-state-resolver/src/integrations/udpSend/index.ts
  • packages/timeline-state-resolver/src/integrations/viscaOverIP/index.ts
  • packages/timeline-state-resolver/src/integrations/vizMSE/index.ts
  • packages/timeline-state-resolver/src/integrations/vizMSE/messages.ts
  • packages/timeline-state-resolver/src/integrations/vmix/index.ts
  • packages/timeline-state-resolver/src/integrations/vmix/messages.ts
  • packages/timeline-state-resolver/src/integrations/websocketClient/__tests__/websocketClient.spec.ts
  • packages/timeline-state-resolver/src/integrations/websocketClient/connection.ts
  • packages/timeline-state-resolver/src/integrations/websocketClient/index.ts
  • packages/timeline-state-resolver/src/integrations/websocketClient/messages.ts
  • packages/timeline-state-resolver/src/service/DeviceInstance.ts
  • packages/timeline-state-resolver/src/service/__tests__/deviceInstance.spec.ts
✅ Files skipped from review due to trivial changes (4)
  • packages/timeline-state-resolver/src/service/tests/deviceInstance.spec.ts
  • packages/timeline-state-resolver/src/integrations/kairos/index.ts
  • packages/timeline-state-resolver-types/src/integrations/websocketClient.ts
  • packages/timeline-state-resolver-types/src/integrations/casparcg.ts
🚧 Files skipped from review as they are similar to previous changes (61)
  • packages/timeline-state-resolver/src/integrations/sisyfos/messages.ts
  • packages/timeline-state-resolver/src/integrations/pharos/messages.ts
  • packages/timeline-state-resolver/src/integrations/udpSend/index.ts
  • packages/timeline-state-resolver/src/integrations/sofieChef/tests/sofieChef.spec.ts
  • packages/timeline-state-resolver/src/integrations/atem/messages.ts
  • packages/timeline-state-resolver/src/integrations/panasonicPTZ/messages.ts
  • packages/timeline-state-resolver/src/integrations/abstract/index.ts
  • packages/timeline-state-resolver-types/src/integrations/hyperdeck.ts
  • packages/timeline-state-resolver/src/integrations/ograf/index.ts
  • packages/timeline-state-resolver/src/devices/device.ts
  • packages/timeline-state-resolver/src/integrations/obs/messages.ts
  • packages/timeline-state-resolver/src/integrations/osc/messages.ts
  • packages/timeline-state-resolver/src/integrations/vizMSE/messages.ts
  • packages/timeline-state-resolver/src/integrations/quantel/index.ts
  • packages/timeline-state-resolver/src/integrations/websocketClient/index.ts
  • packages/timeline-state-resolver/src/integrations/shotoku/messages.ts
  • packages/timeline-state-resolver/src/integrations/httpWatcher/messages.ts
  • packages/timeline-state-resolver/src/integrations/quantel/messages.ts
  • packages/timeline-state-resolver/src/integrations/httpSend/index.ts
  • packages/timeline-state-resolver-types/src/integrations/vizMSE.ts
  • packages/timeline-state-resolver-types/src/integrations/obs.ts
  • packages/timeline-state-resolver-types/src/integrations/panasonicPTZ.ts
  • packages/timeline-state-resolver/src/integrations/sofieChef/messages.ts
  • packages/timeline-state-resolver-types/src/integrations/tricaster.ts
  • packages/timeline-state-resolver/src/integrations/tricaster/index.ts
  • packages/timeline-state-resolver/src/integrations/hyperdeck/messages.ts
  • packages/timeline-state-resolver/src/integrations/vmix/messages.ts
  • packages/timeline-state-resolver-types/src/integrations/osc.ts
  • packages/timeline-state-resolver-types/src/integrations/lawo.ts
  • packages/timeline-state-resolver/src/integrations/telemetrics/tests/telemetrics.spec.ts
  • packages/timeline-state-resolver/src/integrations/obs/index.ts
  • packages/timeline-state-resolver/src/integrations/websocketClient/messages.ts
  • packages/timeline-state-resolver/src/integrations/vizMSE/index.ts
  • packages/timeline-state-resolver-types/src/integrations/vmix.ts
  • packages/timeline-state-resolver/src/integrations/sofieChef/index.ts
  • packages/timeline-state-resolver/src/integrations/pharos/index.ts
  • packages/timeline-state-resolver/src/integrations/httpWatcher/tests/httpwatcher.spec.ts
  • packages/timeline-state-resolver/src/integrations/vmix/index.ts
  • packages/timeline-state-resolver/src/integrations/singularLive/index.ts
  • packages/timeline-state-resolver/src/integrations/shotoku/index.ts
  • packages/timeline-state-resolver/src/integrations/telemetrics/messages.ts
  • packages/timeline-state-resolver/src/integrations/casparCG/index.ts
  • packages/timeline-state-resolver/src/integrations/lawo/index.ts
  • packages/timeline-state-resolver/src/integrations/atem/tests/atem.spec.ts
  • packages/timeline-state-resolver/src/integrations/viscaOverIP/index.ts
  • packages/timeline-state-resolver/src/integrations/hyperdeck/tests/hyperdeck.spec.ts
  • packages/timeline-state-resolver-types/src/integrations/httpWatcher.ts
  • packages/timeline-state-resolver-types/src/integrations/sisyfos.ts
  • packages/timeline-state-resolver/src/integrations/sisyfos/index.ts
  • packages/timeline-state-resolver/src/integrations/osc/index.ts
  • packages/timeline-state-resolver/src/integrations/httpWatcher/index.ts
  • packages/timeline-state-resolver/src/integrations/casparCG/messages.ts
  • packages/timeline-state-resolver/src/integrations/telemetrics/index.ts
  • packages/timeline-state-resolver-types/src/integrations/pharos.ts
  • packages/timeline-state-resolver/src/integrations/panasonicPTZ/index.ts
  • packages/timeline-state-resolver-types/src/integrations/atem.ts
  • packages/timeline-state-resolver/src/integrations/atem/index.ts
  • packages/timeline-state-resolver-types/src/integrations/shotoku.ts
  • packages/timeline-state-resolver-types/src/integrations/telemetrics.ts
  • packages/timeline-state-resolver/src/integrations/websocketClient/connection.ts
  • packages/timeline-state-resolver-types/src/integrations/sofieChef.ts

Comment thread packages/timeline-state-resolver/src/service/DeviceInstance.ts Outdated
rjmunro added 20 commits May 11, 2026 17:47
Adds normaliseDeviceStatus() which converts the DeviceStatusInput union
(legacy messages[] or new statusDetails[]) into a full DeviceStatus with
both fields populated. Conversion is applied at the two points where
status crosses into the conductor: DeviceInstance.getStatus() and the
connectionChanged handler in the device context API.

The abstract device base class and DeviceInstance are updated to reflect
the DeviceStatusInput return type from device implementations.
Updates getStatus() return type from Omit<DeviceStatus, 'active'> to
DeviceStatusInput for kairos, multiOsc, singularLive, tcpSend, udpSend,
viscaOverIP, ograf and httpSend. Also adds deviceName to the shared test
device context mock.
@rjmunro rjmunro force-pushed the rjmunro/error-message-customisation branch from 945a3c6 to f8a8172 Compare May 11, 2026 16:48
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/timeline-state-resolver/src/integrations/vmix/messages.ts (1)

4-13: 💤 Low value

Consider showing diverse context examples.

Both examples use identical context structures. If different VMixStatusCode values require different context shapes (as enforced by VMixStatusContextMap[T]), showing examples with varying contexts would better demonstrate the type-safety benefit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/timeline-state-resolver/src/integrations/vmix/messages.ts` around
lines 4 - 13, Update the JSDoc examples for createVMixStatusDetail to show
different context shapes tied to different VMixStatusCode values so readers see
the type-safety in action: add at least one example using a status whose context
differs from { deviceName, host } (reference VMixStatusCode and
VMixStatusContextMap) — e.g., an example for a code that requires an error or
reason field and one for a code that requires additional fields — and ensure the
examples call createVMixStatusDetail with the correct context shape for each
VMixStatusCode to illustrate compile-time enforcement.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/timeline-state-resolver/src/integrations/vmix/messages.ts`:
- Around line 4-13: Update the JSDoc examples for createVMixStatusDetail to show
different context shapes tied to different VMixStatusCode values so readers see
the type-safety in action: add at least one example using a status whose context
differs from { deviceName, host } (reference VMixStatusCode and
VMixStatusContextMap) — e.g., an example for a code that requires an error or
reason field and one for a code that requires additional fields — and ensure the
examples call createVMixStatusDetail with the correct context shape for each
VMixStatusCode to illustrate compile-time enforcement.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 35399cff-0f87-4500-97e0-506c809f432d

📥 Commits

Reviewing files that changed from the base of the PR and between 945a3c6 and f8a8172.

⛔ Files ignored due to path filters (2)
  • packages/timeline-state-resolver-types/src/__tests__/__snapshots__/index.spec.ts.snap is excluded by !**/*.snap
  • packages/timeline-state-resolver/src/__tests__/__snapshots__/index.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (75)
  • packages/timeline-state-resolver-types/src/index.ts
  • packages/timeline-state-resolver-types/src/integrations/atem.ts
  • packages/timeline-state-resolver-types/src/integrations/casparcg.ts
  • packages/timeline-state-resolver-types/src/integrations/httpWatcher.ts
  • packages/timeline-state-resolver-types/src/integrations/hyperdeck.ts
  • packages/timeline-state-resolver-types/src/integrations/lawo.ts
  • packages/timeline-state-resolver-types/src/integrations/obs.ts
  • packages/timeline-state-resolver-types/src/integrations/osc.ts
  • packages/timeline-state-resolver-types/src/integrations/panasonicPTZ.ts
  • packages/timeline-state-resolver-types/src/integrations/pharos.ts
  • packages/timeline-state-resolver-types/src/integrations/quantel.ts
  • packages/timeline-state-resolver-types/src/integrations/shotoku.ts
  • packages/timeline-state-resolver-types/src/integrations/sisyfos.ts
  • packages/timeline-state-resolver-types/src/integrations/sofieChef.ts
  • packages/timeline-state-resolver-types/src/integrations/telemetrics.ts
  • packages/timeline-state-resolver-types/src/integrations/tricaster.ts
  • packages/timeline-state-resolver-types/src/integrations/vizMSE.ts
  • packages/timeline-state-resolver-types/src/integrations/vmix.ts
  • packages/timeline-state-resolver-types/src/integrations/websocketClient.ts
  • packages/timeline-state-resolver/src/devices/device.ts
  • packages/timeline-state-resolver/src/integrations/__tests__/testlib.ts
  • packages/timeline-state-resolver/src/integrations/abstract/index.ts
  • packages/timeline-state-resolver/src/integrations/atem/__tests__/atem.spec.ts
  • packages/timeline-state-resolver/src/integrations/atem/index.ts
  • packages/timeline-state-resolver/src/integrations/atem/messages.ts
  • packages/timeline-state-resolver/src/integrations/casparCG/index.ts
  • packages/timeline-state-resolver/src/integrations/casparCG/messages.ts
  • packages/timeline-state-resolver/src/integrations/httpSend/index.ts
  • packages/timeline-state-resolver/src/integrations/httpWatcher/__tests__/httpwatcher.spec.ts
  • packages/timeline-state-resolver/src/integrations/httpWatcher/index.ts
  • packages/timeline-state-resolver/src/integrations/httpWatcher/messages.ts
  • packages/timeline-state-resolver/src/integrations/hyperdeck/__tests__/hyperdeck.spec.ts
  • packages/timeline-state-resolver/src/integrations/hyperdeck/index.ts
  • packages/timeline-state-resolver/src/integrations/hyperdeck/messages.ts
  • packages/timeline-state-resolver/src/integrations/kairos/index.ts
  • packages/timeline-state-resolver/src/integrations/lawo/index.ts
  • packages/timeline-state-resolver/src/integrations/lawo/messages.ts
  • packages/timeline-state-resolver/src/integrations/multiOsc/index.ts
  • packages/timeline-state-resolver/src/integrations/obs/index.ts
  • packages/timeline-state-resolver/src/integrations/obs/messages.ts
  • packages/timeline-state-resolver/src/integrations/ograf/index.ts
  • packages/timeline-state-resolver/src/integrations/osc/index.ts
  • packages/timeline-state-resolver/src/integrations/osc/messages.ts
  • packages/timeline-state-resolver/src/integrations/panasonicPTZ/index.ts
  • packages/timeline-state-resolver/src/integrations/panasonicPTZ/messages.ts
  • packages/timeline-state-resolver/src/integrations/pharos/index.ts
  • packages/timeline-state-resolver/src/integrations/pharos/messages.ts
  • packages/timeline-state-resolver/src/integrations/quantel/index.ts
  • packages/timeline-state-resolver/src/integrations/quantel/messages.ts
  • packages/timeline-state-resolver/src/integrations/shotoku/index.ts
  • packages/timeline-state-resolver/src/integrations/shotoku/messages.ts
  • packages/timeline-state-resolver/src/integrations/singularLive/index.ts
  • packages/timeline-state-resolver/src/integrations/sisyfos/index.ts
  • packages/timeline-state-resolver/src/integrations/sisyfos/messages.ts
  • packages/timeline-state-resolver/src/integrations/sofieChef/__tests__/sofieChef.spec.ts
  • packages/timeline-state-resolver/src/integrations/sofieChef/index.ts
  • packages/timeline-state-resolver/src/integrations/sofieChef/messages.ts
  • packages/timeline-state-resolver/src/integrations/tcpSend/index.ts
  • packages/timeline-state-resolver/src/integrations/telemetrics/__tests__/telemetrics.spec.ts
  • packages/timeline-state-resolver/src/integrations/telemetrics/index.ts
  • packages/timeline-state-resolver/src/integrations/telemetrics/messages.ts
  • packages/timeline-state-resolver/src/integrations/tricaster/index.ts
  • packages/timeline-state-resolver/src/integrations/tricaster/messages.ts
  • packages/timeline-state-resolver/src/integrations/udpSend/index.ts
  • packages/timeline-state-resolver/src/integrations/viscaOverIP/index.ts
  • packages/timeline-state-resolver/src/integrations/vizMSE/index.ts
  • packages/timeline-state-resolver/src/integrations/vizMSE/messages.ts
  • packages/timeline-state-resolver/src/integrations/vmix/index.ts
  • packages/timeline-state-resolver/src/integrations/vmix/messages.ts
  • packages/timeline-state-resolver/src/integrations/websocketClient/__tests__/websocketClient.spec.ts
  • packages/timeline-state-resolver/src/integrations/websocketClient/connection.ts
  • packages/timeline-state-resolver/src/integrations/websocketClient/index.ts
  • packages/timeline-state-resolver/src/integrations/websocketClient/messages.ts
  • packages/timeline-state-resolver/src/service/DeviceInstance.ts
  • packages/timeline-state-resolver/src/service/__tests__/deviceInstance.spec.ts
✅ Files skipped from review due to trivial changes (10)
  • packages/timeline-state-resolver/src/integrations/telemetrics/tests/telemetrics.spec.ts
  • packages/timeline-state-resolver/src/integrations/udpSend/index.ts
  • packages/timeline-state-resolver/src/integrations/httpSend/index.ts
  • packages/timeline-state-resolver/src/integrations/ograf/index.ts
  • packages/timeline-state-resolver/src/devices/device.ts
  • packages/timeline-state-resolver/src/integrations/sofieChef/tests/sofieChef.spec.ts
  • packages/timeline-state-resolver/src/integrations/websocketClient/tests/websocketClient.spec.ts
  • packages/timeline-state-resolver/src/integrations/singularLive/index.ts
  • packages/timeline-state-resolver/src/integrations/viscaOverIP/index.ts
  • packages/timeline-state-resolver/src/service/tests/deviceInstance.spec.ts
🚧 Files skipped from review as they are similar to previous changes (62)
  • packages/timeline-state-resolver/src/integrations/multiOsc/index.ts
  • packages/timeline-state-resolver/src/integrations/vizMSE/messages.ts
  • packages/timeline-state-resolver/src/integrations/osc/index.ts
  • packages/timeline-state-resolver/src/integrations/atem/messages.ts
  • packages/timeline-state-resolver/src/integrations/quantel/messages.ts
  • packages/timeline-state-resolver/src/integrations/shotoku/messages.ts
  • packages/timeline-state-resolver/src/integrations/sisyfos/messages.ts
  • packages/timeline-state-resolver/src/integrations/hyperdeck/messages.ts
  • packages/timeline-state-resolver/src/integrations/websocketClient/messages.ts
  • packages/timeline-state-resolver/src/integrations/sofieChef/index.ts
  • packages/timeline-state-resolver/src/integrations/tests/testlib.ts
  • packages/timeline-state-resolver/src/integrations/abstract/index.ts
  • packages/timeline-state-resolver-types/src/integrations/hyperdeck.ts
  • packages/timeline-state-resolver/src/integrations/obs/messages.ts
  • packages/timeline-state-resolver/src/integrations/osc/messages.ts
  • packages/timeline-state-resolver/src/integrations/httpWatcher/index.ts
  • packages/timeline-state-resolver-types/src/integrations/casparcg.ts
  • packages/timeline-state-resolver-types/src/integrations/quantel.ts
  • packages/timeline-state-resolver-types/src/integrations/pharos.ts
  • packages/timeline-state-resolver-types/src/integrations/websocketClient.ts
  • packages/timeline-state-resolver-types/src/integrations/sofieChef.ts
  • packages/timeline-state-resolver-types/src/index.ts
  • packages/timeline-state-resolver-types/src/integrations/atem.ts
  • packages/timeline-state-resolver/src/integrations/sisyfos/index.ts
  • packages/timeline-state-resolver-types/src/integrations/vizMSE.ts
  • packages/timeline-state-resolver-types/src/integrations/sisyfos.ts
  • packages/timeline-state-resolver/src/service/DeviceInstance.ts
  • packages/timeline-state-resolver/src/integrations/lawo/index.ts
  • packages/timeline-state-resolver/src/integrations/kairos/index.ts
  • packages/timeline-state-resolver/src/integrations/telemetrics/messages.ts
  • packages/timeline-state-resolver/src/integrations/atem/index.ts
  • packages/timeline-state-resolver/src/integrations/websocketClient/index.ts
  • packages/timeline-state-resolver/src/integrations/tricaster/messages.ts
  • packages/timeline-state-resolver/src/integrations/telemetrics/index.ts
  • packages/timeline-state-resolver-types/src/integrations/tricaster.ts
  • packages/timeline-state-resolver/src/integrations/httpWatcher/tests/httpwatcher.spec.ts
  • packages/timeline-state-resolver/src/integrations/lawo/messages.ts
  • packages/timeline-state-resolver/src/integrations/panasonicPTZ/messages.ts
  • packages/timeline-state-resolver-types/src/integrations/shotoku.ts
  • packages/timeline-state-resolver-types/src/integrations/httpWatcher.ts
  • packages/timeline-state-resolver/src/integrations/obs/index.ts
  • packages/timeline-state-resolver/src/integrations/httpWatcher/messages.ts
  • packages/timeline-state-resolver/src/integrations/shotoku/index.ts
  • packages/timeline-state-resolver/src/integrations/pharos/messages.ts
  • packages/timeline-state-resolver-types/src/integrations/vmix.ts
  • packages/timeline-state-resolver-types/src/integrations/telemetrics.ts
  • packages/timeline-state-resolver-types/src/integrations/obs.ts
  • packages/timeline-state-resolver/src/integrations/casparCG/index.ts
  • packages/timeline-state-resolver-types/src/integrations/panasonicPTZ.ts
  • packages/timeline-state-resolver/src/integrations/casparCG/messages.ts
  • packages/timeline-state-resolver/src/integrations/sofieChef/messages.ts
  • packages/timeline-state-resolver-types/src/integrations/lawo.ts
  • packages/timeline-state-resolver/src/integrations/atem/tests/atem.spec.ts
  • packages/timeline-state-resolver/src/integrations/quantel/index.ts
  • packages/timeline-state-resolver/src/integrations/pharos/index.ts
  • packages/timeline-state-resolver/src/integrations/panasonicPTZ/index.ts
  • packages/timeline-state-resolver-types/src/integrations/osc.ts
  • packages/timeline-state-resolver/src/integrations/vizMSE/index.ts
  • packages/timeline-state-resolver/src/integrations/hyperdeck/tests/hyperdeck.spec.ts
  • packages/timeline-state-resolver/src/integrations/tricaster/index.ts
  • packages/timeline-state-resolver/src/integrations/vmix/index.ts
  • packages/timeline-state-resolver/src/integrations/websocketClient/connection.ts

@rjmunro rjmunro merged commit 5f60335 into Sofie-Automation:main May 14, 2026
26 checks passed
@rjmunro rjmunro deleted the rjmunro/error-message-customisation branch May 14, 2026 15:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contribution from BBC Contributions sponsored by BBC (bbc.co.uk)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants