Skip to content

Add GTFS-RT service alerts to SMS and voice (#8) - #23

Open
aaronbrethorst wants to merge 12 commits into
mainfrom
fix-8
Open

Add GTFS-RT service alerts to SMS and voice (#8)#23
aaronbrethorst wants to merge 12 commits into
mainfrom
fix-8

Conversation

@aaronbrethorst

Copy link
Copy Markdown
Member

Closes #8.

Summary

  • Surfaces active OneBusAway service alerts ("situations") tied to a queried stop or its routes in both SMS and voice responses. Alerts are read from the REST arrivals-and-departures-for-stop response's references.situations — no new data source, config, or dependency.
  • SMS: active alerts are prepended above the arrivals (first page only), each truncated to keep the message short; a "+N more" line covers overflow. Voice: alerts are spoken before arrivals on the initial stop lookup, then the normal arrivals + menu follow.
  • Adds an ActiveSituations() model method that filters situations by their activeWindows against the server's currentTime (ms, with defensive seconds→ms normalization; fails open when the server time is unknown). Alert presentation is localized across all 10 supported languages.
  • Refactors OneBusAwayResponse's anonymous nested structs into named exported types (OBAResponseData/OBAStopEntry/…) so situation fields could be added cleanly; field-access paths are unchanged.

Scope note: per discussion, this iteration covers alerts tied to a queried stop/route. System-wide disruptions played at the very start of a call (before a stop is chosen) require a separate global source (e.g. a GTFS-RT ServiceAlerts feed) and are deferred to a follow-up issue — the Situation model and formatter layer are built to be reused for it. See spec §8.

How it was built

Spec → context7 review → live verification against api.pugetsound (confirmed activeWindows are ms, field shapes, and that a real alert parses) → implementation plan → plan review → subagent-driven TDD execution (7 tasks, each spec+quality reviewed) → whole-branch review → /simplify. Design docs live under docs/superpowers/.

Test plan

  • make fmt, make vet, make lint (0 issues), make test — full suite green (15 packages)
  • Unit tests: ActiveSituations active-window filtering (active/expired/future/open-ended/seconds-normalized/fail-open), SMS+voice formatters (caps, overflow, truncation, no-URL-read-aloud), SMS & voice handler injection, locale key parity
  • Live smoke against real API (stop 1_75403, which had an active alert):
    • SMS: alert prepended + truncated, then arrivals, then hints
    • Voice: alert spoken first in its own <Say>, then arrivals <Say>, then menu <Gather>
    • Voice "extend departures" loop: alert not repeated (regression-tested)
    • No-alert stops (1_431, 1_13721): output unchanged (no-op verified)

Review notes (non-blocking)

  • SMS segment cost: the prefix forces UCS-2 encoding and up to 2 alerts × 140 runes can add SMS segments. Left at smsMaxAlerts = 2; dropping to 1 is a one-line lever if segment cost matters.
  • Alert body language: OBA emits summary/description in the agency's language (English on Puget Sound); only the lead-in/prefix is localized. Inherent to single-language situation data.
  • Deferred follow-up: open a separate issue for system-wide-at-call-start alerts (spec §8).

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d48986e-d903-4d74-b47f-51c9ed048ebe

📥 Commits

Reviewing files that changed from the base of the PR and between 8d5bf34 and 438a1c3.

📒 Files selected for processing (27)
  • client/onebusaway_test.go
  • client/validation_test.go
  • docs/superpowers/plans/2026-07-01-service-alerts.md
  • docs/superpowers/specs/2026-07-01-service-alerts-design.md
  • formatters/alerts.go
  • formatters/alerts_test.go
  • handlers/disambiguation_test.go
  • handlers/sms.go
  • handlers/sms_session_test.go
  • handlers/sms_test.go
  • handlers/voice/find_stop.go
  • handlers/voice/find_stop_test.go
  • handlers/voice_menu_test.go
  • locales/ar-US.json
  • locales/de-US.json
  • locales/en-US.json
  • locales/es-US.json
  • locales/fr-US.json
  • locales/ko-US.json
  • locales/pl-PL.json
  • locales/pt-US.json
  • locales/ru-US.json
  • locales/zh-CN.json
  • main_test.go
  • models/situation.go
  • models/situation_test.go
  • models/types.go
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-8

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

❤️ Share

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

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/OneBusAway/twilio/issues/comments/4860526389","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- review_stack_entry_start -->\n\n[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/OneBusAway/twilio/pull/23?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)\n\n<!-- review_stack_entry_end -->\n<!-- This is an auto-generated comment: review in progress by coderabbit.ai -->\n\n> [!NOTE]\n> Currently processing new changes in this PR. This may take a few minutes, please wait...\n> \n> <details>\n> <summary>⚙️ Run configuration</summary>\n> \n> **Configuration used**: Organization UI\n> \n> **Review profile**: CHILL\n> \n> **Plan**: Pro Plus\n> \n> **Run ID**: `0d48986e-d903-4d74-b47f-51c9ed048ebe`\n> \n> </details>\n> \n> <details>\n> <summary>📥 Commits</summary>\n> \n> Reviewing files that changed from the base of the PR and between 8d5bf346559fd5021e07984de6f398cde51ecddc and 438a1c34953edc450c1a127a06792829e836cff3.\n> \n> </details>\n> \n> <details>\n> <summary>📒 Files selected for processing (27)</summary>\n> \n> * `client/onebusaway_test.go`\n> * `client/validation_test.go`\n> * `docs/superpowers/plans/2026-07-01-service-alerts.md`\n> * `docs/superpowers/specs/2026-07-01-service-alerts-design.md`\n> * `formatters/alerts.go`\n> * `formatters/alerts_test.go`\n> * `handlers/disambiguation_test.go`\n> * `handlers/sms.go`\n> * `handlers/sms_session_test.go`\n> * `handlers/sms_test.go`\n> * `handlers/voice/find_stop.go`\n> * `handlers/voice/find_stop_test.go`\n> * `handlers/voice_menu_test.go`\n> * `locales/ar-US.json`\n> * `locales/de-US.json`\n> * `locales/en-US.json`\n> * `locales/es-US.json`\n> * `locales/fr-US.json`\n> * `locales/ko-US.json`\n> * `locales/pl-PL.json`\n> * `locales/pt-US.json`\n> * `locales/ru-US.json`\n> * `locales/zh-CN.json`\n> * `main_test.go`\n> * `models/situation.go`\n> * `models/situation_test.go`\n> * `models/types.go`\n> \n> </details>\n> \n> \n\n<!-- end of auto-generated comment: review in progress by coderabbit.ai -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing Touches</summary>\n\n<details>\n<summary>📝 Generate docstrings</summary>\n\n- [ ] <!-- {\"checkboxId\": \"7962f53c-55bc-4827-bfbf-6a18da830691\"} --> Create stacked PR\n- [ ] <!-- {\"checkboxId\": \"3e1879ae-f29b-4d0d-8e06-d12b7ba33d98\"} --> Commit on current branch\n\n</details>\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Commit unit tests in branch `fix-8`\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=OneBusAway/twilio&utm_content=23)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n\n<sub>Comment `@coderabbitai help` to get the list of available commands.</sub>\n\n<!-- tips_end -->"},"request":{"retryCount":3,"signal":{},"retries":3,"retryAfter":16}}}

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for GTFS-RT Service Alerts

1 participant