patches for jibo version 18 testing

This commit is contained in:
Jacob Dubin
2026-04-26 08:32:19 -05:00
parent f3dbd6c7fd
commit a95766e7f6
10 changed files with 5233 additions and 42 deletions

View File

@@ -21,16 +21,15 @@ Release `1.0.18` is now in feature-hardening. Its main bug-fix theme is alarm an
## Latest Live Evidence ## Latest Live Evidence
`jibo test 22` was captured against a robot that spoke `Open Jibo Cloud version 1 dot 0 dot 18.` `jibo test 23` was captured after the `jibo test 22` alarm/photo hardening pass.
- Radio live validation passed. - Radio remained live-valid.
- News routing was observed in websocket telemetry from the phrase `So, play the news.`, but the user did not get enough live confidence to call news complete because a backup notification/slowness path was active during the session. - News live validation passed. The robot spoke the current synthetic quick brief: `technology companies are still racing on AI...`. This proves the Nimbus-shaped path, not provider-backed headline expansion.
- The backup notification came from stock `@be/surprises-ota` checking `jibo.scheduler.backupStatus`; no `Backup_*` HTTP operation appeared in the captured cloud traffic. The update-menu block therefore looks more like a robot-local scheduler/backup load issue than a cloud `Backup.List` response issue. - Backup/update remains unresolved. The user observed backup-in-progress sluggishness and the update menu could not proceed while backups were active; the spoken "start backups" style command is not currently a wired OpenJibo voice path.
- The robot log showed high load, a `jibo-server-service` broken pipe, a settings error path for `Q4-Server_connection_lost`, and the stock backup prompt: `hey i'm sorry if I seem a little slow, I can be that way while i'm doing a backup.` - The first alarm path succeeded: `set an alarm for 743` scheduled a local `7:43 AM` alarm and the alarm fired. A later clarification answer was logged by the robot as `- Time. - 7, 14.` and local NLU interpreted it as `7:00 PM`, causing the confusing replacement prompt.
- Photo/gallery reached the local gallery/create path, but a missed short reply left repeated `create/is_it_a_keeper` listens and the visible blue-ring/listening state. - Current source now accepts short clock value answers during `clock/alarm_set_value` / `clock/timer_set_value`, parses comma-separated alarm digits such as `7, 44`, and maps stock alarm yes/no prompt rules `clock/alarm_timer_change` and `clock/alarm_timer_none_set`.
- Alarm attempts were dominated by collapsed transcripts such as `set and alarm`, `Set and Alonzo`, and `Set an alarm for...`; one path reached local alarm clarification but did not get a complete value-setting pass. - Photo/gallery still needs a clean live pass. The gallery path opened, but several `shared/yes_no` turns had empty ASR text, so there was no positive `yes` transcript for the cloud to map.
- The turn telemetry contained `ffmpeg` failures where local whisper tried to decode buffered Ogg/Opus turns that were not usable by `ffmpeg`. - No `ffmpeg` / `whisper.cpp` error was evident in the `jibo test 23` turn timeline, unlike `jibo test 22`. Treat any future decode errors separately from the gallery yes/no payload path.
- The websocket capture still contained `OPENJIBO_TURN_PENDING`, `OPENJIBO_CONTEXT_ACK`, and proactive `OPENJIBO_ACK` output in the deployed run. The current source has no references to those synthetic OpenJibo events, so the next deployment needs an artifact/build verification pass.
## Release Rhythm ## Release Rhythm
@@ -94,14 +93,15 @@ The following behavior is present in source and covered by focused tests:
- radio voice launch supports `open the radio` and genre launch such as `play country music`, using local `@be/radio` `menu` payloads, `SKILL_REDIRECT`, and silent completion - radio voice launch supports `open the radio` and genre launch such as `play country music`, using local `@be/radio` `menu` payloads, `SKILL_REDIRECT`, and silent completion
- news has a first Nimbus-shaped cloud path using `match.cloudSkill = news` and a `news` `SKILL_ACTION` with synthetic briefing content - news has a first Nimbus-shaped cloud path using `match.cloudSkill = news` and a `news` `SKILL_ACTION` with synthetic briefing content
- stock-shaped clock handoffs cover time, date, day, clock open, timer/alarm menu, timer/alarm value, timer/alarm clarification, and timer/alarm delete - stock-shaped clock handoffs cover time, date, day, clock open, timer/alarm menu, timer/alarm value, timer/alarm clarification, and timer/alarm delete
- alarm parsing covers forms such as `7:30 am`, `830`, `8 30`, `10-25`, `10:25 pm`, and `10 25 p m` - alarm parsing covers forms such as `7:30 am`, `830`, `8 30`, `7, 44`, `10-25`, `10:25 pm`, and `10 25 p m`
- ambiguous alarm times can prefer the next local occurrence when the robot context includes `runtime.location.iso` - ambiguous alarm times can prefer the next local occurrence when the robot context includes `runtime.location.iso`
- short clock value follow-up transcripts are accepted under `clock/alarm_set_value` and `clock/timer_set_value` instead of being dropped before parsing
- `CLIENT_NLU intent=set` with only `domain=alarm` stays on the local clock clarification path instead of defaulting to a fabricated time - `CLIENT_NLU intent=set` with only `domain=alarm` stays on the local clock clarification path instead of defaulting to a fabricated time
- `CLIENT_NLU intent=cancel` on `clock/alarm_timer_query_menu` can reuse the last active clock domain - `CLIENT_NLU intent=cancel` on `clock/alarm_timer_query_menu` can reuse the last active clock domain
- photo flows route `open photo gallery` to `@be/gallery`, `snap a picture` to `@be/create/createOnePhoto`, and `open photobooth` to `@be/create/createSomePhotos` - photo flows route `open photo gallery` to `@be/gallery`, `snap a picture` to `@be/create/createOnePhoto`, and `open photobooth` to `@be/create/createSomePhotos`
- passive gallery/create context does not reopen a stale cloud turn - passive gallery/create context does not reopen a stale cloud turn
- media metadata persists across store recreation and `/media/{path}` can serve the current text-body placeholder payload - media metadata persists across store recreation and `/media/{path}` can serve the current text-body placeholder payload
- constrained yes/no handling covers `create/is_it_a_keeper`, `shared/yes_no`, `settings/download_now_later`, `surprises-date/offer_date_fact`, `surprises-ota/want_to_download_now`, and `$YESNO` hints - constrained yes/no handling covers `clock/alarm_timer_change`, `clock/alarm_timer_none_set`, `create/is_it_a_keeper`, `shared/yes_no`, `settings/download_now_later`, `surprises-date/offer_date_fact`, `surprises-ota/want_to_download_now`, and `$YESNO` hints
- outbound constrained yes/no responses strip unrelated `globals/*` rules so stock OS stays local - outbound constrained yes/no responses strip unrelated `globals/*` rules so stock OS stays local
- no-input fallback for constrained yes/no prompts emits local `LISTEN`/`EOS` instead of relaunching generic Nimbus speech, including `shared/yes_no` after STT failure - no-input fallback for constrained yes/no prompts emits local `LISTEN`/`EOS` instead of relaunching generic Nimbus speech, including `shared/yes_no` after STT failure
- repeated empty `create/is_it_a_keeper` replies redirect to `@be/idle` after the second miss so the photo/create flow can settle instead of leaving a stale listening state - repeated empty `create/is_it_a_keeper` replies redirect to `@be/idle` after the second miss so the photo/create flow can settle instead of leaving a stale listening state
@@ -115,8 +115,9 @@ Use these sources as evidence, not as code to copy blindly:
- OpenJibo Node oracle: [open-jibo-link.js](../src/Jibo.Cloud/node/open-jibo-link.js) - OpenJibo Node oracle: [open-jibo-link.js](../src/Jibo.Cloud/node/open-jibo-link.js)
- Current hosted `.NET` cloud: [src/Jibo.Cloud/dotnet](../src/Jibo.Cloud/dotnet) - Current hosted `.NET` cloud: [src/Jibo.Cloud/dotnet](../src/Jibo.Cloud/dotnet)
- Live captures and robot logs: `.\artifact-output` - Live captures and robot logs: `.\artifact-output`
- Original Pegasus cloud source: `..\jibo\pegasus` - User-provided original source snapshot: `..\jibo` when extracted locally
- Original SDK and skill source snapshot: `..\jibo\sdk` - Original Pegasus cloud source inside that snapshot: `pegasus`
- Original SDK and skill source inside that snapshot: `sdk`
- JiboOS reference tree: `..\JiboOS` - JiboOS reference tree: `..\JiboOS`
- JiboOS skill snapshot: `..\JiboOS\opt\jibo\Jibo\Skills\@be` - JiboOS skill snapshot: `..\JiboOS\opt\jibo\Jibo\Skills\@be`
@@ -132,14 +133,14 @@ Before calling `1.0.18` complete, prove or explicitly defer these:
- Run the focused `.NET` cloud test suite after the last feature slice. - Run the focused `.NET` cloud test suite after the last feature slice.
- Confirm the running robot build reports cloud version `1.0.18`. - Confirm the running robot build reports cloud version `1.0.18`.
- Regression test alarm flows again after the `jibo test 22` fixes: set with explicit time, set with compact/spoken time, clarify missing time, cancel alarm, and local cleanup prompts. - Regression test alarm flows again after the `jibo test 23` fixes: set with explicit time, set with compact/spoken/comma-separated time, clarify missing time, replace an existing alarm, cancel/delete by voice, and verify the menu agrees.
- Regression test photo/gallery flows again after the `jibo test 22` fixes: open gallery, answer the stock `shared/yes_no` prompt, hand into create, take one photo, and avoid blue-ring stale turns. - Regression test photo/gallery flows again after the `jibo test 23` fixes: open gallery, answer the stock `shared/yes_no` prompt with a transcript-bearing `yes`, hand into create, take one photo, and avoid blue-ring stale turns.
- Live-test radio launch: `open the radio` passed in `jibo test 22`; re-run `play country music` if that exact phrase was not captured. - Live-test radio launch: `open the radio` passed in `jibo test 22`; re-run `play country music` if that exact phrase was not captured.
- Live-test first news path again: `jibo test 22` reached the news intent, but the live behavior still needs a clean non-backup session. - Treat basic news as live-proven by `jibo test 23`; defer provider-backed or category-expanded news unless it is chosen as an optional feature slice.
- Recheck constrained yes/no prompts for update/backup/share/gallery without leaking global rules. - Recheck constrained yes/no prompts for update/backup/share/gallery/alarm replacement without leaking global rules.
- Recheck that stock OS no longer logs OpenJibo-only websocket events such as synthetic pending/context/ack packets from the current build. - Recheck that stock OS no longer logs OpenJibo-only websocket events such as synthetic pending/context/ack packets from the current build.
- Recheck backup/update behavior with explicit attention to robot-local `jibo.scheduler.backupStatus`, CPU/load, and whether the deployed cloud is involved at all. - Recheck backup/update behavior with explicit attention to robot-local `jibo.scheduler.backupStatus`, CPU/load, and whether the deployed cloud is involved at all.
- Treat remaining `ffmpeg` / `whisper.cpp` transcript failures as STT work unless the capture proves a separate turn-routing regression. - Treat remaining empty-ASR, `ffmpeg`, or `whisper.cpp` transcript failures as STT work unless the capture proves a separate turn-routing regression.
## Known Gaps ## Known Gaps
@@ -150,7 +151,8 @@ These are not blockers for calling `1.0.18` complete unless the live test shows
- state persistence is local JSON, not Azure SQL / Blob Storage - state persistence is local JSON, not Azure SQL / Blob Storage
- update, backup, and restore are not end-to-end proven, and the `jibo test 22` sluggishness appears tied to robot-local backup status/load - update, backup, and restore are not end-to-end proven, and the `jibo test 22` sluggishness appears tied to robot-local backup status/load
- deployed-build verification needs to prove that synthetic OpenJibo websocket events are gone from the hosted artifact, not just from source - deployed-build verification needs to prove that synthetic OpenJibo websocket events are gone from the hosted artifact, not just from source
- news content is synthetic - news content is synthetic; `jibo test 23` proved the path but not live provider-backed headlines
- gallery `shared/yes_no` still needs a successful transcript-bearing live `yes` pass
- weather, calendar, commute, personal report, identity, memory, and proactivity are still mostly discovery or placeholder content paths - weather, calendar, commute, personal report, identity, memory, and proactivity are still mostly discovery or placeholder content paths
- volume, stop, robot age, and command-versus-question personality routing are not implemented yet - volume, stop, robot age, and command-versus-question personality routing are not implemented yet

View File

@@ -36,8 +36,9 @@ Current release theme:
- alarm and photo/gallery quirks have received the main bug-fix attention - alarm and photo/gallery quirks have received the main bug-fix attention
- Word of the Day cleanup, constrained yes/no routing, unknown websocket event suppression, and local state persistence are already in the current code - Word of the Day cleanup, constrained yes/no routing, unknown websocket event suppression, and local state persistence are already in the current code
- radio, ESML apostrophe cleanup, and first news are implemented in source/tests and need live confidence before the version is called complete - radio, ESML apostrophe cleanup, and first news are implemented in source/tests; radio and basic news are live-proven as of `jibo test 23`
- `jibo test 22` validated radio, exposed backup/load interference, exposed a shared yes/no no-input gap, exposed repeated create keeper prompts after photo handoff, and showed local whisper `ffmpeg` failures on unusable buffered audio - `jibo test 22` validated radio, exposed backup/load interference, exposed a shared yes/no no-input gap, exposed repeated create keeper prompts after photo handoff, and showed local whisper `ffmpeg` failures on unusable buffered audio
- `jibo test 23` validated basic news, proved one alarm set/fire path at `7:43 AM`, exposed comma-separated/short alarm follow-up parsing risk, showed stock alarm replacement yes/no rules that needed cloud handling, and showed photo gallery still failing when `shared/yes_no` ASR came back empty
## Immediate `1.0.18` Queue ## Immediate `1.0.18` Queue
@@ -63,9 +64,9 @@ Current release theme:
### 2. News Through Nimbus ### 2. News Through Nimbus
- Status: `polish` - Status: `implemented`
- Tags: `protocol`, `content` - Tags: `protocol`, `content`
- Why now: the first Nimbus-compatible cloud path is implemented and test-backed; content can stay synthetic for `1.0.18`. - Why now: the first Nimbus-compatible cloud path is implemented, test-backed, and live-proven; content can stay synthetic for `1.0.18`.
- Current code: - Current code:
- `tell me the news` maps to `IntentName = news` - `tell me the news` maps to `IntentName = news`
- outbound listen match includes `cloudSkill = news` - outbound listen match includes `cloudSkill = news`
@@ -73,11 +74,12 @@ Current release theme:
- Evidence: - Evidence:
- JiboOS Nimbus checks `match.cloudSkill === "news"` and waits for a cloud response - JiboOS Nimbus checks `match.cloudSkill === "news"` and waits for a cloud response
- `jibo test 22` captured the phrase `So, play the news.` reaching the `news` intent, but live behavior was not cleanly confirmed - `jibo test 22` captured the phrase `So, play the news.` reaching the `news` intent, but live behavior was not cleanly confirmed
- `jibo test 23` successfully played the synthetic quick brief
- Exit criteria: - Exit criteria:
- live `tell me the news` reaches a non-placeholder Nimbus path - live `tell me the news` reaches the Nimbus-shaped path
- the robot behavior feels like a cloud skill response, not generic chat playback - the robot behavior feels like a cloud skill response, not generic chat playback
- Next action: - Next action:
- live-test the first pass; provider-backed headlines can wait for `1.0.19` - keep the basic path in regression; provider-backed or category-expanded headlines can wait for `1.0.19` unless chosen as the optional feature slice
### 3. Backup / OTA / Share Yes-No Reliability ### 3. Backup / OTA / Share Yes-No Reliability
@@ -86,7 +88,7 @@ Current release theme:
- Why now: constrained yes/no behavior affects daily-use prompts and was tangled with the alarm/photo/gallery work. - Why now: constrained yes/no behavior affects daily-use prompts and was tangled with the alarm/photo/gallery work.
- Current code: - Current code:
- yes/no detection reads `listenRules`, `clientRules`, and `$YESNO` hints - yes/no detection reads `listenRules`, `clientRules`, and `$YESNO` hints
- covered prompt families include `settings/download_now_later`, `surprises-ota/want_to_download_now`, `surprises-date/offer_date_fact`, `shared/yes_no`, and `create/is_it_a_keeper` - covered prompt families include `settings/download_now_later`, `surprises-ota/want_to_download_now`, `surprises-date/offer_date_fact`, `shared/yes_no`, `create/is_it_a_keeper`, `clock/alarm_timer_change`, and `clock/alarm_timer_none_set`
- outbound replies strip global rules and keep the local rule - outbound replies strip global rules and keep the local rule
- no-input fallback for constrained prompts emits local `LISTEN`/`EOS` - no-input fallback for constrained prompts emits local `LISTEN`/`EOS`
- `shared/yes_no` now participates in the STT-failure no-input path instead of staying pending behind `$YESNO` hints - `shared/yes_no` now participates in the STT-failure no-input path instead of staying pending behind `$YESNO` hints
@@ -95,11 +97,12 @@ Current release theme:
- `jibo test 22` did not show `Backup_*` HTTP traffic during the backup complaint - `jibo test 22` did not show `Backup_*` HTTP traffic during the backup complaint
- stock `@be/surprises-ota` drives the backup notification from robot-local `jibo.scheduler.backupStatus` - stock `@be/surprises-ota` drives the backup notification from robot-local `jibo.scheduler.backupStatus`
- a spoken `take a backup` command currently routes as generic chat and is not the same as proving the local backup scheduler path - a spoken `take a backup` command currently routes as generic chat and is not the same as proving the local backup scheduler path
- `jibo test 23` again showed backup-in-progress sluggishness and update-menu blockage while backups were active; explicit backup voice launch remains unwired
- Exit criteria: - Exit criteria:
- spoken `yes` and `no` work on update, backup, share/offer, and gallery/create prompts - spoken `yes` and `no` work on update, backup, share/offer, and gallery/create prompts
- empty or missed short replies retry locally instead of relaunching Nimbus or generic chat - empty or missed short replies retry locally instead of relaunching Nimbus or generic chat
- Next action: - Next action:
- re-run these prompt families in the `1.0.18` live regression pass after the shared yes/no and create no-input fixes - re-run these prompt families in the `1.0.18` live regression pass after the shared yes/no, alarm yes/no, and create no-input fixes
- keep explicit backup creation as part of the update/backup/restore proof slice, not as an assumed yes/no prompt test - keep explicit backup creation as part of the update/backup/restore proof slice, not as an assumed yes/no prompt test
### 4. Alarm And Photo Gallery Release Regression ### 4. Alarm And Photo Gallery Release Regression
@@ -108,9 +111,11 @@ Current release theme:
- Tags: `protocol`, `stt` - Tags: `protocol`, `stt`
- Why now: this is the main bug-fix theme for `1.0.18`. - Why now: this is the main bug-fix theme for `1.0.18`.
- Current code: - Current code:
- alarm values parse explicit, compact, spaced, hyphenated, and local-context ambiguous times - alarm values parse explicit, compact, spaced, comma-separated, hyphenated, and local-context ambiguous times
- short alarm/timer value replies are accepted during clock value follow-up rules instead of being filtered out before parsing
- missing alarm times stay in local `@be/clock` clarification - missing alarm times stay in local `@be/clock` clarification
- alarm cancel can reuse the last active clock domain - alarm cancel can reuse the last active clock domain
- stock alarm replacement/no-alarm prompts use the constrained yes/no path
- gallery opens as `@be/gallery`; snapshot and photobooth open through `@be/create` - gallery opens as `@be/gallery`; snapshot and photobooth open through `@be/create`
- passive gallery/create context no longer reopens stale cloud turns - passive gallery/create context no longer reopens stale cloud turns
- `shared/yes_no` no-input fallback and repeated create keeper cleanup were added after `jibo test 22` - `shared/yes_no` no-input fallback and repeated create keeper cleanup were added after `jibo test 22`
@@ -118,9 +123,11 @@ Current release theme:
- gallery opened and handed into create, but repeated `create/is_it_a_keeper` prompts could leave the blue ring/listening state - gallery opened and handed into create, but repeated `create/is_it_a_keeper` prompts could leave the blue ring/listening state
- alarm recognition collapsed several attempts before a complete alarm value could be set - alarm recognition collapsed several attempts before a complete alarm value could be set
- `ffmpeg` failures were present during the same test window, so alarm/gallery retest should separate transcript quality from payload shape - `ffmpeg` failures were present during the same test window, so alarm/gallery retest should separate transcript quality from payload shape
- `jibo test 23` set and fired a `7:43 AM` alarm, then failed a later clarify/replacement path when the robot heard `- Time. - 7, 14.` and stock NLU converted that to `7:00 PM`
- `jibo test 23` photo gallery got stuck on `shared/yes_no` turns with empty ASR, not on a transcript-bearing `yes` that the cloud mapped incorrectly
- Exit criteria: - Exit criteria:
- gallery opens, offers to take a picture if empty, accepts `yes`, and hands into create - gallery opens, offers to take a picture if empty, accepts `yes`, and hands into create
- alarm set, clarify, and cancel flows behave locally without blue-ring stale turns - alarm set, clarify, replacement yes/no, and cancel/delete flows behave locally and agree with the menu state
- failures caused by collapsed STT transcripts are logged as STT issues rather than misdiagnosed as payload bugs - failures caused by collapsed STT transcripts are logged as STT issues rather than misdiagnosed as payload bugs
- Next action: - Next action:
- re-run a stock OS `1.9` regression bundle before declaring `1.0.18` complete - re-run a stock OS `1.9` regression bundle before declaring `1.0.18` complete
@@ -168,7 +175,7 @@ Current release theme:
- Result: - Result:
- Nimbus-shaped `news` cloud-skill lane is implemented with synthetic briefing content - Nimbus-shaped `news` cloud-skill lane is implemented with synthetic briefing content
- Follow-up: - Follow-up:
- live validation remains in the immediate queue - basic live validation passed in `jibo test 23`
- provider-backed headlines belong in `1.0.19` or later - provider-backed headlines belong in `1.0.19` or later
### Clock / Alarm Family ### Clock / Alarm Family
@@ -178,7 +185,9 @@ Current release theme:
- Result: - Result:
- time/date/day and clock open route through local `@be/clock` - time/date/day and clock open route through local `@be/clock`
- timer/alarm menu, value, clarify, and delete are implemented - timer/alarm menu, value, clarify, and delete are implemented
- compact and spoken alarm parsing has focused tests - compact, spoken, comma-separated, and local-context alarm parsing has focused tests
- short clock value replies under `clock/alarm_set_value` and `clock/timer_set_value` are not filtered out by websocket finalization
- alarm replacement/no-alarm yes/no prompts are mapped as constrained local prompts
- client NLU alarm clarify/cancel cases from `jibo test 20` and `jibo test 21` are reflected in source - client NLU alarm clarify/cancel cases from `jibo test 20` and `jibo test 21` are reflected in source
- Follow-up: - Follow-up:
- live regression remains in the immediate queue - live regression remains in the immediate queue
@@ -203,9 +212,9 @@ Current release theme:
- Result: - Result:
- `shared/yes_no` is included in yes/no STT-failure detection - `shared/yes_no` is included in yes/no STT-failure detection
- local no-input replies strip global rules and keep the active constrained rule - local no-input replies strip global rules and keep the active constrained rule
- update, OTA, share/date-offer, gallery shared yes/no, and create keeper rules share the same no-input fallback machinery - update, OTA, share/date-offer, gallery shared yes/no, alarm replacement/no-alarm, and create keeper rules share the same no-input fallback machinery
- Follow-up: - Follow-up:
- live update/backup/share/gallery prompts still need another clean pass - live update/backup/share/gallery/alarm replacement prompts still need another clean pass
### Word Of The Day Cleanup ### Word Of The Day Cleanup
@@ -299,7 +308,8 @@ Current release theme:
- Why next: - Why next:
- feature paths are now often correct when a transcript exists, but short replies and low-quality audio still block otherwise-correct flows - feature paths are now often correct when a transcript exists, but short replies and low-quality audio still block otherwise-correct flows
- Current evidence: - Current evidence:
- live captures still show `ffmpeg` and `whisper.cpp` failures - `jibo test 22` showed `ffmpeg` and `whisper.cpp` failures
- `jibo test 23` did not show the same decode failure pattern, but gallery yes/no turns still produced empty ASR
- current source now skips local whisper when buffered audio does not contain an Opus identification header - current source now skips local whisper when buffered audio does not contain an Opus identification header
- yes/no and alarm flows are especially sensitive to short or collapsed transcripts - yes/no and alarm flows are especially sensitive to short or collapsed transcripts
- Implementation notes: - Implementation notes:
@@ -467,7 +477,7 @@ Current release theme:
Before closing `1.0.18`: Before closing `1.0.18`:
1. Radio live validation 1. Radio live validation
2. News live validation 2. Basic news regression, with provider-backed expansion deferred
3. Backup / OTA / share yes-no regression 3. Backup / OTA / share yes-no regression
4. Alarm and photo/gallery regression 4. Alarm and photo/gallery regression
5. Optional small feature only if the regression pass stays calm 5. Optional small feature only if the regression pass stays calm

View File

@@ -680,6 +680,8 @@ public sealed class JiboInteractionService(
.Concat(ReadRules(turn, "listenAsrHints")) .Concat(ReadRules(turn, "listenAsrHints"))
.Any(static rule => .Any(static rule =>
string.Equals(rule, "$YESNO", StringComparison.OrdinalIgnoreCase) || string.Equals(rule, "$YESNO", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "clock/alarm_timer_change", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "clock/alarm_timer_none_set", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "create/is_it_a_keeper", StringComparison.OrdinalIgnoreCase) || string.Equals(rule, "create/is_it_a_keeper", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "shared/yes_no", StringComparison.OrdinalIgnoreCase) || string.Equals(rule, "shared/yes_no", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "settings/download_now_later", StringComparison.OrdinalIgnoreCase) || string.Equals(rule, "settings/download_now_later", StringComparison.OrdinalIgnoreCase) ||
@@ -1204,7 +1206,7 @@ public sealed class JiboInteractionService(
private sealed record ClockAlarmValue(string Time, string AmPm); private sealed record ClockAlarmValue(string Time, string AmPm);
private static readonly Regex SplitAlarmPattern = new( private static readonly Regex SplitAlarmPattern = new(
@"\b(?<hour>\d{1,2}|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)(?:[:\s-](?<minute>\d{2}|[a-z\-]+(?:\s+[a-z\-]+)?))?\s*(?<ampm>a[\s\.]*m\.?|p[\s\.]*m\.?)?\b", @"\b(?<hour>\d{1,2}|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)(?:[:\s,-]+(?<minute>\d{2}|[a-z\-]+(?:\s+[a-z\-]+)?))?\s*(?<ampm>a[\s\.]*m\.?|p[\s\.]*m\.?)?\b",
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled); RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
private static readonly Regex CompactAlarmPattern = new( private static readonly Regex CompactAlarmPattern = new(

View File

@@ -456,6 +456,8 @@ public sealed class ResponsePlanToSocketMessagesMapper
{ {
return ReadRuleValues(turn) return ReadRuleValues(turn)
.FirstOrDefault(static rule => .FirstOrDefault(static rule =>
string.Equals(rule, "clock/alarm_timer_change", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "clock/alarm_timer_none_set", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "create/is_it_a_keeper", StringComparison.OrdinalIgnoreCase) || string.Equals(rule, "create/is_it_a_keeper", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "shared/yes_no", StringComparison.OrdinalIgnoreCase) || string.Equals(rule, "shared/yes_no", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "settings/download_now_later", StringComparison.OrdinalIgnoreCase) || string.Equals(rule, "settings/download_now_later", StringComparison.OrdinalIgnoreCase) ||

View File

@@ -691,6 +691,11 @@ public sealed class WebSocketTurnFinalizationService(
return false; return false;
} }
if (listenRules.Any(IsClockValueRule))
{
return true;
}
if (transcript.Length >= 6) if (transcript.Length >= 6)
{ {
return true; return true;
@@ -733,12 +738,7 @@ public sealed class WebSocketTurnFinalizationService(
{ {
return ReadRules(turn, "listenRules") return ReadRules(turn, "listenRules")
.Concat(ReadRules(turn, "clientRules")) .Concat(ReadRules(turn, "clientRules"))
.FirstOrDefault(static rule => .FirstOrDefault(IsLocalNoInputRule);
string.Equals(rule, "clock/alarm_timer_okay", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "create/is_it_a_keeper", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "settings/download_now_later", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "surprises-date/offer_date_fact", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "surprises-ota/want_to_download_now", StringComparison.OrdinalIgnoreCase));
} }
private static string? ReadPrimaryYesNoRule(TurnContext turn) private static string? ReadPrimaryYesNoRule(TurnContext turn)
@@ -797,9 +797,17 @@ public sealed class WebSocketTurnFinalizationService(
IsConstrainedYesNoRule(rule); IsConstrainedYesNoRule(rule);
} }
private static bool IsClockValueRule(string rule)
{
return string.Equals(rule, "clock/alarm_set_value", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "clock/timer_set_value", StringComparison.OrdinalIgnoreCase);
}
private static bool IsConstrainedYesNoRule(string rule) private static bool IsConstrainedYesNoRule(string rule)
{ {
return string.Equals(rule, "create/is_it_a_keeper", StringComparison.OrdinalIgnoreCase) || return string.Equals(rule, "clock/alarm_timer_change", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "clock/alarm_timer_none_set", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "create/is_it_a_keeper", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "shared/yes_no", StringComparison.OrdinalIgnoreCase) || string.Equals(rule, "shared/yes_no", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "settings/download_now_later", StringComparison.OrdinalIgnoreCase) || string.Equals(rule, "settings/download_now_later", StringComparison.OrdinalIgnoreCase) ||
string.Equals(rule, "surprises-date/offer_date_fact", StringComparison.OrdinalIgnoreCase) || string.Equals(rule, "surprises-date/offer_date_fact", StringComparison.OrdinalIgnoreCase) ||

View File

@@ -133,6 +133,46 @@ public sealed class JiboInteractionServiceTests
Assert.Equal("Yes.", decision.ReplyText); Assert.Equal("Yes.", decision.ReplyText);
} }
[Fact]
public async Task BuildDecisionAsync_AlarmTimerChangePrompt_MapsShortAffirmationToYesIntent()
{
var service = CreateService();
var decision = await service.BuildDecisionAsync(new TurnContext
{
RawTranscript = "yes",
NormalizedTranscript = "yes",
Attributes = new Dictionary<string, object?>
{
["listenRules"] = new[] { "clock/alarm_timer_change", "globals/gui_nav" },
["listenAsrHints"] = new[] { "$YESNO" }
}
});
Assert.Equal("yes", decision.IntentName);
Assert.Equal("Yes.", decision.ReplyText);
}
[Fact]
public async Task BuildDecisionAsync_AlarmTimerNoneSetPrompt_MapsShortDenialToNoIntent()
{
var service = CreateService();
var decision = await service.BuildDecisionAsync(new TurnContext
{
RawTranscript = "no",
NormalizedTranscript = "no",
Attributes = new Dictionary<string, object?>
{
["listenRules"] = new[] { "clock/alarm_timer_none_set", "globals/global_commands_launch" },
["listenAsrHints"] = new[] { "$YESNO" }
}
});
Assert.Equal("no", decision.IntentName);
Assert.Equal("No.", decision.ReplyText);
}
[Fact] [Fact]
public async Task BuildDecisionAsync_SettingsDownloadPrompt_MapsShortDenialToNoIntent() public async Task BuildDecisionAsync_SettingsDownloadPrompt_MapsShortDenialToNoIntent()
{ {
@@ -486,6 +526,29 @@ public sealed class JiboInteractionServiceTests
Assert.Equal("am", decision.SkillPayload["ampm"]); Assert.Equal("am", decision.SkillPayload["ampm"]);
} }
[Fact]
public async Task BuildDecisionAsync_AlarmValueFollowUp_ParsesCommaSeparatedSpokenDigits()
{
var service = CreateService();
var decision = await service.BuildDecisionAsync(new TurnContext
{
RawTranscript = "7, 44",
NormalizedTranscript = "7, 44",
Attributes = new Dictionary<string, object?>
{
["listenRules"] = new[] { "clock/alarm_set_value" },
["context"] = """{"runtime":{"location":{"iso":"2026-04-26T07:43:00-05:00"}}}"""
}
});
Assert.Equal("alarm_value", decision.IntentName);
Assert.Equal("@be/clock", decision.SkillName);
Assert.Equal("start", decision.SkillPayload!["clockIntent"]);
Assert.Equal("7:44", decision.SkillPayload["time"]);
Assert.Equal("am", decision.SkillPayload["ampm"]);
}
[Fact] [Fact]
public async Task BuildDecisionAsync_SetAlarmWithoutTime_AsksForClarification() public async Task BuildDecisionAsync_SetAlarmWithoutTime_AsksForClarification()
{ {

View File

@@ -648,6 +648,35 @@ public sealed class JiboWebSocketServiceTests
Assert.Equal("am", listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("entities").GetProperty("ampm").GetString()); Assert.Equal("am", listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("entities").GetProperty("ampm").GetString());
} }
[Fact]
public async Task ClientAsr_AlarmValueFollowUp_ParsesCommaSeparatedSpokenDigitsIntoClockStartIntent()
{
await _service.HandleMessageAsync(new WebSocketMessageEnvelope
{
HostName = "neo-hub.jibo.com",
Path = "/listen",
Kind = "neo-hub-listen",
Token = "hub-clock-alarm-comma-followup-token",
Text = """{"type":"LISTEN","transID":"trans-clock-alarm-comma-followup","data":{"rules":["clock/alarm_set_value"]}}"""
});
var replies = await _service.HandleMessageAsync(new WebSocketMessageEnvelope
{
HostName = "neo-hub.jibo.com",
Path = "/listen",
Kind = "neo-hub-listen",
Token = "hub-clock-alarm-comma-followup-token",
Text = """{"type":"CLIENT_ASR","transID":"trans-clock-alarm-comma-followup","data":{"text":"7, 44"}}"""
});
Assert.Equal(4, replies.Count);
using var listenPayload = JsonDocument.Parse(replies[0].Text!);
Assert.Equal("start", listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("intent").GetString());
Assert.Equal("alarm", listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("entities").GetProperty("domain").GetString());
Assert.Equal("7:44", listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("entities").GetProperty("time").GetString());
}
[Fact] [Fact]
public async Task ClientAsr_SetAlarmWithoutTime_RedirectsIntoClockSkillWithoutDefaultingTime() public async Task ClientAsr_SetAlarmWithoutTime_RedirectsIntoClockSkillWithoutDefaultingTime()
{ {
@@ -1063,6 +1092,37 @@ public sealed class JiboWebSocketServiceTests
Assert.Equal("shared/yes_no", listenPayload.RootElement.GetProperty("data").GetProperty("match").GetProperty("rule").GetString()); Assert.Equal("shared/yes_no", listenPayload.RootElement.GetProperty("data").GetProperty("match").GetProperty("rule").GetString());
} }
[Fact]
public async Task ClientAsr_AlarmTimerChangeYesNoPrompt_StripsGlobalRulesAndStaysLocal()
{
await _service.HandleMessageAsync(new WebSocketMessageEnvelope
{
HostName = "neo-hub.jibo.com",
Path = "/listen",
Kind = "neo-hub-listen",
Token = "hub-alarm-change-yesno-token",
Text = """{"type":"LISTEN","transID":"trans-alarm-change-yesno","data":{"rules":["clock/alarm_timer_change","globals/gui_nav","globals/mim_repeat","globals/global_commands_launch"],"asr":{"hints":["$YESNO"]}}}"""
});
var replies = await _service.HandleMessageAsync(new WebSocketMessageEnvelope
{
HostName = "neo-hub.jibo.com",
Path = "/listen",
Kind = "neo-hub-listen",
Token = "hub-alarm-change-yesno-token",
Text = """{"type":"CLIENT_ASR","transID":"trans-alarm-change-yesno","data":{"text":"yes"}}"""
});
Assert.Equal(3, replies.Count);
using var listenPayload = JsonDocument.Parse(replies[0].Text!);
Assert.Equal("yes", listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("intent").GetString());
var rules = listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("rules");
Assert.Single(rules.EnumerateArray());
Assert.Equal("clock/alarm_timer_change", rules[0].GetString());
Assert.Equal("clock/alarm_timer_change", listenPayload.RootElement.GetProperty("data").GetProperty("match").GetProperty("rule").GetString());
}
[Fact] [Fact]
public async Task BufferedAudio_YesNoPromptWithSttFailure_AutoFinalizesAsLocalNoInput() public async Task BufferedAudio_YesNoPromptWithSttFailure_AutoFinalizesAsLocalNoInput()
{ {

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff