fix(ask): show multi-select state on remote asks - #3899
Conversation
The ask tool owns the multi-select loop and re-issues one remote request per toggle, but AskAnswerRequest carried no selection state: Telegram posted an identical prompt each time, so nothing showed that option 1 had been picked. Selection rendering existed only for durable workflow gates, which take a different presentation path. AskAnswerRequest now carries multi plus the selected option labels, the interactive presentation forwards them so the bus publishes selectedOptionIndices and the "(N selected)" prefix, and a presentation that brings its own navigation control keeps it instead of the gate's synthesized Done. Option renumbering now looks past a leading selection marker so pre-numbered deep-interview options render as "1. checked Alpha" rather than "1. checked 1. Alpha". Lore-id: b48e2d17 Confidence: high Scope-risk: narrow Reversibility: easy Tested: arbiter publication after a toggle, ask-tool remote requests across a toggle, daemon rendering of pre-numbered options Not-tested: live Telegram client rendering of the checkbox glyphs
Generation 52 is already claimed by the pre-readiness daemon-child exit diagnostics slice (Yeachan-Heo#3844), which is ahead of this branch in review, so this slice claims 53 instead of guaranteeing a guard failure for whichever PR lands second. Lore-id: 5a3c91d7 Confidence: high Scope-risk: narrow Reversibility: easy Tested: telegram-daemon generation guard against current dev
160159c to
6d1c1b6
Compare
|
Rebased onto current One substantive change beyond the rebase: the daemon generation claim moved 52 → 53. #3844 (pre-readiness daemon-child exit diagnostics) also claims 52 and is ahead of this branch in review, so leaving both at 52 guaranteed a generation-guard failure for whichever landed second. Re-verified on the new base:
Merge order: land #3844 first (52), then this (53). If #3844 is dropped instead, this branch should collapse back to 52. |
|
CI on head Failure (job): Line 19583 is Evidence that it is not this PR:
I do not have rerun rights on this repository ( |
…l-clock `effects.join` counts its deadline from the call, and the held-state race plus its assertion run inside that window before the persistence barrier is released. A 100 ms budget therefore reports `false` for a barrier that did hold as soon as the runner is loaded: shard-1-of-8 failed exactly this assertion on head 6d1c1b6, while the same shard passed on 160159c where every source file on that path is identical. The held half of the contract is proven by the race, not by the deadline, so the deadline only has to outlast the released work. The daemon's own shutdown join uses 1s; 5s here keeps the assertion load-bearing (a barrier that never settles still fails) without timing out on a slow runner. Lore-id: 9b2d5e14 Confidence: high Scope-risk: narrow Reversibility: easy Tested: assertion still fails when the released work never settles; suite green pinned to one core
|
Root cause of the failed shard is confirmed and now fixed at head All three red checks are one failure. It was not this branch's code. On the pre-rebase head It was a wall-clock race in the test. async join(deadlineMs: number): Promise<boolean> {
const expiresAt = Date.now() + deadlineMs;
while (this.#pending.size > 0) {
const remaining = expiresAt - Date.now();
if (remaining <= 0) return false;
...The test called The fix raises that one deadline to 5s and documents why. The contract is unchanged and still load-bearing: the "barrier held" half is proven by the race, not by the deadline, and I verified the assertion still fails ( Verification on this head:
Generation claim is unchanged at 53, so the merge order behind #3844 (52) still holds. |
|
This item is being moved to the Tier 3 review lane. Further review is deferred until after the current emergency stabilization period. — |
What
Make a remote multi-select ask show what is already selected.
AskAnswerRequestgainsmultiandselectedOptions(authoritative option labels).ask.tsfills both on every re-issued remote request, including the per-toggleonRemoteStaterebuild.registerInteractiveAnswerSourceforwards them into the presentation, soPresentationArbiterpublishesselectedOptionIndicesand the(N selected)question prefix that the Telegram daemon already knows how to render (☑/☐).Next/Done) keeps it; only a durable workflow gate, which presents no controls, still gets the synthesized one.1. ☑ Alphainstead of1. ☑ 1. Alpha.Why
In Telegram, a multi-select ask reissues a new message per tap until
Done. Selecting option 1 produced a byte-identical prompt: no marker, no count, no way to tell what had been picked.Selection rendering shipped in daemon generation 33, but only along the durable workflow-gate path (
presentGate→multi: stage_state.multi). The attended ask tool goes throughregisterInteractiveAnswerSource, which hard-codedmulti: false/selectedOptions: [], andAskAnswerRequesthad no field to carry the selection in the first place —onRemoteStateonly passedselectedCount, and only to enable/disable the navigation control.Testing
packages/coding-agent/test/tools/ask.test.ts— the multi-select toggle test now asserts the second remote request carriesmulti: trueandselectedOptions: ["alpha"].packages/coding-agent/test/sdk-host-wiring.test.ts— newPresentationArbitertest: an ask-tool style presentation publishesselectedOptionIndices, the(1 selected)prefix, and keeps its ownNextcontrol.packages/coding-agent/test/notifications-telegram-daemon.test.ts— new render test: pre-numbered options withselectedOptionIndices: [0]render1. ☑ Alpha/2. ☐ Beta.devand pass with this change.bun testfor ask / sdk-ask-answer-source / notifications-html-format / notifications-telegram-reference / sdk-host-wiring / notifications-telegram-daemon — pass.bun --cwd=packages/coding-agent run checkclean;scripts/telegram-daemon-generation-guard.tsreportsv43 required generation bump verified(DAEMON_GENERATION 51 → 52, manifest regenerated with--write-manifest); its 48 guard tests pass.GJC verdict
devbun checkpasses