Skip to content

Commit 351157b

Browse files
Staacksclaude
andcommitted
Record the toggle/dropdown default-over-init divergence
Found while running the store screenshot capture on iOS (plan phase 4): the tone-generator scene, which sets `channels` and `advanced` as element defaults because that is what Android obeys, came out with one channel on iOS, because there the container's `init` wins. The spec already settles which is right - the remark on `default` says it applies only while the buffer is EMPTY and never over a value in it - and iOS implements exactly that. Android's toggle and dropdown build their widget from `defaultValue` with `triggered` already set, so the first write pass clears the buffer and appends the widget's value over whatever `init` put there; the `seedIfEmpty()` path added for this in 93a31966 is simply reached too late. `edit` destroys the init value on that pass too, by writing NaN. So: one entry, status decided with the canonical behaviour quoted from the spec, and `agreement: divergent` on the three attributes it covers. The fix belongs to Android and is written up in the working root's Android-TODO.md. Until then the tone-generator scene says both spellings, identically, which is the only thing that produces that picture on both platforms. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent ca06acd commit 351157b

3 files changed

Lines changed: 71 additions & 7 deletions

File tree

‎inconsistencies.yml‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,51 @@
8989
A non-power-of-two number of input samples yields a different output
9090
length and different values on the two platforms; a power-of-two input
9191
gives identical results on both.
92+
93+
- id: toggle-dropdown-default-overwrites-init
94+
title: A toggle's or dropdown's default against the container's init
95+
area: file-format
96+
status: decided
97+
canonical: >-
98+
The spec already states it, in the remark on the default attribute of
99+
edit, toggle, dropdown and slider: a default is applied whenever the
100+
buffer is EMPTY, and never over a value that is in it. A container's
101+
init therefore wins over a control's default, and an author who writes
102+
both is asking for the init value.
103+
affects:
104+
android: >-
105+
toggle and dropdown overwrite the container regardless. Both start with
106+
their trigger flag set (ExpView.java, toggleElement line 3093 and
107+
dropDownElement line 3244) and build their widget showing defaultValue,
108+
so the first onMayWriteToBuffers pass after the view exists does
109+
clear(false) + append(<what the widget shows>) over whatever init put
110+
there (lines 3176-3183 and 3397-3401). The conditional seedIfEmpty()
111+
right above it (lines 305-313), added in 93a31966 with the message
112+
"never over a value somebody set", is the intended path and is the one
113+
the spec describes - it is simply reached too late to matter. edit is a
114+
third case again: it writes NaN over the init value on that same first
115+
pass and only picks its default up on the next one.
116+
ios: >-
117+
Follows the spec. Experiment.seedInputDefaults() writes an input
118+
element's default only into a buffer whose last value is nil, at load
119+
and at the start of each analysis cycle, and the view modules write
120+
nothing until the user touches them (ExperimentSwitchView.setState,
121+
ExperimentDropdownView, ExperimentEditView). A container with init
122+
keeps its value.
123+
verified: >-
124+
Observed on both platforms with the same file, the store screenshot
125+
system's tone-generator scene (phyphox-docs/screenshots/scenes.yml), which
126+
wants two channels with the advanced controls shown. On Android
127+
(2026-08-31, API 36 emulator) setting only the containers' init produced
128+
one channel and no advanced controls, i.e. the element defaults won; on
129+
iOS (2026-09-01, iPhone 17 Pro Max simulator, Release build of
130+
development) setting only the element defaults produced the same picture,
131+
i.e. the init values won. The scene now sets both, identically, which is
132+
the only spelling that works on both.
133+
summary: >-
134+
An input control's default attribute and its container's init attribute
135+
disagree about which one an experiment starts with. The spec says init
136+
wins - a default fills an empty buffer and never overwrites one - which is
137+
what iOS does; on Android a toggle or dropdown writes its own default over
138+
the container on the first pass after its view is built, so the same file
139+
starts in two different states.

‎screenshots/scenes.yml‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,26 @@ scenes:
146146
init:
147147
f1: [440]
148148
f2: [660]
149+
# Deliberately the same two values again, as containers, because the two
150+
# apps do not agree on which of the two a control obeys (see
151+
# docs/reference/known-inconsistencies.md, `toggle-dropdown-default-
152+
# overwrites-init`). The spec's rule - a `default` applies only while the
153+
# buffer is EMPTY - is what iOS does, so there `init` alone would be
154+
# enough; Android's toggle and dropdown write their `default` over the
155+
# container on the first pass after the view is built, so there `default`
156+
# alone is what works. Saying both, identically, is the only spelling
157+
# that produces this scene on both platforms today and stays correct once
158+
# Android is fixed.
159+
channels: [2]
160+
advanced: [1]
149161
note: >-
150162
Two channels with the advanced controls shown, on the first tab. The
151-
channel count and the advanced switch are set as element DEFAULTS, not as
152-
container init values: both are written by controls in the Settings view
163+
channel count and the advanced switch are written both as element
164+
DEFAULTS and as container init values, because the two apps disagree
165+
about which of those a control obeys at load (see the comment above the
166+
values). Both are controls in the Settings view
153167
(`<dropdown label="Channels" default="1">` and
154-
`<toggle label="Advanced" default="0">`), and a control writes its own
155-
default over the container when the experiment loads. c2 and c2advanced
168+
`<toggle label="Advanced" default="0">`). c2 and c2advanced
156169
are then derived by the file's own analysis (`channels > 1`, and
157170
c2 * advanced), which is what makes the extra controls visible.
158171

‎spec/views.yml‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -917,7 +917,8 @@ elements:
917917
with clearGroup on the data container, which is what it is for.
918918
default: 0.0
919919
summary: Value the field starts with.
920-
agreement: agreed
920+
agreement: divergent
921+
inconsistency: toggle-dropdown-default-overwrites-init
921922
- name: output
922923
parent: edit
923924
since: "1.0"
@@ -1030,7 +1031,8 @@ elements:
10301031
with clearGroup on the data container, which is what it is for.
10311032
default: 0.0
10321033
summary: State the switch starts in.
1033-
agreement: agreed
1034+
agreement: divergent
1035+
inconsistency: toggle-dropdown-default-overwrites-init
10341036

10351037
- name: output
10361038
parent: toggle
@@ -1164,7 +1166,8 @@ elements:
11641166
with clearGroup on the data container, which is what it is for.
11651167
default: 0.0
11661168
summary: Value selected initially.
1167-
agreement: agreed
1169+
agreement: divergent
1170+
inconsistency: toggle-dropdown-default-overwrites-init
11681171
- name: color
11691172
type: color
11701173
rules: [color-invalid-value]

0 commit comments

Comments
 (0)