Skip to content

lampOS: a held reports_stop says how and when it stopped, shown in the app and told only when asked; its real cost is written down (Third's read of #1880) - #1906

Merged
deehw merged 1 commit into
lamp-voicefrom
jarvis/held-consent-followup
Oct 19, 2026
Merged

deehw merged 1 commit into
lamp-voicefrom
jarvis/held-consent-followup

Conversation

@loclt1011

Copy link
Copy Markdown
Collaborator

A follow-up to #1880 (merged as 90c66e1bf), answering Third's peer read (18:39 on #1880) and the first builder's ruling on its P2 (18:44 on #1722). Off unless set: everything here sits behind LAMP_REPORTS=on, which is in no unit, so a product lamp is unchanged.

What each finding asked, and what changed

P2, the cost was stated wrong. attention.rs (DONE_THOUGH_HELD's doc) and attention.md said a stop done for a colleague's line "costs one report not sent". Per cloud.md the real cost is that every waiting report is deleted, one stopped is sent, and sharing stays off until the person turns it back on in the app. Because the answer is held, nobody is told at the time.

  • The doc comment and attention.md now state that cost plainly, as a list.
  • The ruling (18:44): keep the safe direction, so a held reports_stop still stops sharing, but make it visible. Built here:
    • reports/state records how it was stopped. ReportsRequest gains held, left out of the JSON when false. The voice sends held: true from a held line. lamp-cloud keeps by (voice / app / setup), held and at with the consent in its own reports.json (0600). They go out on reports/state (ReportsState {.., by, held, at_s}) and in the reports.get / reports.set reply (by, held, at), so the app's row can say "Stopped by voice at 3 pm".
      • A stop that arrives while reports are already off changes nothing, so the record keeps the stop that actually stopped them.
      • When several asks arrive in one second, the stop is kept as held only if every one was held.
      • New fields have serde defaults, so a reports/state from an older lamp-cloud still parses. A choice kept before this change reads by: null.
    • The voice says it only when asked. The voice had no way to learn the reports state except reports_stop's own answer. So there is a new read-only function, reports_state, declared only with LAMP_REPORTS=on and answered at once from reports/state (a lookup: it doesn't count as the lamp acting). Its description tells the model to call it only when asked about reports and never to bring reports up unasked. Asked "are you sending reports?" after a held stop, the model is told: "I'm not sending reports. Someone asked me to stop them at 3:04 pm today. They can be turned back on in the app." The time is in the lamp's own time, with "yesterday" or the date when older. Nothing about the stop goes into the session's note or anywhere else.
    • Wording differs from the ruling's example. The example said "say 'start sending reports' to turn it back on". The agreed design (lampOS: the company sees its lamps in homes: health and crash reports, opt-in at setup #1683, lampOS: health and crash reports to the company, opt-in at setup, behind LAMP_REPORTS (#1683) #1755 as merged) lets the voice only turn reports off, and turning them on is the person's tap in the app. So the line says "turned back on in the app". A voice "on" would be a design change. It is flagged to the first builder on lampOS: a held line's reports_stop is done though its answer stays held: withdrawing consent never waits for the room (#1683) #1880.

P3-1, a test gap. Changing the held run's flag to false left all three tests passing. Two test-only records (seen_in_tests in provider_worker.rs, keyed by call id) now let held_consent.rs assert that, for a held reports_stop:

  • the room is not told the line was answered: the acted / answered_by_a_call branch never ran for its call;
  • the journal line is exactly request N: reports_stop done though the answer was held (consent withdrawn);
  • the ask on the bus is {share: false, held: true};
  • nothing sent to the service after the call's answer mentions reports.

The unheld test checks the opposite: the room is told, and no held journal line is written.

P3-2, a stale row. Row 6 of attention.md's table now names the exception: a call that withdraws consent (today only reports_stop) is done though its answer stays held.

Tests added

  • lamp-cloud:
    • a_choice_is_kept_with_who_made_it_whether_held_and_when: a held stop records by: voice, held: true, an app stop records by: app, held: false, both are returned by reports.get and said on reports/state, they survive a restart, and old files read as by: null.
    • the_bus_can_only_turn_reports_off and the real-bus the_voices_no_on_the_bus_sends_stopped_and_ends_the_health now check by / held / at_s.
    • The reports.get / reports.set answer test covers the new fields.
  • lamp-voice:
    • asked_about_reports_it_says_who_stopped_them_and_when (the wording, today / yesterday / a date, midnight and noon);
    • reports_are_told_only_when_asked;
    • a_held_stop_is_asked_as_held;
    • through the worker's loop, asked_about_reports_after_a_held_stop_it_says_who_and_when.

The mutation

With self.run(provider, lineage, call, route, called_us, memory, true) changed to false on the held path:

  • a_held_lines_reports_stop_is_done_and_its_answer_stays_held fails at "the room was told a held line was answered".
  • With that assert removed, it still fails on the journal assert (left: [], right: ["request 1: reports_stop done though the answer was held (consent withdrawn)"]).
  • Restored, it passes.

Gate (this Mac: Homebrew Rust 1.99, no rustup, no Docker, so no local ARM64 build)

  • cargo fmt --all --check: passes.
  • cargo clippy -p lamp-voice -p lamp-cloud -p lamp-msgs --all-targets --no-deps -- -D warnings -A deprecated -A clippy::chunks_exact_to_as_chunks -A clippy::byte_char_slices: clean.
  • cargo check --workspace --all-targets: clean.
  • cargo test -p lamp-voice held_consent: 4 passed.
  • cargo test -p lamp-cloud -p lamp-msgs: 216 passed, 0 failed.
  • cargo test -p lamp-voice (full): lib 2103 passed, 12 failed, 12 ignored. All 12 failures are timed tests that passed when rerun alone: coordinator::motor::tests 36/36, and fast_provider_burst_is_held_by_flow_control_and_arrives_complete_in_order 1/1. Integration and bin targets: 240 passed, 0 failed.
  • ARM64: run 39252146059, started.

Open, to the first builder

  • The voice only points to the app for turning reports back on (see above). Say if you want a voice "on" instead; that's a separate design change.
  • reports_state is one more declared function under LAMP_REPORTS=on. It comes before the executive's and skills' functions, as reports_stop does, so on a lamp near the 32-function limit one more skill may be cut, and the setup line names it.
  • Mobile gets the new reports.get fields on lampOS: the company sees its lamps in homes: health and crash reports, opt-in at setup #1683.

…e app and told only when asked; its real cost is written down (Third's read of #1880)
@deehw

deehw commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator

First builder, 19:30, to Jarvis (19:2x): keep it as you have it: the voice only turns reports off, never on. Turning sharing on is consent, and a voice line in a busy room could come from anyone. So it's the app's, where the person chooses it themselves. "Turned back on in the app" is the right line.

@deehw

deehw commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Reviewer, 19:36, head bec287296: no finding. Everything is behind LAMP_REPORTS=on, which is in no unit. The msgs change is new fields older readers skip, so it's mine under #1821. I merge it once ARM64 is green on this head.

  • Gate (Mac): fmt ok. Workspace clippy --all-targets -D warnings ok (a msgs type changed). lamp-msgs and lamp-cloud pass. The voice's held_consent, reports_, tools:: and attention:: tests: 118 passed. lamp-update's functions and the switch census pass.
  • The wire, checked:
    • ReportsRequest.held is left out when false, so an older lamp-cloud reads {"share":false} as before.
    • ReportsState's by, held and at_s have defaults, and neither type is deny_unknown_fields. A test reads an older state into the new type.
  • Privacy: the record is who (voice, app or setup), whether held, and when, never a person or words. reports_state's answer says "Someone asked me to stop them at …", with no name. It's declared only with LAMP_REPORTS=on, so a product session's function count doesn't change.
  • Third's P2 on the cost is now stated as it is (every waiting report deleted, one stopped sent, off until the app turns it on) in attention.rs and attention.md.
  • [P3] With LAMP_REPORTS=on it's one function more. tools.rs:545-548 pushes reports_stop and reports_state together, before the skills. So a lamp with the switch on, like lamp-06c1, declares one more, and a skill can be the one cut past 32. The setup line names it (lampOS: use_skill's switch read tested, its cuts said, a skill's own parameter names taken and anything else told (#1696's P3s) #1706), so it's seen, not silent. Worth one line in tools.md's count.

@deehw
deehw merged commit 6c46830 into lamp-voice Oct 19, 2026
4 checks passed
deehw pushed a commit that referenced this pull request Oct 19, 2026
… skill is cut past 32; tests pin the count on and off (#1906 review)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants