Custom tool demo — set_sleep_time

One natural-language line sets both bedtime and wake-up via a composite tool-call — the interaction-mode authoring records it as a menu-tree edge, and the same test replays as a generated on-device regression that asserts the value. Samsung Health · SM-S928N (test rig, dummy data).
on-device regression: 1/1 PASS · bedtime 9:30 3-family review: unanimous SAFE (3 rounds, 11 findings fixed)

The value change — before → after (one NL line)

BEFORE — editor default
BEFORE — editor default11:00 PM / 7:00 AM · "Sleep time: 8 hours"
set_sleep_time
"set the sleep bedtime to 9:30 PM and the wake-up time to 11:20 AM"
AFTER — one set_sleep_time call
AFTER — one set_sleep_time call9:30 PM / 11:20 AM · "Sleep time: 13 hours 50 minutes"

The editor opens at its default (11:00 PM / 7:00 AM). One set_sleep_time call moves it to 9:30 PM / 11:20 AM — both fields, from one NL line. The clock arc and the "Sleep time" total both change; the deterministic :verify asserts bedtime = 9:30.

The pipeline — one authored test, three uses

① NL RUN

"set the sleep bedtime to 9:30 PM and the wake-up time to 11:20 AM" → the model emits set_sleep_time(bedtime 9:30, wakeup 11:20) — one line, both fields.

② INTERACTION TC

The authoring walk records the tool-call as a menu-tree edge (action.class = set_sleep_time + both time args), then :verify value "9:30" bakes a deterministic value proof.

③ REGRESSION

--from-feature sleep code-gens Kotlin: SleepTimeHelper.setSleepTime(...) + assertNotNull(...text("9:30"))connectedAndroidTest passes on-device.

② Interaction mode — the device walkthrough (authored golden SH-SLEEP-0012)

1 · open Samsung Health
1 · open Samsung HealthCold-launched Samsung Health Home dashboard.
2 · tap Quick add
2 · tap Quick addThe Quick add sheet — "Sleep time" sits under Log manually.
3 · tap Sleep time
3 · tap Sleep timeSleep clock-face editor opens at its default: 11:00 PM / 7:00 AM (before the tool).
4 · set_sleep_time + :verify "9:30"  ← the one-line tool-call
4 · set_sleep_time + :verify "9:30" ← the one-line tool-callONE NL line set BOTH (9:30 PM / 11:20 AM); the deterministic :verify then asserts bedtime = 9:30 on this screen.
5 · tap Save
5 · tap SaveThe Save button is tapped; the editor stays on 9:30 / 11:20 (Save does not navigate away). The record's commit is not separately verified in this capture (a soft waypoint).

③ Generated regression (a pure function of the authored capture-log)

@Test fun sh_sleep_0012() {
    // ... launch → Quick add → Sleep time (editor opens at 11:00 PM / 7:00 AM) ...
    SleepTimeHelper.setSleepTime(device, 9, 30, "pm", 11, 20, "am")   // ← the composite tool: both times
    // value proof (:verify value "9:30")
    var found5 = device.wait(Until.findObject(
        By.res(PKG, "sleep_top_center_duration_bedtime").text("9:30")), NAV)
    assertNotNull(found5)                                             // ← asserts bedtime text == 9:30
    device.findObject(By.res(PKG, "textview").text("Save")).click()
}

The composite tool code-gens to a hand-written SleepTimeHelper (UiAutomator mirror of the runtime tool). The value proof rides on the paired :verify — a CI gate now enforces that every set_sleep_time step ships with a time-shaped value-verify (no vacuous-green).

Why the value verify is deterministic (not an AI judgment)

The first live replay set 9:00, not 9:30 — the byhand helper committed the hour but not the reflowed minute (fixed by typing digits as key events). The deterministic :verify caught it; an AI arrival judgment would have hallucinated past it. That is the point: interaction-mode authoring is 100% reliability-critical, the regression run is allowed to fail — but it must fail loud on a real value miss.

Both times are set — the value proof anchors on bedtime

The one NL line sets both bedtime (9:30 PM) and wake-up (11:20 AM) — both visible in the AFTER frame ("Sleep time: 13 hours 50 minutes"); the byhand helper is called with both (setSleepTime(device, 9, 30, "pm", 11, 20, "am")). The deterministic value proof asserts bedtime only (sleep_top_center_duration_bedtime.text("9:30")) — bedtime commits reliably, so it is the stable replay anchor. Wake-up is passed but not separately asserted: on some replays the SH editor keeps a current-time-derived wake-up (an app policy), so anchoring the assertion on bedtime is deliberate — not a defect.

Note — Save does not navigate; the value proof is the before → after pair

Tapping Save keeps you on the clock-face editor (frame 5 stays on 9:30) — the editor does not navigate away, and this capture does not separately verify that the record committed (the Save step is a soft waypoint, not a hard assertion). So set_sleep_time is honestly a self-edge in the menu tree — a value-set that does not change screens. The proof of the value change is the before → after pair above, not a screen transition.

Generated by gen_sleep_demo_report.py from the committed blackboard + screen sidecars (device screenshots embedded; test rig with dummy data).